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..7b9f87c079e3a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,745 @@ +############################################################# +# 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 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: {} + 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 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: 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-cargo + env: + SCRIPT: python x.py test src/tools/cargotest src/tools/cargo + RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-lld" + VCVARS_BAT: vcvars64.bat + NO_DEBUG_ASSERTIONS: 1 + 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 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-fallible: + name: auto-fallible + 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: 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 + 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 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..15ca403456a4e 100644 --- a/.mailmap +++ b/.mailmap @@ -44,11 +44,13 @@ Brian Anderson Brian Anderson Brian Dawn Brian Leibig Brian Leibig +Camelid <37223377+camelid@users.noreply.github.com> Carl-Anton Ingmarsson Carol (Nichols || Goulding) <193874+carols10cents@users.noreply.github.com> Carol (Nichols || Goulding) Carol (Nichols || Goulding) Carol Willing +Charles Lew CrLF0710 Chris C Cerami Chris C Cerami Chris Pressey Chris Thorn Chris Thorn @@ -69,6 +71,8 @@ David Manescu David Ross Derek Chiang Derek Chiang (Enchi Jiang) Diggory Hardy Diggory Hardy +Donough Liu +Donough Liu DingMing Liu Dustin Bensing Dylan Braithwaite Dzmitry Malyshau @@ -133,7 +137,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 @@ -149,11 +153,15 @@ Kang Seonghoon Keegan McAllister Kevin Butler Kyeongwoon Lee +Kyle J Strand +Kyle J Strand +Kyle J Strand +Kyle J Strand Laurențiu Nicola Lee Jeffery Lee Jeffery Lee Wondong Lennart Kudling -Léo Testard +Léo Testard Lindsey Kuper Lindsey Kuper Luke Metz @@ -258,6 +266,7 @@ Tim Chevalier Tim JIANG Tim Joseph Dumol Torsten Weber +Trevor Spiteri Ty Overby Ulrik Sverdrup bluss Ulrik Sverdrup bluss 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 42049da8e6add..b54566e7176b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,9 +8,9 @@ checksum = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" [[package]] name = "aho-corasick" -version = "0.7.3" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" dependencies = [ "memchr", ] @@ -32,7 +32,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e266e1f4be5ffa05309f650e2586fe1d3ae6034eb24025a7ae1dfecc330823a" dependencies = [ "html5ever", - "lazy_static 1.4.0", + "lazy_static", "maplit", "matches", "tendril", @@ -48,6 +48,12 @@ dependencies = [ "ansi_term", ] +[[package]] +name = "annotate-snippets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78ea013094e5ea606b1c05fe35f1dd7ea1eb1ea259908d040b25bd5ec677ee5" + [[package]] name = "ansi_term" version = "0.11.0" @@ -69,14 +75,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1025aeae2b664ca0ea726a89d574fe8f4e77dd712d443236ad1de00379450cf6" -[[package]] -name = "arena" -version = "0.0.0" -dependencies = [ - "rustc_data_structures", - "smallvec 1.0.0", -] - [[package]] name = "argon2rs" version = "0.2.5" @@ -87,12 +85,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" @@ -104,12 +96,12 @@ dependencies = [ [[package]] name = "atty" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ + "hermit-abi", "libc", - "termion", "winapi 0.3.8", ] @@ -119,11 +111,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.44" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536" +checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" dependencies = [ "backtrace-sys", "cfg-if", @@ -135,9 +133,9 @@ dependencies = [ [[package]] name = "backtrace-sys" -version = "0.1.32" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399" dependencies = [ "cc", "compiler_builtins", @@ -181,11 +179,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", ] @@ -199,9 +208,10 @@ dependencies = [ "filetime", "getopts", "ignore", - "lazy_static 1.4.0", + "lazy_static", "libc", "num_cpus", + "opener", "pretty_assertions", "serde", "serde_json", @@ -234,9 +244,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" @@ -270,18 +280,9 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010" -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -dependencies = [ - "ppv-lite86", -] - [[package]] name = "cargo" -version = "0.44.0" +version = "0.47.0" dependencies = [ "anyhow", "atty", @@ -292,15 +293,13 @@ dependencies = [ "clap", "core-foundation 0.7.0", "crates-io", - "crossbeam-channel", - "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", @@ -311,7 +310,7 @@ dependencies = [ "ignore", "im-rc", "jobserver", - "lazy_static 1.4.0", + "lazy_static", "lazycell", "libc", "libgit2-sys", @@ -325,9 +324,9 @@ dependencies = [ "pretty_env_logger", "remove_dir_all", "rustc-workspace-hack", - "rustfix 0.5.0", + "rustfix", "same-file", - "semver", + "semver 0.10.0", "serde", "serde_ignored", "serde_json", @@ -338,11 +337,25 @@ dependencies = [ "termcolor", "toml", "unicode-width", + "unicode-xid 0.2.0", "url 2.1.0", "walkdir", "winapi 0.3.8", ] +[[package]] +name = "cargo-miri" +version = "0.1.0" +dependencies = [ + "cargo_metadata 0.9.1", + "directories", + "rustc-workspace-hack", + "rustc_version", + "serde", + "serde_json", + "vergen", +] + [[package]] name = "cargo-platform" version = "0.1.1" @@ -364,7 +377,7 @@ dependencies = [ "flate2", "git2", "glob", - "lazy_static 1.4.0", + "lazy_static", "remove_dir_all", "serde_json", "tar", @@ -378,7 +391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "929766d993a2fde7a0ae962ee82429069cd7b68839cd9375b98efd719df65d3a" dependencies = [ "failure", - "semver", + "semver 0.9.0", "serde", "serde_derive", "serde_json", @@ -386,11 +399,11 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2d1617e838936c0d2323a65cc151e03ae19a7678dd24f72bccf27119b90a5d" +checksum = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202" dependencies = [ - "semver", + "semver 0.9.0", "serde", "serde_derive", "serde_json", @@ -402,9 +415,9 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.50" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" dependencies = [ "jobserver", ] @@ -419,6 +432,54 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "chalk-derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9bd01eab87277d973183a1d2e56bace1c11f8242c52c20636fb7dddf343ac9" +dependencies = [ + "proc-macro2 1.0.3", + "quote 1.0.2", + "syn 1.0.11", + "synstructure", +] + +[[package]] +name = "chalk-engine" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c7a637c3d17ed555aef16e16952a5d1e127bd55178cc30be22afeb92da90c7d" +dependencies = [ + "chalk-derive", + "chalk-ir", + "rustc-hash", +] + +[[package]] +name = "chalk-ir" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595e5735ded16c3f3dc348f7b15bbb2521a0080b1863cac38ad5271589944670" +dependencies = [ + "chalk-derive", + "lazy_static", +] + +[[package]] +name = "chalk-solve" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d9d938139db425867a30cc0cfec0269406d8238d0571d829041eaa7a8455d11" +dependencies = [ + "chalk-derive", + "chalk-engine", + "chalk-ir", + "ena", + "itertools 0.9.0", + "petgraph", + "rustc-hash", +] + [[package]] name = "chrono" version = "0.4.6" @@ -443,24 +504,22 @@ dependencies = [ "textwrap", "unicode-width", "vec_map", - "yaml-rust", + "yaml-rust 0.3.5", ] [[package]] name = "clippy" version = "0.0.212" dependencies = [ - "cargo_metadata 0.9.0", + "cargo_metadata 0.9.1", "clippy-mini-macro-test", "clippy_lints", "compiletest_rs", "derive-new", - "git2", - "lazy_static 1.4.0", - "regex", + "lazy_static", "rustc-workspace-hack", "rustc_tools_util 0.2.0", - "semver", + "semver 0.9.0", "serde", "tempfile", "tester", @@ -474,17 +533,18 @@ version = "0.2.0" name = "clippy_lints" version = "0.0.212" dependencies = [ - "cargo_metadata 0.9.0", + "cargo_metadata 0.9.1", "if_chain", "itertools 0.9.0", - "lazy_static 1.4.0", - "matches", - "pulldown-cmark 0.7.0", + "lazy_static", + "pulldown-cmark 0.7.1", "quine-mc_cluskey", + "quote 1.0.2", "regex-syntax", - "semver", + "semver 0.9.0", "serde", - "smallvec 1.0.0", + "smallvec 1.4.0", + "syn 1.0.11", "toml", "unicode-normalization", "url 2.1.0", @@ -530,11 +590,13 @@ dependencies = [ [[package]] name = "colored" -version = "1.6.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" dependencies = [ - "lazy_static 0.2.11", + "atty", + "lazy_static", + "winapi 0.3.8", ] [[package]] @@ -557,9 +619,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.25" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "438ac08ddc5efe81452f984a9e33ba425b00b31d1f48e6acd9e2210aa28cc52e" +checksum = "7bc4ac2c824d2bfc612cba57708198547e9a26943af0632aff033e0693074d5c" dependencies = [ "cc", "rustc-std-workspace-core", @@ -572,12 +634,12 @@ dependencies = [ "diff", "env_logger 0.7.1", "getopts", - "lazy_static 1.4.0", + "lazy_static", "libc", "log", "miow 0.3.3", "regex", - "rustfix 0.5.0", + "rustfix", "serde", "serde_json", "walkdir", @@ -586,9 +648,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", @@ -597,7 +659,7 @@ dependencies = [ "log", "miow 0.3.3", "regex", - "rustfix 0.4.6", + "rustfix", "serde", "serde_derive", "serde_json", @@ -681,7 +743,7 @@ checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" [[package]] name = "crates-io" -version = "0.31.0" +version = "0.31.1" dependencies = [ "anyhow", "curl", @@ -707,7 +769,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]] @@ -729,7 +791,7 @@ dependencies = [ "arrayvec", "cfg-if", "crossbeam-utils 0.6.5", - "lazy_static 1.4.0", + "lazy_static", "memoffset", "scopeguard", ] @@ -750,18 +812,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" dependencies = [ "cfg-if", - "lazy_static 1.4.0", + "lazy_static", ] [[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", + "lazy_static", ] [[package]] @@ -776,6 +838,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" @@ -849,13 +921,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]] @@ -883,9 +955,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", ] @@ -924,9 +996,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", @@ -957,7 +1029,7 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a99a310cd1f9770e7bf8e48810c7bcbb0e078c8fb23a8c7bcf0da4c2bf61a455" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", "regex", "serde", "serde_derive", @@ -968,9 +1040,9 @@ dependencies = [ [[package]] name = "ena" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8944dc8fa28ce4a38f778bd46bf7d923fe73eed5a439398507246c8e017e6f36" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" dependencies = [ "log", ] @@ -1027,6 +1099,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" @@ -1039,14 +1119,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]] @@ -1057,9 +1137,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", @@ -1067,6 +1147,12 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + [[package]] name = "flate2" version = "1.0.12" @@ -1080,14 +1166,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "fmt_macros" -version = "0.0.0" -dependencies = [ - "rustc_lexer", - "rustc_span", -] - [[package]] name = "fnv" version = "1.0.6" @@ -1119,16 +1197,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" @@ -1204,9 +1272,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", ] @@ -1235,9 +1303,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.12.0" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26e07ef27260a78f7e8d218ebac2c72f2c4db50493741b190b6e8eade1da7c68" +checksum = "e1e02a51cd90229028c9bd8be0a0364f85b6b3199cccaa0ef39005ddbd5ac165" dependencies = [ "bitflags", "libc", @@ -1250,9 +1318,9 @@ dependencies = [ [[package]] name = "git2-curl" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1754ec4170e7dcaf9bb43743bb16eddb8d827b2e0291deb6f220a6e16fe46a" +checksum = "502d532a2d06184beb3bc869d4d90236e60934e3382c921b203fa3c33e212bd7" dependencies = [ "curl", "git2", @@ -1279,10 +1347,6 @@ dependencies = [ "regex", ] -[[package]] -name = "graphviz" -version = "0.0.0" - [[package]] name = "h2" version = "0.1.25" @@ -1303,37 +1367,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", @@ -1350,9 +1402,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.1" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22b8f315b98f415780ddbe9163c7dbbc5a07225b6d102ace1d8aeef85775140" +checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909" dependencies = [ "compiler_builtins", "libc", @@ -1524,21 +1576,21 @@ checksum = "522daefc3b69036f80c7d2990b28ff9e0471c683bad05ca258e0a01dd22c5a1e" dependencies = [ "crossbeam-channel", "globset", - "lazy_static 1.4.0", + "lazy_static", "log", "memchr", "regex", "same-file", - "thread_local 1.0.1", + "thread_local", "walkdir", "winapi-util", ] [[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", @@ -1561,7 +1613,7 @@ dependencies = [ "clap", "failure", "flate2", - "lazy_static 1.4.0", + "lazy_static", "num_cpus", "rayon", "remove_dir_all", @@ -1586,15 +1638,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e5b386aef33a1c677be65237cb9d32c3f3ef56bd035949710c4bb13083eb053" -[[package]] -name = "itertools" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.8.0" @@ -1720,7 +1763,7 @@ checksum = "5b31c9b90731276fdd24d896f31bb10aecf2e5151733364ae81123186643d939" dependencies = [ "jsonrpc-core", "log", - "parking_lot 0.10.0", + "parking_lot 0.10.2", "serde", ] @@ -1733,7 +1776,7 @@ dependencies = [ "bytes", "globset", "jsonrpc-core", - "lazy_static 1.4.0", + "lazy_static", "log", "tokio", "tokio-codec", @@ -1750,12 +1793,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" - [[package]] name = "lazy_static" version = "1.4.0" @@ -1770,18 +1807,18 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" dependencies = [ "rustc-std-workspace-core", ] [[package]] name = "libgit2-sys" -version = "0.11.0+0.99.0" +version = "0.12.7+1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5d1459353d397a029fb18862166338de938e6be976606bd056cf8f1a912ecf" +checksum = "bcd07968649bcb7b9351ecfde53ca4d27673cccfdf57c84255ec18710f3153e0" dependencies = [ "cc", "libc", @@ -1831,11 +1868,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", ] @@ -1855,7 +1898,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19af41f0565d7c19b2058153ad0b42d4d5ce89ec4dbf06ed6741114a8b63e7cd" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", ] [[package]] @@ -1934,11 +1977,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", @@ -1948,7 +2002,7 @@ dependencies = [ "error-chain", "handlebars", "itertools 0.8.0", - "lazy_static 1.4.0", + "lazy_static", "log", "memchr", "open", @@ -1983,7 +2037,7 @@ dependencies = [ "rayon", "regex", "reqwest", - "semver", + "semver 0.9.0", "serde", "serde_derive", "serde_json", @@ -2004,9 +2058,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "memmap" @@ -2133,22 +2187,17 @@ name = "miri" version = "0.1.0" dependencies = [ "byteorder", - "cargo_metadata 0.9.0", "colored", "compiletest_rs", - "directories", "env_logger 0.7.1", "getrandom", "hex 0.4.0", + "libc", "log", - "num-traits", "rand 0.7.3", "rustc-workspace-hack", "rustc_version", - "serde", - "serde_json", "shell-escape", - "vergen", ] [[package]] @@ -2157,7 +2206,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", "libc", "log", "openssl", @@ -2221,6 +2270,15 @@ name = "once_cell" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6a04cb71e910d0034815600180f62a95bf6e67942d7ab52a166a68c7d7e9cd0" +dependencies = [ + "parking_lot 0.9.0", +] + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "open" @@ -2246,7 +2304,7 @@ dependencies = [ "bitflags", "cfg-if", "foreign-types", - "lazy_static 1.4.0", + "lazy_static", "libc", "openssl-sys", ] @@ -2259,20 +2317,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", @@ -2286,6 +2344,15 @@ 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" @@ -2347,12 +2414,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]] @@ -2372,15 +2439,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", ] @@ -2417,28 +2484,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.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "phf" version = "0.7.24" @@ -2485,9 +2562,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", @@ -2496,9 +2573,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "precomputed-hash" @@ -2508,12 +2585,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]] @@ -2580,6 +2659,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" @@ -2588,7 +2676,7 @@ checksum = "9bf259a81de2b2eb9850ec990ec78e6a25319715584fd7652b9b26f96fcb1510" dependencies = [ "error-chain", "idna 0.2.0", - "lazy_static 1.4.0", + "lazy_static", "regex", "url 2.1.0", ] @@ -2607,9 +2695,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", @@ -2624,9 +2712,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" @@ -2654,25 +2742,25 @@ dependencies = [ [[package]] name = "racer" -version = "2.1.31" +version = "2.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ff33fa15ac0384376741d16ddb05a65263dde4e2c5d0f7a9f3747db788764aa" +checksum = "421174f19211ba9e5fda34aa0cbc292188aae8e0cfbff4aebbae23f1a416bfb3" dependencies = [ "bitflags", "clap", "derive_more", "env_logger 0.7.1", "humantime 2.0.0", - "lazy_static 1.4.0", + "lazy_static", "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]] @@ -2702,7 +2790,7 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom", "libc", - "rand_chacha 0.2.1", + "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc 0.2.0", ] @@ -2719,11 +2807,11 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "c2-chacha", + "ppv-lite86", "rand_core 0.5.1", ] @@ -2846,7 +2934,7 @@ dependencies = [ "crossbeam-deque", "crossbeam-queue", "crossbeam-utils 0.6.5", - "lazy_static 1.4.0", + "lazy_static", "num_cpus", ] @@ -2865,15 +2953,6 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -dependencies = [ - "redox_syscall", -] - [[package]] name = "redox_users" version = "0.3.0" @@ -2888,25 +2967,21 @@ dependencies = [ [[package]] name = "regex" -version = "1.1.6" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58" +checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local 0.3.6", - "utf8-ranges", + "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.6" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" -dependencies = [ - "ucd-util", -] +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" [[package]] name = "remote-test-client" @@ -2975,7 +3050,7 @@ dependencies = [ "home", "itertools 0.8.0", "jsonrpc-core", - "lazy_static 1.4.0", + "lazy_static", "log", "lsp-codec", "lsp-types", @@ -3009,13 +3084,13 @@ dependencies = [ [[package]] name = "rls-analysis" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0d208ad66717501222c74b42d9e823a7612592e85ed78b04074c8f58c0be0a" +checksum = "534032993e1b60e5db934eab2dde54da7afd1e46c3465fddb2b29eb47cb1ed3a" dependencies = [ "derive-new", "fst", - "itertools 0.7.8", + "itertools 0.8.0", "json", "log", "rls-data", @@ -3063,9 +3138,9 @@ dependencies = [ [[package]] name = "rls-span" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1cb4694410d8d2ce43ccff3682f1c782158a018d5a9a92185675677f7533eb3" +checksum = "f2e9bed56f6272bd85d9d06d1aaeef80c5fddc78a82199eb36dceb5f94e7d934" dependencies = [ "serde", ] @@ -3094,274 +3169,286 @@ dependencies = [ ] [[package]] -name = "rustc" -version = "0.0.0" +name = "rustc-ap-rustc_arena" +version = "664.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6683b49209f8b132bec33dc6b6c8f9958c8c94eb3586d4cb495e092b61c1da" 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_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-rustc_ast" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea82fa3d9a8add7422228ca1a2cbba0784fa8861f56148ff64da08b3c7921b03" +checksum = "5b21784d92fb2d584800f528866f00fe814f73abda794f406bfd1fbb2f1ca7f7" dependencies = [ + "bitflags", + "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_serialize", + "rustc-ap-rustc_span", + "scoped-tls", + "smallvec 1.4.0", ] [[package]] -name = "rustc-ap-graphviz" -version = "642.0.0" +name = "rustc-ap-rustc_ast_passes" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "638d0b2b3bcf99824e0cb5a25dbc547b61dc20942e11daf6a97e981918aa18e5" +checksum = "820c46fde7ef1df0432073090d775f097b7279ca75ea34ba954081ce4b884d4c" +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 = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38bab04dd676dee6d2f9670506a18c31bfce38bf7f8420aa83eb1140ecde049" +checksum = "013db7dd198fe95962d2cefa5bd0b350cf2028af77c169b17b4baa9c3bbf77d1" dependencies = [ "log", - "rustc-ap-rustc_data_structures", + "rustc-ap-rustc_ast", "rustc-ap-rustc_span", - "rustc-ap-syntax", + "rustc-ap-rustc_target", ] [[package]] name = "rustc-ap-rustc_attr" -version = "642.0.0" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b843ba8b1ed43739133047673b9f6a54d3b3b4d328d69c6ea89ff971395f35" +checksum = "35b5a85c90eb341eec543600ffdd9e262da5ea72a73a23ae4ca2f4ab8cd1a188" dependencies = [ + "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", "rustc-ap-rustc_feature", "rustc-ap-rustc_macros", + "rustc-ap-rustc_serialize", "rustc-ap-rustc_session", "rustc-ap-rustc_span", - "rustc-ap-serialize", - "rustc-ap-syntax", - "smallvec 1.0.0", + "version_check", ] [[package]] name = "rustc-ap-rustc_data_structures" -version = "642.0.0" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc3d1c6d0a80ab0c1df76405377cec0f3d5423fb5b0953a8eac70a2ad6c44df2" +checksum = "b92e4c6cb6c43ee9031a71709dc12853b358253c2b41d12a26379994fab625e0" dependencies = [ "bitflags", "cfg-if", - "crossbeam-utils 0.6.5", + "crossbeam-utils 0.7.2", "ena", "indexmap", "jobserver", - "lazy_static 1.4.0", + "lazy_static", + "libc", "log", "measureme", - "parking_lot 0.9.0", - "rustc-ap-graphviz", + "once_cell", + "parking_lot 0.10.2", + "rustc-ap-rustc_graphviz", "rustc-ap-rustc_index", - "rustc-ap-serialize", + "rustc-ap-rustc_serialize", "rustc-hash", "rustc-rayon", "rustc-rayon-core", - "smallvec 1.0.0", + "smallvec 1.4.0", "stable_deref_trait", + "stacker", "winapi 0.3.8", ] [[package]] name = "rustc-ap-rustc_errors" -version = "642.0.0" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4909a1eca29331332257230f29120a8ff68c9e37d868c564fcd599e430cf8914" +checksum = "6b0aa79423260c1b9e2f856e144e040f606b0f5d43644408375becf9d7bcdf86" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.8.0", "atty", "log", "rustc-ap-rustc_data_structures", + "rustc-ap-rustc_serialize", "rustc-ap-rustc_span", - "rustc-ap-serialize", "termcolor", "termize", "unicode-width", "winapi 0.3.8", ] +[[package]] +name = "rustc-ap-rustc_expand" +version = "664.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07d76ba2a1b7d4325a2ed21d6345ccebd89ddc6666a1535a6edd489fb4cbc11" +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_serialize", + "rustc-ap-rustc_session", + "rustc-ap-rustc_span", + "smallvec 1.4.0", +] + [[package]] name = "rustc-ap-rustc_feature" -version = "642.0.0" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63ab887a181d795cf5fd3edadf367760deafb90aefb844f168ab5255266e3478" +checksum = "1bbd625705c1db42a0c7503736292813d7b76ada5da20578fb55c63228c80ab5" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_span", ] [[package]] name = "rustc-ap-rustc_fs_util" -version = "642.0.0" +version = "664.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cca6e2942fa0b059c582437ead666d5bcf20fa7c242599e2bbea9b609f29ae" + +[[package]] +name = "rustc-ap-rustc_graphviz" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70814116df3c5fbec8f06f6a1d013ca481f620fd22a9475754e9bf3ee9ba70d8" +checksum = "13d6a029b81f5e02da85763f82c135507f278a4a0c776432c728520563059529" [[package]] name = "rustc-ap-rustc_index" -version = "642.0.0" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1bf1d3cf3d119d41353d6fd229ef7272d5097bc0924de021c0294bf86d48bf" +checksum = "bae50852d303e230b2781c994513788136dc6c2fe4ebe032959f0b990a425767" dependencies = [ - "rustc-ap-serialize", - "smallvec 1.0.0", + "rustc-ap-rustc_serialize", + "smallvec 1.4.0", ] [[package]] name = "rustc-ap-rustc_lexer" -version = "642.0.0" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cda21a32cebdc11ec4f5393aa2fcde5ed1b2f673a8571e5a4dcdf07e4ae9cac" +checksum = "b7186e74aa2d31bf0e2454325fefcdf0a3da77d9344134592144b9e40d45b15d" dependencies = [ "unicode-xid 0.2.0", ] [[package]] name = "rustc-ap-rustc_macros" -version = "642.0.0" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c47b48ea51910ecfd853c9248a9bf4c767bc823449ab6a1d864dff65fbae16" +checksum = "4fc1add04e9d2301164118660ee0bc3266e9a7b1973fc2303fdbe002a12e5401" 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 = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd88e89cd5b5d28dcd3a347a3d534c08627d9455570dc1a2d402cb8437b9d30" +checksum = "9cd7fc4968bd60084f2fa4f280fa450b0cf98660a7983d6b93a7ae41b6d1d322" 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", "unicode-normalization", ] +[[package]] +name = "rustc-ap-rustc_serialize" +version = "664.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bf4c110271d9a2b7dfd2c6eb82e56fd80606a8bad6c102e158c54e44044046" +dependencies = [ + "indexmap", + "smallvec 1.4.0", +] + [[package]] name = "rustc-ap-rustc_session" -version = "642.0.0" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8487b4575fbb2d1fc6f1cd61225efd108a4d36817e6fb9b643d57fcae9cb12" +checksum = "431cf962de71d4c03fb877d54f331ec36eca77350b0539017abc40a4410d6501" dependencies = [ + "getopts", "log", "num_cpus", + "rustc-ap-rustc_ast", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", "rustc-ap-rustc_feature", "rustc-ap-rustc_fs_util", - "rustc-ap-rustc_index", + "rustc-ap-rustc_serialize", "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 = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f69746c0d4c21bf20a5bb2bd247261a1aa8631f04202d7303352942dde70d987" +checksum = "b912039640597624f4bcb75f1e1fcfa5710267d715a7f73a6336baef341b23d1" dependencies = [ "cfg-if", "log", - "rustc-ap-arena", + "md-5", + "rustc-ap-rustc_arena", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_index", "rustc-ap-rustc_macros", - "rustc-ap-serialize", + "rustc-ap-rustc_serialize", "scoped-tls", + "sha-1", "unicode-width", ] [[package]] name = "rustc-ap-rustc_target" -version = "642.0.0" +version = "664.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bbc6ae09b5d42ec66edd520e8412e0615c53a7c93607fe33dc4abab60ba7c8b" +checksum = "51347a9dadc5ad0b5916cc12d42624b31955285ad13745dbe72f0140038b84e9" dependencies = [ "bitflags", "log", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_index", "rustc-ap-rustc_macros", + "rustc-ap-rustc_serialize", "rustc-ap-rustc_span", - "rustc-ap-serialize", -] - -[[package]] -name = "rustc-ap-serialize" -version = "642.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13a1ead0252fc3d96da4c336a95950be6795f2b00c84a67ccadf26142f8cb41" -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", ] [[package]] @@ -3376,12 +3463,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" -dependencies = [ - "byteorder", -] +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-main" @@ -3412,7 +3496,7 @@ dependencies = [ "crossbeam-deque", "crossbeam-queue", "crossbeam-utils 0.6.5", - "lazy_static 1.4.0", + "lazy_static", "num_cpus", ] @@ -3441,12 +3525,14 @@ 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", - "syn 0.15.35", + "smallvec 1.4.0", + "syn 1.0.11", "url 2.1.0", "winapi 0.3.8", ] @@ -3456,22 +3542,31 @@ name = "rustc_apfloat" version = "0.0.0" dependencies = [ "bitflags", - "smallvec 1.0.0", + "smallvec 1.4.0", +] + +[[package]] +name = "rustc_arena" +version = "0.0.0" +dependencies = [ + "rustc_data_structures", + "smallvec 1.4.0", ] [[package]] name = "rustc_ast" version = "0.0.0" dependencies = [ + "bitflags", "log", "rustc_data_structures", "rustc_index", "rustc_lexer", "rustc_macros", + "rustc_serialize", "rustc_span", "scoped-tls", - "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3479,7 +3574,7 @@ name = "rustc_ast_lowering" version = "0.0.0" dependencies = [ "log", - "rustc", + "rustc_arena", "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", @@ -3489,13 +3584,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 +3610,8 @@ version = "0.0.0" dependencies = [ "log", "rustc_ast", - "rustc_data_structures", "rustc_span", + "rustc_target", ] [[package]] @@ -3528,17 +3624,16 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_macros", + "rustc_serialize", "rustc_session", "rustc_span", - "serialize", - "smallvec 1.0.0", + "version_check", ] [[package]] name = "rustc_builtin_macros" version = "0.0.0" dependencies = [ - "fmt_macros", "log", "rustc_ast", "rustc_ast_pretty", @@ -3548,10 +3643,11 @@ dependencies = [ "rustc_expand", "rustc_feature", "rustc_parse", + "rustc_parse_format", "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3563,12 +3659,10 @@ dependencies = [ "libc", "log", "measureme", - "rustc", "rustc-demangle", "rustc_ast", "rustc_attr", "rustc_codegen_ssa", - "rustc_codegen_utils", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -3577,11 +3671,12 @@ dependencies = [ "rustc_incremental", "rustc_index", "rustc_llvm", + "rustc_middle", + "rustc_serialize", "rustc_session", "rustc_span", "rustc_target", - "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3595,62 +3690,49 @@ dependencies = [ "log", "memmap", "num_cpus", - "rustc", "rustc_apfloat", "rustc_ast", "rustc_attr", - "rustc_codegen_utils", "rustc_data_structures", "rustc_errors", "rustc_fs_util", "rustc_hir", "rustc_incremental", "rustc_index", + "rustc_middle", + "rustc_serialize", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", - "serialize", "tempfile", ] -[[package]] -name = "rustc_codegen_utils" -version = "0.0.0" -dependencies = [ - "log", - "punycode", - "rustc", - "rustc-demangle", - "rustc_ast", - "rustc_data_structures", - "rustc_hir", - "rustc_metadata", - "rustc_span", - "rustc_target", -] - [[package]] name = "rustc_data_structures" version = "0.0.0" dependencies = [ "bitflags", "cfg-if", - "crossbeam-utils 0.6.5", + "crossbeam-utils 0.7.2", "ena", - "graphviz", "indexmap", "jobserver", - "lazy_static 1.4.0", + "lazy_static", + "libc", "log", "measureme", - "parking_lot 0.9.0", + "once_cell", + "parking_lot 0.10.2", "rustc-hash", "rustc-rayon", "rustc-rayon-core", + "rustc_graphviz", "rustc_index", - "serialize", - "smallvec 1.0.0", + "rustc_serialize", + "smallvec 1.4.0", "stable_deref_trait", + "stacker", "winapi 0.3.8", ] @@ -3659,28 +3741,30 @@ name = "rustc_driver" version = "0.0.0" dependencies = [ "env_logger 0.7.1", - "lazy_static 1.4.0", + "lazy_static", + "libc", "log", - "rustc", "rustc_ast", "rustc_ast_pretty", "rustc_codegen_ssa", - "rustc_codegen_utils", "rustc_data_structures", "rustc_error_codes", "rustc_errors", "rustc_feature", "rustc_hir", + "rustc_hir_pretty", "rustc_interface", "rustc_lint", "rustc_metadata", + "rustc_middle", "rustc_mir", "rustc_parse", "rustc_plugin_impl", "rustc_save_analysis", + "rustc_serialize", + "rustc_session", "rustc_span", "rustc_target", - "serialize", "winapi 0.3.8", ] @@ -3692,12 +3776,12 @@ version = "0.0.0" name = "rustc_errors" version = "0.0.0" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.8.0", "atty", "log", "rustc_data_structures", + "rustc_serialize", "rustc_span", - "serialize", "termcolor", "termize", "unicode-width", @@ -3718,17 +3802,17 @@ dependencies = [ "rustc_feature", "rustc_lexer", "rustc_parse", + "rustc_serialize", "rustc_session", "rustc_span", - "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] name = "rustc_feature" version = "0.0.0" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", "rustc_data_structures", "rustc_span", ] @@ -3737,73 +3821,89 @@ dependencies = [ name = "rustc_fs_util" version = "0.0.0" +[[package]] +name = "rustc_graphviz" +version = "0.0.0" + [[package]] name = "rustc_hir" version = "0.0.0" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", + "log", "rustc_ast", - "rustc_ast_pretty", "rustc_data_structures", - "rustc_errors", "rustc_index", "rustc_macros", + "rustc_serialize", + "rustc_span", + "rustc_target", + "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", - "serialize", - "smallvec 1.0.0", ] [[package]] name = "rustc_incremental" version = "0.0.0" dependencies = [ - "graphviz", "log", "rand 0.7.3", - "rustc", "rustc_ast", "rustc_data_structures", "rustc_fs_util", + "rustc_graphviz", "rustc_hir", + "rustc_middle", + "rustc_serialize", "rustc_session", "rustc_span", - "serialize", ] [[package]] name = "rustc_index" version = "0.0.0" dependencies = [ - "serialize", - "smallvec 1.0.0", + "rustc_serialize", + "smallvec 1.4.0", ] [[package]] name = "rustc_infer" version = "0.0.0" dependencies = [ - "graphviz", "log", - "rustc", "rustc_ast", "rustc_data_structures", "rustc_errors", + "rustc_graphviz", "rustc_hir", "rustc_index", "rustc_macros", + "rustc_middle", + "rustc_serialize", + "rustc_session", "rustc_span", "rustc_target", - "smallvec 1.0.0", + "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", @@ -3812,15 +3912,14 @@ dependencies = [ "rustc_builtin_macros", "rustc_codegen_llvm", "rustc_codegen_ssa", - "rustc_codegen_utils", "rustc_data_structures", "rustc_errors", "rustc_expand", "rustc_hir", "rustc_incremental", - "rustc_infer", "rustc_lint", "rustc_metadata", + "rustc_middle", "rustc_mir", "rustc_mir_build", "rustc_parse", @@ -3828,15 +3927,16 @@ dependencies = [ "rustc_plugin_impl", "rustc_privacy", "rustc_resolve", + "rustc_serialize", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", "rustc_trait_selection", "rustc_traits", "rustc_ty", "rustc_typeck", - "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", "tempfile", "winapi 0.3.8", ] @@ -3853,7 +3953,6 @@ name = "rustc_lint" version = "0.0.0" dependencies = [ "log", - "rustc", "rustc_ast", "rustc_ast_pretty", "rustc_attr", @@ -3862,7 +3961,7 @@ dependencies = [ "rustc_feature", "rustc_hir", "rustc_index", - "rustc_infer", + "rustc_middle", "rustc_session", "rustc_span", "rustc_target", @@ -3886,7 +3985,7 @@ dependencies = [ "proc-macro2 1.0.3", "quote 1.0.2", "syn 1.0.11", - "synstructure 0.12.1", + "synstructure", ] [[package]] @@ -3894,61 +3993,93 @@ 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_serialize", + "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 = [ + "bitflags", + "byteorder", + "chalk-ir", + "log", + "measureme", + "polonius-engine", + "rustc-rayon-core", + "rustc_apfloat", + "rustc_arena", + "rustc_ast", + "rustc_attr", + "rustc_data_structures", + "rustc_errors", + "rustc_feature", + "rustc_hir", + "rustc_index", + "rustc_macros", + "rustc_query_system", + "rustc_serialize", + "rustc_session", + "rustc_span", + "rustc_target", + "scoped-tls", + "smallvec 1.4.0", +] + [[package]] name = "rustc_mir" version = "0.0.0" dependencies = [ "either", - "graphviz", "itertools 0.8.0", "log", "log_settings", "polonius-engine", - "rustc", "rustc_apfloat", "rustc_ast", - "rustc_ast_pretty", "rustc_attr", "rustc_data_structures", "rustc_errors", + "rustc_graphviz", "rustc_hir", "rustc_index", "rustc_infer", "rustc_lexer", "rustc_macros", + "rustc_middle", + "rustc_serialize", + "rustc_session", "rustc_span", "rustc_target", "rustc_trait_selection", - "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] name = "rustc_mir_build" version = "0.0.0" dependencies = [ - "arena", "log", - "rustc", "rustc_apfloat", + "rustc_arena", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -3956,13 +4087,13 @@ dependencies = [ "rustc_hir", "rustc_index", "rustc_infer", - "rustc_macros", + "rustc_middle", + "rustc_serialize", "rustc_session", "rustc_span", "rustc_target", "rustc_trait_selection", - "serialize", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -3973,31 +4104,35 @@ 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", ] +[[package]] +name = "rustc_parse_format" +version = "0.0.0" +dependencies = [ + "rustc_lexer", + "rustc_span", +] + [[package]] 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", @@ -4008,12 +4143,13 @@ 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", ] @@ -4022,24 +4158,39 @@ 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", ] +[[package]] +name = "rustc_query_system" +version = "0.0.0" +dependencies = [ + "log", + "parking_lot 0.10.2", + "rustc-rayon-core", + "rustc_arena", + "rustc_data_structures", + "rustc_errors", + "rustc_index", + "rustc_serialize", + "rustc_span", + "smallvec 1.4.0", +] + [[package]] name = "rustc_resolve" version = "0.0.0" dependencies = [ - "arena", "bitflags", "log", - "rustc", + "rustc_arena", "rustc_ast", "rustc_ast_lowering", "rustc_ast_pretty", @@ -4049,10 +4200,12 @@ dependencies = [ "rustc_expand", "rustc_feature", "rustc_hir", + "rustc_index", "rustc_metadata", + "rustc_middle", "rustc_session", "rustc_span", - "smallvec 1.0.0", + "smallvec 1.4.0", ] [[package]] @@ -4062,21 +4215,32 @@ dependencies = [ "log", "rls-data", "rls-span", - "rustc", "rustc_ast", "rustc_ast_pretty", - "rustc_codegen_utils", "rustc_data_structures", "rustc_hir", + "rustc_hir_pretty", + "rustc_middle", "rustc_parse", + "rustc_session", "rustc_span", "serde_json", ] +[[package]] +name = "rustc_serialize" +version = "0.0.0" +dependencies = [ + "indexmap", + "smallvec 1.4.0", +] + [[package]] name = "rustc_session" version = "0.0.0" dependencies = [ + "bitflags", + "getopts", "log", "num_cpus", "rustc_ast", @@ -4084,27 +4248,44 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_fs_util", - "rustc_index", + "rustc_serialize", "rustc_span", "rustc_target", - "serialize", ] [[package]] name = "rustc_span" version = "0.0.0" dependencies = [ - "arena", "cfg-if", "log", + "md-5", + "rustc_arena", "rustc_data_structures", "rustc_index", "rustc_macros", + "rustc_serialize", "scoped-tls", - "serialize", + "sha-1", "unicode-width", ] +[[package]] +name = "rustc_symbol_mangling" +version = "0.0.0" +dependencies = [ + "log", + "punycode", + "rustc-demangle", + "rustc_ast", + "rustc_data_structures", + "rustc_hir", + "rustc_middle", + "rustc_session", + "rustc_span", + "rustc_target", +] + [[package]] name = "rustc_target" version = "0.0.0" @@ -4114,8 +4295,8 @@ dependencies = [ "rustc_data_structures", "rustc_index", "rustc_macros", + "rustc_serialize", "rustc_span", - "serialize", ] [[package]] @@ -4132,9 +4313,7 @@ checksum = "b725dadae9fabc488df69a287f5a99c5eaf5d10853842a8a3dfac52476f544ee" name = "rustc_trait_selection" version = "0.0.0" dependencies = [ - "fmt_macros", "log", - "rustc", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -4143,27 +4322,30 @@ dependencies = [ "rustc_index", "rustc_infer", "rustc_macros", + "rustc_middle", + "rustc_parse_format", "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-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]] @@ -4171,10 +4353,12 @@ 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", "rustc_trait_selection", @@ -4184,9 +4368,8 @@ dependencies = [ name = "rustc_typeck" version = "0.0.0" dependencies = [ - "arena", "log", - "rustc", + "rustc_arena", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -4194,10 +4377,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]] @@ -4206,7 +4391,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", ] [[package]] @@ -4215,7 +4400,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", @@ -4233,18 +4418,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" @@ -4269,36 +4442,38 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.12" +version = "1.4.18" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.6.1", + "anyhow", "bytecount", "cargo_metadata 0.8.0", "derive-new", "diff", "dirs", "env_logger 0.6.2", - "failure", "getopts", "ignore", "itertools 0.8.0", - "lazy_static 1.4.0", + "lazy_static", "log", "regex", + "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_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", "serde_json", "structopt", "term 0.6.0", + "thiserror", "toml", "unicode-segmentation", "unicode-width", @@ -4326,7 +4501,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", "winapi 0.3.8", ] @@ -4379,6 +4554,16 @@ dependencies = [ "serde", ] +[[package]] +name = "semver" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "394cec28fa623e00903caf7ba4fa6fb9a0e260280bb8cdbbba029611108a0190" +dependencies = [ + "semver-parser", + "serde", +] + [[package]] name = "semver-parser" version = "0.7.0" @@ -4396,13 +4581,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]] @@ -4448,24 +4633,16 @@ dependencies = [ "url 1.7.2", ] -[[package]] -name = "serialize" -version = "0.0.0" -dependencies = [ - "indexmap", - "smallvec 1.0.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]] @@ -4498,9 +4675,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", @@ -4520,15 +4697,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", @@ -4542,6 +4719,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" @@ -4553,7 +4743,7 @@ dependencies = [ "core", "dlmalloc", "fortanix-sgx-abi", - "hashbrown 0.6.2", + "hashbrown", "hermit-abi", "libc", "panic_abort", @@ -4579,7 +4769,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", "new_debug_unreachable", "phf_shared", "precomputed-hash", @@ -4685,18 +4875,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" @@ -4774,17 +4952,6 @@ dependencies = [ "wincolor", ] -[[package]] -name = "termion" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" -dependencies = [ - "libc", - "redox_syscall", - "redox_termios", -] - [[package]] name = "termize" version = "0.1.1" @@ -4830,12 +4997,23 @@ dependencies = [ ] [[package]] -name = "thread_local" -version = "0.3.6" +name = "thiserror" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +checksum = "f9fb62ff737e573b1e677459bea6fd023cd5d6e868c3242d3cdf3ef2f0554824" dependencies = [ - "lazy_static 1.4.0", + "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]] @@ -4844,17 +5022,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", ] [[package]] name = "tidy" version = "0.1.0" dependencies = [ - "lazy_static 1.4.0", + "cargo_metadata 0.9.1", + "lazy_static", "regex", - "serde", - "serde_json", "walkdir", ] @@ -4978,7 +5155,7 @@ checksum = "afbd6ef1b8cc2bd2c2b580d882774d443ebb1c6ceefe35ba9ea4ab586c89dbe8" dependencies = [ "crossbeam-queue", "futures", - "lazy_static 1.4.0", + "lazy_static", "libc", "log", "mio", @@ -4997,7 +5174,7 @@ checksum = "6732fe6b53c8d11178dcb77ac6d9682af27fc6d4cb87789449152e5377377146" dependencies = [ "crossbeam-utils 0.6.5", "futures", - "lazy_static 1.4.0", + "lazy_static", "log", "mio", "num_cpus", @@ -5068,7 +5245,7 @@ dependencies = [ "crossbeam-queue", "crossbeam-utils 0.6.5", "futures", - "lazy_static 1.4.0", + "lazy_static", "log", "num_cpus", "slab", @@ -5138,7 +5315,7 @@ dependencies = [ "failure", "failure_derive", "is-match", - "lazy_static 1.4.0", + "lazy_static", "regex", "toml", "toml-query_derive", @@ -5172,9 +5349,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" @@ -5182,7 +5359,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6b52bf4da6512f0f07785a04769222e50d29639e7ecd016b7806fd2de306b4" dependencies = [ - "lazy_static 1.4.0", + "lazy_static", "regex", ] @@ -5192,12 +5369,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71a9c5b1fe77426cf144cc30e49e955270f5086e31a6441dfa8b32efc09b9d77" -[[package]] -name = "ucd-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" - [[package]] name = "unicase" version = "2.6.0" @@ -5225,11 +5396,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]] @@ -5240,10 +5411,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", ] @@ -5330,12 +5502,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1262dfab4c30d5cb7c07026be00ee343a6cf5027fdc0104a9160f354e5db75c" -[[package]] -name = "utf8-ranges" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" - [[package]] name = "utf8parse" version = "0.1.1" @@ -5365,13 +5531,12 @@ checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" [[package]] name = "vergen" -version = "3.0.4" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aba5e34f93dc7051dfad05b98a18e9156f27e7b431fe1d2398cb6061c0a1dba" +checksum = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb" dependencies = [ "bitflags", "chrono", - "failure", ] [[package]] @@ -5512,8 +5677,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", + "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..f10d539d8296b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,10 @@ members = [ "src/tools/rls", "src/tools/rustfmt", "src/tools/miri", + "src/tools/miri/cargo-miri", "src/tools/rustdoc-themes", "src/tools/unicode-table-generator", + "src/tools/expand-yaml-anchors", ] exclude = [ "build", @@ -40,6 +42,19 @@ debug-assertions = false debug = false debug-assertions = false +[profile.release.package.compiler_builtins] +# For compiler-builtins we always use a high number of codegen units. +# The goal here is to place every single intrinsic into its own object +# file to avoid symbol clashes with the system libgcc if possible. Note +# that this number doesn't actually produce this many object files, we +# just don't create more than this number of object files. +# +# It's a bit of a bummer that we have to pass this here, unfortunately. +# Ideally this would be specified through an env var to Cargo so Cargo +# knows how many CGUs are for this specific crate, but for now +# per-crate configuration isn't specifiable in the environment. +codegen-units = 10000 + # We want the RLS to use the version of Cargo that we've got vendored in this # repository to ensure that the same exact version of Cargo is used by both the # RLS and the Cargo binary itself. The RLS depends on Cargo as a git repository diff --git a/README.md b/README.md index 1cd9b8a3a2ae1..42fc0a63c0ffb 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,11 @@ or reading the [rustc dev guide][rustcguidebuild]. [rustcguidebuild]: https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html -### Building on *nix +### Building on a Unix-like system 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 7e18f1befddec..006682f505936 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,326 @@ +Version 1.44.0 (2020-06-04) +========================== + +Language +-------- +- [You can now use `async/.await` with `#[no_std]` enabled.][69033] +- [Added the `unused_braces` lint.][70081] + +**Syntax-only changes** + +- [Expansion-driven outline module parsing][69838] +```rust +#[cfg(FALSE)] +mod foo { + mod bar { + mod baz; // `foo/bar/baz.rs` doesn't exist, but no error! + } +} +``` + +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 +-------- +- [Rustc now respects the `-C codegen-units` flag in incremental mode.][70156] + Additionally when in incremental mode rustc defaults to 256 codegen units. +- [Refactored `catch_unwind` to have zero-cost, unless unwinding is enabled and + a panic is thrown.][67502] +- [Added tier 3\* support for the `aarch64-unknown-none` and + `aarch64-unknown-none-softfloat` targets.][68334] +- [Added tier 3 support for `arm64-apple-tvos` and + `x86_64-apple-tvos` targets.][68191] + + +Libraries +--------- +- [Special cased `vec![]` to map directly to `Vec::new()`.][70632] This allows + `vec![]` to be able to be used in `const` contexts. +- [`convert::Infallible` now implements `Hash`.][70281] +- [`OsString` now implements `DerefMut` and `IndexMut` returning + a `&mut OsStr`.][70048] +- [Unicode 13 is now supported.][69929] +- [`String` now implements `From<&mut str>`.][69661] +- [`IoSlice` now implements `Copy`.][69403] +- [`Vec` now implements `From<[T; N]>`.][68692] Where `N` is at most 32. +- [`proc_macro::LexError` now implements `fmt::Display` and `Error`.][68899] +- [`from_le_bytes`, `to_le_bytes`, `from_be_bytes`, `to_be_bytes`, + `from_ne_bytes`, and `to_ne_bytes` methods are now `const` for all + integer types.][69373] + +Stabilized APIs +--------------- +- [`PathBuf::with_capacity`] +- [`PathBuf::capacity`] +- [`PathBuf::clear`] +- [`PathBuf::reserve`] +- [`PathBuf::reserve_exact`] +- [`PathBuf::shrink_to_fit`] +- [`f32::to_int_unchecked`] +- [`f64::to_int_unchecked`] +- [`Layout::align_to`] +- [`Layout::pad_to_align`] +- [`Layout::array`] +- [`Layout::extend`] + +Cargo +----- +- [Added the `cargo tree` command which will print a tree graph of + your dependencies.][cargo/8062] E.g. + ``` + mdbook v0.3.2 (/Users/src/rust/mdbook) + ├── ammonia v3.0.0 + │ ├── html5ever v0.24.0 + │ │ ├── log v0.4.8 + │ │ │ └── cfg-if v0.1.9 + │ │ ├── mac v0.1.1 + │ │ └── markup5ever v0.9.0 + │ │ ├── log v0.4.8 (*) + │ │ ├── phf v0.7.24 + │ │ │ └── phf_shared v0.7.24 + │ │ │ ├── siphasher v0.2.3 + │ │ │ └── unicase v1.4.2 + │ │ │ [build-dependencies] + │ │ │ └── version_check v0.1.5 + ... + ``` + You can also display dependencies on multiple versions of the same crate with + `cargo tree -d` (short for `cargo tree --duplicates`). + +Misc +---- +- [Rustdoc now allows you to specify `--crate-version` to have rustdoc include + the version in the sidebar.][69494] + +Compatibility Notes +------------------- +- [Rustc now correctly generates static libraries on Windows GNU targets with + the `.a` extension, rather than the previous `.lib`.][70937] +- [Removed the `-C no_integrated_as` flag from rustc.][70345] +- [The `file_name` property in JSON output of macro errors now points the actual + source file rather than the previous format of ``.][70969] + **Note:** this may not point to a file that actually exists on the user's system. +- [The minimum required external LLVM version has been bumped to LLVM 8.][71147] +- [`mem::{zeroed, uninitialised}` will now panic when used with types that do + not allow zero initialization such as `NonZeroU8`.][66059] This was + previously a warning. +- [In 1.45.0 (the next release) converting a `f64` to `u32` using the `as` + operator has been defined as a saturating operation.][71269] This was previously + undefined behaviour, but you can use the `{f64, f32}::to_int_unchecked` methods to + continue using the current behaviour, which may be desirable in rare performance + sensitive situations. + +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. + +- [dep_graph Avoid allocating a set on when the number reads are small.][69778] +- [Replace big JS dict with JSON parsing.][71250] + +[69373]: https://github.com/rust-lang/rust/pull/69373/ +[66059]: https://github.com/rust-lang/rust/pull/66059/ +[68191]: https://github.com/rust-lang/rust/pull/68191/ +[68899]: https://github.com/rust-lang/rust/pull/68899/ +[71147]: https://github.com/rust-lang/rust/pull/71147/ +[71250]: https://github.com/rust-lang/rust/pull/71250/ +[70937]: https://github.com/rust-lang/rust/pull/70937/ +[70969]: https://github.com/rust-lang/rust/pull/70969/ +[70632]: https://github.com/rust-lang/rust/pull/70632/ +[70281]: https://github.com/rust-lang/rust/pull/70281/ +[70345]: https://github.com/rust-lang/rust/pull/70345/ +[70048]: https://github.com/rust-lang/rust/pull/70048/ +[70081]: https://github.com/rust-lang/rust/pull/70081/ +[70156]: https://github.com/rust-lang/rust/pull/70156/ +[71269]: https://github.com/rust-lang/rust/pull/71269/ +[69838]: https://github.com/rust-lang/rust/pull/69838/ +[69929]: https://github.com/rust-lang/rust/pull/69929/ +[69661]: https://github.com/rust-lang/rust/pull/69661/ +[69778]: https://github.com/rust-lang/rust/pull/69778/ +[69494]: https://github.com/rust-lang/rust/pull/69494/ +[69403]: https://github.com/rust-lang/rust/pull/69403/ +[69033]: https://github.com/rust-lang/rust/pull/69033/ +[68692]: https://github.com/rust-lang/rust/pull/68692/ +[68334]: https://github.com/rust-lang/rust/pull/68334/ +[67502]: https://github.com/rust-lang/rust/pull/67502/ +[cargo/8062]: https://github.com/rust-lang/cargo/pull/8062/ +[`PathBuf::with_capacity`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.with_capacity +[`PathBuf::capacity`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.capacity +[`PathBuf::clear`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.clear +[`PathBuf::reserve`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.reserve +[`PathBuf::reserve_exact`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.reserve_exact +[`PathBuf::shrink_to_fit`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.shrink_to_fit +[`f32::to_int_unchecked`]: https://doc.rust-lang.org/std/primitive.f32.html#method.to_int_unchecked +[`f64::to_int_unchecked`]: https://doc.rust-lang.org/std/primitive.f64.html#method.to_int_unchecked +[`Layout::align_to`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.align_to +[`Layout::pad_to_align`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.pad_to_align +[`Layout::array`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.array +[`Layout::extend`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.extend + + +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 +347,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 +639,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/ @@ -589,7 +912,7 @@ Compatibility Notes [`Duration::mul_f32`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.mul_f32 [`Duration::mul_f64`]: https://doc.rust-lang.org/std/time/struct.Duration.html#method.mul_f64 [`any::type_name`]: https://doc.rust-lang.org/std/any/fn.type_name.html -[forge-platform-support]: https://forge.rust-lang.org/platform-support.html +[forge-platform-support]: https://forge.rust-lang.org/release/platform-support.html [pipeline-internals]: https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199 Version 1.37.0 (2019-08-15) @@ -4838,7 +5161,6 @@ Version 1.11.0 (2016-08-18) Language -------- -* [`cfg_attr` works on `path` attributes](https://github.com/rust-lang/rust/pull/34546) * [Support nested `cfg_attr` attributes](https://github.com/rust-lang/rust/pull/34216) * [Allow statement-generating braced macro invocations at the end of blocks](https://github.com/rust-lang/rust/pull/34436) * [Macros can be expanded inside of trait definitions](https://github.com/rust-lang/rust/pull/34213) @@ -4957,8 +5279,6 @@ Version 1.10.0 (2016-07-07) Language -------- -* [Allow `concat_idents!` in type positions as well as in expression - positions](https://github.com/rust-lang/rust/pull/33735). * [`Copy` types are required to have a trivial implementation of `Clone`](https://github.com/rust-lang/rust/pull/33420). [RFC 1521](https://github.com/rust-lang/rfcs/blob/master/text/1521-copy-clone-semantics.md). * [Single-variant enums support the `#[repr(..)]` attribute](https://github.com/rust-lang/rust/pull/33355). diff --git a/config.toml.example b/config.toml.example index ce21b63467f53..bc6760334170b 100644 --- a/config.toml.example +++ b/config.toml.example @@ -69,7 +69,7 @@ # the same format as above, but since these targets are experimental, they are # not built by default and the experimental Rust compilation targets that depend # on them will not work unless the user opts in to building them. -#experimental-targets = "" +#experimental-targets = "AVR" # Cap the number of parallel linker invocations when compiling LLVM. # This can be useful when building LLVM with debug info, which significantly @@ -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. @@ -206,7 +209,8 @@ # Build the sanitizer runtimes #sanitizers = false -# Build the profiler runtime +# Build the profiler runtime (required when compiling with options that depend +# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). #profiler = false # Indicates whether the native libraries linked into Cargo will be statically @@ -309,7 +313,11 @@ # Whether or not debug assertions are enabled for the compiler and standard # library. -#debug-assertions = false +#debug-assertions = debug + +# Whether or not debug assertions are enabled for the standard library. +# Overrides the `debug-assertions` option, if defined. +#debug-assertions-std = debug-assertions # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. # `0` - no debug info @@ -408,10 +416,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..c4918d7f2e714 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -48,10 +48,11 @@ toml = "0.5" lazy_static = "1.3.0" time = "0.1" ignore = "0.4.10" +opener = "0.4" [target.'cfg(windows)'.dependencies.winapi] 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/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index 04345867bf5c1..ba644e6111827 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -52,12 +52,7 @@ fn main() { // Bootstrap's Cargo-command builder sets this variable to the current Rust version; let's pick // it up so we can make rustdoc print this into the docs if let Some(version) = env::var_os("RUSTDOC_CRATE_VERSION") { - // This "unstable-options" can be removed when `--crate-version` is stabilized - if !has_unstable { - cmd.arg("-Z").arg("unstable-options"); - } cmd.arg("--crate-version").arg(version); - has_unstable = true; } // Needed to be able to run all rustdoc tests. diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 50e1726240fff..82a755c7892b1 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 @@ -664,6 +676,10 @@ def build_bootstrap(self): if self.clean and os.path.exists(build_dir): shutil.rmtree(build_dir) env = os.environ.copy() + # `CARGO_BUILD_TARGET` breaks bootstrap build. + # See also: . + if "CARGO_BUILD_TARGET" in env: + del env["CARGO_BUILD_TARGET"] env["RUSTC_BOOTSTRAP"] = '1' env["CARGO_TARGET_DIR"] = build_dir env["RUSTC"] = self.rustc() @@ -739,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"], @@ -759,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() @@ -880,11 +893,18 @@ def bootstrap(help_triggered): build.verbose = args.verbose build.clean = args.clean - try: - with open(args.config or 'config.toml') as config: + # Read from `RUST_BOOTSTRAP_CONFIG`, then `--config`, then fallback to `config.toml` (if it + # exists). + toml_path = os.getenv('RUST_BOOTSTRAP_CONFIG') or args.config + if not toml_path and os.path.exists('config.toml'): + toml_path = 'config.toml' + + if toml_path: + 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 config_verbose = build.get_toml('verbose', 'build') if config_verbose is not None: @@ -896,6 +916,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'] @@ -931,6 +954,8 @@ def bootstrap(help_triggered): env["RUSTC_BOOTSTRAP"] = '1' env["CARGO"] = build.cargo() env["RUSTC"] = build.rustc() + if toml_path: + env["BOOTSTRAP_CONFIG"] = toml_path if build.rustfmt(): env["RUSTFMT"] = build.rustfmt() run(args, env=env, verbose=build.verbose) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index e4b57cddfb891..8f0a245a5658a 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{Duration, Instant}; -use build_helper::t; +use build_helper::{output, t}; use crate::cache::{Cache, Interned, INTERNER}; use crate::check; @@ -21,9 +21,10 @@ 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_lib_path, exe, libdir}; +use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir}; use crate::{Build, DocTests, GitRepo, Mode}; pub use crate::Compiler; @@ -51,6 +52,8 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { /// it's been assembled. type Output: Clone; + /// Whether this step is run by default as part of its respective phase. + /// `true` here can still be overwritten by `should_run` calling `default_condition`. const DEFAULT: bool = false; /// If true, then this rule should be skipped if --target was specified, but --host was not @@ -96,9 +99,21 @@ struct StepDescription { name: &'static str, } +/// Collection of paths used to match a task rule. #[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)] pub enum PathSet { + /// A collection of individual paths. + /// + /// These are generally matched as a path suffix. For example, a + /// command-line value of `libstd` will match if `src/libstd` is in the + /// set. Set(BTreeSet), + /// A "suite" of paths. + /// + /// These can match as a path suffix (like `Set`), or as a prefix. For + /// example, a command-line value of `src/test/ui/abi/variadic-ffi.rs` + /// will match `src/test/ui`. A command-line value of `ui` would also + /// match `src/test/ui`. Suite(PathBuf), } @@ -248,21 +263,33 @@ impl<'a> ShouldRun<'a> { self } - // Unlike `krate` this will create just one pathset. As such, it probably shouldn't actually - // ever be used, but as we transition to having all rules properly handle passing krate(...) by - // actually doing something different for every crate passed. + /// Indicates it should run if the command-line selects the given crate or + /// any of its (local) dependencies. + /// + /// Compared to `krate`, this treats the dependencies as aliases for the + /// same job. Generally it is preferred to use `krate`, and treat each + /// individual path separately. For example `./x.py test src/liballoc` + /// (which uses `krate`) will test just `liballoc`. However, `./x.py check + /// src/liballoc` (which uses `all_krates`) will check all of `libtest`. + /// `all_krates` should probably be removed at some point. pub fn all_krates(mut self, name: &str) -> Self { let mut set = BTreeSet::new(); for krate in self.builder.in_tree_crates(name) { - set.insert(PathBuf::from(&krate.path)); + let path = krate.local_path(self.builder); + set.insert(path); } self.paths.insert(PathSet::Set(set)); self } + /// Indicates it should run if the command-line selects the given crate or + /// any of its (local) dependencies. + /// + /// `make_run` will be called separately for each matching command-line path. pub fn krate(mut self, name: &str) -> Self { for krate in self.builder.in_tree_crates(name) { - self.paths.insert(PathSet::one(&krate.path)); + let path = krate.local_path(self.builder); + self.paths.insert(PathSet::one(path)); } self } @@ -313,6 +340,7 @@ pub enum Kind { Dist, Doc, Install, + Run, } impl<'a> Builder<'a> { @@ -342,21 +370,23 @@ impl<'a> Builder<'a> { tool::Rls, tool::Rustdoc, tool::Clippy, + tool::CargoClippy, native::Llvm, native::Sanitizers, tool::Rustfmt, tool::Miri, + tool::CargoMiri, 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,8 +397,6 @@ impl<'a> Builder<'a> { test::UiFullDeps, test::Rustdoc, test::Pretty, - test::RunFailPretty, - test::RunPassValgrindPretty, test::Crate, test::CrateLibrustc, test::CrateRustdoc, @@ -438,7 +466,6 @@ impl<'a> Builder<'a> { dist::Clippy, dist::Miri, dist::LlvmTools, - dist::Lldb, dist::Extended, dist::HashSign ), @@ -454,6 +481,7 @@ impl<'a> Builder<'a> { install::Src, install::Rustc ), + Kind::Run => describe!(run::ExpandYamlAnchors,), } } @@ -484,13 +512,19 @@ impl<'a> Builder<'a> { should_run = (desc.should_run)(should_run); } let mut help = String::from("Available paths:\n"); + let mut add_path = |path: &Path| { + help.push_str(&format!(" ./x.py {} {}\n", subcommand, path.display())); + }; for pathset in should_run.paths { - if let PathSet::Set(set) = pathset { - set.iter().for_each(|path| { - help.push_str( - format!(" ./x.py {} {}\n", subcommand, path.display()).as_str(), - ) - }) + match pathset { + PathSet::Set(set) => { + for path in set { + add_path(&path); + } + } + PathSet::Suite(path) => { + add_path(&path.join("...")); + } } } Some(help) @@ -502,11 +536,12 @@ impl<'a> Builder<'a> { Subcommand::Check { ref paths } => (Kind::Check, &paths[..]), Subcommand::Clippy { ref paths } => (Kind::Clippy, &paths[..]), Subcommand::Fix { ref paths } => (Kind::Fix, &paths[..]), - Subcommand::Doc { ref paths } => (Kind::Doc, &paths[..]), + Subcommand::Doc { ref paths, .. } => (Kind::Doc, &paths[..]), Subcommand::Test { ref paths, .. } => (Kind::Test, &paths[..]), 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!(), }; @@ -646,6 +681,7 @@ impl<'a> Builder<'a> { pub fn sysroot_libdir_relative(&self, compiler: Compiler) -> &Path { match self.config.libdir_relative() { Some(relative_libdir) if compiler.stage >= 1 => relative_libdir, + _ if compiler.stage == 0 => &self.build.initial_libdir, _ => Path::new("lib"), } } @@ -660,7 +696,7 @@ impl<'a> Builder<'a> { return; } - add_lib_path(vec![self.rustc_libdir(compiler)], &mut cmd.command); + add_dylib_path(vec![self.rustc_libdir(compiler)], &mut cmd.command); } /// Gets a path to the compiler specified. @@ -698,6 +734,20 @@ impl<'a> Builder<'a> { cmd } + /// Return the path to `llvm-config` for the target, if it exists. + /// + /// Note that this returns `None` if LLVM is disabled, or if we're in a + /// check build or dry-run, where there's no need to build all of LLVM. + fn llvm_config(&self, target: Interned) -> Option { + if self.config.llvm_enabled() && self.kind != Kind::Check && !self.config.dry_run { + let llvm_config = self.ensure(native::Llvm { target }); + if llvm_config.is_file() { + return Some(llvm_config); + } + } + None + } + /// Prepares an invocation of `cargo` to be run. /// /// This will create a `Command` that represents a pending execution of @@ -725,7 +775,7 @@ impl<'a> Builder<'a> { self.clear_if_dirty(&my_out, &rustdoc); } - cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd).arg("-Zconfig-profile"); + cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd); let profile_var = |name: &str| { let profile = if self.config.rust_optimize { "RELEASE" } else { "DEV" }; @@ -746,9 +796,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 { @@ -772,6 +830,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()); } @@ -847,13 +910,7 @@ impl<'a> Builder<'a> { rustflags.arg("-Zforce-unstable-if-unmarked"); } - // cfg(bootstrap): the flag was renamed from `-Zexternal-macro-backtrace` - // to `-Zmacro-backtrace`, keep only the latter after beta promotion. - if stage == 0 { - rustflags.arg("-Zexternal-macro-backtrace"); - } else { - rustflags.arg("-Zmacro-backtrace"); - } + rustflags.arg("-Zmacro-backtrace"); let want_rustdoc = self.doc_tests != DocTests::No; @@ -892,7 +949,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")) @@ -956,27 +1020,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); } @@ -1009,8 +1057,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 @@ -1040,6 +1093,17 @@ impl<'a> Builder<'a> { .env("RUSTC_SNAPSHOT_LIBDIR", self.rustc_libdir(compiler)); } + // Tools that use compiler libraries may inherit the `-lLLVM` link + // requirement, but the `-L` library path is not propagated across + // separate Cargo projects. We can add LLVM's library path to the + // platform-specific environment variable as a workaround. + if mode == Mode::ToolRustc { + if let Some(llvm_config) = self.llvm_config(target) { + let llvm_libdir = output(Command::new(&llvm_config).arg("--libdir")); + add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo); + } + } + if self.config.incremental { cargo.env("CARGO_INCREMENTAL", "1"); } else { @@ -1071,6 +1135,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 { @@ -1135,7 +1206,7 @@ impl<'a> Builder<'a> { ); } - // If Control Flow Guard is enabled, pass the `control_flow_guard=checks` flag to rustc + // If Control Flow Guard is enabled, pass the `control-flow-guard` flag to rustc // when compiling the standard library, since this might be linked into the final outputs // produced by rustc. Since this mitigation is only available on Windows, only enable it // for the standard library in case the compiler is run on a non-Windows platform. @@ -1146,7 +1217,7 @@ impl<'a> Builder<'a> { && self.config.control_flow_guard && compiler.stage >= 1 { - rustflags.arg("-Zcontrol_flow_guard=checks"); + rustflags.arg("-Zcontrol-flow-guard"); } // For `cargo doc` invocations, make rustdoc print the Rust version into the docs @@ -1249,7 +1320,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 @@ -1307,7 +1378,7 @@ impl<'a> Builder<'a> { #[cfg(test)] mod tests; -#[derive(Debug)] +#[derive(Debug, Clone)] struct Rustflags(String); impl Rustflags { @@ -1347,6 +1418,7 @@ impl Rustflags { pub struct Cargo { command: Command, rustflags: Rustflags, + rustdocflags: Rustflags, } impl Cargo { @@ -1379,7 +1451,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/cc_detect.rs b/src/bootstrap/cc_detect.rs index a236edf971fcc..ab16ca3732c1f 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -37,7 +37,9 @@ use crate::{Build, GitRepo}; // try to infer the archiver path from the C compiler path. // In the future this logic should be replaced by calling into the `cc` crate. fn cc2ar(cc: &Path, target: &str) -> Option { - if let Some(ar) = env::var_os("AR") { + if let Some(ar) = env::var_os(format!("AR_{}", target.replace("-", "_"))) { + Some(PathBuf::from(ar)) + } else if let Some(ar) = env::var_os("AR") { Some(PathBuf::from(ar)) } else if target.contains("msvc") { None diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 504cba45570c1..a4115904ac76f 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.43.0"; +pub const CFG_RELEASE_NUM: &str = "1.46.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 65a00db33949e..afcf0dcac7e55 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -22,8 +22,8 @@ use serde::Deserialize; use crate::builder::Cargo; use crate::dist; use crate::native; -use crate::util::{exe, is_dylib}; -use crate::{Compiler, GitRepo, Mode}; +use crate::util::{exe, is_dylib, symlink_dir}; +use crate::{Compiler, DependencyType, GitRepo, Mode}; use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::cache::{Interned, INTERNER}; @@ -74,6 +74,7 @@ impl Step for Std { // Even if we're not building std this stage, the new sysroot must // still contain the third party objects needed by various targets. copy_third_party_objects(builder, &compiler, target); + copy_self_contained_objects(builder, &compiler, target); builder.ensure(StdLink { compiler: compiler_to_use, @@ -83,10 +84,11 @@ impl Step for Std { return; } - target_deps.extend(copy_third_party_objects(builder, &compiler, target).into_iter()); + target_deps.extend(copy_third_party_objects(builder, &compiler, target)); + target_deps.extend(copy_self_contained_objects(builder, &compiler, target)); 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 ({} -> {})", @@ -109,38 +111,29 @@ impl Step for Std { } } +fn copy_and_stamp( + builder: &Builder<'_>, + libdir: &Path, + sourcedir: &Path, + name: &str, + target_deps: &mut Vec<(PathBuf, DependencyType)>, + dependency_type: DependencyType, +) { + let target = libdir.join(name); + builder.copy(&sourcedir.join(name), &target); + + target_deps.push((target, dependency_type)); +} + /// Copies third party objects needed by various targets. fn copy_third_party_objects( builder: &Builder<'_>, compiler: &Compiler, target: Interned, -) -> Vec { +) -> Vec<(PathBuf, DependencyType)> { let libdir = builder.sysroot_libdir(*compiler, target); - let mut target_deps = vec![]; - let mut copy_and_stamp = |sourcedir: &Path, name: &str| { - let target = libdir.join(name); - builder.copy(&sourcedir.join(name), &target); - target_deps.push(target); - }; - - // Copies the crt(1,i,n).o startup 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. - if target.contains("musl") { - let srcdir = builder.musl_root(target).unwrap().join("lib"); - for &obj in &["crt1.o", "crti.o", "crtn.o"] { - copy_and_stamp(&srcdir, obj); - } - } else if target.ends_with("-wasi") { - let srcdir = builder.wasi_root(target).unwrap().join("lib/wasm32-wasi"); - copy_and_stamp(&srcdir, "crt1.o"); - } - // Copies libunwind.a compiled to be linked with x86_64-fortanix-unknown-sgx. // // This target needs to be linked to Fortanix's port of llvm's libunwind. @@ -150,13 +143,81 @@ fn copy_third_party_objects( let src_path_env = "X86_FORTANIX_SGX_LIBS"; let src = env::var(src_path_env).unwrap_or_else(|_| panic!("{} not found in env", src_path_env)); - copy_and_stamp(Path::new(&src), "libunwind.a"); + copy_and_stamp( + builder, + &*libdir, + Path::new(&src), + "libunwind.a", + &mut target_deps, + DependencyType::Target, + ); } if builder.config.sanitizers && compiler.stage != 0 { // The sanitizers are only copied in stage1 or above, // to avoid creating dependency on LLVM. - target_deps.extend(copy_sanitizers(builder, &compiler, target)); + target_deps.extend( + copy_sanitizers(builder, &compiler, target) + .into_iter() + .map(|d| (d, DependencyType::Target)), + ); + } + + target_deps +} + +/// Copies third party objects needed by various targets for self-contained linkage. +fn copy_self_contained_objects( + builder: &Builder<'_>, + compiler: &Compiler, + target: Interned, +) -> Vec<(PathBuf, DependencyType)> { + // cfg(bootstrap) + // Remove when upgrading bootstrap compiler. + let libdir_self_contained = if compiler.stage == 0 { + builder.sysroot_libdir(*compiler, target).to_path_buf() + } else { + builder.sysroot_libdir(*compiler, target).join("self-contained") + }; + t!(fs::create_dir_all(&libdir_self_contained)); + let mut target_deps = vec![]; + + // Copies the CRT objects. + // + // 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", "Scrt1.o", "rcrt1.o", "crti.o", "crtn.o"] { + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir, + obj, + &mut target_deps, + DependencyType::TargetSelfContained, + ); + } + } else if target.ends_with("-wasi") { + let srcdir = builder.wasi_root(target).unwrap().join("lib/wasm32-wasi"); + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir, + "crt1.o", + &mut target_deps, + DependencyType::TargetSelfContained, + ); + } else if target.contains("windows-gnu") { + for obj in ["crt2.o", "dllcrt2.o"].iter() { + let src = compiler_file(builder, builder.cc(target), target, obj); + let target = libdir_self_contained.join(obj); + builder.copy(&src, &target); + target_deps.push((target, DependencyType::TargetSelfContained)); + } } target_deps @@ -164,7 +225,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 +247,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 +292,28 @@ 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"); + } + + // By default, rustc does not include unwind tables unless they are required + // for a particular target. They are not required by RISC-V targets, but + // compiling the standard library with them means that users can get + // backtraces without having to recompile the standard library themselves. + // + // This choice was discussed in https://github.com/rust-lang/rust/pull/69890 + if target.contains("riscv") { + cargo.rustflag("-Cforce-unwind-tables=yes"); + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -310,7 +395,7 @@ pub struct StartupObjects { } impl Step for StartupObjects { - type Output = Vec; + type Output = Vec<(PathBuf, DependencyType)>; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/rtstartup") @@ -329,7 +414,7 @@ impl Step for StartupObjects { /// They don't require any library support as they're just plain old object /// files, so we just use the nightly snapshot compiler to always build them (as /// no other compilers are guaranteed to be available). - fn run(self, builder: &Builder<'_>) -> Vec { + fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> { let for_compiler = self.compiler; let target = self.target; if !target.contains("windows-gnu") { @@ -363,14 +448,7 @@ impl Step for StartupObjects { let target = sysroot_dir.join((*file).to_string() + ".o"); builder.copy(dst_file, &target); - target_deps.push(target); - } - - for obj in ["crt2.o", "dllcrt2.o"].iter() { - let src = compiler_file(builder, builder.cc(target), target, obj); - let target = sysroot_dir.join(obj); - builder.copy(&src, &target); - target_deps.push(target); + target_deps.push((target, DependencyType::Target)); } target_deps @@ -451,44 +529,6 @@ impl Step for Rustc { false, ); - // We used to build librustc_codegen_llvm as a separate step, - // which produced a dylib that the compiler would dlopen() at runtime. - // This meant that we only needed to make sure that libLLVM.so was - // installed by the time we went to run a tool using it - since - // librustc_codegen_llvm was effectively a standalone artifact, - // other crates were completely oblivious to its dependency - // on `libLLVM.so` during build time. - // - // However, librustc_codegen_llvm is now built as an ordinary - // crate during the same step as the rest of the compiler crates. - // This means that any crates depending on it will see the fact - // that it uses `libLLVM.so` as a native library, and will - // cause us to pass `-llibLLVM.so` to the linker when we link - // a binary. - // - // For `rustc` itself, this works out fine. - // During the `Assemble` step, we call `dist::maybe_install_llvm_dylib` - // to copy libLLVM.so into the `stage` directory. We then link - // the compiler binary, which will find `libLLVM.so` in the correct place. - // - // However, this is insufficient for tools that are build against stage0 - // (e.g. stage1 rustdoc). Since `Assemble` for stage0 doesn't actually do anything, - // we won't have `libLLVM.so` in the stage0 sysroot. In the past, this wasn't - // a problem - we would copy the tool binary into its correct stage directory - // (e.g. stage1 for a stage1 rustdoc built against a stage0 compiler). - // Since libLLVM.so wasn't resolved until runtime, it was fine for it to - // not exist while we were building it. - // - // To ensure that we can still build stage1 tools against a stage0 compiler, - // we explicitly copy libLLVM.so into the stage0 sysroot when building - // the stage0 compiler. This ensures that tools built against stage0 - // will see libLLVM.so at build time, making the linker happy. - if compiler.stage == 0 { - builder.info(&format!("Installing libLLVM.so to stage 0 ({})", compiler.host)); - let sysroot = builder.sysroot(compiler); - dist::maybe_install_llvm_dylib(builder, compiler.host, &sysroot); - } - builder.ensure(RustcLink { compiler: builder.compiler(compiler.stage, builder.config.build), target_compiler: compiler, @@ -541,9 +581,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"); } @@ -671,6 +715,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) } } @@ -768,7 +836,8 @@ impl Step for Assemble { // Ensure that `libLLVM.so` ends up in the newly build compiler directory, // so that it can be found when the newly built `rustc` is run. - dist::maybe_install_llvm_dylib(builder, target_compiler.host, &sysroot); + dist::maybe_install_llvm_runtime(builder, target_compiler.host, &sysroot); + dist::maybe_install_llvm_target(builder, target_compiler.host, &sysroot); // Link the compiler binary itself into place let out_dir = builder.cargo_out(build_compiler, Mode::Rustc, host); @@ -792,14 +861,17 @@ pub fn add_to_sysroot( sysroot_host_dst: &Path, stamp: &Path, ) { + let self_contained_dst = &sysroot_dst.join("self-contained"); t!(fs::create_dir_all(&sysroot_dst)); t!(fs::create_dir_all(&sysroot_host_dst)); - for (path, host) in builder.read_stamp_file(stamp) { - if host { - builder.copy(&path, &sysroot_host_dst.join(path.file_name().unwrap())); - } else { - builder.copy(&path, &sysroot_dst.join(path.file_name().unwrap())); - } + t!(fs::create_dir_all(&self_contained_dst)); + for (path, dependency_type) in builder.read_stamp_file(stamp) { + let dst = match dependency_type { + DependencyType::Host => sysroot_host_dst, + DependencyType::Target => sysroot_dst, + DependencyType::TargetSelfContained => self_contained_dst, + }; + builder.copy(&path, &dst.join(path.file_name().unwrap())); } } @@ -808,7 +880,7 @@ pub fn run_cargo( cargo: Cargo, tail_args: Vec, stamp: &Path, - additional_target_deps: Vec, + additional_target_deps: Vec<(PathBuf, DependencyType)>, is_check: bool, ) -> Vec { if builder.config.dry_run { @@ -859,7 +931,7 @@ pub fn run_cargo( if filename.starts_with(&host_root_dir) { // Unless it's a proc macro used in the compiler if crate_types.iter().any(|t| t == "proc-macro") { - deps.push((filename.to_path_buf(), true)); + deps.push((filename.to_path_buf(), DependencyType::Host)); } continue; } @@ -867,7 +939,7 @@ pub fn run_cargo( // If this was output in the `deps` dir then this is a precise file // name (hash included) so we start tracking it. if filename.starts_with(&target_deps_dir) { - deps.push((filename.to_path_buf(), false)); + deps.push((filename.to_path_buf(), DependencyType::Target)); continue; } @@ -919,17 +991,21 @@ pub fn run_cargo( let candidate = format!("{}.lib", path_to_add); let candidate = PathBuf::from(candidate); if candidate.exists() { - deps.push((candidate, false)); + deps.push((candidate, DependencyType::Target)); } } - deps.push((path_to_add.into(), false)); + deps.push((path_to_add.into(), DependencyType::Target)); } - deps.extend(additional_target_deps.into_iter().map(|d| (d, false))); + deps.extend(additional_target_deps); deps.sort(); let mut new_contents = Vec::new(); - for (dep, proc_macro) in deps.iter() { - new_contents.extend(if *proc_macro { b"h" } else { b"t" }); + for (dep, dependency_type) in deps.iter() { + new_contents.extend(match *dependency_type { + DependencyType::Host => b"h", + DependencyType::Target => b"t", + DependencyType::TargetSelfContained => b"s", + }); new_contents.extend(dep.to_str().unwrap().as_bytes()); new_contents.extend(b"\0"); } @@ -949,7 +1025,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); @@ -973,7 +1053,13 @@ pub fn stream_cargo( for line in stdout.lines() { let line = t!(line); match serde_json::from_str::>(&line) { - Ok(msg) => cb(msg), + Ok(msg) => { + if builder.config.json_output { + // Forward JSON to stdout. + println!("{}", line); + } + cb(msg) + } // If this was informational, just print it out and continue Err(_) => println!("{}", line), } @@ -1008,4 +1094,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..47673ce1e8703 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") @@ -142,6 +141,8 @@ def v(*args): "rootfs in qemu testing, you probably don't want to use this") v("qemu-aarch64-rootfs", "target.aarch64-unknown-linux-gnu.qemu-rootfs", "rootfs in qemu testing, you probably don't want to use this") +v("qemu-riscv64-rootfs", "target.riscv64gc-unknown-linux-gnu.qemu-rootfs", + "rootfs in qemu testing, you probably don't want to use this") v("experimental-targets", "llvm.experimental-targets", "experimental LLVM targets to build") v("release-channel", "rust.channel", "the name of the release channel to build") diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 8215211ea1c9d..8a2463d378fdb 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -22,7 +22,7 @@ use crate::channel; use crate::compile; use crate::tool::{self, Tool}; use crate::util::{exe, is_dylib, timeit}; -use crate::{Compiler, Mode, LLVM_TOOLS}; +use crate::{Compiler, DependencyType, Mode, LLVM_TOOLS}; use time::{self, Timespec}; pub fn pkgname(builder: &Builder<'_>, component: &str) -> String { @@ -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()) @@ -308,7 +306,12 @@ fn make_win_dist( } //Copy platform tools to platform-specific bin directory - let target_bin_dir = plat_root.join("lib").join("rustlib").join(target_triple).join("bin"); + let target_bin_dir = plat_root + .join("lib") + .join("rustlib") + .join(target_triple) + .join("bin") + .join("self-contained"); fs::create_dir_all(&target_bin_dir).expect("creating target_bin_dir failed"); for src in target_tools { builder.copy_to_folder(&src, &target_bin_dir); @@ -323,7 +326,12 @@ fn make_win_dist( ); //Copy platform libs to platform-specific lib directory - let target_lib_dir = plat_root.join("lib").join("rustlib").join(target_triple).join("lib"); + let target_lib_dir = plat_root + .join("lib") + .join("rustlib") + .join(target_triple) + .join("lib") + .join("self-contained"); fs::create_dir_all(&target_lib_dir).expect("creating target_lib_dir failed"); for src in target_libs { builder.copy_to_folder(&src, &target_lib_dir); @@ -516,7 +524,7 @@ impl Step for Rustc { // components like the llvm tools and LLD. LLD is included below and // tools/LLDB come later, so let's just throw it in the rustc // component for now. - maybe_install_llvm_dylib(builder, host, image); + maybe_install_llvm_runtime(builder, host, image); // Copy over lld if it's there if builder.config.lld_enabled { @@ -621,19 +629,21 @@ impl Step for DebuggerScripts { cp_debugger_script("natvis/libcore.natvis"); cp_debugger_script("natvis/libstd.natvis"); } else { - cp_debugger_script("debugger_pretty_printers_common.py"); + cp_debugger_script("rust_types.py"); // gdb debugger scripts builder.install(&builder.src.join("src/etc/rust-gdb"), &sysroot.join("bin"), 0o755); builder.install(&builder.src.join("src/etc/rust-gdbgui"), &sysroot.join("bin"), 0o755); cp_debugger_script("gdb_load_rust_pretty_printers.py"); - cp_debugger_script("gdb_rust_pretty_printing.py"); + cp_debugger_script("gdb_lookup.py"); + cp_debugger_script("gdb_providers.py"); // lldb debugger scripts builder.install(&builder.src.join("src/etc/rust-lldb"), &sysroot.join("bin"), 0o755); - cp_debugger_script("lldb_rust_formatters.py"); + cp_debugger_script("lldb_lookup.py"); + cp_debugger_script("lldb_providers.py"); } } } @@ -652,9 +662,13 @@ fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { /// Copy stamped files into an image's `target/lib` directory. fn copy_target_libs(builder: &Builder<'_>, target: &str, image: &Path, stamp: &Path) { let dst = image.join("lib/rustlib").join(target).join("lib"); + let self_contained_dst = dst.join("self-contained"); t!(fs::create_dir_all(&dst)); - for (path, host) in builder.read_stamp_file(stamp) { - if !host || builder.config.build == target { + t!(fs::create_dir_all(&self_contained_dst)); + for (path, dependency_type) in builder.read_stamp_file(stamp) { + if dependency_type == DependencyType::TargetSelfContained { + builder.copy(&path, &self_contained_dst.join(path.file_name().unwrap())); + } else if dependency_type == DependencyType::Target || builder.config.build == target { builder.copy(&path, &dst.join(path.file_name().unwrap())); } } @@ -1330,7 +1344,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 +1362,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 +1382,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 +1424,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 +1659,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 +1690,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 +1767,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 +1808,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 +1864,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 +1988,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 +2070,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 +2081,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 +2096,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 +2131,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 +2234,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)); @@ -2254,27 +2244,18 @@ impl Step for HashSign { } } -// Maybe add libLLVM.so to the lib-dir. It will only have been built if -// LLVM tools are linked dynamically. -// -// We add this to both the libdir of the rustc binary itself (for it to load at -// runtime) and also to the target directory so it can find it at link-time. -// -// Note: This function does no yet support Windows but we also don't support -// linking LLVM tools dynamically on Windows yet. -pub fn maybe_install_llvm_dylib(builder: &Builder<'_>, target: Interned, sysroot: &Path) { +/// Maybe add libLLVM.so to the given destination lib-dir. It will only have +/// been built if LLVM tools are linked dynamically. +/// +/// Note: This function does not yet support Windows, but we also don't support +/// linking LLVM tools dynamically on Windows yet. +fn maybe_install_llvm(builder: &Builder<'_>, target: Interned, dst_libdir: &Path) { let src_libdir = builder.llvm_out(target).join("lib"); - let dst_libdir1 = sysroot.join("lib/rustlib").join(&*target).join("lib"); - let dst_libdir2 = - sysroot.join(builder.sysroot_libdir_relative(Compiler { stage: 1, host: target })); - t!(fs::create_dir_all(&dst_libdir1)); - t!(fs::create_dir_all(&dst_libdir2)); if target.contains("apple-darwin") { let llvm_dylib_path = src_libdir.join("libLLVM.dylib"); if llvm_dylib_path.exists() { - builder.install(&llvm_dylib_path, &dst_libdir1, 0o644); - builder.install(&llvm_dylib_path, &dst_libdir2, 0o644); + builder.install(&llvm_dylib_path, dst_libdir, 0o644); } return; } @@ -2288,11 +2269,23 @@ pub fn maybe_install_llvm_dylib(builder: &Builder<'_>, target: Interned, panic!("dist: Error calling canonicalize path `{}`: {}", llvm_dylib_path.display(), e); }); - builder.install(&llvm_dylib_path, &dst_libdir1, 0o644); - builder.install(&llvm_dylib_path, &dst_libdir2, 0o644); + builder.install(&llvm_dylib_path, dst_libdir, 0o644); } } +/// Maybe add libLLVM.so to the target lib-dir for linking. +pub fn maybe_install_llvm_target(builder: &Builder<'_>, target: Interned, sysroot: &Path) { + let dst_libdir = sysroot.join("lib/rustlib").join(&*target).join("lib"); + maybe_install_llvm(builder, target, &dst_libdir); +} + +/// Maybe add libLLVM.so to the runtime lib-dir for rustc itself. +pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: Interned, sysroot: &Path) { + let dst_libdir = + sysroot.join(builder.sysroot_libdir_relative(Compiler { stage: 1, host: target })); + maybe_install_llvm(builder, target, &dst_libdir); +} + #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct LlvmTools { pub target: Interned, @@ -2340,6 +2333,12 @@ impl Step for LlvmTools { builder.install(&exe, &dst_bindir, 0o755); } + // Copy libLLVM.so to the target lib dir as well, so the RPATH like + // `$ORIGIN/../lib` can find it. It may also be used as a dependency + // of `rustc-dev` to support the inherited `-lLLVM` when using the + // compiler libraries. + maybe_install_llvm_target(builder, target, &image); + // Prepare the overlay let overlay = tmp.join("llvm-tools-overlay"); drop(fs::remove_dir_all(&overlay)); @@ -2370,119 +2369,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 b0d9a5b94641c..6d7fb7acfcb04 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -70,6 +70,35 @@ book!( RustdocBook, "src/doc/rustdoc", "rustdoc"; ); +fn open(builder: &Builder<'_>, path: impl AsRef) { + if builder.config.dry_run || !builder.config.cmd.open() { + return; + } + + let path = path.as_ref(); + builder.info(&format!("Opening doc {}", path.display())); + if let Err(err) = opener::open(path) { + builder.info(&format!("{}\n", err)); + } +} + +// "src/libstd" -> ["src", "libstd"] +// +// Used for deciding whether a particular step is one requested by the user on +// the `x.py doc` command line, which determines whether `--open` will open that +// page. +fn components_simplified(path: &PathBuf) -> Vec<&str> { + path.iter().map(|component| component.to_str().unwrap_or("???")).collect() +} + +fn is_explicit_request(builder: &Builder<'_>, path: &str) -> bool { + builder + .paths + .iter() + .map(components_simplified) + .any(|requested| requested.iter().copied().eq(path.split("/"))) +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct UnstableBook { target: Interned, @@ -200,6 +229,12 @@ impl Step for TheBook { invoke_rustdoc(builder, compiler, target, path); } + + if is_explicit_request(builder, "src/doc/book") { + let out = builder.doc_out(target); + let index = out.join("book").join("index.html"); + open(builder, &index); + } } } @@ -313,6 +348,9 @@ impl Step for Standalone { } let mut cmd = builder.rustdoc_cmd(compiler); + // Needed for --index-page flag + cmd.arg("-Z").arg("unstable-options"); + cmd.arg("--html-after-content") .arg(&footer) .arg("--html-before-content") @@ -335,6 +373,13 @@ impl Step for Standalone { } builder.run(&mut cmd); } + + // We open doc/index.html as the default if invoked as `x.py doc --open` + // with no particular explicit doc requested (e.g. src/libcore). + if builder.paths.is_empty() || is_explicit_request(builder, "src/doc") { + let index = out.join("index.html"); + open(builder, &index); + } } } @@ -391,11 +436,11 @@ 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. - cargo.arg("-Z").arg("unstable-options").arg("-p").arg(package); + cargo.arg("-p").arg(package); // Create all crate output directories first to make sure rustdoc uses // relative links. // FIXME: Cargo should probably do this itself. @@ -406,6 +451,8 @@ impl Step for Std { .arg("rust.css") .arg("--markdown-no-toc") .arg("--generate-redirect-pages") + .arg("-Z") + .arg("unstable-options") .arg("--resource-suffix") .arg(crate::channel::CFG_RELEASE_NUM) .arg("--index-page") @@ -413,10 +460,25 @@ impl Step for Std { builder.run(&mut cargo.into()); }; - for krate in &["alloc", "core", "std", "proc_macro", "test"] { + let krates = ["alloc", "core", "std", "proc_macro", "test"]; + for krate in &krates { run_cargo_rustdoc_for(krate); } builder.cp_r(&my_out, &out); + + // Look for src/libstd, src/libcore etc in the `x.py doc` arguments and + // open the corresponding rendered docs. + for path in builder.paths.iter().map(components_simplified) { + if path.get(0) == Some(&"src") + && path.get(1).map_or(false, |dir| dir.starts_with("lib")) + { + let requested_crate = &path[1][3..]; + if krates.contains(&requested_crate) { + let index = out.join(requested_crate).join("index.html"); + open(builder, &index); + } + } + } } } @@ -473,7 +535,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`. @@ -482,8 +548,8 @@ impl Step for Rustc { // Find dependencies for top level crates. let mut compiler_crates = HashSet::new(); for root_crate in &["rustc_driver", "rustc_codegen_llvm", "rustc_codegen_ssa"] { - let interned_root_crate = INTERNER.intern_str(root_crate); - find_compiler_crates(builder, &interned_root_crate, &mut compiler_crates); + compiler_crates + .extend(builder.in_tree_crates(root_crate).into_iter().map(|krate| krate.name)); } for krate in &compiler_crates { @@ -498,22 +564,6 @@ impl Step for Rustc { } } -fn find_compiler_crates( - builder: &Builder<'_>, - name: &Interned, - crates: &mut HashSet>, -) { - // Add current crate. - crates.insert(*name); - - // Look for dependencies. - for dep in builder.crates.get(name).unwrap().deps.iter() { - if builder.crates.get(dep).unwrap().is_local(builder) { - find_compiler_crates(builder, dep, crates); - } - } -} - #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Rustdoc { stage: u32, diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index d8831c6d9e566..f477c75293385 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -3,19 +3,17 @@ //! This module implements the command-line parsing of the build system which //! has various flags to configure how it's run. -use std::fs; +use std::env; use std::path::PathBuf; use std::process; use getopts::Options; use crate::builder::Builder; +use crate::cache::{Interned, INTERNER}; use crate::config::Config; -use crate::metadata; use crate::{Build, DocTests}; -use crate::cache::{Interned, INTERNER}; - /// Deserialized version of all flags for this compile. pub struct Flags { pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo @@ -31,6 +29,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, @@ -60,6 +59,7 @@ pub enum Subcommand { }, Doc { paths: Vec, + open: bool, }, Test { paths: Vec, @@ -86,6 +86,9 @@ pub enum Subcommand { Install { paths: Vec, }, + Run { + paths: Vec, + }, } impl Default for Subcommand { @@ -102,17 +105,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`", ); @@ -143,7 +147,12 @@ To learn more about a subcommand, run `./x.py -h`", "N", ); opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); - opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS"); + let j_msg = format!( + "number of jobs to run in parallel; \ + defaults to {} (this host's logical CPU count)", + num_cpus::get() + ); + opts.optopt("j", "jobs", &j_msg, "JOBS"); opts.optflag("h", "help", "print this help message"); opts.optopt( "", @@ -152,6 +161,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 +188,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 +218,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( @@ -237,6 +252,9 @@ To learn more about a subcommand, run `./x.py -h`", "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); } + "doc" => { + opts.optflag("", "open", "open the docs in a browser"); + } "clean" => { opts.optflag("", "all", "clean all build artifacts"); } @@ -278,7 +296,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 +323,7 @@ Arguments: Once this is done, build/$ARCH/stage1 contains a usable compiler.", ); } - "check" => { + "check" | "c" => { subcommand_help.push_str( "\n Arguments: @@ -355,7 +373,7 @@ Arguments: ./x.py fmt --check", ); } - "test" => { + "test" | "t" => { subcommand_help.push_str( "\n Arguments: @@ -393,6 +411,7 @@ Arguments: ./x.py doc src/doc/book ./x.py doc src/doc/nomicon ./x.py doc src/doc/book src/libstd + ./x.py doc src/libstd --open If no arguments are passed then everything is documented: @@ -400,24 +419,29 @@ 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 let paths = matches.free[1..].iter().map(|p| p.into()).collect::>(); - let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| { - if fs::metadata("config.toml").is_ok() { - Some(PathBuf::from("config.toml")) - } else { - None - } - }); + let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from); // All subcommands except `clean` can have an optional "Available paths" section if matches.opt_present("verbose") { let config = Config::parse(&["build".to_string()]); - let mut build = Build::new(config); - metadata::build(&mut build); + let build = Build::new(config); let maybe_rules_help = Builder::get_help(&build, subcommand.as_str()); extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str()); @@ -434,11 +458,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"), @@ -456,7 +480,7 @@ Arguments: }, }, "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") }, - "doc" => Subcommand::Doc { paths }, + "doc" => Subcommand::Doc { paths, open: matches.opt_present("open") }, "clean" => { if !paths.is_empty() { println!("\nclean does not take a path argument\n"); @@ -468,17 +492,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() @@ -568,6 +614,13 @@ impl Subcommand { _ => None, } } + + pub fn open(&self) -> bool { + match *self { + Subcommand::Doc { open, .. } => open, + _ => false, + } + } } fn split(s: &[String]) -> Vec { diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs index a4acb14ee4b14..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, ); @@ -37,6 +37,9 @@ struct RustfmtConfig { } pub fn format(build: &Build, check: bool) { + if build.config.dry_run { + return; + } let mut builder = ignore::types::TypesBuilder::new(); builder.add_defaults(); builder.select("rust"); @@ -53,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..fbdef9d8272f7 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -70,7 +70,10 @@ fn install_sh( let libdir_default = PathBuf::from("lib"); let mandir_default = datadir_default.join("man"); let prefix = builder.config.prefix.as_ref().map_or(prefix_default, |p| { - fs::canonicalize(p).unwrap_or_else(|_| panic!("could not canonicalize {}", p.display())) + fs::create_dir_all(p) + .unwrap_or_else(|err| panic!("could not create {}: {}", p.display(), err)); + fs::canonicalize(p) + .unwrap_or_else(|err| panic!("could not canonicalize {}: {}", p.display(), err)) }); let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default); let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default); @@ -214,10 +217,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..e7aeb08643c29 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; @@ -242,6 +243,7 @@ pub struct Build { initial_rustc: PathBuf, initial_cargo: PathBuf, initial_lld: PathBuf, + initial_libdir: PathBuf, // Runtime state filled in later on // C/C++ compilers and archiver for all targets @@ -268,16 +270,22 @@ struct Crate { } impl Crate { - fn is_local(&self, build: &Build) -> bool { - self.path.starts_with(&build.config.src) && !self.path.to_string_lossy().ends_with("_shim") - } - fn local_path(&self, build: &Build) -> PathBuf { - assert!(self.is_local(build)); self.path.strip_prefix(&build.config.src).unwrap().into() } } +/// When building Rust various objects are handled differently. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum DependencyType { + /// Libraries originating from proc-macros. + Host, + /// Typical Rust libraries. + Target, + /// Non Rust libraries and objects shipped to ease usage of certain targets. + TargetSelfContained, +} + /// The various "modes" of invoking Cargo. /// /// These entries currently correspond to the various output directories of the @@ -343,18 +351,39 @@ impl Build { // we always try to use git for LLVM builds let in_tree_llvm_info = channel::GitInfo::new(false, &src.join("src/llvm-project")); - let initial_sysroot = config.initial_rustc.parent().unwrap().parent().unwrap(); - let initial_lld = initial_sysroot - .join("lib") - .join("rustlib") - .join(config.build) - .join("bin") - .join("rust-lld"); + let initial_target_libdir_str = if config.dry_run { + "/dummy/lib/path/to/lib/".to_string() + } else { + output( + Command::new(&config.initial_rustc) + .arg("--target") + .arg(config.build) + .arg("--print") + .arg("target-libdir"), + ) + }; + let initial_target_dir = Path::new(&initial_target_libdir_str).parent().unwrap(); + let initial_lld = initial_target_dir.join("bin").join("rust-lld"); + + let initial_sysroot = if config.dry_run { + "/dummy".to_string() + } else { + output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot")) + }; + let initial_libdir = initial_target_dir + .parent() + .unwrap() + .parent() + .unwrap() + .strip_prefix(initial_sysroot.trim()) + .unwrap() + .to_path_buf(); let mut build = Build { initial_rustc: config.initial_rustc.clone(), initial_cargo: config.initial_cargo.clone(), initial_lld, + initial_libdir, local_rebuild: config.local_rebuild, fail_fast: config.cmd.fail_fast(), doc_tests: config.cmd.doc_tests(), @@ -739,19 +768,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 +814,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)); @@ -940,29 +969,15 @@ impl Build { return s; } - let beta = output( - Command::new("git").arg("ls-remote").arg("origin").arg("beta").current_dir(&self.src), - ); - let beta = beta.trim().split_whitespace().next().unwrap(); - let master = output( - Command::new("git").arg("ls-remote").arg("origin").arg("master").current_dir(&self.src), - ); - let master = master.trim().split_whitespace().next().unwrap(); - - // Figure out where the current beta branch started. - let base = output( - Command::new("git").arg("merge-base").arg(beta).arg(master).current_dir(&self.src), - ); - let base = base.trim(); - - // Next figure out how many merge commits happened since we branched off - // beta. That's our beta number! + // Figure out how many merge commits happened since we branched off master. + // That's our beta number! + // (Note that we use a `..` range, not the `...` symmetric difference.) let count = output( Command::new("git") .arg("rev-list") .arg("--count") .arg("--merges") - .arg(format!("{}...HEAD", base)) + .arg("refs/remotes/origin/master..HEAD") .current_dir(&self.src), ); let n = count.trim().parse().unwrap(); @@ -1028,14 +1043,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") } @@ -1078,17 +1085,29 @@ impl Build { } } + /// Returns a Vec of all the dependencies of the given root crate, + /// including transitive dependencies and the root itself. Only includes + /// "local" crates (those in the local source tree, not from a registry). fn in_tree_crates(&self, root: &str) -> Vec<&Crate> { let mut ret = Vec::new(); let mut list = vec![INTERNER.intern_str(root)]; let mut visited = HashSet::new(); while let Some(krate) = list.pop() { let krate = &self.crates[&krate]; - if krate.is_local(self) { - ret.push(krate); - } + ret.push(krate); for dep in &krate.deps { - if visited.insert(dep) && dep != "build_helper" { + // Don't include optional deps if their features are not + // enabled. Ideally this would be computed from `cargo + // metadata --features …`, but that is somewhat slow. Just + // skip `build_helper` since there aren't any operations we + // want to perform on it. In the future, we may want to + // consider just filtering all build and dev dependencies in + // metadata::build. + if visited.insert(dep) + && dep != "build_helper" + && (dep != "profiler_builtins" || self.config.profiler) + && (dep != "rustc_codegen_llvm" || self.config.llvm_enabled()) + { list.push(*dep); } } @@ -1096,7 +1115,7 @@ impl Build { ret } - fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, bool)> { + fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> { if self.config.dry_run { return Vec::new(); } @@ -1109,9 +1128,14 @@ impl Build { if part.is_empty() { continue; } - let host = part[0] as char == 'h'; + let dependency_type = match part[0] as char { + 'h' => DependencyType::Host, + 's' => DependencyType::TargetSelfContained, + 't' => DependencyType::Target, + _ => unreachable!(), + }; let path = PathBuf::from(t!(str::from_utf8(&part[1..]))); - paths.push((path, host)); + paths.push((path, dependency_type)); } paths } diff --git a/src/bootstrap/metadata.rs b/src/bootstrap/metadata.rs index 292aa3b1e24a7..a38391c7b88f2 100644 --- a/src/bootstrap/metadata.rs +++ b/src/bootstrap/metadata.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; -use std::collections::HashSet; use std::path::PathBuf; use std::process::Command; @@ -12,7 +10,6 @@ use crate::{Build, Crate}; #[derive(Deserialize)] struct Output { packages: Vec, - resolve: Resolve, } #[derive(Deserialize)] @@ -21,63 +18,25 @@ struct Package { name: String, source: Option, manifest_path: String, + dependencies: Vec, } #[derive(Deserialize)] -struct Resolve { - nodes: Vec, -} - -#[derive(Deserialize)] -struct ResolveNode { - id: String, - dependencies: Vec, +struct Dependency { + name: String, + source: Option, } pub fn build(build: &mut Build) { - let mut resolves = Vec::new(); - build_krate(&build.std_features(), build, &mut resolves, "src/libstd"); - build_krate("", build, &mut resolves, "src/libtest"); - build_krate(&build.rustc_features(), build, &mut resolves, "src/rustc"); - - let mut id2name = HashMap::with_capacity(build.crates.len()); - for (name, krate) in build.crates.iter() { - id2name.insert(krate.id.clone(), name.clone()); - } - - for node in resolves { - let name = match id2name.get(&node.id) { - Some(name) => name, - None => continue, - }; - - let krate = build.crates.get_mut(name).unwrap(); - for dep in node.dependencies.iter() { - let dep = match id2name.get(dep) { - Some(dep) => dep, - None => continue, - }; - krate.deps.insert(*dep); - } - } -} - -fn build_krate(features: &str, build: &mut Build, resolves: &mut Vec, krate: &str) { // Run `cargo metadata` to figure out what crates we're testing. - // - // Down below we're going to call `cargo test`, but to test the right set - // of packages we're going to have to know what `-p` arguments to pass it - // to know what crates to test. Here we run `cargo metadata` to learn about - // the dependency graph and what `-p` arguments there are. let mut cargo = Command::new(&build.initial_cargo); cargo .arg("metadata") .arg("--format-version") .arg("1") - .arg("--features") - .arg(features) + .arg("--no-deps") .arg("--manifest-path") - .arg(build.src.join(krate).join("Cargo.toml")); + .arg(build.src.join("Cargo.toml")); let output = output(&mut cargo); let output: Output = serde_json::from_str(&output).unwrap(); for package in output.packages { @@ -85,8 +44,13 @@ fn build_krate(features: &str, build: &mut Build, resolves: &mut Vec, + 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, @@ -45,60 +112,14 @@ 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 llvm_info = &builder.in_tree_llvm_info; - 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 done_stamp = out_dir.join("llvm-finished-building"); - - if done_stamp.exists() { - if builder.config.llvm_skip_rebuild { - builder.info( - "Warning: \ - Using a potentially stale build of LLVM; \ - This may not behave well.", - ); - return build_llvm_config; - } - - if let Some(llvm_commit) = llvm_info.sha() { - let done_contents = t!(fs::read(&done_stamp)); - - // If LLVM was already built previously and the submodule's commit didn't change - // from the previous build, then no action is required. - if done_contents == llvm_commit.as_bytes() { - return build_llvm_config; - } - } else { - 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 `{}`", - done_stamp.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()); let _time = util::timeit(&builder); t!(fs::create_dir_all(&out_dir)); @@ -123,7 +144,7 @@ impl Step for Llvm { let llvm_exp_targets = match builder.config.llvm_experimental_targets { Some(ref s) => s, - None => "", + None => "AVR", }; let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" }; @@ -137,7 +158,6 @@ impl Step for Llvm { .define("LLVM_INCLUDE_TESTS", "OFF") .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") - .define("LLVM_ENABLE_ZLIB", "OFF") .define("WITH_POLLY", "OFF") .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") @@ -147,6 +167,14 @@ impl Step for Llvm { .define("LLVM_TARGET_ARCH", target.split('-').next().unwrap()) .define("LLVM_DEFAULT_TARGET_TRIPLE", target); + if !target.contains("netbsd") { + cfg.define("LLVM_ENABLE_ZLIB", "ON"); + } else { + // FIXME: Enable zlib on NetBSD too + // https://github.com/rust-lang/rust/pull/72696#issuecomment-641517185 + cfg.define("LLVM_ENABLE_ZLIB", "OFF"); + } + if builder.config.llvm_thin_lto { cfg.define("LLVM_ENABLE_LTO", "Thin"); if !target.contains("apple") { @@ -163,7 +191,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++"); @@ -191,17 +219,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(); @@ -240,10 +260,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 { @@ -271,7 +295,7 @@ impl Step for Llvm { cfg.build(); - t!(fs::write(&done_stamp, llvm_info.sha().unwrap_or(""))); + t!(stamp.write()); build_llvm_config } @@ -290,11 +314,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( @@ -480,10 +504,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)); @@ -584,17 +631,21 @@ impl Step for Sanitizers { return runtimes; } - let done_stamp = out_dir.join("sanitizers-finished-building"); - if done_stamp.exists() { - builder.info(&format!( - "Assuming that sanitizers rebuild is not necessary. \ - To force a rebuild, remove the file `{}`", - done_stamp.display() - )); + let stamp = out_dir.join("sanitizers-finished-building"); + let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); + + if stamp.is_done() { + if stamp.hash.is_none() { + builder.info(&format!( + "Rebuild sanitizers by removing the file `{}`", + stamp.path.display() + )); + } return runtimes; } builder.info(&format!("Building sanitizers for {}", self.target)); + t!(stamp.remove()); let _time = util::timeit(&builder); let mut cfg = cmake::Config::new(&compiler_rt_dir); @@ -623,8 +674,7 @@ impl Step for Sanitizers { cfg.build_target(&runtime.cmake_target); cfg.build(); } - - t!(fs::write(&done_stamp, b"")); + t!(stamp.write()); runtimes } @@ -646,46 +696,77 @@ fn supported_sanitizers( target: Interned, channel: &str, ) -> Vec { - let mut result = Vec::new(); + let darwin_libs = |os: &str, components: &[&str]| -> Vec { + components + .into_iter() + .map(move |c| SanitizerRuntime { + cmake_target: format!("clang_rt.{}_{}_dynamic", c, os), + path: out_dir + .join(&format!("build/lib/darwin/libclang_rt.{}_{}_dynamic.dylib", c, os)), + name: format!("librustc-{}_rt.{}.dylib", channel, c), + }) + .collect() + }; + + let common_libs = |os: &str, arch: &str, components: &[&str]| -> Vec { + components + .into_iter() + .map(move |c| SanitizerRuntime { + cmake_target: format!("clang_rt.{}-{}", c, arch), + path: out_dir.join(&format!("build/lib/{}/libclang_rt.{}-{}.a", os, c, arch)), + name: format!("librustc-{}_rt.{}.a", channel, c), + }) + .collect() + }; + match &*target { - "x86_64-apple-darwin" => { - for s in &["asan", "lsan", "tsan"] { - result.push(SanitizerRuntime { - cmake_target: format!("clang_rt.{}_osx_dynamic", s), - path: out_dir - .join(&format!("build/lib/darwin/libclang_rt.{}_osx_dynamic.dylib", s)), - name: format!("librustc-{}_rt.{}.dylib", channel, s), - }); - } + "aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), + "aarch64-unknown-linux-gnu" => { + common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan"]) } + "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), + "x86_64-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]), "x86_64-unknown-linux-gnu" => { - for s in &["asan", "lsan", "msan", "tsan"] { - result.push(SanitizerRuntime { - cmake_target: format!("clang_rt.{}-x86_64", s), - path: out_dir.join(&format!("build/lib/linux/libclang_rt.{}-x86_64.a", s)), - name: format!("librustc-{}_rt.{}.a", channel, s), - }); - } + common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"]) } - "x86_64-fuchsia" => { - for s in &["asan"] { - result.push(SanitizerRuntime { - cmake_target: format!("clang_rt.{}-x86_64", s), - path: out_dir.join(&format!("build/lib/fuchsia/libclang_rt.{}-x86_64.a", s)), - name: format!("librustc-{}_rt.{}.a", channel, s), - }); + _ => Vec::new(), + } +} + +struct HashStamp { + path: PathBuf, + hash: Option>, +} + +impl HashStamp { + fn new(path: PathBuf, hash: Option<&str>) -> Self { + HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) } + } + + fn is_done(&self) -> bool { + match fs::read(&self.path) { + Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(), + Err(e) if e.kind() == io::ErrorKind::NotFound => false, + Err(e) => { + panic!("failed to read stamp file `{}`: {}", self.path.display(), e); } } - "aarch64-fuchsia" => { - for s in &["asan"] { - result.push(SanitizerRuntime { - cmake_target: format!("clang_rt.{}-aarch64", s), - path: out_dir.join(&format!("build/lib/fuchsia/libclang_rt.{}-aarch64.a", s)), - name: format!("librustc-{}_rt.{}.a", channel, s), - }); + } + + fn remove(&self) -> io::Result<()> { + match fs::remove_file(&self.path) { + Ok(()) => Ok(()), + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + Ok(()) + } else { + Err(e) + } } } - _ => {} } - result + + fn write(&self) -> io::Result<()> { + fs::write(&self.path, self.hash.as_deref().unwrap_or(b"")) + } } 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 66fd2985cb459..bb35203c82604 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}; @@ -154,6 +154,7 @@ impl Step for Cargotest { fn run(self, builder: &Builder<'_>) { let compiler = builder.compiler(self.stage, self.host); builder.ensure(compile::Rustc { compiler, target: compiler.host }); + let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host }); // Note that this is a short, cryptic, and not scoped directory name. This // is currently to minimize the length of path on Windows where we otherwise @@ -165,7 +166,7 @@ impl Step for Cargotest { let mut cmd = builder.tool_cmd(Tool::CargoTest); try_run( builder, - cmd.arg(&builder.initial_cargo) + cmd.arg(&cargo) .arg(&out_dir) .env("RUSTC", builder.rustc(compiler)) .env("RUSTDOC", builder.rustdoc(compiler)), @@ -360,7 +361,12 @@ impl Step for Miri { let miri = builder.ensure(tool::Miri { compiler, target: self.host, extra_features: Vec::new() }); - if let Some(miri) = miri { + let cargo_miri = builder.ensure(tool::CargoMiri { + compiler, + target: self.host, + extra_features: Vec::new(), + }); + if let (Some(miri), Some(_cargo_miri)) = (miri, cargo_miri) { let mut cargo = builder.cargo(compiler, Mode::ToolRustc, host, "install"); cargo.arg("xargo"); // Configure `cargo install` path. cargo adds a `bin/`. @@ -378,14 +384,16 @@ impl Step for Miri { Mode::ToolRustc, host, "run", - "src/tools/miri", + "src/tools/miri/cargo-miri", SourceType::Submodule, &[], ); - cargo.arg("--bin").arg("cargo-miri").arg("--").arg("miri").arg("setup"); + cargo.arg("--").arg("miri").arg("setup"); // Tell `cargo miri setup` where to find the sources. cargo.env("XARGO_RUST_SRC", builder.src.join("src")); + // Tell it where to find Miri. + cargo.env("MIRI", &miri); // Debug things. cargo.env("RUST_BACKTRACE", "1"); // Overwrite bootstrap's `rustc` wrapper overwriting our flags. @@ -436,9 +444,10 @@ 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); + cargo.env("MIRI", miri); + + cargo.arg("--").args(builder.config.cmd.test_args()); builder.add_rustc_lib_path(compiler, &mut cargo); @@ -515,45 +524,37 @@ impl Step for Clippy { let host = self.host; let compiler = builder.compiler(stage, host); - let clippy = builder.ensure(tool::Clippy { + let clippy = builder + .ensure(tool::Clippy { compiler, target: self.host, extra_features: Vec::new() }) + .expect("in-tree tool"); + let mut cargo = tool::prepare_tool_cargo( + builder, compiler, - target: self.host, - extra_features: Vec::new(), - }); - if let Some(clippy) = clippy { - let mut cargo = tool::prepare_tool_cargo( - builder, - compiler, - Mode::ToolRustc, - host, - "test", - "src/tools/clippy", - SourceType::Submodule, - &[], - ); + Mode::ToolRustc, + host, + "test", + "src/tools/clippy", + SourceType::InTree, + &[], + ); - // clippy tests need to know about the stage sysroot - cargo.env("SYSROOT", builder.sysroot(compiler)); - cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler)); - cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); - let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir()); - let target_libs = builder - .stage_out(compiler, Mode::ToolRustc) - .join(&self.host) - .join(builder.cargo_dir()); - cargo.env("HOST_LIBS", host_libs); - cargo.env("TARGET_LIBS", target_libs); - // clippy tests need to find the driver - cargo.env("CLIPPY_DRIVER_PATH", clippy); + // clippy tests need to know about the stage sysroot + cargo.env("SYSROOT", builder.sysroot(compiler)); + cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler)); + cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); + let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir()); + let target_libs = + builder.stage_out(compiler, Mode::ToolRustc).join(&self.host).join(builder.cargo_dir()); + cargo.env("HOST_LIBS", host_libs); + cargo.env("TARGET_LIBS", target_libs); + // clippy tests need to find the driver + cargo.env("CLIPPY_DRIVER_PATH", clippy); - builder.add_rustc_lib_path(compiler, &mut cargo); + cargo.arg("--").args(builder.config.cmd.test_args()); - if try_run(builder, &mut cargo.into()) { - builder.save_toolstate("clippy-driver", ToolState::TestPass); - } - } else { - eprintln!("failed to test clippy: could not build"); - } + builder.add_rustc_lib_path(compiler, &mut cargo); + + builder.run(&mut cargo.into()); } } @@ -627,8 +628,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); @@ -728,9 +735,6 @@ impl Step for Tidy { let mut cmd = builder.tool_cmd(Tool::Tidy); cmd.arg(builder.src.join("src")); cmd.arg(&builder.initial_cargo); - if !builder.config.vendor { - cmd.arg("--no-vendor"); - } if builder.is_verbose() { cmd.arg("--verbose"); } @@ -753,6 +757,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") } @@ -868,8 +901,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", @@ -899,20 +930,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", - suite: "run-pass-valgrind", - default: false, - host: true -}); default_test!(RunMake { path: "src/test/run-make", mode: "run-make", suite: "run-make" }); @@ -1076,20 +1093,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); } @@ -1144,12 +1156,23 @@ impl Step for Compiletest { let llvm_config = builder.ensure(native::Llvm { target: builder.config.build }); if !builder.config.dry_run { let llvm_version = output(Command::new(&llvm_config).arg("--version")); + // Remove trailing newline from llvm-config output. + let llvm_version = llvm_version.trim_end(); cmd.arg("--llvm-version").arg(llvm_version); } if !builder.is_rust_llvm(target) { 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" { @@ -1495,7 +1518,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<'_>) { @@ -1503,14 +1526,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); } } @@ -1622,14 +1645,8 @@ impl Step for Crate { type Output = (); const DEFAULT: bool = true; - fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { - let builder = run.builder; - for krate in run.builder.in_tree_crates("test") { - if !(krate.name.starts_with("rustc_") && krate.name.ends_with("san")) { - run = run.path(krate.local_path(&builder).to_str().unwrap()); - } - } - run + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.krate("test") } fn make_run(run: RunConfig<'_>) { @@ -1682,7 +1699,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 }); @@ -1740,7 +1757,7 @@ impl Step for Crate { } else if builder.remote_tested(target) { cargo.env( format!("CARGO_TARGET_{}_RUNNER", envify(&target)), - format!("{} run", builder.tool_exe(Tool::RemoteTestClient).display()), + format!("{} run 0", builder.tool_exe(Tool::RemoteTestClient).display()), ); } diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 67e0ed5c58029..c92082a942316 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -12,7 +12,7 @@ use crate::channel; use crate::channel::GitInfo; use crate::compile; use crate::toolstate::ToolState; -use crate::util::{add_lib_path, exe, CiEnv}; +use crate::util::{add_dylib_path, exe, CiEnv}; use crate::Compiler; use crate::Mode; @@ -252,6 +252,10 @@ pub fn prepare_tool_cargo( // own copy cargo.env("LZMA_API_STATIC", "1"); + // CFG_RELEASE is needed by rustfmt (and possibly other tools) which + // import rustc-ap-rustc_attr which requires this to be set for the + // `#[cfg(version(...))]` attribute. + cargo.env("CFG_RELEASE", builder.rust_release()); cargo.env("CFG_RELEASE_CHANNEL", &builder.config.channel); cargo.env("CFG_VERSION", builder.rust_version()); cargo.env("CFG_RELEASE_NUM", channel::CFG_RELEASE_NUM); @@ -378,6 +382,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)] @@ -388,7 +393,7 @@ pub struct ErrorIndex { impl ErrorIndex { pub fn command(builder: &Builder<'_>, compiler: Compiler) -> Command { let mut cmd = Command::new(builder.ensure(ErrorIndex { compiler })); - add_lib_path( + add_dylib_path( vec![PathBuf::from(&builder.sysroot_libdir(compiler, compiler.host))], &mut cmd, ); @@ -476,7 +481,7 @@ impl Step for Rustdoc { const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("src/tools/rustdoc") + run.path("src/tools/rustdoc").path("src/librustdoc") } fn make_run(run: RunConfig<'_>) { @@ -590,6 +595,7 @@ macro_rules! tool_extended { $toolstate:ident, $path:expr, $tool_name:expr, + stable = $stable:expr, $extra_deps:block;)+) => { $( #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -601,12 +607,25 @@ macro_rules! tool_extended { impl Step for $name { type Output = Option; - const DEFAULT: bool = true; + const DEFAULT: bool = true; // Overwritten below const ONLY_HOSTS: bool = true; 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( + // By default, on nightly/dev enable all tools, else only + // build stable tools. + $stable || builder.build.unstable_features(), + // If `tools` is set, search list for this tool. + |tools| { + tools.iter().any(|tool| match tool.as_ref() { + "clippy" => $tool_name == "clippy-driver", + x => $tool_name == x, + }) + }), + ) } fn make_run(run: RunConfig<'_>) { @@ -636,23 +655,23 @@ macro_rules! tool_extended { } } +// Note: tools need to be also added to `Builder::get_step_descriptions` in `build.rs` +// to make `./x.py build ` work. tool_extended!((self, builder), - Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", {}; - CargoClippy, clippy, "src/tools/clippy", "cargo-clippy", {}; - Clippy, clippy, "src/tools/clippy", "clippy-driver", {}; - Miri, miri, "src/tools/miri", "miri", {}; - CargoMiri, miri, "src/tools/miri", "cargo-miri", {}; - Rls, rls, "src/tools/rls", "rls", { - let clippy = builder.ensure(Clippy { + Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", stable=true, {}; + CargoClippy, clippy, "src/tools/clippy", "cargo-clippy", stable=true, {}; + Clippy, clippy, "src/tools/clippy", "clippy-driver", stable=true, {}; + Miri, miri, "src/tools/miri", "miri", stable=false, {}; + CargoMiri, miri, "src/tools/miri/cargo-miri", "cargo-miri", stable=false, {}; + Rls, rls, "src/tools/rls", "rls", stable=true, { + 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", {}; + Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, {}; ); impl<'a> Builder<'a> { @@ -689,7 +708,7 @@ impl<'a> Builder<'a> { } } - add_lib_path(lib_paths, &mut cmd); + add_dylib_path(lib_paths, &mut cmd); cmd } } diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs index f0e0f92af55fc..8740393288c48 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."); @@ -273,6 +272,18 @@ impl Builder<'_> { /// `rust.save-toolstates` in `config.toml`. If unspecified, nothing will be /// done. The file is updated immediately after this function completes. pub fn save_toolstate(&self, tool: &str, state: ToolState) { + // If we're in a dry run setting we don't want to save toolstates as + // that means if we e.g. panic down the line it'll look like we tested + // everything (but we actually haven't). + if self.config.dry_run { + return; + } + // Toolstate isn't tracked for clippy, but since most tools do, we avoid + // checking in all the places we could save toolstate and just do so + // here. + if tool == "clippy-driver" { + return; + } if let Some(ref path) = self.config.save_toolstates { if let Some(parent) = path.parent() { // Ensure the parent directory always exists diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index eac790fe504b8..2bc6f1939d97b 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -40,7 +40,7 @@ pub fn libdir(target: &str) -> &'static str { } /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path. -pub fn add_lib_path(path: Vec, cmd: &mut Command) { +pub fn add_dylib_path(path: Vec, cmd: &mut Command) { let mut list = dylib_path(); for path in path { list.insert(0, path); @@ -72,6 +72,31 @@ pub fn dylib_path() -> Vec { env::split_paths(&var).collect() } +/// Adds a list of lookup paths to `cmd`'s link library lookup path. +pub fn add_link_lib_path(path: Vec, cmd: &mut Command) { + let mut list = link_lib_path(); + for path in path { + list.insert(0, path); + } + cmd.env(link_lib_path_var(), t!(env::join_paths(list))); +} + +/// Returns the environment variable which the link library lookup path +/// resides in for this platform. +fn link_lib_path_var() -> &'static str { + if cfg!(target_env = "msvc") { "LIB" } else { "LIBRARY_PATH" } +} + +/// Parses the `link_lib_path_var()` environment variable, returning a list of +/// paths that are members of this lookup path. +fn link_lib_path() -> Vec { + let var = match env::var_os(link_lib_path_var()) { + Some(v) => v, + None => return vec![], + }; + env::split_paths(&var).collect() +} + /// `push` all components to `buf`. On windows, append `.exe` to the last component. pub fn push_exe_path(mut buf: PathBuf, components: &[&str]) -> PathBuf { let (&file, components) = components.split_last().expect("at least one component required"); diff --git a/src/ci/azure-pipelines/auto.yml b/src/ci/azure-pipelines/auto.yml index 74b7469ea27b7..3de27bc54c5c0 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: @@ -76,7 +87,7 @@ jobs: # version that we're using, 8.2, cannot compile LLVM for OSX 10.7. x86_64-apple: SCRIPT: ./x.py test - RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc + INITIAL_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 @@ -85,7 +96,7 @@ jobs: dist-x86_64-apple: 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 + INITIAL_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 @@ -94,7 +105,7 @@ jobs: dist-x86_64-apple-alt: SCRIPT: ./x.py dist - RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc + INITIAL_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 @@ -111,33 +122,29 @@ jobs: matrix: # 32/64 bit MSVC tests x86_64-msvc-1: - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler + INITIAL_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 x86_64-msvc-2: - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler + INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler SCRIPT: make ci-subset-2 i686-msvc-1: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc + INITIAL_RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc SCRIPT: make ci-subset-1 # FIXME(#59637) NO_DEBUG_ASSERTIONS: 1 NO_LLVM_ASSERTIONS: 1 i686-msvc-2: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc + INITIAL_RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc SCRIPT: make ci-subset-2 # FIXME(#59637) NO_DEBUG_ASSERTIONS: 1 NO_LLVM_ASSERTIONS: 1 - # MSVC aux tests - x86_64-msvc-aux: - RUST_CHECK_TARGET: check-aux EXCLUDE_CARGO=1 - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc x86_64-msvc-cargo: SCRIPT: python x.py test src/tools/cargotest src/tools/cargo - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc + INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld VCVARS_BAT: vcvars64.bat # FIXME(#59637) NO_DEBUG_ASSERTIONS: 1 @@ -145,7 +152,7 @@ jobs: # MSVC tools tests x86_64-msvc-tools: SCRIPT: src/ci/docker/x86_64-gnu-tools/checktools.sh x.py - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --save-toolstates=/tmp/toolstate/toolstates.json + INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --save-toolstates=/tmp/toolstate/toolstates.json # 32/64-bit MinGW builds. # @@ -161,31 +168,31 @@ jobs: # came from the mingw-w64 SourceForge download site. Unfortunately # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. i686-mingw-1: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu + INITIAL_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 i686-mingw-2: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu + INITIAL_RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu SCRIPT: make ci-mingw-subset-2 CUSTOM_MINGW: 1 x86_64-mingw-1: SCRIPT: make ci-mingw-subset-1 - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu + INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu CUSTOM_MINGW: 1 # FIXME(#59637) NO_DEBUG_ASSERTIONS: 1 NO_LLVM_ASSERTIONS: 1 x86_64-mingw-2: SCRIPT: make ci-mingw-subset-2 - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu + INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu CUSTOM_MINGW: 1 # 32/64 bit MSVC and GNU deployment dist-x86_64-msvc: - RUST_CONFIGURE_ARGS: >- + INITIAL_RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc --enable-full-tools @@ -193,7 +200,7 @@ jobs: SCRIPT: python x.py dist DIST_REQUIRE_ALL_TOOLS: 1 dist-i686-msvc: - RUST_CONFIGURE_ARGS: >- + INITIAL_RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-msvc --target=i586-pc-windows-msvc --enable-full-tools @@ -201,17 +208,17 @@ jobs: SCRIPT: python x.py dist DIST_REQUIRE_ALL_TOOLS: 1 dist-i686-mingw: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-full-tools --enable-profiler + INITIAL_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 dist-x86_64-mingw: SCRIPT: python x.py dist - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-full-tools --enable-profiler + INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-full-tools --enable-profiler CUSTOM_MINGW: 1 DIST_REQUIRE_ALL_TOOLS: 1 # "alternate" deployment, see .travis.yml for more info dist-x86_64-msvc-alt: - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler + INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler SCRIPT: python x.py dist 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..e43116c06b6b7 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 @@ -71,10 +82,6 @@ steps: displayName: Install msys2 condition: and(succeeded(), not(variables.SKIP_JOB)) -- bash: src/ci/scripts/install-msys2-packages.sh - displayName: Install msys2 packages - condition: and(succeeded(), not(variables.SKIP_JOB)) - - bash: src/ci/scripts/install-mingw.sh displayName: Install MinGW condition: and(succeeded(), not(variables.SKIP_JOB)) diff --git a/src/ci/azure-pipelines/try.yml b/src/ci/azure-pipelines/try.yml index a29d6f9ae1ec6..818306a009229 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 @@ -15,8 +26,6 @@ jobs: strategy: matrix: dist-x86_64-linux: {} - dist-x86_64-linux-alt: - IMAGE: dist-x86_64-linux # The macOS and Windows builds here are currently disabled due to them not being # overly necessary on `try` builds. We also don't actually have anything that @@ -32,7 +41,7 @@ jobs: # matrix: # dist-x86_64-apple: # SCRIPT: ./x.py dist -# RUST_CONFIGURE_ARGS: --target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc +# INITIAL_RUST_CONFIGURE_ARGS: --target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc # DEPLOY: 1 # RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 # MACOSX_DEPLOYMENT_TARGET: 10.7 @@ -42,7 +51,7 @@ jobs: # # dist-x86_64-apple-alt: # SCRIPT: ./x.py dist -# RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc +# INITIAL_RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --set rust.jemalloc # DEPLOY_ALT: 1 # RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 # MACOSX_DEPLOYMENT_TARGET: 10.7 @@ -58,7 +67,7 @@ jobs: # strategy: # matrix: # dist-x86_64-msvc: -# RUST_CONFIGURE_ARGS: > +# INITIAL_RUST_CONFIGURE_ARGS: > # --build=x86_64-pc-windows-msvc # --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc # --enable-full-tools @@ -68,6 +77,6 @@ jobs: # DEPLOY: 1 # # dist-x86_64-msvc-alt: -# RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler +# INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler # SCRIPT: python x.py dist # DEPLOY_ALT: 1 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/disabled/riscv64gc-linux/0001-Remove-stime-function-calls.patch b/src/ci/docker/disabled/riscv64gc-linux/0001-Remove-stime-function-calls.patch new file mode 100644 index 0000000000000..08d0c5b2cac1e --- /dev/null +++ b/src/ci/docker/disabled/riscv64gc-linux/0001-Remove-stime-function-calls.patch @@ -0,0 +1,96 @@ +From c820da85c65c7f3aa9e9cb3ed71ada69bf9b783e Mon Sep 17 00:00:00 2001 +From: Alistair Francis +Date: Tue, 19 Nov 2019 13:06:40 +0100 +Subject: [PATCH] Remove stime() function calls + +stime() has been deprecated in glibc 2.31 and replaced with +clock_settime(). Let's replace the stime() function calls with +clock_settime() in preperation. + +function old new delta +rdate_main 197 224 +27 +clock_settime - 27 +27 +date_main 926 941 +15 +stime 37 - -37 +------------------------------------------------------------------------------ +(add/remove: 2/2 grow/shrink: 2/0 up/down: 69/-37) Total: 32 bytes + +Signed-off-by: Alistair Francis +Signed-off-by: Denys Vlasenko + +[Tom Eccles: adjust patch context to apply on top of 1.31.1-stable] +Signed-off-by: Tom Eccles +--- + coreutils/date.c | 6 +++++- + libbb/missing_syscalls.c | 8 -------- + util-linux/rdate.c | 8 ++++++-- + 3 files changed, 11 insertions(+), 11 deletions(-) + +diff --git a/coreutils/date.c b/coreutils/date.c +index 3414d38ae..4ade6abb4 100644 +--- a/coreutils/date.c ++++ b/coreutils/date.c +@@ -279,6 +279,9 @@ int date_main(int argc UNUSED_PARAM, char **argv) + time(&ts.tv_sec); + #endif + } ++#if !ENABLE_FEATURE_DATE_NANO ++ ts.tv_nsec = 0; ++#endif + localtime_r(&ts.tv_sec, &tm_time); + + /* If date string is given, update tm_time, and maybe set date */ +@@ -301,9 +304,10 @@ int date_main(int argc UNUSED_PARAM, char **argv) + if (date_str[0] != '@') + tm_time.tm_isdst = -1; + ts.tv_sec = validate_tm_time(date_str, &tm_time); ++ ts.tv_nsec = 0; + + /* if setting time, set it */ +- if ((opt & OPT_SET) && stime(&ts.tv_sec) < 0) { ++ if ((opt & OPT_SET) && clock_settime(CLOCK_REALTIME, &ts) < 0) { + bb_perror_msg("can't set date"); + } + } +diff --git a/libbb/missing_syscalls.c b/libbb/missing_syscalls.c +index 87cf59b3d..dc40d9155 100644 +--- a/libbb/missing_syscalls.c ++++ b/libbb/missing_syscalls.c +@@ -15,14 +15,6 @@ pid_t getsid(pid_t pid) + return syscall(__NR_getsid, pid); + } + +-int stime(const time_t *t) +-{ +- struct timeval tv; +- tv.tv_sec = *t; +- tv.tv_usec = 0; +- return settimeofday(&tv, NULL); +-} +- + int sethostname(const char *name, size_t len) + { + return syscall(__NR_sethostname, name, len); +diff --git a/util-linux/rdate.c b/util-linux/rdate.c +index 70f829e7f..878375d78 100644 +--- a/util-linux/rdate.c ++++ b/util-linux/rdate.c +@@ -95,9 +95,13 @@ int rdate_main(int argc UNUSED_PARAM, char **argv) + if (!(flags & 2)) { /* no -p (-s may be present) */ + if (time(NULL) == remote_time) + bb_error_msg("current time matches remote time"); +- else +- if (stime(&remote_time) < 0) ++ else { ++ struct timespec ts; ++ ts.tv_sec = remote_time; ++ ts.tv_nsec = 0; ++ if (clock_settime(CLOCK_REALTIME, &ts) < 0) + bb_perror_msg_and_die("can't set time of day"); ++ } + } + + if (flags != 1) /* not lone -s */ +-- +2.25.1 + diff --git a/src/ci/docker/disabled/riscv64gc-linux/Dockerfile b/src/ci/docker/disabled/riscv64gc-linux/Dockerfile new file mode 100644 index 0000000000000..f21dc2ba309b8 --- /dev/null +++ b/src/ci/docker/disabled/riscv64gc-linux/Dockerfile @@ -0,0 +1,102 @@ +# based on armhf-gnu/Dockerfile +FROM ubuntu:20.04 + +RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections +RUN apt-get update -y && apt-get install -y --no-install-recommends \ + bc \ + bison \ + ca-certificates \ + cmake \ + cpio \ + curl \ + debian-ports-archive-keyring \ + debootstrap \ + flex \ + gcc \ + gcc-riscv64-linux-gnu \ + git \ + g++-riscv64-linux-gnu \ + g++ \ + libc6-dev \ + libc6-dev-riscv64-cross \ + make \ + patch \ + python3 \ + qemu-system-misc \ + xz-utils + +ENV ARCH=riscv +ENV CROSS_COMPILE=riscv64-linux-gnu- + +WORKDIR /build + +# From https://github.com/michaeljclark/busybear-linux/blob/master/conf/linux.config +COPY riscv64gc-linux/linux.config /build + +# Compile the kernel that we're going to be emulating with. This is +# basically just done to be compatible with the QEMU target that we're going +# to be using when running tests. +RUN curl https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.6.16.tar.xz | tar xJf - && \ + cp linux.config linux-5.6.16/.config && \ + cd /build/linux-5.6.16 && \ + make olddefconfig && \ + make -j$(nproc) vmlinux +RUN cp linux-5.6.16/vmlinux /tmp +RUN rm -rf linux-5.6.16 + +# Compile an instance of busybox as this provides a lightweight system and init +# binary which we will boot into. Only trick here is configuring busybox to +# build static binaries. +RUN curl https://busybox.net/downloads/busybox-1.31.1.tar.bz2 | tar xjf - +COPY riscv64gc-linux/0001-Remove-stime-function-calls.patch /build/busybox-1.31.1/ +RUN cd /build/busybox-1.31.1 && \ + patch -p1 -i 0001-Remove-stime-function-calls.patch && \ + make defconfig && \ + sed -i 's/.*CONFIG_STATIC.*/CONFIG_STATIC=y/' .config && \ + make -j$(nproc) && \ + make install && \ + mv _install /tmp/rootfs && \ + cd /build && \ + rm -rf busybox-1.31.1 + +# Download the ubuntu rootfs, which we'll use as a chroot for all our tests +# This is only needed to provide /lib/* and /usr/lib/* +WORKDIR /tmp +RUN debootstrap --variant=minbase --arch=riscv64 --foreign focal /tmp/rootfs/ubuntu +RUN cd rootfs && mkdir proc sys dev etc etc/init.d +# rootfs/ubuntu/proc is in a weird state (access fails with ELOOP) until +# rootfs/ubuntu/debootstrap/debootstrap --second-stage is run (under emulation), +# but this takes ages. Instead hack it into a good enough state. +# /proc is used by std::env::current_exe() (which is roughly +# `readlink /proc/self/exe`) +RUN cd rootfs/ubuntu && rm -rf proc && mkdir proc + +# Copy over our init script, which starts up our test server and also a few other +# misc tasks +COPY scripts/qemu-bare-bones-rcS rootfs/etc/init.d/rcS +RUN chmod +x rootfs/etc/init.d/rcS + +# Helper to quickly fill the entropy pool in the kernel +COPY scripts/qemu-bare-bones-addentropy.c /tmp/addentropy.c +RUN riscv64-linux-gnu-gcc addentropy.c -o rootfs/addentropy -static + +# download and build the riscv bootloader +RUN git clone https://github.com/riscv/riscv-pk +WORKDIR /tmp/riscv-pk +# nothing special about this revision: it is just master at the time of writing +# v1.0.0 doesn't build +RUN git checkout 5d9ed238e1cabfbca3c47f50d32894ce94bfc304 +RUN mkdir build && cd build && \ + ../configure --with-payload=/tmp/vmlinux --host=riscv64-linux-gnu && \ + make -j$(nproc) && \ + cp bbl /tmp +WORKDIR /tmp +RUN rm -rf /tmp/riscv-pk + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV RUST_CONFIGURE_ARGS --qemu-riscv64-rootfs=/tmp/rootfs +ENV SCRIPT python3 ../x.py test --target riscv64gc-unknown-linux-gnu + +ENV NO_CHANGE_USER=1 diff --git a/src/ci/docker/disabled/riscv64gc-linux/linux.config b/src/ci/docker/disabled/riscv64gc-linux/linux.config new file mode 100644 index 0000000000000..5142664742f20 --- /dev/null +++ b/src/ci/docker/disabled/riscv64gc-linux/linux.config @@ -0,0 +1,51 @@ +CONFIG_DEFAULT_HOSTNAME="busybear" +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_BPF_SYSCALL=y +CONFIG_SMP=y +CONFIG_MODULES=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_PACKET_DIAG=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_NETLINK_DIAG=y +# CONFIG_WIRELESS is not set +CONFIG_PCI=y +CONFIG_DEVTMPFS=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_VIRTIO_BLK=y +CONFIG_NETDEVICES=y +CONFIG_VIRTIO_NET=y +# CONFIG_ETHERNET is not set +# CONFIG_WLAN is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_HVC_RISCV_SBI=y +# CONFIG_HW_RANDOM is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_VIRTIO_MMIO=y +CONFIG_SIFIVE_PLIC=y +CONFIG_RAS=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_AUTOFS4_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +# CONFIG_CRYPTO_ECHAINIV is not set +# CONFIG_CRYPTO_HW is not set +CONFIG_PRINTK_TIME=y diff --git a/src/ci/docker/dist-aarch64-linux/Dockerfile b/src/ci/docker/dist-aarch64-linux/Dockerfile index f5eb66ed7142f..fd764965ef21b 100644 --- a/src/ci/docker/dist-aarch64-linux/Dockerfile +++ b/src/ci/docker/dist-aarch64-linux/Dockerfile @@ -33,7 +33,8 @@ 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 \ + --enable-sanitizers \ --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 a54a2d003b649..6d38e199564b1 100644 --- a/src/ci/docker/dist-android/Dockerfile +++ b/src/ci/docker/dist-android/Dockerfile @@ -23,6 +23,7 @@ ENV TARGETS=$TARGETS,x86_64-linux-android ENV RUST_CONFIGURE_ARGS \ --enable-extended \ + --enable-profiler \ --arm-linux-androideabi-ndk=/android/ndk/arm-14 \ --armv7-linux-androideabi-ndk=/android/ndk/arm-14 \ --thumbv7neon-linux-androideabi-ndk=/android/ndk/arm-14 \ @@ -31,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..43f5581f996ea 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: @@ -28,6 +28,29 @@ RUN apt-get update && apt-get build-dep -y clang llvm && apt-get install -y --no RUN apt-key adv --batch --yes --keyserver keyserver.ubuntu.com --recv-keys 74DA7924C5513486 RUN add-apt-repository -y 'deb http://apt.dilos.org/dilos dilos2 main' +ENV \ + AR_x86_64_fuchsia=x86_64-fuchsia-ar \ + CC_x86_64_fuchsia=x86_64-fuchsia-clang \ + CXX_x86_64_fuchsia=x86_64-fuchsia-clang++ \ + AR_aarch64_fuchsia=aarch64-fuchsia-ar \ + CC_aarch64_fuchsia=aarch64-fuchsia-clang \ + CXX_aarch64_fuchsia=aarch64-fuchsia-clang++ \ + AR_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-ar \ + CC_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-gcc \ + CXX_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-g++ \ + AR_x86_64_sun_solaris=x86_64-sun-solaris2.10-ar \ + CC_x86_64_sun_solaris=x86_64-sun-solaris2.10-gcc \ + CXX_x86_64_sun_solaris=x86_64-sun-solaris2.10-g++ \ + CC_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-gcc-7 \ + CXX_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-g++-7 \ + AR_x86_64_fortanix_unknown_sgx=ar \ + CC_x86_64_fortanix_unknown_sgx=x86_64-fortanix-unknown-sgx-clang-11 \ + CFLAGS_x86_64_fortanix_unknown_sgx="-mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ + CXX_x86_64_fortanix_unknown_sgx=x86_64-fortanix-unknown-sgx-clang++-11 \ + CXXFLAGS_x86_64_fortanix_unknown_sgx="-mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ + CC=gcc-7 \ + CXX=g++-7 + WORKDIR /build COPY scripts/musl.sh /build RUN env \ @@ -46,9 +69,11 @@ COPY dist-various-2/build-solaris-toolchain.sh /tmp/ RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386 RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc COPY dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ +COPY dist-various-2/x86_64-fortanix-unknown-sgx-clang-wrap.sh /usr/bin/x86_64-fortanix-unknown-sgx-clang-11 +RUN ln -s /usr/bin/x86_64-fortanix-unknown-sgx-clang-11 /usr/bin/x86_64-fortanix-unknown-sgx-clang++-11 # We pass the commit id of the port of LLVM's libunwind to the build script. # Any update to the commit id here, should cause the container image to be re-built from this point on. -RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh "5125c169b30837208a842f85f7ae44a83533bd0e" +RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh "800f95131fe6acd20b96b6f4723ca3c820f3d379" COPY dist-various-2/build-wasi-toolchain.sh /tmp/ RUN /tmp/build-wasi-toolchain.sh @@ -56,24 +81,6 @@ RUN /tmp/build-wasi-toolchain.sh COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -ENV \ - AR_x86_64_fuchsia=x86_64-fuchsia-ar \ - CC_x86_64_fuchsia=x86_64-fuchsia-clang \ - CXX_x86_64_fuchsia=x86_64-fuchsia-clang++ \ - AR_aarch64_fuchsia=aarch64-fuchsia-ar \ - CC_aarch64_fuchsia=aarch64-fuchsia-clang \ - CXX_aarch64_fuchsia=aarch64-fuchsia-clang++ \ - AR_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-ar \ - CC_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-gcc \ - CXX_sparcv9_sun_solaris=sparcv9-sun-solaris2.10-g++ \ - AR_x86_64_sun_solaris=x86_64-sun-solaris2.10-ar \ - CC_x86_64_sun_solaris=x86_64-sun-solaris2.10-gcc \ - CXX_x86_64_sun_solaris=x86_64-sun-solaris2.10-g++ \ - CC_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-gcc-7 \ - CXX_armv7_unknown_linux_gnueabi=arm-linux-gnueabi-g++-7 \ - CC=gcc-7 \ - CXX=g++-7 - ENV CARGO_TARGET_X86_64_FUCHSIA_AR /usr/local/bin/llvm-ar ENV CARGO_TARGET_X86_64_FUCHSIA_RUSTFLAGS \ -C link-arg=--sysroot=/usr/local/x86_64-fuchsia \ @@ -110,4 +117,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-various-2/build-wasi-toolchain.sh b/src/ci/docker/dist-various-2/build-wasi-toolchain.sh index b868677564298..c82031690ab6a 100755 --- a/src/ci/docker/dist-various-2/build-wasi-toolchain.sh +++ b/src/ci/docker/dist-various-2/build-wasi-toolchain.sh @@ -12,7 +12,7 @@ export PATH=`pwd`/clang+llvm-9.0.0-x86_64-linux-gnu-ubuntu-14.04/bin:$PATH git clone https://github.com/CraneStation/wasi-libc cd wasi-libc -git reset --hard 1fad33890a5e299027ce0eab7b6ad5260585e347 +git reset --hard 9efc2f428358564fe64c374d762d0bfce1d92507 make -j$(nproc) INSTALL_DIR=/wasm32-wasi install cd .. diff --git a/src/ci/docker/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh b/src/ci/docker/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh index 725ec341b9497..4294b1ef93dd8 100755 --- a/src/ci/docker/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh +++ b/src/ci/docker/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh @@ -13,12 +13,15 @@ url="https://github.com/fortanix/llvm-project/archive/${1}.tar.gz" repo_name="llvm-project" install_prereq() { + curl https://apt.llvm.org/llvm-snapshot.gpg.key|apt-key add - + add-apt-repository -y 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main' apt-get update apt-get install -y --no-install-recommends \ build-essential \ ca-certificates \ cmake \ - git + git \ + clang-11 } build_unwind() { @@ -35,7 +38,14 @@ build_unwind() { # Build libunwind mkdir -p build cd build + target_CC="CC_${target//-/_}" + target_CXX="CXX_${target//-/_}" + target_CFLAGS="CFLAGS_${target//-/_}" + target_CXXFLAGS="CXXFLAGS_${target//-/_}" cmake -DCMAKE_BUILD_TYPE="RELEASE" -DRUST_SGX=1 -G "Unix Makefiles" \ + -DCMAKE_C_COMPILER="${!target_CC}" -DCMAKE_CXX_COMPILER="${!target_CXX}" \ + -DCMAKE_C_FLAGS="${!target_CFLAGS}" -DCMAKE_CXX_FLAGS="${!target_CXXFLAGS}" \ + -DCMAKE_C_COMPILER_TARGET=$target -DCMAKE_CXX_COMPILER_TARGET=$target \ -DLLVM_ENABLE_WARNINGS=1 -DLIBUNWIND_ENABLE_WERROR=1 -DLIBUNWIND_ENABLE_PEDANTIC=0 \ -DLLVM_PATH=../../llvm/ ../ make unwind_static diff --git a/src/ci/docker/dist-various-2/x86_64-fortanix-unknown-sgx-clang-wrap.sh b/src/ci/docker/dist-various-2/x86_64-fortanix-unknown-sgx-clang-wrap.sh new file mode 100755 index 0000000000000..c4ff44c37b1e3 --- /dev/null +++ b/src/ci/docker/dist-various-2/x86_64-fortanix-unknown-sgx-clang-wrap.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +args=("$@") + +for i in "${!args[@]}"; do + # x86_64-fortanix-unknown-sgx doesn't have a C sysroot for things like + # stdint.h and the C++ STL. Unlike GCC, clang will not use the host's + # sysroot instead. Force it. + if [ "${args[$i]}" = "--target=x86_64-fortanix-unknown-sgx" ]; then + args[$i]="--target=x86_64-unknown-linux-gnu" + fi +done + +exec "${0/x86_64-fortanix-unknown-sgx-clang/clang}" "${args[@]}" 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 b2d96aed2a9e8..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,7 +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 && \ +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/scripts/musl-toolchain.sh b/src/ci/docker/scripts/musl-toolchain.sh index 74ba2f0eadb25..1ae412340cb11 100644 --- a/src/ci/docker/scripts/musl-toolchain.sh +++ b/src/ci/docker/scripts/musl-toolchain.sh @@ -3,7 +3,7 @@ # # Versions of the toolchain components are configurable in `musl-cross-make/Makefile` and # musl unlike GLIBC is forward compatible so upgrading it shouldn't break old distributions. -# Right now we have: Binutils 2.27, GCC 6.4.0, musl 1.1.22. +# Right now we have: Binutils 2.31.1, GCC 9.2.0, musl 1.1.24. set -ex hide_output() { @@ -33,11 +33,13 @@ shift # Apparently applying `-fPIC` everywhere allows them to link successfully. export CFLAGS="-fPIC $CFLAGS" -git clone https://github.com/richfelker/musl-cross-make -b v0.9.8 +git clone https://github.com/richfelker/musl-cross-make # -b v0.9.9 cd musl-cross-make +# A few commits ahead of v0.9.9 to include the cowpatch fix: +git checkout a54eb56f33f255dfca60be045f12a5cfaf5a72a9 -hide_output make -j$(nproc) TARGET=$TARGET -hide_output make install TARGET=$TARGET OUTPUT=$OUTPUT +hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.1.24 +hide_output make install TARGET=$TARGET MUSL_VER=1.1.24 OUTPUT=$OUTPUT cd - diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh index d847c407aba67..58393a5719a10 100644 --- a/src/ci/docker/scripts/musl.sh +++ b/src/ci/docker/scripts/musl.sh @@ -24,7 +24,7 @@ shift # Apparently applying `-fPIC` everywhere allows them to link successfully. export CFLAGS="-fPIC $CFLAGS" -MUSL=musl-1.1.22 +MUSL=musl-1.1.24 # may have been downloaded in a previous run if [ ! -d $MUSL ]; then 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..8232539edda77 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,10 +20,16 @@ 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/ ENV BINARYEN_ROOT=/emsdk-portable/upstream/ +ENV EMSDK=/emsdk-portable +ENV EM_CONFIG=/emsdk-portable/.emscripten +ENV EM_CACHE=/emsdk-portable/upstream/emscripten/cache ENV TARGETS=wasm32-unknown-emscripten @@ -35,7 +41,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 4cd9e164558c0..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 src/tools/tidy && python2.7 ../x.py test - -# 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..b4b23a245e0aa 100755 --- a/src/ci/docker/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/x86_64-gnu-tools/checktools.sh @@ -7,19 +7,20 @@ 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 \ src/tools/miri \ set -e -python2.7 "$X_PY" test check-tools +# debugging: print out the saved toolstates +cat /tmp/toolstate/toolstates.json +python3 "$X_PY" test check-tools +python3 "$X_PY" test src/tools/clippy 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..5e67567c76b4f --- /dev/null +++ b/src/ci/github-actions/ci.yml @@ -0,0 +1,643 @@ +###################################################### +# 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 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 + + 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 + + ###################### + # 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-cargo + env: + SCRIPT: python x.py test src/tools/cargotest src/tools/cargo + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld + 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 + + auto-fallible: + <<: *base-ci-job + name: auto-fallible + 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: + #################### + # 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 + + 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 deleted file mode 100755 index 843a2bf2d5e55..0000000000000 --- a/src/ci/scripts/install-msys2-packages.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -euo pipefail -IFS=$'\n\t' - -source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" - -if isWindows; then - pacman -S --noconfirm --needed base-devel ca-certificates make diffutils tar \ - binutils - - # 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. - cp C:/Python27amd64/python.exe C:/Python27amd64/python2.7.exe - ciCommandAddPath "C:\\Python27amd64" -fi diff --git a/src/ci/scripts/install-msys2.sh b/src/ci/scripts/install-msys2.sh index 9e899ba9d8947..3a0c965a67710 100755 --- a/src/ci/scripts/install-msys2.sh +++ b/src/ci/scripts/install-msys2.sh @@ -1,10 +1,6 @@ #!/bin/bash # Download and install MSYS2, needed primarily for the test suite (run-make) but # also used by the MinGW toolchain for assembling things. -# -# FIXME: we should probe the default azure image and see if we can use the MSYS2 -# toolchain there. (if there's even one there). For now though this gets the job -# done. set -euo pipefail IFS=$'\n\t' @@ -12,14 +8,26 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" if isWindows; then - # Pre-followed the api/v2 URL to the CDN since the API can be a bit flakey - curl -sSL https://packages.chocolatey.org/msys2.20190524.0.0.20191030.nupkg > \ - msys2.nupkg - curl -sSL https://packages.chocolatey.org/chocolatey-core.extension.1.3.5.1.nupkg > \ - chocolatey-core.extension.nupkg - choco install -s . msys2 \ - --params="/InstallDir:$(ciCheckoutPath)/msys2 /NoPath" -y --no-progress - rm msys2.nupkg chocolatey-core.extension.nupkg - mkdir -p "$(ciCheckoutPath)/msys2/home/${USERNAME}" - ciCommandAddPath "$(ciCheckoutPath)/msys2/usr/bin" + msys2Path="c:/msys64" + mkdir -p "${msys2Path}/home/${USERNAME}" + ciCommandAddPath "${msys2Path}/usr/bin" + + echo "switching shell to use our own bash" + ciCommandSetEnv CI_OVERRIDE_SHELL "${msys2Path}/usr/bin/bash.exe" + + # 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/${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/setup-environment.sh b/src/ci/scripts/setup-environment.sh index e126a06edab73..411ef6f9b2822 100755 --- a/src/ci/scripts/setup-environment.sh +++ b/src/ci/scripts/setup-environment.sh @@ -8,6 +8,37 @@ IFS=$'\n\t' 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 +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..4e7c00bece154 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 6fb3705e5230311b096d47f7e2c91f9ce24393d0 +Subproject commit 4e7c00bece1544d409312ec93467beb62b5bd0cb diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 37f9e68484111..82bec5877c77c 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 37f9e6848411188a1062ead1bd8ebe4b8aa16899 +Subproject commit 82bec5877c77cfad530ca11095db4456d757f668 diff --git a/src/doc/embedded-book b/src/doc/embedded-book index b81ffb7a6f4c5..616962ad0dd80 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit b81ffb7a6f4c5aaed92786e770e99db116aa4ebd +Subproject commit 616962ad0dd80f34d8b802da038d0aed9dd691bb diff --git a/src/doc/nomicon b/src/doc/nomicon index 9f797e65e6bcc..bfe1ab96d717d 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 9f797e65e6bcc79419975b17aff8e21c9adc039f +Subproject commit bfe1ab96d717d1dda50e499b360f2e2f57e1750a diff --git a/src/doc/reference b/src/doc/reference index 559e09caa9661..04d5d5d7ba624 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 559e09caa9661043744cf7af7bd88432d966f743 +Subproject commit 04d5d5d7ba624b6f5016298451f3a63d557f3260 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index db57f899ea2a5..6f94ccb48da6f 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit db57f899ea2a56a544c8d280cbf033438666273d +Subproject commit 6f94ccb48da6fa4ed0031290f21411cf789f7d5e 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-ux-guidelines.md b/src/doc/rustc-ux-guidelines.md deleted file mode 100644 index b626923bcb59c..0000000000000 --- a/src/doc/rustc-ux-guidelines.md +++ /dev/null @@ -1,90 +0,0 @@ -% Rustc UX guidelines - -Don't forget the user. Whether human or another program, such as an IDE, a -good user experience with the compiler goes a long way toward making developers' -lives better. We do not want users to be baffled by compiler output or -learn arcane patterns to compile their program. - -## Error, Warning, Help, Note Messages - -When the compiler detects a problem, it can emit one of the following: an error, a warning, -a note, or a help message. - -An `error` is emitted when the compiler detects a problem that makes it unable - to compile the program, either because the program is invalid or the - programmer has decided to make a specific `warning` into an error. - -A `warning` is emitted when the compiler detects something odd about a -program. For instance, dead code and unused `Result` values. - -A `help` message is emitted following an `error` or `warning` to give additional -information to the user about how to solve their problem. - -A `note` is emitted to identify additional circumstances and parts of the code -that caused the warning or error. For example, the borrow checker will note any -previous conflicting borrows. - -* Write in plain simple English. If your message, when shown on a – possibly -small – screen (which hasn't been cleaned for a while), cannot be understood -by a normal programmer, who just came out of bed after a night partying, it's -too complex. -* `Errors` and `Warnings` should not suggest how to fix the problem. A `Help` -message should be emitted instead. -* `Error`, `Warning`, `Note`, and `Help` messages start with a lowercase -letter and do not end with punctuation. -* Error messages should be succinct. Users will see these error messages many -times, and more verbose descriptions can be viewed with the `--explain` flag. -That said, don't make it so terse that it's hard to understand. -* The word "illegal" is illegal. Prefer "invalid" or a more specific word -instead. -* Errors should document the span of code where they occur – the `span_..` -methods allow to easily do this. Also `note` other spans that have contributed -to the error if the span isn't too large. -* When emitting a message with span, try to reduce the span to the smallest -amount possible that still signifies the issue -* Try not to emit multiple error messages for the same error. This may require -detecting duplicates. -* When the compiler has too little information for a specific error message, -lobby for annotations for library code that allow adding more. For example see -`#[on_unimplemented]`. Use these annotations when available! -* Keep in mind that Rust's learning curve is rather steep, and that the -compiler messages are an important learning tool. - -## Error Explanations - -Error explanations are long form descriptions of error messages provided with -the compiler. They are accessible via the `--explain` flag. Each explanation -comes with an example of how to trigger it and advice on how to fix it. - -Please read [RFC 1567](https://github.com/rust-lang/rfcs/blob/master/text/1567-long-error-codes-explanation-normalization.md) -for details on how to format and write long error codes. - -* All of them are accessible [online](http://doc.rust-lang.org/error-index.html), - which are auto-generated from rustc source code in different places: - [librustc](https://github.com/rust-lang/rust/blob/master/src/librustc/error_codes.rs), - [librustc_ast](https://github.com/rust-lang/rust/blob/master/src/librustc_ast/error_codes.rs), - [librustc_borrowck](https://github.com/rust-lang/rust/blob/master/src/librustc_borrowck/error_codes.rs), - [librustc_metadata](https://github.com/rust-lang/rust/blob/master/src/librustc_metadata/error_codes.rs), - [librustc_mir](https://github.com/rust-lang/rust/blob/master/src/librustc_mir/error_codes.rs), - [librustc_passes](https://github.com/rust-lang/rust/blob/master/src/librustc_passes/error_codes.rs), - [librustc_privacy](https://github.com/rust-lang/rust/blob/master/src/librustc_privacy/error_codes.rs), - [librustc_resolve](https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/error_codes.rs), - [librustc_codegen_llvm](https://github.com/rust-lang/rust/blob/master/src/librustc_codegen_llvm/error_codes.rs), - [librustc_plugin_impl](https://github.com/rust-lang/rust/blob/master/src/librustc_plugin/error_codes.rs), - [librustc_typeck](https://github.com/rust-lang/rust/blob/master/src/librustc_typeck/error_codes.rs). -* Explanations have full markdown support. Use it, especially to highlight -code with backticks. -* When talking about the compiler, call it `the compiler`, not `Rust` or -`rustc`. - -## Compiler Flags - -* Flags should be orthogonal to each other. For example, if we'd have a -json-emitting variant of multiple actions `foo` and `bar`, an additional ---json flag is better than adding `--foo-json` and `--bar-json`. -* Always give options a long descriptive name, if only for more -understandable compiler scripts. -* The `--verbose` flag is for adding verbose information to `rustc` output -when not compiling a program. For example, using it with the `--version` flag -gives information about the hashes of the code. -* Experimental flags and options must be guarded behind the `-Z unstable-options` flag. 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/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 7a7838d965bc7..30b18eb56a125 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -273,10 +273,18 @@ This flag, when combined with other flags, makes them produce extra output. This flag allows you to pass the name and location for an external crate of a direct dependency. Indirect dependencies (dependencies of dependencies) are located using the [`-L` flag](#option-l-search-path). The given crate name is -added to the [extern prelude], which is the same as specifying `extern crate` -within the root module. The given crate name does not need to match the name +added to the [extern prelude], similar to specifying `extern crate` within the +root module. The given crate name does not need to match the name the library was built with. +Specifying `--extern` has one behavior difference from `extern crate`: +`--extern` merely makes the crate a _candidate_ for being linked; it does not +actually link it unless it's actively used. In rare occasions you may wish +to ensure a crate is linked even if you don't actively use it from your +code: for example, if it changes the global allocator or if it contains +`#[no_mangle]` symbols for use by other programming languages. In such +cases you'll need to use `extern crate`. + This flag may be specified multiple times. This flag takes an argument with either of the following formats: diff --git a/src/doc/rustc/src/json.md b/src/doc/rustc/src/json.md index b737849516310..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). */ @@ -168,7 +171,7 @@ Diagnostics have the following format: "rendered": null }, { - "message": "consider prefixing with an underscore", + "message": "if this is intentional, prefix it with an underscore", "code": null, "level": "help", "spans": [ @@ -201,7 +204,7 @@ Diagnostics have the following format: /* Optional string of the rendered version of the diagnostic as displayed by rustc. Note that this may be influenced by the `--json` flag. */ - "rendered": "warning: unused variable: `x`\n --> lib.rs:2:9\n |\n2 | let x = 123;\n | ^ help: consider prefixing with an underscore: `_x`\n |\n = note: `#[warn(unused_variables)]` on by default\n\n" + "rendered": "warning: unused variable: `x`\n --> lib.rs:2:9\n |\n2 | let x = 123;\n | ^ help: if this is intentional, prefix it with an underscore: `_x`\n |\n = note: `#[warn(unused_variables)]` on by default\n\n" } ``` 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/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index ddbe26389fdc8..31e002810ce4b 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -390,3 +390,15 @@ the same CSS rules as the official `light` theme. `--check-theme` is a separate mode in `rustdoc`. When `rustdoc` sees the `--check-theme` flag, it discards all other flags and only performs the CSS rule comparison operation. + +### `--crate-version`: control the crate version + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs --crate-version 1.3.37 +``` + +When `rustdoc` receives this flag, it will print an extra "Version (version)" into the sidebar of +the crate root's docs. You can use this flag to differentiate between different versions of your +library's documentation. diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index 78181156e250f..18010bebcf0e7 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 @@ -416,7 +416,7 @@ without including it in your main documentation. For example, you could write th `lib.rs` to test your README as part of your doctests: ```rust,ignore -#![feature(extern_doc)] +#![feature(external_doc)] #[doc(include="../README.md")] #[cfg(doctest)] 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/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index f704fe8e099b8..84e1ebe5e01f5 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -248,18 +248,6 @@ Markdown file, the URL given to `--markdown-playground-url` will take precedence `--playground-url` and `#![doc(html_playground_url = "url")]` are present when rendering crate docs, the attribute will take precedence. -### `--crate-version`: control the crate version - -Using this flag looks like this: - -```bash -$ rustdoc src/lib.rs -Z unstable-options --crate-version 1.3.37 -``` - -When `rustdoc` receives this flag, it will print an extra "Version (version)" into the sidebar of -the crate root's docs. You can use this flag to differentiate between different versions of your -library's documentation. - ### `--sort-modules-by-appearance`: control how items on module pages are sorted Using this flag looks like this: 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/control-flow-guard.md b/src/doc/unstable-book/src/compiler-flags/control-flow-guard.md index f871df46250ba..4115825e92083 100644 --- a/src/doc/unstable-book/src/compiler-flags/control-flow-guard.md +++ b/src/doc/unstable-book/src/compiler-flags/control-flow-guard.md @@ -1,18 +1,42 @@ -# `control_flow_guard` +# `control-flow-guard` The tracking issue for this feature is: [#68793](https://github.com/rust-lang/rust/issues/68793). ------------------------ -The `-Zcontrol_flow_guard=checks` compiler flag enables the Windows [Control Flow Guard][cfguard-docs] platform security feature. When enabled, the compiler outputs a list of valid indirect call targets, and inserts runtime checks on all indirect jump instructions to ensure that the destination is in the list of valid call targets. +The rustc flag `-Z control-flow-guard` enables the Windows [Control Flow Guard](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard) (CFG) platform security feature. -[cfguard-docs]: https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard +CFG is an exploit mitigation designed to enforce control-flow integrity for software running on supported Windows platforms (Windows 8.1 onwards). Specifically, CFG uses runtime checks to validate the target address of every indirect call/jump before allowing the call to complete. -For testing purposes, the `-Zcontrol_flow_guard=nochecks` compiler flag can be used to emit only the list of valid call targets, but not the runtime checks. +During compilation, the compiler identifies all indirect calls/jumps and adds CFG checks. It also emits metadata containing the relative addresses of all address-taken functions. At runtime, if the binary is run on a CFG-aware operating system, the loader uses the CFG metadata to generate a bitmap of the address space and marks those addresses that contain valid targets. On each indirect call, the inserted check determines whether the target address is marked in this bitmap. If the target is not valid, the process is terminated. -It is strongly recommended to also enable Control Flow Guard checks in all linked libraries, including the standard library. +In terms of interoperability: +- Code compiled with CFG enabled can be linked with libraries and object files that are not compiled with CFG. In this case, a CFG-aware linker can identify address-taken functions in the non-CFG libraries. +- Libraries compiled with CFG can linked into non-CFG programs. In this case, the CFG runtime checks in the libraries are not used (i.e. the mitigation is completely disabled). -To enable Control Flow Guard in the standard library, you can use the [cargo `-Zbuild-std` functionality][build-std] to recompile the standard library with the same configuration options as the main program. +CFG functionality is completely implemented in the LLVM backend and is supported for X86 (32-bit and 64-bit), ARM, and Aarch64 targets. The rustc flag adds the relevant LLVM module flags to enable the feature. This flag will be ignored for all non-Windows targets. + + +## When to use Control Flow Guard + +The primary motivation for enabling CFG in Rust is to enhance security when linking against non-Rust code, especially C/C++ code. To achieve full CFG protection, all indirect calls (including any from Rust code) must have the appropriate CFG checks, as added by this flag. CFG can also improve security for Rust code that uses the `unsafe` keyword + + +## Overhead of Control Flow Guard + +The CFG checks and metadata can potentially increase binary size and runtime overhead. The magnitude of any increase depends on the number and frequency of indirect calls. For example, enabling CFG for the Rust standard library increases binary size by approximately 0.14%. Enabling CFG in the SPEC CPU 2017 Integer Speed benchmark suite (compiled with Clang/LLVM) incurs approximate runtime overheads of between 0% and 8%, with a geometric mean of 2.9%. + + +## Testing Control Flow Guard + +The rustc flag `-Z control-flow-guard=nochecks` instructs LLVM to emit the list of valid call targets without inserting runtime checks. This flag should only be used for testing purposes as it does not provide security enforcement. + + +## Control Flow Guard in libraries + +It is strongly recommended to also enable CFG checks for all linked libraries, including the standard library. + +To enable CFG in the standard library, use the [cargo `-Z build-std` functionality][build-std] to recompile the standard library with the same configuration options as the main program. [build-std]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std @@ -20,14 +44,14 @@ For example: ```cmd rustup toolchain install --force nightly rustup component add rust-src -SET RUSTFLAGS=-Zcontrol_flow_guard=checks +SET RUSTFLAGS=-Z control-flow-guard cargo +nightly build -Z build-std --target x86_64-pc-windows-msvc ``` ```PowerShell rustup toolchain install --force nightly rustup component add rust-src -$Env:RUSTFLAGS = "-Zcontrol_flow_guard=checks" +$Env:RUSTFLAGS = "-Z control-flow-guard" cargo +nightly build -Z build-std --target x86_64-pc-windows-msvc ``` diff --git a/src/doc/unstable-book/src/compiler-flags/profile.md b/src/doc/unstable-book/src/compiler-flags/profile.md index 452aca51532c9..7973b3e4f2f32 100644 --- a/src/doc/unstable-book/src/compiler-flags/profile.md +++ b/src/doc/unstable-book/src/compiler-flags/profile.md @@ -12,10 +12,16 @@ For example: ```Bash cargo new testgcov --bin cd testgcov -export RUSTFLAGS="-Zprofile" +export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" +export CARGO_INCREMENTAL=0 cargo build cargo run ``` Once you've built and run your program, files with the `gcno` (after build) and `gcda` (after execution) extensions will be created. You can parse them with [llvm-cov gcov](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-gcov) or [grcov](https://github.com/mozilla/grcov). + +Please note that `RUSTFLAGS` by default applies to everything that cargo builds and runs during a build! +When the `--target` flag is explicitly passed to cargo, the `RUSTFLAGS` no longer apply to build scripts and procedural macros. +For more fine-grained control consider passing a `RUSTC_WRAPPER` program to cargo that only adds the profiling flags to +rustc for the specific crates you want to profile. diff --git a/src/doc/unstable-book/src/compiler-flags/report-time.md b/src/doc/unstable-book/src/compiler-flags/report-time.md index ed4e9c6b56842..68265d8a9e810 100644 --- a/src/doc/unstable-book/src/compiler-flags/report-time.md +++ b/src/doc/unstable-book/src/compiler-flags/report-time.md @@ -22,7 +22,7 @@ Available options: ```sh --report-time [plain|colored] - Show execution time of each test. Awailable values: + Show execution time of each test. Available values: plain = do not colorize the execution time (default); colored = colorize output according to the `color` parameter value; diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 414ac7e63a331..5e2e04c063bc4 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -6,73 +6,77 @@ 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`. -# 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 +94,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 +111,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 +163,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 +192,9 @@ fn main() { println!("{}", a[2]); } } +``` +```shell $ export \ CC=clang \ CXX=clang++ \ @@ -193,7 +203,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 +215,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 +290,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 5c8124c9c6b7d..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,18 +10,20 @@ 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 ```rust,ignore -impl !Type for Trait +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..a941bc9348f2c 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,725 @@ 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"); +} ``` -Any use of `asm` is feature gated (requires `#![feature(asm)]` on the -crate to allow) and of course requires an `unsafe` block. +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. -> **Note**: the examples here are given in x86/x86-64 assembly, but -> all platforms are supported. +## Inputs and outputs -## Assembly template +Now inserting an instruction that does nothing is rather boring. Let us do something that +actually acts on data: -The `assembly template` is the only required parameter and must be a -literal string (i.e. `""`) +```rust,allow_fail +# #![feature(asm)] +let x: u64; +unsafe { + asm!("mov {}, 5", out(reg) x); +} +assert_eq!(x, 5); +``` -```rust -#![feature(asm)] +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. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -fn foo() { - unsafe { - asm!("NOP"); - } +Let us see another example that also uses an input: + +```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); +``` + +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. + +The example shows a few things: + +First, we can see that `asm!` allows multiple template string arguments; each +one is treated as a separate line of assembly code, as if they were all joined +together with newlines between them. This makes it easy to format assembly +code. + +Second, we can see that inputs are declared by writing `in` instead of `out`. + +Third, 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. -// Other platforms: -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -fn foo() { /* ... */ } +Fourth, 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. -fn main() { - // ... - foo(); - // ... +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); ``` -(The `feature(asm)` and `#[cfg]`s are omitted from now on.) +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); +``` + +## Late output operands + +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. -Output operands, input operands, clobbers and options are all optional -but you must add the right number of `:` if you skip them: +Here is an example where `inlateout` *cannot* be used: -```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; +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); ``` -Whitespace also doesn't matter: +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. + +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); ``` -## Operands +As you can see, this assembly fragment will still work correctly if `a` and `b` are assigned to the same register. + +## Explicit register operands -Input and output operands follow the same format: `: -"constraints1"(expr1), "constraints2"(expr2), ..."`. Output operand -expressions must be mutable lvalues, or not yet assigned: +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 add(a: i32, b: i32) -> i32 { - let c: i32; +let cmd = 0xd1; +unsafe { + asm!("out 0x64, eax", in("eax") cmd); +} +``` + +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. + +Consider this example which uses the x86 `mul` instruction: + +```rust,allow_fail +# #![feature(asm)] +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 *("," format_string) *("," [ident "="] operand) ["," options] [","] ")" +``` + +The macro will initially be supported only on ARM, AArch64, Hexagon, 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 arguments + +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. + +An `asm!` invocation may have one or more template string arguments; an `asm!` with multiple template string arguments is treated as if all the strings were concatenated with a `\n` between them. The expected usage is for each template string argument to correspond to a line of assembly code. All template string arguments must appear before any other arguments. + +As with format strings, named arguments must appear after positional arguments. Explicit register operands must appear at the end of the operand list, after named arguments if any. + +Explicit register operands cannot be used by placeholders in the template string. All other named and positional 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 5 targets specified in this RFC (x86, ARM, AArch64, RISC-V, Hexagon) 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` | +| NVPTX | `reg16` | None\* | `h` | +| NVPTX | `reg32` | None\* | `r` | +| NVPTX | `reg64` | None\* | `l` | +| RISC-V | `reg` | `x1`, `x[5-7]`, `x[9-15]`, `x[16-31]` (non-RV32E) | `r` | +| RISC-V | `freg` | `f[0-31]` | `f` | +| Hexagon | `reg` | `r[0-28]` | `r` | + +> **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. +> +> Note #3: NVPTX doesn't have a fixed register set, so named registers are not supported. + +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` | +| NVPTX | `reg16` | None | `i8`, `i16` | +| NVPTX | `reg32` | None | `i8`, `i16`, `i32`, `f32` | +| NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | +| 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` | +| Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | + +> **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]` | +| Hexagon | `r29` | `sp` | +| Hexagon | `r30` | `fr` | +| Hexagon | `r31` | `lr` | + +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), `fr` (Hexagon) | 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. | +| Hexagon | `lr` | This is the link register which cannot be used as an input or output. | + +## 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` | +| NVPTX | `reg16` | None | `rs0` | None | +| NVPTX | `reg32` | None | `r0` | None | +| NVPTX | `reg64` | None | `rd0` | None | +| RISC-V | `reg` | None | `x1` | None | +| RISC-V | `freg` | None | `f0` | None | +| Hexagon | `reg` | None | `r0` | 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/default-free-fn.md b/src/doc/unstable-book/src/library-features/default-free-fn.md new file mode 100644 index 0000000000000..5dff73a94dd87 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/default-free-fn.md @@ -0,0 +1,45 @@ +# `default_free_fn` + +The tracking issue for this feature is: [#73014] + +[#73014]: https://github.com/rust-lang/rust/issues/73014 + +------------------------ + +Adds a free `default()` function to the `std::default` module. This function +just forwards to [`Default::default()`], but may remove repetition of the word +"default" from the call site. + +Here is an example: + +```rust +#![feature(default_free_fn)] +use std::default::default; + +#[derive(Default)] +struct AppConfig { + foo: FooConfig, + bar: BarConfig, +} + +#[derive(Default)] +struct FooConfig { + foo: i32, +} + +#[derive(Default)] +struct BarConfig { + bar: f32, + baz: u8, +} + +fn main() { + let options = AppConfig { + foo: default(), + bar: BarConfig { + bar: 10.1, + ..default() + }, + }; +} +``` 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/debugger_pretty_printers_common.py b/src/etc/debugger_pretty_printers_common.py deleted file mode 100644 index b3f8f50636bee..0000000000000 --- a/src/etc/debugger_pretty_printers_common.py +++ /dev/null @@ -1,401 +0,0 @@ -""" -This module provides an abstraction layer over common Rust pretty printing -functionality needed by both GDB and LLDB. -""" - -import re - -# Type codes that indicate the kind of type as it appears in DWARF debug -# information. This code alone is not sufficient to determine the Rust type. -# For example structs, tuples, fat pointers, or enum variants will all have -# DWARF_TYPE_CODE_STRUCT. -DWARF_TYPE_CODE_STRUCT = 1 -DWARF_TYPE_CODE_UNION = 2 -DWARF_TYPE_CODE_PTR = 3 -DWARF_TYPE_CODE_ARRAY = 4 -DWARF_TYPE_CODE_ENUM = 5 - -# These constants specify the most specific kind of type that could be -# determined for a given value. -TYPE_KIND_UNKNOWN = -1 -TYPE_KIND_EMPTY = 0 -TYPE_KIND_SLICE = 1 -TYPE_KIND_REGULAR_STRUCT = 2 -TYPE_KIND_TUPLE = 3 -TYPE_KIND_TUPLE_STRUCT = 4 -TYPE_KIND_CSTYLE_VARIANT = 5 -TYPE_KIND_TUPLE_VARIANT = 6 -TYPE_KIND_STRUCT_VARIANT = 7 -TYPE_KIND_STR_SLICE = 8 -TYPE_KIND_STD_VEC = 9 -TYPE_KIND_STD_STRING = 10 -TYPE_KIND_REGULAR_ENUM = 11 -TYPE_KIND_COMPRESSED_ENUM = 12 -TYPE_KIND_SINGLETON_ENUM = 13 -TYPE_KIND_CSTYLE_ENUM = 14 -TYPE_KIND_PTR = 15 -TYPE_KIND_FIXED_SIZE_VEC = 16 -TYPE_KIND_REGULAR_UNION = 17 -TYPE_KIND_OS_STRING = 18 -TYPE_KIND_STD_VECDEQUE = 19 -TYPE_KIND_STD_BTREESET = 20 -TYPE_KIND_STD_BTREEMAP = 21 - -ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$" -ENUM_DISR_FIELD_NAME = "RUST$ENUM$DISR" - -# Slice related constants -SLICE_FIELD_NAME_DATA_PTR = "data_ptr" -SLICE_FIELD_NAME_LENGTH = "length" -SLICE_FIELD_NAMES = [SLICE_FIELD_NAME_DATA_PTR, SLICE_FIELD_NAME_LENGTH] - -# std::Vec<> related constants -STD_VEC_FIELD_NAME_LENGTH = "len" -STD_VEC_FIELD_NAME_BUF = "buf" -STD_VEC_FIELD_NAMES = [STD_VEC_FIELD_NAME_BUF, - STD_VEC_FIELD_NAME_LENGTH] - -# std::collections::VecDeque<> related constants -STD_VECDEQUE_FIELD_NAME_TAIL = "tail" -STD_VECDEQUE_FIELD_NAME_HEAD = "head" -STD_VECDEQUE_FIELD_NAME_BUF = "buf" -STD_VECDEQUE_FIELD_NAMES = [STD_VECDEQUE_FIELD_NAME_TAIL, - STD_VECDEQUE_FIELD_NAME_HEAD, - STD_VECDEQUE_FIELD_NAME_BUF] - -# std::collections::BTreeSet<> related constants -STD_BTREESET_FIELD_NAMES = ["map"] - -# std::collections::BTreeMap<> related constants -STD_BTREEMAP_FIELD_NAMES = ["root", "length"] - -# std::String related constants -STD_STRING_FIELD_NAMES = ["vec"] - -# std::ffi::OsString related constants -OS_STRING_FIELD_NAMES = ["inner"] - - -class Type(object): - """ - This class provides a common interface for type-oriented operations. - Sub-classes are supposed to wrap a debugger-specific type-object and - provide implementations for the abstract methods in this class. - """ - - def __init__(self): - self.__type_kind = None - - def get_unqualified_type_name(self): - """ - Implementations of this method should return the unqualified name of the - type-object they are wrapping. Some examples: - - 'int' -> 'int' - 'std::vec::Vec' -> 'Vec' - '&std::option::Option' -> '&std::option::Option' - - As you can see, type arguments stay fully qualified. - """ - raise NotImplementedError("Override this method") - - def get_dwarf_type_kind(self): - """ - Implementations of this method should return the correct - DWARF_TYPE_CODE_* value for the wrapped type-object. - """ - raise NotImplementedError("Override this method") - - def get_fields(self): - """ - Implementations of this method should return a list of field-objects of - this type. For Rust-enums (i.e. with DWARF_TYPE_CODE_UNION) these field- - objects represent the variants of the enum. Field-objects must have a - `name` attribute that gives their name as specified in DWARF. - """ - assert ((self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT) or - (self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION)) - raise NotImplementedError("Override this method") - - def get_wrapped_value(self): - """ - Returns the debugger-specific type-object wrapped by this object. This - is sometimes needed for doing things like pointer-arithmetic in GDB. - """ - raise NotImplementedError("Override this method") - - def get_type_kind(self): - """This method returns the TYPE_KIND_* value for this type-object.""" - if self.__type_kind is None: - dwarf_type_code = self.get_dwarf_type_kind() - - if dwarf_type_code == DWARF_TYPE_CODE_STRUCT: - self.__type_kind = self.__classify_struct() - elif dwarf_type_code == DWARF_TYPE_CODE_UNION: - self.__type_kind = self.__classify_union() - elif dwarf_type_code == DWARF_TYPE_CODE_PTR: - self.__type_kind = TYPE_KIND_PTR - elif dwarf_type_code == DWARF_TYPE_CODE_ARRAY: - self.__type_kind = TYPE_KIND_FIXED_SIZE_VEC - else: - self.__type_kind = TYPE_KIND_UNKNOWN - return self.__type_kind - - def __classify_struct(self): - assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT - - unqualified_type_name = self.get_unqualified_type_name() - - # STR SLICE - if unqualified_type_name == "&str": - return TYPE_KIND_STR_SLICE - - # REGULAR SLICE - if (unqualified_type_name.startswith(("&[", "&mut [")) and - unqualified_type_name.endswith("]") and - self.__conforms_to_field_layout(SLICE_FIELD_NAMES)): - return TYPE_KIND_SLICE - - fields = self.get_fields() - field_count = len(fields) - - # EMPTY STRUCT - if field_count == 0: - return TYPE_KIND_EMPTY - - # STD VEC - if (unqualified_type_name.startswith("Vec<") and - self.__conforms_to_field_layout(STD_VEC_FIELD_NAMES)): - return TYPE_KIND_STD_VEC - - # STD COLLECTION VECDEQUE - if (unqualified_type_name.startswith("VecDeque<") and - self.__conforms_to_field_layout(STD_VECDEQUE_FIELD_NAMES)): - return TYPE_KIND_STD_VECDEQUE - - # STD COLLECTION BTREESET - if (unqualified_type_name.startswith("BTreeSet<") and - self.__conforms_to_field_layout(STD_BTREESET_FIELD_NAMES)): - return TYPE_KIND_STD_BTREESET - - # STD COLLECTION BTREEMAP - if (unqualified_type_name.startswith("BTreeMap<") and - self.__conforms_to_field_layout(STD_BTREEMAP_FIELD_NAMES)): - return TYPE_KIND_STD_BTREEMAP - - # STD STRING - if (unqualified_type_name.startswith("String") and - self.__conforms_to_field_layout(STD_STRING_FIELD_NAMES)): - return TYPE_KIND_STD_STRING - - # OS STRING - if (unqualified_type_name == "OsString" and - self.__conforms_to_field_layout(OS_STRING_FIELD_NAMES)): - return TYPE_KIND_OS_STRING - - # ENUM VARIANTS - if fields[0].name == ENUM_DISR_FIELD_NAME: - if field_count == 1: - return TYPE_KIND_CSTYLE_VARIANT - elif self.__all_fields_conform_to_tuple_field_naming(1): - return TYPE_KIND_TUPLE_VARIANT - else: - return TYPE_KIND_STRUCT_VARIANT - - # TUPLE - if self.__all_fields_conform_to_tuple_field_naming(0): - if unqualified_type_name.startswith("("): - return TYPE_KIND_TUPLE - else: - return TYPE_KIND_TUPLE_STRUCT - - # REGULAR STRUCT - return TYPE_KIND_REGULAR_STRUCT - - def __classify_union(self): - assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION - - union_members = self.get_fields() - union_member_count = len(union_members) - if union_member_count == 0: - return TYPE_KIND_EMPTY - - first_variant_name = union_members[0].name - if first_variant_name is None: - if union_member_count == 1: - return TYPE_KIND_SINGLETON_ENUM - else: - return TYPE_KIND_REGULAR_ENUM - elif first_variant_name.startswith(ENCODED_ENUM_PREFIX): - assert union_member_count == 1 - return TYPE_KIND_COMPRESSED_ENUM - else: - return TYPE_KIND_REGULAR_UNION - - def __conforms_to_field_layout(self, expected_fields): - actual_fields = self.get_fields() - actual_field_count = len(actual_fields) - - if actual_field_count != len(expected_fields): - return False - - for i in range(0, actual_field_count): - if actual_fields[i].name != expected_fields[i]: - return False - - return True - - def __all_fields_conform_to_tuple_field_naming(self, start_index): - fields = self.get_fields() - field_count = len(fields) - - for i in range(start_index, field_count): - field_name = fields[i].name - if (field_name is None) or (re.match(r"__\d+$", field_name) is None): - return False - return True - - -class Value(object): - """ - This class provides a common interface for value-oriented operations. - Sub-classes are supposed to wrap a debugger-specific value-object and - provide implementations for the abstract methods in this class. - """ - def __init__(self, ty): - self.type = ty - - def get_child_at_index(self, index): - """Returns the value of the field, array element or variant at the given index""" - raise NotImplementedError("Override this method") - - def as_integer(self): - """ - Try to convert the wrapped value into a Python integer. This should - always succeed for values that are pointers or actual integers. - """ - raise NotImplementedError("Override this method") - - def get_wrapped_value(self): - """ - Returns the debugger-specific value-object wrapped by this object. This - is sometimes needed for doing things like pointer-arithmetic in GDB. - """ - raise NotImplementedError("Override this method") - - -class EncodedEnumInfo(object): - """ - This class provides facilities for handling enum values with compressed - encoding where a non-null field in one variant doubles as the discriminant. - """ - - def __init__(self, enum_val): - assert enum_val.type.get_type_kind() == TYPE_KIND_COMPRESSED_ENUM - variant_name = enum_val.type.get_fields()[0].name - last_separator_index = variant_name.rfind("$") - start_index = len(ENCODED_ENUM_PREFIX) - indices_substring = variant_name[start_index:last_separator_index].split("$") - self.__enum_val = enum_val - self.__disr_field_indices = [int(index) for index in indices_substring] - self.__null_variant_name = variant_name[last_separator_index + 1:] - - def is_null_variant(self): - ty = self.__enum_val.type - sole_variant_val = self.__enum_val.get_child_at_index(0) - discriminant_val = sole_variant_val - for disr_field_index in self.__disr_field_indices: - discriminant_val = discriminant_val.get_child_at_index(disr_field_index) - - # If the discriminant field is a fat pointer we have to consider the - # first word as the true discriminant - if discriminant_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT: - discriminant_val = discriminant_val.get_child_at_index(0) - - return discriminant_val.as_integer() == 0 - - def get_non_null_variant_val(self): - return self.__enum_val.get_child_at_index(0) - - def get_null_variant_name(self): - return self.__null_variant_name - - -def get_discriminant_value_as_integer(enum_val): - assert enum_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION - # we can take any variant here because the discriminant has to be the same - # for all of them. - variant_val = enum_val.get_child_at_index(0) - disr_val = variant_val.get_child_at_index(0) - return disr_val.as_integer() - - -def extract_length_ptr_and_cap_from_std_vec(vec_val): - assert vec_val.type.get_type_kind() == TYPE_KIND_STD_VEC - length_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_LENGTH) - buf_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_BUF) - - length = vec_val.get_child_at_index(length_field_index).as_integer() - buf = vec_val.get_child_at_index(buf_field_index) - - vec_ptr_val = buf.get_child_at_index(0) - capacity = buf.get_child_at_index(1).as_integer() - data_ptr = vec_ptr_val.get_child_at_index(0) - assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR - return (length, data_ptr, capacity) - - -def extract_tail_head_ptr_and_cap_from_std_vecdeque(vec_val): - assert vec_val.type.get_type_kind() == TYPE_KIND_STD_VECDEQUE - tail_field_index = STD_VECDEQUE_FIELD_NAMES.index(STD_VECDEQUE_FIELD_NAME_TAIL) - head_field_index = STD_VECDEQUE_FIELD_NAMES.index(STD_VECDEQUE_FIELD_NAME_HEAD) - buf_field_index = STD_VECDEQUE_FIELD_NAMES.index(STD_VECDEQUE_FIELD_NAME_BUF) - - tail = vec_val.get_child_at_index(tail_field_index).as_integer() - head = vec_val.get_child_at_index(head_field_index).as_integer() - buf = vec_val.get_child_at_index(buf_field_index) - - vec_ptr_val = buf.get_child_at_index(0) - capacity = buf.get_child_at_index(1).as_integer() - data_ptr = vec_ptr_val.get_child_at_index(0) - assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR - return (tail, head, data_ptr, capacity) - - -def extract_length_and_ptr_from_slice(slice_val): - assert (slice_val.type.get_type_kind() == TYPE_KIND_SLICE or - slice_val.type.get_type_kind() == TYPE_KIND_STR_SLICE) - - length_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_LENGTH) - ptr_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_DATA_PTR) - - length = slice_val.get_child_at_index(length_field_index).as_integer() - data_ptr = slice_val.get_child_at_index(ptr_field_index) - - assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR - return (length, data_ptr) - - -UNQUALIFIED_TYPE_MARKERS = frozenset(["(", "[", "&", "*"]) - - -def extract_type_name(qualified_type_name): - """Extracts the type name from a fully qualified path""" - if qualified_type_name[0] in UNQUALIFIED_TYPE_MARKERS: - return qualified_type_name - - end_of_search = qualified_type_name.find("<") - if end_of_search < 0: - end_of_search = len(qualified_type_name) - - index = qualified_type_name.rfind("::", 0, end_of_search) - if index < 0: - return qualified_type_name - else: - return qualified_type_name[index + 2:] - - -try: - compat_str = unicode # Python 2 -except NameError: - compat_str = str 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_load_rust_pretty_printers.py b/src/etc/gdb_load_rust_pretty_printers.py index fe38c49d2707d..856b5df2de70b 100644 --- a/src/etc/gdb_load_rust_pretty_printers.py +++ b/src/etc/gdb_load_rust_pretty_printers.py @@ -1,3 +1,3 @@ import gdb -import gdb_rust_pretty_printing -gdb_rust_pretty_printing.register_printers(gdb.current_objfile()) +import gdb_lookup +gdb_lookup.register_printers(gdb.current_objfile()) diff --git a/src/etc/gdb_lookup.py b/src/etc/gdb_lookup.py new file mode 100644 index 0000000000000..2a46eaadad6f9 --- /dev/null +++ b/src/etc/gdb_lookup.py @@ -0,0 +1,92 @@ +import gdb +import re + +from gdb_providers import * +from rust_types import * + + +rust_enabled = 'set language rust' in gdb.execute('complete set language ru', to_string=True) +_gdb_version_matched = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION) +gdb_version = [int(num) for num in _gdb_version_matched.groups()] if _gdb_version_matched else [] + +def register_printers(objfile): + objfile.pretty_printers.append(lookup) + + +# BACKCOMPAT: rust 1.35 +def is_hashbrown_hashmap(hash_map): + return len(hash_map.type.fields()) == 1 + + +def classify_rust_type(type): + type_class = type.code + if type_class == gdb.TYPE_CODE_STRUCT: + return classify_struct(type.tag, type.fields()) + if type_class == gdb.TYPE_CODE_UNION: + return classify_union(type.fields()) + + return RustType.OTHER + + +def check_enum_discriminant(valobj): + content = valobj[valobj.type.fields()[0]] + fields = content.type.fields() + if len(fields) > 1: + discriminant = int(content[fields[0]]) + 1 + if discriminant > len(fields): + # invalid discriminant + return False + return True + + +def lookup(valobj): + rust_type = classify_rust_type(valobj.type) + + if rust_type == RustType.ENUM: + # use enum provider only for GDB <7.12 + if gdb_version[0] < 7 or (gdb_version[0] == 7 and gdb_version[1] < 12): + if check_enum_discriminant(valobj): + return EnumProvider(valobj) + + if rust_type == RustType.STD_STRING: + return StdStringProvider(valobj) + if rust_type == RustType.STD_OS_STRING: + return StdOsStringProvider(valobj) + if rust_type == RustType.STD_STR and not rust_enabled: + return StdStrProvider(valobj) + + if rust_type == RustType.STD_VEC: + return StdVecProvider(valobj) + if rust_type == RustType.STD_VEC_DEQUE: + return StdVecDequeProvider(valobj) + if rust_type == RustType.STD_BTREE_SET: + return StdBTreeSetProvider(valobj) + if rust_type == RustType.STD_BTREE_MAP: + return StdBTreeMapProvider(valobj) + if rust_type == RustType.STD_HASH_MAP: + if is_hashbrown_hashmap(valobj): + return StdHashMapProvider(valobj) + else: + return StdOldHashMapProvider(valobj) + if rust_type == RustType.STD_HASH_SET: + hash_map = valobj["map"] + if is_hashbrown_hashmap(hash_map): + return StdHashMapProvider(hash_map, show_values=False) + else: + return StdOldHashMapProvider(hash_map, show_values=False) + + if rust_type == RustType.STD_RC: + return StdRcProvider(valobj) + if rust_type == RustType.STD_ARC: + return StdRcProvider(valobj, is_atomic=True) + + if rust_type == RustType.STD_CELL: + return StdCellProvider(valobj) + if rust_type == RustType.STD_REF: + return StdRefProvider(valobj) + if rust_type == RustType.STD_REF_MUT: + return StdRefProvider(valobj) + if rust_type == RustType.STD_REF_CELL: + return StdRefCellProvider(valobj) + + return None diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py new file mode 100644 index 0000000000000..cec9c56a23522 --- /dev/null +++ b/src/etc/gdb_providers.py @@ -0,0 +1,385 @@ +from sys import version_info + +import gdb +from gdb import lookup_type + +if version_info[0] >= 3: + xrange = range + +ZERO_FIELD = "__0" +FIRST_FIELD = "__1" + + +def unwrap_unique_or_non_null(unique_or_nonnull): + # BACKCOMPAT: rust 1.32 + # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067 + ptr = unique_or_nonnull["pointer"] + return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ZERO_FIELD] + + +class EnumProvider: + def __init__(self, valobj): + content = valobj[valobj.type.fields()[0]] + fields = content.type.fields() + self.empty = len(fields) == 0 + if not self.empty: + if len(fields) == 1: + discriminant = 0 + else: + discriminant = int(content[fields[0]]) + 1 + self.active_variant = content[fields[discriminant]] + self.name = fields[discriminant].name + self.full_name = "{}::{}".format(valobj.type.name, self.name) + else: + self.full_name = valobj.type.name + + def to_string(self): + return self.full_name + + def children(self): + if not self.empty: + yield self.name, self.active_variant + + +class StdStringProvider: + def __init__(self, valobj): + self.valobj = valobj + vec = valobj["vec"] + self.length = int(vec["len"]) + self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"]) + + def to_string(self): + return self.data_ptr.lazy_string(encoding="utf-8", length=self.length) + + @staticmethod + def display_hint(): + return "string" + + +class StdOsStringProvider: + def __init__(self, valobj): + self.valobj = valobj + buf = self.valobj["inner"]["inner"] + is_windows = "Wtf8Buf" in buf.type.name + vec = buf[ZERO_FIELD] if is_windows else buf + + self.length = int(vec["len"]) + self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"]) + + def to_string(self): + return self.data_ptr.lazy_string(encoding="utf-8", length=self.length) + + def display_hint(self): + return "string" + + +class StdStrProvider: + def __init__(self, valobj): + self.valobj = valobj + self.length = int(valobj["length"]) + self.data_ptr = valobj["data_ptr"] + + def to_string(self): + return self.data_ptr.lazy_string(encoding="utf-8", length=self.length) + + @staticmethod + def display_hint(): + return "string" + + +class StdVecProvider: + def __init__(self, valobj): + self.valobj = valobj + self.length = int(valobj["len"]) + self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) + + def to_string(self): + return "Vec(size={})".format(self.length) + + def children(self): + saw_inaccessible = False + for index in xrange(self.length): + element_ptr = self.data_ptr + index + if saw_inaccessible: + return + try: + # rust-lang/rust#64343: passing deref expr to `str` allows + # catching exception on garbage pointer + str(element_ptr.dereference()) + yield "[{}]".format(index), element_ptr.dereference() + except RuntimeError: + saw_inaccessible = True + yield str(index), "inaccessible" + + @staticmethod + def display_hint(): + return "array" + + +class StdVecDequeProvider: + def __init__(self, valobj): + self.valobj = valobj + self.head = int(valobj["head"]) + self.tail = int(valobj["tail"]) + self.cap = int(valobj["buf"]["cap"]) + self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) + if self.head >= self.tail: + self.size = self.head - self.tail + else: + self.size = self.cap + self.head - self.tail + + def to_string(self): + return "VecDeque(size={})".format(self.size) + + def children(self): + for index in xrange(0, self.size): + value = (self.data_ptr + ((self.tail + index) % self.cap)).dereference() + yield "[{}]".format(index), value + + @staticmethod + def display_hint(): + return "array" + + +class StdRcProvider: + def __init__(self, valobj, is_atomic=False): + self.valobj = valobj + self.is_atomic = is_atomic + self.ptr = unwrap_unique_or_non_null(valobj["ptr"]) + self.value = self.ptr["data" if is_atomic else "value"] + self.strong = self.ptr["strong"]["v" if is_atomic else "value"]["value"] + self.weak = self.ptr["weak"]["v" if is_atomic else "value"]["value"] - 1 + + def to_string(self): + if self.is_atomic: + return "Arc(strong={}, weak={})".format(int(self.strong), int(self.weak)) + else: + return "Rc(strong={}, weak={})".format(int(self.strong), int(self.weak)) + + def children(self): + yield "value", self.value + yield "strong", self.strong + yield "weak", self.weak + + +class StdCellProvider: + def __init__(self, valobj): + self.value = valobj["value"]["value"] + + def to_string(self): + return "Cell" + + def children(self): + yield "value", self.value + + +class StdRefProvider: + def __init__(self, valobj): + self.value = valobj["value"].dereference() + self.borrow = valobj["borrow"]["borrow"]["value"]["value"] + + def to_string(self): + borrow = int(self.borrow) + if borrow >= 0: + return "Ref(borrow={})".format(borrow) + else: + return "Ref(borrow_mut={})".format(-borrow) + + def children(self): + yield "*value", self.value + yield "borrow", self.borrow + + +class StdRefCellProvider: + def __init__(self, valobj): + self.value = valobj["value"]["value"] + self.borrow = valobj["borrow"]["value"]["value"] + + def to_string(self): + borrow = int(self.borrow) + if borrow >= 0: + return "RefCell(borrow={})".format(borrow) + else: + return "RefCell(borrow_mut={})".format(-borrow) + + def children(self): + yield "value", self.value + yield "borrow", self.borrow + + +# Yield each key (and optionally value) from a BoxedNode. +def children_of_node(boxed_node, height, want_values): + def cast_to_internal(node): + internal_type_name = str(node.type.target()).replace("LeafNode", "InternalNode", 1) + internal_type = lookup_type(internal_type_name) + return node.cast(internal_type.pointer()) + + node_ptr = unwrap_unique_or_non_null(boxed_node["ptr"]) + node_ptr = cast_to_internal(node_ptr) if height > 0 else node_ptr + leaf = node_ptr["data"] if height > 0 else node_ptr.dereference() + keys = leaf["keys"] + values = leaf["vals"] + length = int(leaf["len"]) + + for i in xrange(0, length + 1): + if height > 0: + child_ptr = node_ptr["edges"][i]["value"]["value"] + for child in children_of_node(child_ptr, height - 1, want_values): + yield child + if i < length: + if want_values: + yield keys[i]["value"]["value"], values[i]["value"]["value"] + else: + yield keys[i]["value"]["value"] + + +class StdBTreeSetProvider: + def __init__(self, valobj): + self.valobj = valobj + + def to_string(self): + return "BTreeSet(size={})".format(self.valobj["map"]["length"]) + + def children(self): + inner_map = self.valobj["map"] + if inner_map["length"] > 0: + root = inner_map["root"] + if "core::option::Option<" in root.type.name: + type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1] + root = root.cast(gdb.lookup_type(type_name)) + + node_ptr = root["node"] + for i, child in enumerate(children_of_node(node_ptr, root["height"], False)): + yield "[{}]".format(i), child + + @staticmethod + def display_hint(): + return "array" + + +class StdBTreeMapProvider: + def __init__(self, valobj): + self.valobj = valobj + + def to_string(self): + return "BTreeMap(size={})".format(self.valobj["length"]) + + def children(self): + if self.valobj["length"] > 0: + root = self.valobj["root"] + if "core::option::Option<" in root.type.name: + type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1] + root = root.cast(gdb.lookup_type(type_name)) + + node_ptr = root["node"] + for i, child in enumerate(children_of_node(node_ptr, root["height"], True)): + yield "key{}".format(i), child[0] + yield "val{}".format(i), child[1] + + @staticmethod + def display_hint(): + return "map" + + +# BACKCOMPAT: rust 1.35 +class StdOldHashMapProvider: + def __init__(self, valobj, show_values=True): + self.valobj = valobj + self.show_values = show_values + + self.table = self.valobj["table"] + self.size = int(self.table["size"]) + self.hashes = self.table["hashes"] + self.hash_uint_type = self.hashes.type + self.hash_uint_size = self.hashes.type.sizeof + self.modulo = 2 ** self.hash_uint_size + self.data_ptr = self.hashes[ZERO_FIELD]["pointer"] + + self.capacity_mask = int(self.table["capacity_mask"]) + self.capacity = (self.capacity_mask + 1) % self.modulo + + marker = self.table["marker"].type + self.pair_type = marker.template_argument(0) + self.pair_type_size = self.pair_type.sizeof + + self.valid_indices = [] + for idx in range(self.capacity): + data_ptr = self.data_ptr.cast(self.hash_uint_type.pointer()) + address = data_ptr + idx + hash_uint = address.dereference() + hash_ptr = hash_uint[ZERO_FIELD]["pointer"] + if int(hash_ptr) != 0: + self.valid_indices.append(idx) + + def to_string(self): + if self.show_values: + return "HashMap(size={})".format(self.size) + else: + return "HashSet(size={})".format(self.size) + + def children(self): + start = int(self.data_ptr) & ~1 + + hashes = self.hash_uint_size * self.capacity + align = self.pair_type_size + len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~( + (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo + + pairs_offset = hashes + len_rounded_up + pairs_start = gdb.Value(start + pairs_offset).cast(self.pair_type.pointer()) + + for index in range(self.size): + table_index = self.valid_indices[index] + idx = table_index & self.capacity_mask + element = (pairs_start + idx).dereference() + if self.show_values: + yield "key{}".format(index), element[ZERO_FIELD] + yield "val{}".format(index), element[FIRST_FIELD] + else: + yield "[{}]".format(index), element[ZERO_FIELD] + + def display_hint(self): + return "map" if self.show_values else "array" + + +class StdHashMapProvider: + def __init__(self, valobj, show_values=True): + self.valobj = valobj + self.show_values = show_values + + table = self.valobj["base"]["table"] + capacity = int(table["bucket_mask"]) + 1 + ctrl = table["ctrl"]["pointer"] + + self.size = int(table["items"]) + self.data_ptr = table["data"]["pointer"] + self.pair_type = self.data_ptr.dereference().type + + self.valid_indices = [] + for idx in range(capacity): + address = ctrl + idx + value = address.dereference() + is_presented = value & 128 == 0 + if is_presented: + self.valid_indices.append(idx) + + def to_string(self): + if self.show_values: + return "HashMap(size={})".format(self.size) + else: + return "HashSet(size={})".format(self.size) + + def children(self): + pairs_start = self.data_ptr + + for index in range(self.size): + idx = self.valid_indices[index] + element = (pairs_start + idx).dereference() + if self.show_values: + yield "key{}".format(index), element[ZERO_FIELD] + yield "val{}".format(index), element[FIRST_FIELD] + else: + yield "[{}]".format(index), element[ZERO_FIELD] + + def display_hint(self): + return "map" if self.show_values else "array" diff --git a/src/etc/gdb_rust_pretty_printing.py b/src/etc/gdb_rust_pretty_printing.py deleted file mode 100755 index 0914c22eb13f0..0000000000000 --- a/src/etc/gdb_rust_pretty_printing.py +++ /dev/null @@ -1,458 +0,0 @@ -import gdb -import re -import sys -import debugger_pretty_printers_common as rustpp - -# We want a version of `range` which doesn't allocate an intermediate list, -# specifically it should use a lazy iterator. In Python 2 this was `xrange`, but -# if we're running with Python 3 then we need to use `range` instead. -if sys.version_info[0] >= 3: - xrange = range - -rust_enabled = 'set language rust' in gdb.execute('complete set language ru', to_string=True) - -# The btree pretty-printers fail in a confusing way unless -# https://sourceware.org/bugzilla/show_bug.cgi?id=21763 is fixed. -# This fix went in 8.1, so check for that. -# See https://github.com/rust-lang/rust/issues/56730 -gdb_81 = False -_match = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION) -if _match: - if int(_match.group(1)) > 8 or (int(_match.group(1)) == 8 and int(_match.group(2)) >= 1): - gdb_81 = True - -# =============================================================================== -# GDB Pretty Printing Module for Rust -# =============================================================================== - - -class GdbType(rustpp.Type): - - def __init__(self, ty): - super(GdbType, self).__init__() - self.ty = ty - self.fields = None - - def get_unqualified_type_name(self): - tag = self.ty.tag - - if tag is None: - return tag - - return rustpp.extract_type_name(tag).replace("&'static ", "&") - - def get_dwarf_type_kind(self): - if self.ty.code == gdb.TYPE_CODE_STRUCT: - return rustpp.DWARF_TYPE_CODE_STRUCT - - if self.ty.code == gdb.TYPE_CODE_UNION: - return rustpp.DWARF_TYPE_CODE_UNION - - if self.ty.code == gdb.TYPE_CODE_PTR: - return rustpp.DWARF_TYPE_CODE_PTR - - if self.ty.code == gdb.TYPE_CODE_ENUM: - return rustpp.DWARF_TYPE_CODE_ENUM - - def get_fields(self): - assert ((self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT) or - (self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION)) - if self.fields is None: - self.fields = list(self.ty.fields()) - return self.fields - - def get_wrapped_value(self): - return self.ty - - -class GdbValue(rustpp.Value): - def __init__(self, gdb_val): - super(GdbValue, self).__init__(GdbType(gdb_val.type)) - self.gdb_val = gdb_val - self.children = {} - - def get_child_at_index(self, index): - child = self.children.get(index) - if child is None: - gdb_field = get_field_at_index(self.gdb_val, index) - child = GdbValue(self.gdb_val[gdb_field]) - self.children[index] = child - return child - - def as_integer(self): - if self.gdb_val.type.code == gdb.TYPE_CODE_PTR: - as_str = rustpp.compat_str(self.gdb_val).split()[0] - return int(as_str, 0) - return int(self.gdb_val) - - def get_wrapped_value(self): - return self.gdb_val - - -def register_printers(objfile): - """Registers Rust pretty printers for the given objfile""" - objfile.pretty_printers.append(rust_pretty_printer_lookup_function) - - -def rust_pretty_printer_lookup_function(gdb_val): - """ - Returns the correct Rust pretty printer for the given value - if there is one - """ - - val = GdbValue(gdb_val) - type_kind = val.type.get_type_kind() - - if type_kind == rustpp.TYPE_KIND_SLICE: - return RustSlicePrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_VEC: - return RustStdVecPrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_VECDEQUE: - return RustStdVecDequePrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_BTREESET and gdb_81: - return RustStdBTreeSetPrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_BTREEMAP and gdb_81: - return RustStdBTreeMapPrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_STRING: - return RustStdStringPrinter(val) - - if type_kind == rustpp.TYPE_KIND_OS_STRING: - return RustOsStringPrinter(val) - - # Checks after this point should only be for "compiler" types -- - # things that gdb's Rust language support knows about. - if rust_enabled: - return None - - if type_kind == rustpp.TYPE_KIND_EMPTY: - return RustEmptyPrinter(val) - - if type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT: - return RustStructPrinter(val, - omit_first_field=False, - omit_type_name=False, - is_tuple_like=False) - - if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT: - return RustStructPrinter(val, - omit_first_field=True, - omit_type_name=False, - is_tuple_like=False) - - if type_kind == rustpp.TYPE_KIND_STR_SLICE: - return RustStringSlicePrinter(val) - - if type_kind == rustpp.TYPE_KIND_TUPLE: - return RustStructPrinter(val, - omit_first_field=False, - omit_type_name=True, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT: - return RustStructPrinter(val, - omit_first_field=False, - omit_type_name=False, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT: - return RustCStyleVariantPrinter(val.get_child_at_index(0)) - - if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT: - return RustStructPrinter(val, - omit_first_field=True, - omit_type_name=False, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM: - variant = get_field_at_index(gdb_val, 0) - return rust_pretty_printer_lookup_function(gdb_val[variant]) - - if type_kind == rustpp.TYPE_KIND_REGULAR_ENUM: - # This is a regular enum, extract the discriminant - discriminant_val = rustpp.get_discriminant_value_as_integer(val) - variant = get_field_at_index(gdb_val, discriminant_val) - return rust_pretty_printer_lookup_function(gdb_val[variant]) - - if type_kind == rustpp.TYPE_KIND_COMPRESSED_ENUM: - encoded_enum_info = rustpp.EncodedEnumInfo(val) - if encoded_enum_info.is_null_variant(): - return IdentityPrinter(encoded_enum_info.get_null_variant_name()) - - non_null_val = encoded_enum_info.get_non_null_variant_val() - return rust_pretty_printer_lookup_function(non_null_val.get_wrapped_value()) - - # No pretty printer has been found - return None - - -# =------------------------------------------------------------------------------ -# Pretty Printer Classes -# =------------------------------------------------------------------------------ -class RustEmptyPrinter(object): - def __init__(self, val): - self.__val = val - - def to_string(self): - return self.__val.type.get_unqualified_type_name() - - -class RustStructPrinter(object): - def __init__(self, val, omit_first_field, omit_type_name, is_tuple_like): - self.__val = val - self.__omit_first_field = omit_first_field - self.__omit_type_name = omit_type_name - self.__is_tuple_like = is_tuple_like - - def to_string(self): - if self.__omit_type_name: - return None - return self.__val.type.get_unqualified_type_name() - - def children(self): - cs = [] - wrapped_value = self.__val.get_wrapped_value() - - for number, field in enumerate(self.__val.type.get_fields()): - field_value = wrapped_value[field.name] - if self.__is_tuple_like: - cs.append((str(number), field_value)) - else: - cs.append((field.name, field_value)) - - if self.__omit_first_field: - cs = cs[1:] - - return cs - - def display_hint(self): - if self.__is_tuple_like: - return "array" - else: - return "" - - -class RustSlicePrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "array" - - def to_string(self): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i)" % length)) - - def children(self): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) - assert data_ptr.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR - raw_ptr = data_ptr.get_wrapped_value() - - for index in xrange(0, length): - yield (str(index), (raw_ptr + index).dereference()) - - -class RustStringSlicePrinter(object): - def __init__(self, val): - self.__val = val - - def to_string(self): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) - raw_ptr = data_ptr.get_wrapped_value() - return raw_ptr.lazy_string(encoding="utf-8", length=length) - - def display_hint(self): - return "string" - - -class RustStdVecPrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "array" - - def to_string(self): - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val) - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i, cap: %i)" % (length, cap))) - - def children(self): - saw_inaccessible = False - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val) - gdb_ptr = data_ptr.get_wrapped_value() - for index in xrange(0, length): - if saw_inaccessible: - return - try: - # rust-lang/rust#64343: passing deref expr to `str` allows - # catching exception on garbage pointer - str((gdb_ptr + index).dereference()) - yield (str(index), (gdb_ptr + index).dereference()) - except RuntimeError: - saw_inaccessible = True - yield (str(index), "inaccessible") - - -class RustStdVecDequePrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "array" - - def to_string(self): - (tail, head, data_ptr, cap) = \ - rustpp.extract_tail_head_ptr_and_cap_from_std_vecdeque(self.__val) - if head >= tail: - size = head - tail - else: - size = cap + head - tail - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i, cap: %i)" % (size, cap))) - - def children(self): - (tail, head, data_ptr, cap) = \ - rustpp.extract_tail_head_ptr_and_cap_from_std_vecdeque(self.__val) - gdb_ptr = data_ptr.get_wrapped_value() - if head >= tail: - size = head - tail - else: - size = cap + head - tail - for index in xrange(0, size): - yield (str(index), (gdb_ptr + ((tail + index) % cap)).dereference()) - - -# Yield each key (and optionally value) from a BoxedNode. -def children_of_node(boxed_node, height, want_values): - node_ptr = boxed_node['ptr']['pointer'] - if height > 0: - type_name = str(node_ptr.type.target()).replace('LeafNode', 'InternalNode') - node_type = gdb.lookup_type(type_name) - node_ptr = node_ptr.cast(node_type.pointer()) - leaf = node_ptr['data'] - else: - leaf = node_ptr.dereference() - keys = leaf['keys'] - if want_values: - values = leaf['vals'] - length = int(leaf['len']) - for i in xrange(0, length + 1): - if height > 0: - child_ptr = node_ptr['edges'][i]['value']['value'] - for child in children_of_node(child_ptr, height - 1, want_values): - yield child - if i < length: - if want_values: - yield (keys[i]['value']['value'], values[i]['value']['value']) - else: - yield keys[i]['value']['value'] - - -class RustStdBTreeSetPrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "array" - - def to_string(self): - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i)" % self.__val.get_wrapped_value()['map']['length'])) - - def children(self): - root = self.__val.get_wrapped_value()['map']['root'] - node_ptr = root['node'] - i = 0 - for child in children_of_node(node_ptr, root['height'], False): - yield (str(i), child) - i = i + 1 - - -class RustStdBTreeMapPrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "map" - - def to_string(self): - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i)" % self.__val.get_wrapped_value()['length'])) - - def children(self): - root = self.__val.get_wrapped_value()['root'] - node_ptr = root['node'] - i = 0 - for child in children_of_node(node_ptr, root['height'], True): - yield (str(i), child[0]) - yield (str(i), child[1]) - i = i + 1 - - -class RustStdStringPrinter(object): - def __init__(self, val): - self.__val = val - - def to_string(self): - vec = self.__val.get_child_at_index(0) - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec) - return data_ptr.get_wrapped_value().lazy_string(encoding="utf-8", - length=length) - - def display_hint(self): - return "string" - - -class RustOsStringPrinter(object): - def __init__(self, val): - self.__val = val - - def to_string(self): - buf = self.__val.get_child_at_index(0) - vec = buf.get_child_at_index(0) - if vec.type.get_unqualified_type_name() == "Wtf8Buf": - vec = vec.get_child_at_index(0) - - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec( - vec) - return data_ptr.get_wrapped_value().lazy_string(length=length) - - def display_hint(self): - return "string" - - -class RustCStyleVariantPrinter(object): - def __init__(self, val): - assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ENUM - self.__val = val - - def to_string(self): - return str(self.__val.get_wrapped_value()) - - -class IdentityPrinter(object): - def __init__(self, string): - self.string = string - - def to_string(self): - return self.string - - -def get_field_at_index(gdb_val, index): - i = 0 - for field in gdb_val.type.fields(): - if i == index: - return field - i += 1 - return None 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/lldb_commands b/src/etc/lldb_commands new file mode 100644 index 0000000000000..f470c62d89927 --- /dev/null +++ b/src/etc/lldb_commands @@ -0,0 +1,19 @@ +command script import \"$RUSTC_SYSROOT/lib/rustlib/etc/lldb_lookup.py\" +type synthetic add -l lldb_lookup.synthetic_lookup -x \".*\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)String$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^&str$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^&\\[.+\\]$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(std::ffi::([a-z_]+::)+)OsString$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)Vec<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)VecDeque<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)BTreeSet<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)BTreeMap<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(std::collections::([a-z_]+::)+)HashMap<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(std::collections::([a-z_]+::)+)HashSet<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)Rc<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)Arc<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)Cell<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)Ref<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)RefMut<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)RefCell<.+>$\" --category Rust +type category enable Rust diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py new file mode 100644 index 0000000000000..13420fbaf0a75 --- /dev/null +++ b/src/etc/lldb_lookup.py @@ -0,0 +1,115 @@ +import lldb + +from lldb_providers import * +from rust_types import RustType, classify_struct, classify_union + + +# BACKCOMPAT: rust 1.35 +def is_hashbrown_hashmap(hash_map): + return len(hash_map.type.fields) == 1 + + +def classify_rust_type(type): + type_class = type.GetTypeClass() + if type_class == lldb.eTypeClassStruct: + return classify_struct(type.name, type.fields) + if type_class == lldb.eTypeClassUnion: + return classify_union(type.fields) + + return RustType.OTHER + + +def summary_lookup(valobj, dict): + # type: (SBValue, dict) -> str + """Returns the summary provider for the given value""" + rust_type = classify_rust_type(valobj.GetType()) + + if rust_type == RustType.STD_STRING: + return StdStringSummaryProvider(valobj, dict) + if rust_type == RustType.STD_OS_STRING: + return StdOsStringSummaryProvider(valobj, dict) + if rust_type == RustType.STD_STR: + return StdStrSummaryProvider(valobj, dict) + + if rust_type == RustType.STD_VEC: + return SizeSummaryProvider(valobj, dict) + if rust_type == RustType.STD_VEC_DEQUE: + return SizeSummaryProvider(valobj, dict) + if rust_type == RustType.STD_SLICE: + return SizeSummaryProvider(valobj, dict) + + if rust_type == RustType.STD_HASH_MAP: + return SizeSummaryProvider(valobj, dict) + if rust_type == RustType.STD_HASH_SET: + return SizeSummaryProvider(valobj, dict) + + if rust_type == RustType.STD_RC: + return StdRcSummaryProvider(valobj, dict) + if rust_type == RustType.STD_ARC: + return StdRcSummaryProvider(valobj, dict) + + if rust_type == RustType.STD_REF: + return StdRefSummaryProvider(valobj, dict) + if rust_type == RustType.STD_REF_MUT: + return StdRefSummaryProvider(valobj, dict) + if rust_type == RustType.STD_REF_CELL: + return StdRefSummaryProvider(valobj, dict) + + return "" + + +def synthetic_lookup(valobj, dict): + # type: (SBValue, dict) -> object + """Returns the synthetic provider for the given value""" + rust_type = classify_rust_type(valobj.GetType()) + + if rust_type == RustType.STRUCT: + return StructSyntheticProvider(valobj, dict) + if rust_type == RustType.STRUCT_VARIANT: + return StructSyntheticProvider(valobj, dict, is_variant=True) + if rust_type == RustType.TUPLE: + return TupleSyntheticProvider(valobj, dict) + if rust_type == RustType.TUPLE_VARIANT: + return TupleSyntheticProvider(valobj, dict, is_variant=True) + if rust_type == RustType.EMPTY: + return EmptySyntheticProvider(valobj, dict) + if rust_type == RustType.REGULAR_ENUM: + discriminant = valobj.GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned() + return synthetic_lookup(valobj.GetChildAtIndex(discriminant), dict) + if rust_type == RustType.SINGLETON_ENUM: + return synthetic_lookup(valobj.GetChildAtIndex(0), dict) + + if rust_type == RustType.STD_VEC: + return StdVecSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_VEC_DEQUE: + return StdVecDequeSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_SLICE: + return StdSliceSyntheticProvider(valobj, dict) + + if rust_type == RustType.STD_HASH_MAP: + if is_hashbrown_hashmap(valobj): + return StdHashMapSyntheticProvider(valobj, dict) + else: + return StdOldHashMapSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_HASH_SET: + hash_map = valobj.GetChildAtIndex(0) + if is_hashbrown_hashmap(hash_map): + return StdHashMapSyntheticProvider(hash_map, dict, show_values=False) + else: + return StdOldHashMapSyntheticProvider(hash_map, dict, show_values=False) + + if rust_type == RustType.STD_RC: + return StdRcSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_ARC: + return StdRcSyntheticProvider(valobj, dict, is_atomic=True) + + if rust_type == RustType.STD_CELL: + return StdCellSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_REF: + return StdRefSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_REF_MUT: + return StdRefSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_REF_CELL: + return StdRefSyntheticProvider(valobj, dict, is_cell=True) + + return DefaultSynthteticProvider(valobj, dict) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py new file mode 100644 index 0000000000000..3c7817b3a618d --- /dev/null +++ b/src/etc/lldb_providers.py @@ -0,0 +1,715 @@ +import sys + +from lldb import SBValue, SBData, SBError, eBasicTypeLong, eBasicTypeUnsignedLong, \ + eBasicTypeUnsignedChar + +# from lldb.formatters import Logger + +#################################################################################################### +# This file contains two kinds of pretty-printers: summary and synthetic. +# +# Important classes from LLDB module: +# SBValue: the value of a variable, a register, or an expression +# SBType: the data type; each SBValue has a corresponding SBType +# +# Summary provider is a function with the type `(SBValue, dict) -> str`. +# The first parameter is the object encapsulating the actual variable being displayed; +# The second parameter is an internal support parameter used by LLDB, and you should not touch it. +# +# Synthetic children is the way to provide a children-based representation of the object's value. +# Synthetic provider is a class that implements the following interface: +# +# class SyntheticChildrenProvider: +# def __init__(self, SBValue, dict) +# def num_children(self) +# def get_child_index(self, str) +# def get_child_at_index(self, int) +# def update(self) +# def has_children(self) +# def get_value(self) +# +# +# You can find more information and examples here: +# 1. https://lldb.llvm.org/varformats.html +# 2. https://lldb.llvm.org/python-reference.html +# 3. https://lldb.llvm.org/python_reference/lldb.formatters.cpp.libcxx-pysrc.html +# 4. https://github.com/llvm-mirror/lldb/tree/master/examples/summaries/cocoa +#################################################################################################### + +PY3 = sys.version_info[0] == 3 + + +class ValueBuilder: + def __init__(self, valobj): + # type: (SBValue) -> ValueBuilder + self.valobj = valobj + process = valobj.GetProcess() + self.endianness = process.GetByteOrder() + self.pointer_size = process.GetAddressByteSize() + + def from_int(self, name, value): + # type: (str, int) -> SBValue + type = self.valobj.GetType().GetBasicType(eBasicTypeLong) + data = SBData.CreateDataFromSInt64Array(self.endianness, self.pointer_size, [value]) + return self.valobj.CreateValueFromData(name, data, type) + + def from_uint(self, name, value): + # type: (str, int) -> SBValue + type = self.valobj.GetType().GetBasicType(eBasicTypeUnsignedLong) + data = SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [value]) + return self.valobj.CreateValueFromData(name, data, type) + + +def unwrap_unique_or_non_null(unique_or_nonnull): + # BACKCOMPAT: rust 1.32 + # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067 + ptr = unique_or_nonnull.GetChildMemberWithName("pointer") + return ptr if ptr.TypeIsPointerType() else ptr.GetChildAtIndex(0) + + +class DefaultSynthteticProvider: + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> DefaultSynthteticProvider + # logger = Logger.Logger() + # logger >> "Default synthetic provider for " + str(valobj.GetName()) + self.valobj = valobj + + def num_children(self): + # type: () -> int + return self.valobj.GetNumChildren() + + def get_child_index(self, name): + # type: (str) -> int + return self.valobj.GetIndexOfChildWithName(name) + + def get_child_at_index(self, index): + # type: (int) -> SBValue + return self.valobj.GetChildAtIndex(index) + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return self.valobj.MightHaveChildren() + + +class EmptySyntheticProvider: + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> EmptySyntheticProvider + # logger = Logger.Logger() + # logger >> "[EmptySyntheticProvider] for " + str(valobj.GetName()) + self.valobj = valobj + + def num_children(self): + # type: () -> int + return 0 + + def get_child_index(self, name): + # type: (str) -> int + return None + + def get_child_at_index(self, index): + # type: (int) -> SBValue + return None + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return False + + +def SizeSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + return 'size=' + str(valobj.GetNumChildren()) + + +def vec_to_string(vec): + length = vec.GetNumChildren() + chars = [vec.GetChildAtIndex(i).GetValueAsUnsigned() for i in range(length)] + return bytes(chars).decode(errors='replace') if PY3 else "".join(chr(char) for char in chars) + + +def StdStringSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdStringSummaryProvider] for " + str(valobj.GetName()) + vec = valobj.GetChildAtIndex(0) + return '"%s"' % vec_to_string(vec) + + +def StdOsStringSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdOsStringSummaryProvider] for " + str(valobj.GetName()) + buf = valobj.GetChildAtIndex(0).GetChildAtIndex(0) + is_windows = "Wtf8Buf" in buf.type.name + vec = buf.GetChildAtIndex(0) if is_windows else buf + return '"%s"' % vec_to_string(vec) + + +def StdStrSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdStrSummaryProvider] for " + str(valobj.GetName()) + + length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned() + if length == 0: + return '""' + + data_ptr = valobj.GetChildMemberWithName("data_ptr") + + start = data_ptr.GetValueAsUnsigned() + error = SBError() + process = data_ptr.GetProcess() + data = process.ReadMemory(start, length, error) + data = data.decode(encoding='UTF-8') if PY3 else data + return '"%s"' % data + + +class StructSyntheticProvider: + """Pretty-printer for structs and struct enum variants""" + + def __init__(self, valobj, dict, is_variant=False): + # type: (SBValue, dict, bool) -> StructSyntheticProvider + # logger = Logger.Logger() + self.valobj = valobj + self.is_variant = is_variant + self.type = valobj.GetType() + self.fields = {} + + if is_variant: + self.fields_count = self.type.GetNumberOfFields() - 1 + real_fields = self.type.fields[1:] + else: + self.fields_count = self.type.GetNumberOfFields() + real_fields = self.type.fields + + for number, field in enumerate(real_fields): + self.fields[field.name] = number + + def num_children(self): + # type: () -> int + return self.fields_count + + def get_child_index(self, name): + # type: (str) -> int + return self.fields.get(name, -1) + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if self.is_variant: + field = self.type.GetFieldAtIndex(index + 1) + else: + field = self.type.GetFieldAtIndex(index) + return self.valobj.GetChildMemberWithName(field.name) + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return True + + +class TupleSyntheticProvider: + """Pretty-printer for tuples and tuple enum variants""" + + def __init__(self, valobj, dict, is_variant=False): + # type: (SBValue, dict, bool) -> TupleSyntheticProvider + # logger = Logger.Logger() + self.valobj = valobj + self.is_variant = is_variant + self.type = valobj.GetType() + + if is_variant: + self.size = self.type.GetNumberOfFields() - 1 + else: + self.size = self.type.GetNumberOfFields() + + def num_children(self): + # type: () -> int + return self.size + + def get_child_index(self, name): + # type: (str) -> int + if name.isdigit(): + return int(name) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if self.is_variant: + field = self.type.GetFieldAtIndex(index + 1) + else: + field = self.type.GetFieldAtIndex(index) + element = self.valobj.GetChildMemberWithName(field.name) + return self.valobj.CreateValueFromData(str(index), element.GetData(), element.GetType()) + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return True + + +class StdVecSyntheticProvider: + """Pretty-printer for alloc::vec::Vec + + struct Vec { buf: RawVec, len: usize } + struct RawVec { ptr: Unique, cap: usize, ... } + rust 1.31.1: struct Unique { pointer: NonZero<*const T>, ... } + rust 1.33.0: struct Unique { pointer: *const T, ... } + struct NonZero(T) + """ + + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> StdVecSyntheticProvider + # logger = Logger.Logger() + # logger >> "[StdVecSyntheticProvider] for " + str(valobj.GetName()) + self.valobj = valobj + self.update() + + def num_children(self): + # type: () -> int + return self.length + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit(): + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + start = self.data_ptr.GetValueAsUnsigned() + address = start + index * self.element_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type) + return element + + def update(self): + # type: () -> None + self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() + self.buf = self.valobj.GetChildMemberWithName("buf") + + self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr")) + + self.element_type = self.data_ptr.GetType().GetPointeeType() + self.element_type_size = self.element_type.GetByteSize() + + def has_children(self): + # type: () -> bool + return True + + +class StdSliceSyntheticProvider: + def __init__(self, valobj, dict): + self.valobj = valobj + self.update() + + def num_children(self): + # type: () -> int + return self.length + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit(): + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + start = self.data_ptr.GetValueAsUnsigned() + address = start + index * self.element_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type) + return element + + def update(self): + # type: () -> None + self.length = self.valobj.GetChildMemberWithName("length").GetValueAsUnsigned() + self.data_ptr = self.valobj.GetChildMemberWithName("data_ptr") + + self.element_type = self.data_ptr.GetType().GetPointeeType() + self.element_type_size = self.element_type.GetByteSize() + + def has_children(self): + # type: () -> bool + return True + + +class StdVecDequeSyntheticProvider: + """Pretty-printer for alloc::collections::vec_deque::VecDeque + + struct VecDeque { tail: usize, head: usize, buf: RawVec } + """ + + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> StdVecDequeSyntheticProvider + # logger = Logger.Logger() + # logger >> "[StdVecDequeSyntheticProvider] for " + str(valobj.GetName()) + self.valobj = valobj + self.update() + + def num_children(self): + # type: () -> int + return self.size + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit() and self.tail <= index and (self.tail + index) % self.cap < self.head: + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + start = self.data_ptr.GetValueAsUnsigned() + address = start + ((index + self.tail) % self.cap) * self.element_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type) + return element + + def update(self): + # type: () -> None + self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned() + self.tail = self.valobj.GetChildMemberWithName("tail").GetValueAsUnsigned() + self.buf = self.valobj.GetChildMemberWithName("buf") + self.cap = self.buf.GetChildMemberWithName("cap").GetValueAsUnsigned() + if self.head >= self.tail: + self.size = self.head - self.tail + else: + self.size = self.cap + self.head - self.tail + + self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr")) + + self.element_type = self.data_ptr.GetType().GetPointeeType() + self.element_type_size = self.element_type.GetByteSize() + + def has_children(self): + # type: () -> bool + return True + + +# BACKCOMPAT: rust 1.35 +class StdOldHashMapSyntheticProvider: + """Pretty-printer for std::collections::hash::map::HashMap + + struct HashMap {..., table: RawTable, ... } + struct RawTable { capacity_mask: usize, size: usize, hashes: TaggedHashUintPtr, ... } + """ + + def __init__(self, valobj, dict, show_values=True): + # type: (SBValue, dict, bool) -> StdOldHashMapSyntheticProvider + self.valobj = valobj + self.show_values = show_values + self.update() + + def num_children(self): + # type: () -> int + return self.size + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit(): + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + # logger = Logger.Logger() + start = self.data_ptr.GetValueAsUnsigned() & ~1 + + # See `libstd/collections/hash/table.rs:raw_bucket_at + hashes = self.hash_uint_size * self.capacity + align = self.pair_type_size + # See `libcore/alloc.rs:padding_needed_for` + len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~( + (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo + # len_rounded_up = ((hashes + align - 1) & ~(align - 1)) - hashes + + pairs_offset = hashes + len_rounded_up + pairs_start = start + pairs_offset + + table_index = self.valid_indices[index] + idx = table_index & self.capacity_mask + address = pairs_start + idx * self.pair_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type) + if self.show_values: + return element + else: + key = element.GetChildAtIndex(0) + return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType()) + + def update(self): + # type: () -> None + # logger = Logger.Logger() + + self.table = self.valobj.GetChildMemberWithName("table") # type: SBValue + self.size = self.table.GetChildMemberWithName("size").GetValueAsUnsigned() + self.hashes = self.table.GetChildMemberWithName("hashes") + self.hash_uint_type = self.hashes.GetType() + self.hash_uint_size = self.hashes.GetType().GetByteSize() + self.modulo = 2 ** self.hash_uint_size + self.data_ptr = self.hashes.GetChildAtIndex(0).GetChildAtIndex(0) + + self.capacity_mask = self.table.GetChildMemberWithName("capacity_mask").GetValueAsUnsigned() + self.capacity = (self.capacity_mask + 1) % self.modulo + + marker = self.table.GetChildMemberWithName("marker").GetType() # type: SBType + self.pair_type = marker.template_args[0] + self.pair_type_size = self.pair_type.GetByteSize() + + self.valid_indices = [] + for idx in range(self.capacity): + address = self.data_ptr.GetValueAsUnsigned() + idx * self.hash_uint_size + hash_uint = self.data_ptr.CreateValueFromAddress("[%s]" % idx, address, + self.hash_uint_type) + hash_ptr = hash_uint.GetChildAtIndex(0).GetChildAtIndex(0) + if hash_ptr.GetValueAsUnsigned() != 0: + self.valid_indices.append(idx) + + # logger >> "Valid indices: {}".format(str(self.valid_indices)) + + def has_children(self): + # type: () -> bool + return True + + +class StdHashMapSyntheticProvider: + """Pretty-printer for hashbrown's HashMap""" + + def __init__(self, valobj, dict, show_values=True): + # type: (SBValue, dict, bool) -> StdHashMapSyntheticProvider + self.valobj = valobj + self.show_values = show_values + self.update() + + def num_children(self): + # type: () -> int + return self.size + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit(): + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + pairs_start = self.data_ptr.GetValueAsUnsigned() + idx = self.valid_indices[index] + address = pairs_start + idx * self.pair_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type) + if self.show_values: + return element + else: + key = element.GetChildAtIndex(0) + return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType()) + + def update(self): + # type: () -> None + table = self.valobj.GetChildMemberWithName("base").GetChildMemberWithName("table") + capacity = table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1 + ctrl = table.GetChildMemberWithName("ctrl").GetChildAtIndex(0) + + self.size = table.GetChildMemberWithName("items").GetValueAsUnsigned() + self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0) + self.pair_type = self.data_ptr.Dereference().GetType() + self.pair_type_size = self.pair_type.GetByteSize() + + u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar) + u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize() + + self.valid_indices = [] + for idx in range(capacity): + address = ctrl.GetValueAsUnsigned() + idx * u8_type_size + value = ctrl.CreateValueFromAddress("ctrl[%s]" % idx, address, + u8_type).GetValueAsUnsigned() + is_present = value & 128 == 0 + if is_present: + self.valid_indices.append(idx) + + def has_children(self): + # type: () -> bool + return True + + +def StdRcSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + strong = valobj.GetChildMemberWithName("strong").GetValueAsUnsigned() + weak = valobj.GetChildMemberWithName("weak").GetValueAsUnsigned() + return "strong={}, weak={}".format(strong, weak) + + +class StdRcSyntheticProvider: + """Pretty-printer for alloc::rc::Rc and alloc::sync::Arc + + struct Rc { ptr: NonNull>, ... } + rust 1.31.1: struct NonNull { pointer: NonZero<*const T> } + rust 1.33.0: struct NonNull { pointer: *const T } + struct NonZero(T) + struct RcBox { strong: Cell, weak: Cell, value: T } + struct Cell { value: UnsafeCell } + struct UnsafeCell { value: T } + + struct Arc { ptr: NonNull>, ... } + struct ArcInner { strong: atomic::AtomicUsize, weak: atomic::AtomicUsize, data: T } + struct AtomicUsize { v: UnsafeCell } + """ + + def __init__(self, valobj, dict, is_atomic=False): + # type: (SBValue, dict, bool) -> StdRcSyntheticProvider + self.valobj = valobj + + self.ptr = unwrap_unique_or_non_null(self.valobj.GetChildMemberWithName("ptr")) + + self.value = self.ptr.GetChildMemberWithName("data" if is_atomic else "value") + + self.strong = self.ptr.GetChildMemberWithName("strong").GetChildAtIndex( + 0).GetChildMemberWithName("value") + self.weak = self.ptr.GetChildMemberWithName("weak").GetChildAtIndex( + 0).GetChildMemberWithName("value") + + self.value_builder = ValueBuilder(valobj) + + self.update() + + def num_children(self): + # type: () -> int + # Actually there are 3 children, but only the `value` should be shown as a child + return 1 + + def get_child_index(self, name): + # type: (str) -> int + if name == "value": + return 0 + if name == "strong": + return 1 + if name == "weak": + return 2 + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if index == 0: + return self.value + if index == 1: + return self.value_builder.from_uint("strong", self.strong_count) + if index == 2: + return self.value_builder.from_uint("weak", self.weak_count) + + return None + + def update(self): + # type: () -> None + self.strong_count = self.strong.GetValueAsUnsigned() + self.weak_count = self.weak.GetValueAsUnsigned() - 1 + + def has_children(self): + # type: () -> bool + return True + + +class StdCellSyntheticProvider: + """Pretty-printer for std::cell::Cell""" + + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> StdCellSyntheticProvider + self.valobj = valobj + self.value = valobj.GetChildMemberWithName("value").GetChildAtIndex(0) + + def num_children(self): + # type: () -> int + return 1 + + def get_child_index(self, name): + # type: (str) -> int + if name == "value": + return 0 + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if index == 0: + return self.value + return None + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return True + + +def StdRefSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + borrow = valobj.GetChildMemberWithName("borrow").GetValueAsSigned() + return "borrow={}".format(borrow) if borrow >= 0 else "borrow_mut={}".format(-borrow) + + +class StdRefSyntheticProvider: + """Pretty-printer for std::cell::Ref, std::cell::RefMut, and std::cell::RefCell""" + + def __init__(self, valobj, dict, is_cell=False): + # type: (SBValue, dict, bool) -> StdRefSyntheticProvider + self.valobj = valobj + + borrow = valobj.GetChildMemberWithName("borrow") + value = valobj.GetChildMemberWithName("value") + if is_cell: + self.borrow = borrow.GetChildMemberWithName("value").GetChildMemberWithName("value") + self.value = value.GetChildMemberWithName("value") + else: + self.borrow = borrow.GetChildMemberWithName("borrow").GetChildMemberWithName( + "value").GetChildMemberWithName("value") + self.value = value.Dereference() + + self.value_builder = ValueBuilder(valobj) + + self.update() + + def num_children(self): + # type: () -> int + # Actually there are 2 children, but only the `value` should be shown as a child + return 1 + + def get_child_index(self, name): + if name == "value": + return 0 + if name == "borrow": + return 1 + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if index == 0: + return self.value + if index == 1: + return self.value_builder.from_int("borrow", self.borrow_count) + return None + + def update(self): + # type: () -> None + self.borrow_count = self.borrow.GetValueAsSigned() + + def has_children(self): + # type: () -> bool + return True diff --git a/src/etc/lldb_rust_formatters.py b/src/etc/lldb_rust_formatters.py deleted file mode 100644 index 0c4021b36fb6f..0000000000000 --- a/src/etc/lldb_rust_formatters.py +++ /dev/null @@ -1,305 +0,0 @@ -import lldb -import debugger_pretty_printers_common as rustpp - -# =============================================================================== -# LLDB Pretty Printing Module for Rust -# =============================================================================== - - -class LldbType(rustpp.Type): - - def __init__(self, ty): - super(LldbType, self).__init__() - self.ty = ty - self.fields = None - - def get_unqualified_type_name(self): - qualified_name = self.ty.GetName() - - if qualified_name is None: - return qualified_name - - return rustpp.extract_type_name(qualified_name).replace("&'static ", "&") - - def get_dwarf_type_kind(self): - type_class = self.ty.GetTypeClass() - - if type_class == lldb.eTypeClassStruct: - return rustpp.DWARF_TYPE_CODE_STRUCT - - if type_class == lldb.eTypeClassUnion: - return rustpp.DWARF_TYPE_CODE_UNION - - if type_class == lldb.eTypeClassPointer: - return rustpp.DWARF_TYPE_CODE_PTR - - if type_class == lldb.eTypeClassArray: - return rustpp.DWARF_TYPE_CODE_ARRAY - - if type_class == lldb.eTypeClassEnumeration: - return rustpp.DWARF_TYPE_CODE_ENUM - - return None - - def get_fields(self): - assert ((self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT) or - (self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION)) - if self.fields is None: - self.fields = list(self.ty.fields) - return self.fields - - def get_wrapped_value(self): - return self.ty - - -class LldbValue(rustpp.Value): - def __init__(self, lldb_val): - ty = lldb_val.type - wty = LldbType(ty) - super(LldbValue, self).__init__(wty) - self.lldb_val = lldb_val - self.children = {} - - def get_child_at_index(self, index): - child = self.children.get(index) - if child is None: - lldb_field = self.lldb_val.GetChildAtIndex(index) - child = LldbValue(lldb_field) - self.children[index] = child - return child - - def as_integer(self): - return self.lldb_val.GetValueAsUnsigned() - - def get_wrapped_value(self): - return self.lldb_val - - -def print_val(lldb_val, internal_dict): - val = LldbValue(lldb_val) - type_kind = val.type.get_type_kind() - - if (type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT or - type_kind == rustpp.TYPE_KIND_REGULAR_UNION or - type_kind == rustpp.TYPE_KIND_EMPTY): - return print_struct_val(val, - internal_dict, - omit_first_field=False, - omit_type_name=False, - is_tuple_like=False) - - if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT: - return print_struct_val(val, - internal_dict, - omit_first_field=True, - omit_type_name=False, - is_tuple_like=False) - - if type_kind == rustpp.TYPE_KIND_SLICE: - return print_vec_slice_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_STR_SLICE: - return print_str_slice_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_STD_VEC: - return print_std_vec_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_STD_STRING: - return print_std_string_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_TUPLE: - return print_struct_val(val, - internal_dict, - omit_first_field=False, - omit_type_name=True, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT: - return print_struct_val(val, - internal_dict, - omit_first_field=False, - omit_type_name=False, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT: - return val.type.get_unqualified_type_name() - - if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT: - return print_struct_val(val, - internal_dict, - omit_first_field=True, - omit_type_name=False, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM: - return print_val(lldb_val.GetChildAtIndex(0), internal_dict) - - if type_kind == rustpp.TYPE_KIND_PTR: - return print_pointer_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_FIXED_SIZE_VEC: - return print_fixed_size_vec_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_REGULAR_ENUM: - # This is a regular enum, extract the discriminant - discriminant_val = rustpp.get_discriminant_value_as_integer(val) - return print_val(lldb_val.GetChildAtIndex(discriminant_val), internal_dict) - - if type_kind == rustpp.TYPE_KIND_COMPRESSED_ENUM: - encoded_enum_info = rustpp.EncodedEnumInfo(val) - if encoded_enum_info.is_null_variant(): - return encoded_enum_info.get_null_variant_name() - - non_null_val = encoded_enum_info.get_non_null_variant_val() - return print_val(non_null_val.get_wrapped_value(), internal_dict) - - # No pretty printer has been found - return lldb_val.GetValue() - - -# =--------------------------------------------------------------------------------------- -# Type-Specialized Printing Functions -# =--------------------------------------------------------------------------------------- - -def print_struct_val(val, internal_dict, omit_first_field, omit_type_name, is_tuple_like): - """ - Prints a struct, tuple, or tuple struct value with Rust syntax. - Ignores any fields before field_start_index. - """ - assert (val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT or - val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION) - - if omit_type_name: - type_name = "" - else: - type_name = val.type.get_unqualified_type_name() - - if is_tuple_like: - template = "%(type_name)s(%(body)s)" - separator = ", " - else: - template = "%(type_name)s {\n%(body)s\n}" - separator = ", \n" - - fields = val.type.get_fields() - - def render_child(child_index): - this = "" - if not is_tuple_like: - field_name = fields[child_index].name - this += field_name + ": " - - field_val = val.get_child_at_index(child_index) - - if not field_val.get_wrapped_value().IsValid(): - field = fields[child_index] - # LLDB is not good at handling zero-sized values, so we have to help - # it a little - if field.GetType().GetByteSize() == 0: - return this + rustpp.extract_type_name(field.GetType().GetName()) - else: - return this + "" - - return this + print_val(field_val.get_wrapped_value(), internal_dict) - - if omit_first_field: - field_start_index = 1 - else: - field_start_index = 0 - - body = separator.join([render_child(idx) for idx in range(field_start_index, len(fields))]) - - return template % {"type_name": type_name, - "body": body} - - -def print_pointer_val(val, internal_dict): - """Prints a pointer value with Rust syntax""" - assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR - sigil = "&" - type_name = val.type.get_unqualified_type_name() - if type_name and type_name[0:1] in ["&", "*"]: - sigil = type_name[0:1] - - return sigil + hex(val.as_integer()) - - -def print_fixed_size_vec_val(val, internal_dict): - assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ARRAY - lldb_val = val.get_wrapped_value() - - output = "[" - - for i in range(lldb_val.num_children): - output += print_val(lldb_val.GetChildAtIndex(i), internal_dict) - if i != lldb_val.num_children - 1: - output += ", " - - output += "]" - return output - - -def print_vec_slice_val(val, internal_dict): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(val) - return "&[%s]" % print_array_of_values(val.get_wrapped_value().GetName(), - data_ptr, - length, - internal_dict) - - -def print_std_vec_val(val, internal_dict): - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(val) - return "vec![%s]" % print_array_of_values(val.get_wrapped_value().GetName(), - data_ptr, - length, - internal_dict) - - -def print_str_slice_val(val, internal_dict): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(val) - return read_utf8_string(data_ptr, length) - - -def print_std_string_val(val, internal_dict): - vec = val.get_child_at_index(0) - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec) - return read_utf8_string(data_ptr, length) - -# =----------------------------------------------------------------------- -# Helper Functions -# =----------------------------------------------------------------------- - - -def print_array_of_values(array_name, data_ptr_val, length, internal_dict): - """Prints a contiguous memory range, interpreting it as values of the - pointee-type of data_ptr_val.""" - - data_ptr_type = data_ptr_val.type - assert data_ptr_type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR - - element_type = data_ptr_type.get_wrapped_value().GetPointeeType() - element_type_size = element_type.GetByteSize() - - start_address = data_ptr_val.as_integer() - raw_value = data_ptr_val.get_wrapped_value() - - def render_element(i): - address = start_address + i * element_type_size - element_val = raw_value.CreateValueFromAddress(array_name + ("[%s]" % i), - address, - element_type) - return print_val(element_val, internal_dict) - - return ', '.join([render_element(i) for i in range(length)]) - - -def read_utf8_string(ptr_val, byte_count): - if byte_count == 0: - return '""' - error = lldb.SBError() - process = ptr_val.get_wrapped_value().GetProcess() - data = process.ReadMemory(ptr_val.as_integer(), byte_count, error) - if error.Success(): - return '"%s"' % data.decode(encoding='UTF-8') - else: - return '' % error.GetCString() 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/rust-lldb b/src/etc/rust-lldb index 7b9b40e6b4a0a..28b32ef1ad532 100755 --- a/src/etc/rust-lldb +++ b/src/etc/rust-lldb @@ -30,13 +30,5 @@ EOF fi fi -# Prepare commands that will be loaded before any file on the command line has been loaded -script_import="command script import \"$RUSTC_SYSROOT/lib/rustlib/etc/lldb_rust_formatters.py\"" -category_definition="type summary add --no-value --python-function lldb_rust_formatters.print_val -x \".*\" --category Rust" -category_enable="type category enable Rust" - # Call LLDB with the commands added to the argument list -exec "$lldb" --one-line-before-file "$script_import" \ - --one-line-before-file "$category_definition" \ - --one-line-before-file "$category_enable" \ - "$@" +exec "$lldb" --source-before-file ./lldb_commands "$@" diff --git a/src/etc/rust_types.py b/src/etc/rust_types.py new file mode 100644 index 0000000000000..b49fd19ed4cbb --- /dev/null +++ b/src/etc/rust_types.py @@ -0,0 +1,113 @@ +import re + + +class RustType(object): + OTHER = "Other" + STRUCT = "Struct" + TUPLE = "Tuple" + CSTYLE_VARIANT = "CStyleVariant" + TUPLE_VARIANT = "TupleVariant" + STRUCT_VARIANT = "StructVariant" + ENUM = "Enum" + EMPTY = "Empty" + SINGLETON_ENUM = "SingletonEnum" + REGULAR_ENUM = "RegularEnum" + COMPRESSED_ENUM = "CompressedEnum" + REGULAR_UNION = "RegularUnion" + + STD_STRING = "StdString" + STD_OS_STRING = "StdOsString" + STD_STR = "StdStr" + STD_SLICE = "StdSlice" + STD_VEC = "StdVec" + STD_VEC_DEQUE = "StdVecDeque" + STD_BTREE_SET = "StdBTreeSet" + STD_BTREE_MAP = "StdBTreeMap" + STD_HASH_MAP = "StdHashMap" + STD_HASH_SET = "StdHashSet" + STD_RC = "StdRc" + STD_ARC = "StdArc" + STD_CELL = "StdCell" + STD_REF = "StdRef" + STD_REF_MUT = "StdRefMut" + STD_REF_CELL = "StdRefCell" + + +STD_STRING_REGEX = re.compile(r"^(alloc::(\w+::)+)String$") +STD_STR_REGEX = re.compile(r"^&str$") +STD_SLICE_REGEX = re.compile(r"^&\[.+\]$") +STD_OS_STRING_REGEX = re.compile(r"^(std::ffi::(\w+::)+)OsString$") +STD_VEC_REGEX = re.compile(r"^(alloc::(\w+::)+)Vec<.+>$") +STD_VEC_DEQUE_REGEX = re.compile(r"^(alloc::(\w+::)+)VecDeque<.+>$") +STD_BTREE_SET_REGEX = re.compile(r"^(alloc::(\w+::)+)BTreeSet<.+>$") +STD_BTREE_MAP_REGEX = re.compile(r"^(alloc::(\w+::)+)BTreeMap<.+>$") +STD_HASH_MAP_REGEX = re.compile(r"^(std::collections::(\w+::)+)HashMap<.+>$") +STD_HASH_SET_REGEX = re.compile(r"^(std::collections::(\w+::)+)HashSet<.+>$") +STD_RC_REGEX = re.compile(r"^(alloc::(\w+::)+)Rc<.+>$") +STD_ARC_REGEX = re.compile(r"^(alloc::(\w+::)+)Arc<.+>$") +STD_CELL_REGEX = re.compile(r"^(core::(\w+::)+)Cell<.+>$") +STD_REF_REGEX = re.compile(r"^(core::(\w+::)+)Ref<.+>$") +STD_REF_MUT_REGEX = re.compile(r"^(core::(\w+::)+)RefMut<.+>$") +STD_REF_CELL_REGEX = re.compile(r"^(core::(\w+::)+)RefCell<.+>$") + +TUPLE_ITEM_REGEX = re.compile(r"__\d+$") + +ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$" +ENUM_DISR_FIELD_NAME = "<>" + +STD_TYPE_TO_REGEX = { + RustType.STD_STRING: STD_STRING_REGEX, + RustType.STD_OS_STRING: STD_OS_STRING_REGEX, + RustType.STD_STR: STD_STR_REGEX, + RustType.STD_SLICE: STD_SLICE_REGEX, + RustType.STD_VEC: STD_VEC_REGEX, + RustType.STD_VEC_DEQUE: STD_VEC_DEQUE_REGEX, + RustType.STD_HASH_MAP: STD_HASH_MAP_REGEX, + RustType.STD_HASH_SET: STD_HASH_SET_REGEX, + RustType.STD_BTREE_SET: STD_BTREE_SET_REGEX, + RustType.STD_BTREE_MAP: STD_BTREE_MAP_REGEX, + RustType.STD_RC: STD_RC_REGEX, + RustType.STD_ARC: STD_ARC_REGEX, + RustType.STD_REF: STD_REF_REGEX, + RustType.STD_REF_MUT: STD_REF_MUT_REGEX, + RustType.STD_REF_CELL: STD_REF_CELL_REGEX, + RustType.STD_CELL: STD_CELL_REGEX, +} + +def is_tuple_fields(fields): + # type: (list) -> bool + return all(TUPLE_ITEM_REGEX.match(str(field.name)) for field in fields) + + +def classify_struct(name, fields): + if len(fields) == 0: + return RustType.EMPTY + + for ty, regex in STD_TYPE_TO_REGEX.items(): + if regex.match(name): + return ty + + if fields[0].name == ENUM_DISR_FIELD_NAME: + return RustType.ENUM + + if is_tuple_fields(fields): + return RustType.TUPLE + + return RustType.STRUCT + + +def classify_union(fields): + if len(fields) == 0: + return RustType.EMPTY + + first_variant_name = fields[0].name + if first_variant_name is None: + if len(fields) == 1: + return RustType.SINGLETON_ENUM + else: + return RustType.REGULAR_ENUM + elif first_variant_name.startswith(ENCODED_ENUM_PREFIX): + assert len(fields) == 1 + return RustType.COMPRESSED_ENUM + else: + return RustType.REGULAR_UNION 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/Cargo.toml b/src/liballoc/Cargo.toml index d1119f7b7c0a7..914195f015b5a 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -25,6 +25,7 @@ path = "../liballoc/tests/lib.rs" [[bench]] name = "collectionsbenches" path = "../liballoc/benches/lib.rs" +test = true [[bench]] name = "vec_deque_append_bench" diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 9f82b2c6fa66d..98c7ac3f2ef17 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)] @@ -78,7 +77,7 @@ pub struct Global; #[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { - __rust_alloc(layout.size(), layout.align()) + unsafe { __rust_alloc(layout.size(), layout.align()) } } /// Deallocate memory with the global allocator. @@ -100,7 +99,7 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 { #[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { - __rust_dealloc(ptr, layout.size(), layout.align()) + unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } } /// Reallocate memory with the global allocator. @@ -122,7 +121,7 @@ pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { #[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - __rust_realloc(ptr, layout.size(), layout.align(), new_size) + unsafe { __rust_realloc(ptr, layout.size(), layout.align(), new_size) } } /// Allocate zero-initialized memory with the global allocator. @@ -159,54 +158,110 @@ pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 #[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { - __rust_alloc_zeroed(layout.size(), layout.align()) + unsafe { __rust_alloc_zeroed(layout.size(), layout.align()) } } #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl AllocRef for Global { #[inline] - fn alloc(&mut self, layout: Layout) -> 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 }) + } } } #[inline] unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { if layout.size() != 0 { - dealloc(ptr.as_ptr(), layout) + unsafe { dealloc(ptr.as_ptr(), layout) } } } #[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 = + unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; + self.alloc(new_layout, init) + } + ReallocPlacement::MayMove => { + // `realloc` probably checks for `new_size > size` or something similar. + let ptr = unsafe { + intrinsics::assume(new_size > size); + realloc(ptr.as_ptr(), layout, new_size) + }; + let memory = + MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }; + unsafe { + init.init_offset(memory, size); + } + Ok(memory) } - (_, _) => 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 => { + unsafe { + self.dealloc(ptr, layout); + } + Ok(MemoryBlock { ptr: layout.dangling(), size: 0 }) + } + ReallocPlacement::MayMove => { + // `realloc` probably checks for `new_size < size` or something similar. + let ptr = unsafe { + intrinsics::assume(new_size < size); + realloc(ptr.as_ptr(), layout, new_size) + }; + Ok(MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size }) } } } @@ -218,14 +273,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 = unsafe { Layout::from_size_align_unchecked(size, align) }; + match Global.alloc(layout, AllocInit::Uninitialized) { + Ok(memory) => memory.ptr.as_ptr(), + Err(_) => handle_alloc_error(layout), } } @@ -237,12 +288,11 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { // For example if `Box` is changed to `struct Box(Unique, A)`, // this function has to be changed to `fn box_free(Unique, A)` as well. pub(crate) unsafe fn box_free(ptr: Unique) { - 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 { + unsafe { + let size = size_of_val(ptr.as_ref()); + let align = min_align_of_val(ptr.as_ref()); let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr.cast().into(), layout); + Global.dealloc(ptr.cast().into(), layout) } } 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 9a7d0d9aebaaf..f1b560b9b9685 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -92,11 +92,13 @@ //! pub struct Foo; //! //! #[no_mangle] +//! #[allow(improper_ctypes_definitions)] //! pub extern "C" fn foo_new() -> Box { //! Box::new(Foo) //! } //! //! #[no_mangle] +//! #[allow(improper_ctypes_definitions)] //! pub extern "C" fn foo_delete(_: Option>) {} //! ``` //! @@ -143,10 +145,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 +198,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 +226,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 +242,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]) } + } } impl Box<[T]> { @@ -265,15 +277,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) } } } @@ -309,7 +313,7 @@ impl Box> { #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub unsafe fn assume_init(self) -> Box { - Box::from_raw(Box::into_raw(self) as *mut T) + unsafe { Box::from_raw(Box::into_raw(self) as *mut T) } } } @@ -347,7 +351,7 @@ impl Box<[mem::MaybeUninit]> { #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub unsafe fn assume_init(self) -> Box<[T]> { - Box::from_raw(Box::into_raw(self) as *mut [T]) + unsafe { Box::from_raw(Box::into_raw(self) as *mut [T]) } } } @@ -391,7 +395,7 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub unsafe fn from_raw(raw: *mut T) -> Self { - Box(Unique::new_unchecked(raw)) + Box(unsafe { Unique::new_unchecked(raw) }) } /// Consumes the `Box`, returning a wrapped raw pointer. @@ -437,7 +441,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 +469,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 +479,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 +552,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 +798,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 +832,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]>` @@ -825,6 +867,25 @@ impl From> for Box<[u8]> { } } +#[stable(feature = "box_from_array", since = "1.45.0")] +impl From<[T; N]> for Box<[T]> +where + [T; N]: LengthAtMost32, +{ + /// Converts a `[T; N]` into a `Box<[T]>` + /// + /// This conversion moves the array to newly heap-allocated memory. + /// + /// # Examples + /// ```rust + /// let boxed: Box<[u8]> = Box::from([4, 2]); + /// println!("{:?}", boxed); + /// ``` + fn from(array: [T; N]) -> Box<[T]> { + box array + } +} + #[stable(feature = "boxed_slice_try_from", since = "1.43.0")] impl TryFrom> for Box<[T; N]> where @@ -1050,6 +1111,14 @@ impl Clone for Box<[T]> { fn clone(&self) -> Self { self.to_vec().into_boxed_slice() } + + fn clone_from(&mut self, other: &Self) { + if self.len() == other.len() { + self.clone_from_slice(&other); + } else { + *self = other.clone(); + } + } } #[stable(feature = "box_borrow", since = "1.1.0")] @@ -1105,29 +1174,6 @@ impl AsMut for Box { #[stable(feature = "pin", since = "1.33.0")] impl Unpin for Box {} -#[cfg(bootstrap)] -#[unstable(feature = "generator_trait", issue = "43122")] -impl Generator for Box { - type Yield = G::Yield; - type Return = G::Return; - - fn resume(mut self: Pin<&mut Self>) -> GeneratorState { - G::resume(Pin::new(&mut *self)) - } -} - -#[cfg(bootstrap)] -#[unstable(feature = "generator_trait", issue = "43122")] -impl Generator for Pin> { - type Yield = G::Yield; - type Return = G::Return; - - fn resume(mut self: Pin<&mut Self>) -> GeneratorState { - G::resume((*self).as_mut()) - } -} - -#[cfg(not(bootstrap))] #[unstable(feature = "generator_trait", issue = "43122")] impl + Unpin, R> Generator for Box { type Yield = G::Yield; @@ -1138,7 +1184,6 @@ impl + Unpin, R> Generator for Box { } } -#[cfg(not(bootstrap))] #[unstable(feature = "generator_trait", issue = "43122")] impl, R> Generator for Pin> { type Yield = G::Yield; diff --git a/src/liballoc/collections/binary_heap.rs b/src/liballoc/collections/binary_heap.rs index 9908a3049763a..15313e333ce73 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) @@ -977,7 +1003,7 @@ impl<'a, T> Hole<'a, T> { unsafe fn new(data: &'a mut [T], pos: usize) -> Self { debug_assert!(pos < data.len()); // SAFE: pos should be inside the slice - let elt = ptr::read(data.get_unchecked(pos)); + let elt = unsafe { ptr::read(data.get_unchecked(pos)) }; Hole { data, elt: ManuallyDrop::new(elt), pos } } @@ -999,7 +1025,7 @@ impl<'a, T> Hole<'a, T> { unsafe fn get(&self, index: usize) -> &T { debug_assert!(index != self.pos); debug_assert!(index < self.data.len()); - self.data.get_unchecked(index) + unsafe { self.data.get_unchecked(index) } } /// Move hole to new location @@ -1009,9 +1035,11 @@ impl<'a, T> Hole<'a, T> { unsafe fn move_to(&mut self, index: usize) { debug_assert!(index != self.pos); debug_assert!(index < self.data.len()); - let index_ptr: *const _ = self.data.get_unchecked(index); - let hole_ptr = self.data.get_unchecked_mut(self.pos); - ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); + unsafe { + let index_ptr: *const _ = self.data.get_unchecked(index); + let hole_ptr = self.data.get_unchecked_mut(self.pos); + ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); + } self.pos = index; } } @@ -1243,7 +1271,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() {} } } @@ -1350,6 +1378,16 @@ impl Extend for BinaryHeap { fn extend>(&mut self, iter: I) { >::spec_extend(self, iter); } + + #[inline] + fn extend_one(&mut self, item: T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } impl> SpecExtend for BinaryHeap { @@ -1380,4 +1418,14 @@ impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index 9da324ba2d4f1..34cacebe79636 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 @@ -122,7 +123,7 @@ use UnderflowResult::*; /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct BTreeMap { - root: node::Root, + root: Option>, length: usize, } @@ -147,10 +148,11 @@ impl Clone for BTreeMap { { match node.force() { Leaf(leaf) => { - let mut out_tree = BTreeMap { root: node::Root::new_leaf(), length: 0 }; + let mut out_tree = BTreeMap { root: Some(node::Root::new_leaf()), length: 0 }; { - let mut out_node = match out_tree.root.as_mut().force() { + let root = out_tree.root.as_mut().unwrap(); + let mut out_node = match root.as_mut().force() { Leaf(leaf) => leaf, Internal(_) => unreachable!(), }; @@ -169,9 +171,14 @@ impl Clone for BTreeMap { } Internal(internal) => { let mut out_tree = clone_subtree(internal.first_edge().descend()); + out_tree.ensure_root_is_owned(); { - let mut out_node = out_tree.root.push_level(); + // Ideally we'd use the return of ensure_root_is_owned + // instead of re-unwrapping here but unfortunately that + // borrows all of out_tree and we need access to the + // length below. + let mut out_node = out_tree.root.as_mut().unwrap().push_level(); let mut in_edge = internal.first_edge(); while let Ok(kv) = in_edge.right_kv() { let (k, v) = kv.into_kv(); @@ -184,13 +191,13 @@ 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) }; - out_node.push(k, v, subroot); + out_node.push(k, v, subroot.unwrap_or_else(node::Root::new_leaf)); out_tree.length += 1 + sublength; } } @@ -203,64 +210,11 @@ impl Clone for BTreeMap { if self.is_empty() { // Ideally we'd call `BTreeMap::new` here, but that has the `K: // Ord` constraint, which this method lacks. - BTreeMap { root: node::Root::shared_empty_root(), length: 0 } + BTreeMap { root: None, length: 0 } } else { - clone_subtree(self.root.as_ref()) + 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 @@ -271,14 +225,14 @@ where type Key = K; fn get(&self, key: &Q) -> Option<&K> { - match search::search_tree(self.root.as_ref(), key) { + match search::search_tree(self.root.as_ref()?.as_ref(), key) { Found(handle) => Some(handle.into_kv().0), GoDown(_) => None, } } fn take(&mut self, key: &Q) -> Option { - match search::search_tree(self.root.as_mut(), key) { + match search::search_tree(self.root.as_mut()?.as_mut(), key) { Found(handle) => Some( OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData } .remove_kv() @@ -290,7 +244,7 @@ where fn replace(&mut self, key: K) -> Option { self.ensure_root_is_owned(); - match search::search_tree::, K, (), K>(self.root.as_mut(), &key) { + match search::search_tree::, K, (), K>(self.root.as_mut()?.as_mut(), &key) { Found(handle) => Some(mem::replace(handle.into_kv_mut().0, key)), GoDown(handle) => { VacantEntry { key, handle, length: &mut self.length, _marker: PhantomData } @@ -344,15 +298,18 @@ pub struct IterMut<'a, K: 'a, V: 'a> { /// [`BTreeMap`]: struct.BTreeMap.html #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter { - front: Handle, marker::Edge>, - back: Handle, marker::Edge>, + front: Option, marker::Edge>>, + back: Option, marker::Edge>>, length: usize, } #[stable(feature = "collection_debug", since = "1.17.0")] impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let range = Range { front: self.front.reborrow(), back: self.back.reborrow() }; + let range = Range { + front: self.front.as_ref().map(|f| f.reborrow()), + back: self.back.as_ref().map(|b| b.reborrow()), + }; f.debug_list().entries(range).finish() } } @@ -417,8 +374,8 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { /// [`BTreeMap`]: struct.BTreeMap.html #[stable(feature = "btree_range", since = "1.17.0")] pub struct Range<'a, K: 'a, V: 'a> { - front: Handle, K, V, marker::Leaf>, marker::Edge>, - back: Handle, K, V, marker::Leaf>, marker::Edge>, + front: Option, K, V, marker::Leaf>, marker::Edge>>, + back: Option, K, V, marker::Leaf>, marker::Edge>>, } #[stable(feature = "collection_debug", since = "1.17.0")] @@ -437,8 +394,8 @@ impl fmt::Debug for Range<'_, K, V> { /// [`BTreeMap`]: struct.BTreeMap.html #[stable(feature = "btree_range", since = "1.17.0")] pub struct RangeMut<'a, K: 'a, V: 'a> { - front: Handle, K, V, marker::Leaf>, marker::Edge>, - back: Handle, K, V, marker::Leaf>, marker::Edge>, + front: Option, K, V, marker::Leaf>, marker::Edge>>, + back: Option, K, V, marker::Leaf>, marker::Edge>>, // Be invariant in `K` and `V` _marker: PhantomData<&'a mut (K, V)>, @@ -447,7 +404,10 @@ pub struct RangeMut<'a, K: 'a, V: 'a> { #[stable(feature = "collection_debug", since = "1.17.0")] impl fmt::Debug for RangeMut<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let range = Range { front: self.front.reborrow(), back: self.back.reborrow() }; + let range = Range { + front: self.front.as_ref().map(|f| f.reborrow()), + back: self.back.as_ref().map(|b| b.reborrow()), + }; f.debug_list().entries(range).finish() } } @@ -528,7 +488,9 @@ struct MergeIter> { } impl BTreeMap { - /// Makes a new empty BTreeMap with a reasonable choice for B. + /// Makes a new empty BTreeMap. + /// + /// Does not allocate anything on its own. /// /// # Examples /// @@ -543,8 +505,9 @@ impl BTreeMap { /// map.insert(1, "a"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> BTreeMap { - BTreeMap { root: node::Root::shared_empty_root(), length: 0 } + #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] + pub const fn new() -> BTreeMap { + BTreeMap { root: None, length: 0 } } /// Clears the map, removing all elements. @@ -589,7 +552,7 @@ impl BTreeMap { K: Borrow, Q: Ord, { - match search::search_tree(self.root.as_ref(), key) { + match search::search_tree(self.root.as_ref()?.as_ref(), key) { Found(handle) => Some(handle.into_kv().1), GoDown(_) => None, } @@ -616,7 +579,7 @@ impl BTreeMap { K: Borrow, Q: Ord, { - match search::search_tree(self.root.as_ref(), k) { + match search::search_tree(self.root.as_ref()?.as_ref(), k) { Found(handle) => Some(handle.into_kv()), GoDown(_) => None, } @@ -640,12 +603,8 @@ 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, - { - let front = self.root.as_ref().first_leaf_edge(); + 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) } @@ -654,7 +613,38 @@ impl BTreeMap { /// /// # Examples /// - /// Contrived way to `clear` a map: + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// map.insert(2, "b"); + /// if let Some(mut entry) = map.first_entry() { + /// if *entry.key() > 0 { + /// entry.insert("first"); + /// } + /// } + /// assert_eq!(*map.get(&1).unwrap(), "first"); + /// assert_eq!(*map.get(&2).unwrap(), "b"); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn first_entry(&mut self) -> Option> { + let front = self.root.as_mut()?.as_mut().first_leaf_edge(); + let kv = front.right_kv().ok()?; + Some(OccupiedEntry { + handle: kv.forget_node_type(), + length: &mut self.length, + _marker: PhantomData, + }) + } + + /// Removes and returns the first element in the map. + /// The key of this element is the minimum key that was in the map. + /// + /// # Examples + /// + /// Draining elements in ascending order, while keeping a usable map each iteration. /// /// ``` /// #![feature(map_first_last)] @@ -663,27 +653,14 @@ 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)); + /// 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 first_entry(&mut self) -> Option> - where - T: Ord, - K: Borrow, - { - let front = self.root.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 - } + 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. @@ -703,12 +680,8 @@ 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, - { - let back = self.root.as_ref().last_leaf_edge(); + 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) } @@ -717,7 +690,38 @@ impl BTreeMap { /// /// # Examples /// - /// Contrived way to `clear` a map: + /// ``` + /// #![feature(map_first_last)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// map.insert(2, "b"); + /// if let Some(mut entry) = map.last_entry() { + /// if *entry.key() > 0 { + /// entry.insert("last"); + /// } + /// } + /// assert_eq!(*map.get(&1).unwrap(), "a"); + /// assert_eq!(*map.get(&2).unwrap(), "last"); + /// ``` + #[unstable(feature = "map_first_last", issue = "62924")] + pub fn last_entry(&mut self) -> Option> { + let back = self.root.as_mut()?.as_mut().last_leaf_edge(); + let kv = back.left_kv().ok()?; + Some(OccupiedEntry { + handle: kv.forget_node_type(), + length: &mut self.length, + _marker: PhantomData, + }) + } + + /// Removes and returns the last element in the map. + /// The key of this element is the maximum key that was in the map. + /// + /// # Examples + /// + /// Draining elements in descending order, while keeping a usable map each iteration. /// /// ``` /// #![feature(map_first_last)] @@ -726,27 +730,14 @@ 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)); + /// 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 last_entry(&mut self) -> Option> - where - T: Ord, - K: Borrow, - { - let back = self.root.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 - } + 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. @@ -801,7 +792,7 @@ impl BTreeMap { K: Borrow, Q: Ord, { - match search::search_tree(self.root.as_mut(), key) { + match search::search_tree(self.root.as_mut()?.as_mut(), key) { Found(handle) => Some(handle.into_kv_mut().1), GoDown(_) => None, } @@ -882,7 +873,6 @@ impl BTreeMap { /// Basic usage: /// /// ``` - /// #![feature(btreemap_remove_entry)] /// use std::collections::BTreeMap; /// /// let mut map = BTreeMap::new(); @@ -890,13 +880,13 @@ 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, Q: Ord, { - match search::search_tree(self.root.as_mut(), key) { + match search::search_tree(self.root.as_mut()?.as_mut(), key) { Found(handle) => Some( OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData } .remove_entry(), @@ -992,11 +982,13 @@ impl BTreeMap { K: Borrow, R: RangeBounds, { - let root1 = self.root.as_ref(); - let root2 = self.root.as_ref(); - let (f, b) = range_search(root1, root2, range); + if let Some(root) = &self.root { + let (f, b) = range_search(root.as_ref(), range); - Range { front: f, back: b } + Range { front: Some(f), back: Some(b) } + } else { + Range { front: None, back: None } + } } /// Constructs a mutable double-ended iterator over a sub-range of elements in the map. @@ -1036,11 +1028,13 @@ impl BTreeMap { K: Borrow, R: RangeBounds, { - let root1 = self.root.as_mut(); - let root2 = unsafe { ptr::read(&root1) }; - let (f, b) = range_search(root1, root2, range); + if let Some(root) = &mut self.root { + let (f, b) = range_search(root.as_mut(), range); - RangeMut { front: f, back: b, _marker: PhantomData } + RangeMut { front: Some(f), back: Some(b), _marker: PhantomData } + } else { + RangeMut { front: None, back: None, _marker: PhantomData } + } } /// Gets the given key's corresponding entry in the map for in-place manipulation. @@ -1065,7 +1059,7 @@ impl BTreeMap { pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { // FIXME(@porglezomp) Avoid allocating if we don't insert self.ensure_root_is_owned(); - match search::search_tree(self.root.as_mut(), &key) { + match search::search_tree(self.root.as_mut().unwrap().as_mut(), &key) { Found(handle) => { Occupied(OccupiedEntry { handle, length: &mut self.length, _marker: PhantomData }) } @@ -1077,7 +1071,7 @@ impl BTreeMap { fn from_sorted_iter>(&mut self, iter: I) { self.ensure_root_is_owned(); - let mut cur_node = self.root.as_mut().last_leaf_edge().into_node(); + let mut cur_node = self.root.as_mut().unwrap().as_mut().last_leaf_edge().into_node(); // Iterate through all key-value pairs, pushing them into nodes at the right level. for (key, value) in iter { // Try to push key-value pair into the current leaf node. @@ -1126,7 +1120,7 @@ impl BTreeMap { fn fix_right_edge(&mut self) { // Handle underfull nodes, start from the top. - let mut cur_node = self.root.as_mut(); + let mut cur_node = self.root.as_mut().unwrap().as_mut(); while let Internal(internal) = cur_node.force() { // Check if right-most child is underfull. let mut last_edge = internal.last_edge(); @@ -1187,14 +1181,14 @@ impl BTreeMap { let total_num = self.len(); let mut right = Self::new(); - right.root = node::Root::new_leaf(); - for _ in 0..(self.root.as_ref().height()) { - right.root.push_level(); + let right_root = right.ensure_root_is_owned(); + for _ in 0..(self.root.as_ref().unwrap().as_ref().height()) { + right_root.push_level(); } { - let mut left_node = self.root.as_mut(); - let mut right_node = right.root.as_mut(); + let mut left_node = self.root.as_mut().unwrap().as_mut(); + let mut right_node = right.root.as_mut().unwrap().as_mut(); loop { let mut split_edge = match search::search_node(left_node, key) { @@ -1223,7 +1217,9 @@ impl BTreeMap { self.fix_right_border(); right.fix_left_border(); - if self.root.as_ref().height() < right.root.as_ref().height() { + if self.root.as_ref().unwrap().as_ref().height() + < right.root.as_ref().unwrap().as_ref().height() + { self.recalc_length(); right.length = total_num - self.len(); } else { @@ -1234,6 +1230,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 @@ -1261,19 +1299,19 @@ impl BTreeMap { res } - self.length = dfs(self.root.as_ref()); + self.length = dfs(self.root.as_ref().unwrap().as_ref()); } /// Removes empty levels on the top. fn fix_top(&mut self) { loop { { - let node = self.root.as_ref(); + let node = self.root.as_ref().unwrap().as_ref(); if node.height() == 0 || node.len() > 0 { break; } } - self.root.pop_level(); + self.root.as_mut().unwrap().pop_level(); } } @@ -1281,7 +1319,7 @@ impl BTreeMap { self.fix_top(); { - let mut cur_node = self.root.as_mut(); + let mut cur_node = self.root.as_mut().unwrap().as_mut(); while let Internal(node) = cur_node.force() { let mut last_kv = node.last_kv(); @@ -1307,7 +1345,7 @@ impl BTreeMap { self.fix_top(); { - let mut cur_node = self.root.as_mut(); + let mut cur_node = self.root.as_mut().unwrap().as_mut(); while let Internal(node) = cur_node.force() { let mut first_kv = node.first_kv(); @@ -1326,13 +1364,6 @@ impl BTreeMap { self.fix_top(); } - - /// If the root node is the shared root node, allocate our own node. - fn ensure_root_is_owned(&mut self) { - if self.root.is_shared_root() { - self.root = node::Root::new_leaf(); - } - } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1458,12 +1489,14 @@ impl IntoIterator for BTreeMap { type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { - let root1 = unsafe { ptr::read(&self.root).into_ref() }; - let root2 = unsafe { 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: root1.first_leaf_edge(), back: root2.last_leaf_edge(), length: len } + IntoIter { front: Some(f), back: Some(b), length: me.length } + } else { + IntoIter { front: None, back: None, length: 0 } + } } } @@ -1478,9 +1511,9 @@ impl Drop for IntoIter { // don't have to care about panics this time (they'll abort). while let Some(_) = self.0.next() {} - // No need to avoid the shared root, because the tree was definitely not empty. unsafe { - let mut node = ptr::read(&self.0.front).into_node().forget_type(); + let mut node = + unwrap_unchecked(ptr::read(&self.0.front)).into_node().forget_type(); while let Some(parent) = node.deallocate_and_ascend() { node = parent.into_node().forget_type(); } @@ -1495,14 +1528,13 @@ impl Drop for IntoIter { } unsafe { - let mut node = ptr::read(&self.front).into_node().forget_type(); - if node.is_shared_root() { - return; - } - // Most of the nodes have been deallocated while traversing - // but one pile from a leaf up to the root is left standing. - while let Some(parent) = node.deallocate_and_ascend() { - node = parent.into_node().forget_type(); + if let Some(front) = ptr::read(&self.front) { + let mut node = front.into_node().forget_type(); + // Most of the nodes have been deallocated while traversing + // but one pile from a leaf up to the root is left standing. + while let Some(parent) = node.deallocate_and_ascend() { + node = parent.into_node().forget_type(); + } } } } @@ -1517,7 +1549,7 @@ impl Iterator for IntoIter { None } else { self.length -= 1; - Some(unsafe { self.front.next_unchecked() }) + Some(unsafe { self.front.as_mut().unwrap().next_unchecked() }) } } @@ -1533,7 +1565,7 @@ impl DoubleEndedIterator for IntoIter { None } else { self.length -= 1; - Some(unsafe { self.back.next_back_unchecked() }) + Some(unsafe { self.back.as_mut().unwrap().next_back_unchecked() }) } } } @@ -1630,6 +1662,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()?; + unsafe { ptr::read(edge).next_kv().ok() } + } + + /// Implementation of a typical `DrainFilter::next` method, given the predicate. + pub(super) fn next(&mut self, pred: &mut F) -> Option<(K, V)> + where + F: FnMut(&K, &mut V) -> bool, + { + while let Some(mut kv) = unsafe { self.next_kv() } { + let (k, v) = kv.kv_mut(); + if pred(k, v) { + *self.length -= 1; + let (k, v, leaf_edge_location) = kv.remove_kv_tracking(); + self.cur_leaf_edge = Some(leaf_edge_location); + return Some((k, v)); + } + self.cur_leaf_edge = Some(kv.next_leaf_edge()); + } + None + } + + /// Implementation of a typical `DrainFilter::size_hint` method. + pub(super) fn size_hint(&self) -> (usize, Option) { + (0, Some(*self.length)) + } +} + +#[unstable(feature = "btree_drain_filter", issue = "70530")] +impl FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} + #[stable(feature = "btree_range", since = "1.17.0")] impl<'a, K, V> Iterator for Range<'a, K, V> { type Item = (&'a K, &'a V); @@ -1683,7 +1810,7 @@ impl<'a, K, V> Range<'a, K, V> { } unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { - self.front.next_unchecked() + unsafe { unwrap_unchecked(self.front.as_mut()).next_unchecked() } } } @@ -1696,7 +1823,7 @@ impl<'a, K, V> DoubleEndedIterator for Range<'a, K, V> { impl<'a, K, V> Range<'a, K, V> { unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { - self.back.next_back_unchecked() + unsafe { unwrap_unchecked(self.back.as_mut()).next_back_unchecked() } } } @@ -1734,7 +1861,7 @@ impl<'a, K, V> RangeMut<'a, K, V> { } unsafe fn next_unchecked(&mut self) -> (&'a mut K, &'a mut V) { - self.front.next_unchecked() + unsafe { unwrap_unchecked(self.front.as_mut()).next_unchecked() } } } @@ -1755,7 +1882,7 @@ impl FusedIterator for RangeMut<'_, K, V> {} impl<'a, K, V> RangeMut<'a, K, V> { unsafe fn next_back_unchecked(&mut self) -> (&'a mut K, &'a mut V) { - self.back.next_back_unchecked() + unsafe { unwrap_unchecked(self.back.as_mut()).next_back_unchecked() } } } @@ -1776,6 +1903,11 @@ impl Extend<(K, V)> for BTreeMap { self.insert(k, v); }); } + + #[inline] + fn extend_one(&mut self, (k, v): (K, V)) { + self.insert(k, v); + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -1783,6 +1915,11 @@ impl<'a, K: Ord + Copy, V: Copy> Extend<(&'a K, &'a V)> for BTreeMap { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); } + + #[inline] + fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { + self.insert(k, v); + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1854,9 +1991,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>, @@ -1870,19 +2007,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; @@ -1943,6 +2077,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. /// @@ -1967,12 +2128,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().first_leaf_edge(), - back: self.root.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 } } } @@ -1999,15 +2160,15 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { - let root1 = self.root.as_mut(); - let root2 = unsafe { ptr::read(&root1) }; - IterMut { - range: RangeMut { - front: root1.first_leaf_edge(), - back: root2.last_leaf_edge(), - _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 } } } @@ -2116,6 +2277,12 @@ impl BTreeMap { pub fn is_empty(&self) -> bool { self.len() == 0 } + + /// If the root node is the empty (non-allocated) root node, allocate our + /// own node. + fn ensure_root_is_owned(&mut self) -> &mut node::Root { + self.root.get_or_insert_with(node::Root::new_leaf) + } } impl<'a, K: Ord, V> Entry<'a, K, V> { @@ -2163,6 +2330,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 @@ -2282,15 +2477,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 { @@ -2498,16 +2692,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(); @@ -2515,68 +2726,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(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; } - Stole(_) => 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/mod.rs b/src/liballoc/collections/btree/mod.rs index fb5825ee21a9e..543ff41a4d48d 100644 --- a/src/liballoc/collections/btree/mod.rs +++ b/src/liballoc/collections/btree/mod.rs @@ -19,7 +19,9 @@ pub unsafe fn unwrap_unchecked(val: Option) -> T { if cfg!(debug_assertions) { panic!("'unchecked' unwrap on None in BTreeMap"); } else { - core::intrinsics::unreachable(); + unsafe { + core::intrinsics::unreachable(); + } } }) } diff --git a/src/liballoc/collections/btree/navigate.rs b/src/liballoc/collections/btree/navigate.rs index 5e8dcf247ae59..5478d822438b1 100644 --- a/src/liballoc/collections/btree/navigate.rs +++ b/src/liballoc/collections/btree/navigate.rs @@ -64,8 +64,10 @@ macro_rules! def_next_kv_uncheched_dealloc { edge = match edge.$adjacent_kv() { Ok(internal_kv) => return internal_kv, Err(last_edge) => { - let parent_edge = last_edge.into_node().deallocate_and_ascend(); - unwrap_unchecked(parent_edge).forget_node_type() + unsafe { + let parent_edge = last_edge.into_node().deallocate_and_ascend(); + unwrap_unchecked(parent_edge).forget_node_type() + } } } } @@ -82,9 +84,11 @@ def_next_kv_uncheched_dealloc! {unsafe fn next_back_kv_unchecked_dealloc: left_k /// Safety: The change closure must not panic. #[inline] unsafe fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { - let value = ptr::read(v); + let value = unsafe { ptr::read(v) }; let (new_value, ret) = change(value); - ptr::write(v, new_value); + unsafe { + ptr::write(v, new_value); + } ret } @@ -93,22 +97,26 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Ed /// key and value in between. /// Unsafe because the caller must ensure that the leaf edge is not the last one in the tree. pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { - replace(self, |leaf_edge| { - let kv = leaf_edge.next_kv(); - let kv = unwrap_unchecked(kv.ok()); - (kv.next_leaf_edge(), kv.into_kv()) - }) + unsafe { + replace(self, |leaf_edge| { + let kv = leaf_edge.next_kv(); + let kv = unwrap_unchecked(kv.ok()); + (kv.next_leaf_edge(), kv.into_kv()) + }) + } } /// Moves the leaf edge handle to the previous leaf edge and returns references to the /// key and value in between. /// Unsafe because the caller must ensure that the leaf edge is not the first one in the tree. pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { - replace(self, |leaf_edge| { - let kv = leaf_edge.next_back_kv(); - let kv = unwrap_unchecked(kv.ok()); - (kv.next_back_leaf_edge(), kv.into_kv()) - }) + unsafe { + replace(self, |leaf_edge| { + let kv = leaf_edge.next_back_kv(); + let kv = unwrap_unchecked(kv.ok()); + (kv.next_back_leaf_edge(), kv.into_kv()) + }) + } } } @@ -119,14 +127,16 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge /// - The caller must ensure that the leaf edge is not the last one in the tree. /// - Using the updated handle may well invalidate the returned references. pub unsafe fn next_unchecked(&mut self) -> (&'a mut K, &'a mut V) { - let kv = replace(self, |leaf_edge| { - let kv = leaf_edge.next_kv(); - let kv = unwrap_unchecked(kv.ok()); - (ptr::read(&kv).next_leaf_edge(), kv) - }); - // Doing the descend (and perhaps another move) invalidates the references - // returned by `into_kv_mut`, so we have to do this last. - kv.into_kv_mut() + unsafe { + let kv = replace(self, |leaf_edge| { + let kv = leaf_edge.next_kv(); + let kv = unwrap_unchecked(kv.ok()); + (ptr::read(&kv).next_leaf_edge(), kv) + }); + // Doing the descend (and perhaps another move) invalidates the references + // returned by `into_kv_mut`, so we have to do this last. + kv.into_kv_mut() + } } /// Moves the leaf edge handle to the previous leaf and returns references to the @@ -135,14 +145,16 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge /// - The caller must ensure that the leaf edge is not the first one in the tree. /// - Using the updated handle may well invalidate the returned references. pub unsafe fn next_back_unchecked(&mut self) -> (&'a mut K, &'a mut V) { - let kv = replace(self, |leaf_edge| { - let kv = leaf_edge.next_back_kv(); - let kv = unwrap_unchecked(kv.ok()); - (ptr::read(&kv).next_back_leaf_edge(), kv) - }); - // Doing the descend (and perhaps another move) invalidates the references - // returned by `into_kv_mut`, so we have to do this last. - kv.into_kv_mut() + unsafe { + let kv = replace(self, |leaf_edge| { + let kv = leaf_edge.next_back_kv(); + let kv = unwrap_unchecked(kv.ok()); + (ptr::read(&kv).next_back_leaf_edge(), kv) + }); + // Doing the descend (and perhaps another move) invalidates the references + // returned by `into_kv_mut`, so we have to do this last. + kv.into_kv_mut() + } } } @@ -159,12 +171,14 @@ impl Handle, marker::Edge> { /// if the two preconditions above hold. /// - Using the updated handle may well invalidate the returned references. pub unsafe fn next_unchecked(&mut self) -> (K, V) { - replace(self, |leaf_edge| { - let kv = next_kv_unchecked_dealloc(leaf_edge); - let k = ptr::read(kv.reborrow().into_kv().0); - let v = ptr::read(kv.reborrow().into_kv().1); - (kv.next_leaf_edge(), (k, v)) - }) + unsafe { + replace(self, |leaf_edge| { + let kv = next_kv_unchecked_dealloc(leaf_edge); + let k = ptr::read(kv.reborrow().into_kv().0); + let v = ptr::read(kv.reborrow().into_kv().1); + (kv.next_leaf_edge(), (k, v)) + }) + } } /// Moves the leaf edge handle to the previous leaf edge and returns the key @@ -179,12 +193,14 @@ impl Handle, marker::Edge> { /// if the two preconditions above hold. /// - Using the updated handle may well invalidate the returned references. pub unsafe fn next_back_unchecked(&mut self) -> (K, V) { - replace(self, |leaf_edge| { - let kv = next_back_kv_unchecked_dealloc(leaf_edge); - let k = ptr::read(kv.reborrow().into_kv().0); - let v = ptr::read(kv.reborrow().into_kv().1); - (kv.next_back_leaf_edge(), (k, v)) - }) + unsafe { + replace(self, |leaf_edge| { + let kv = next_back_kv_unchecked_dealloc(leaf_edge); + let k = ptr::read(kv.reborrow().into_kv().0); + let v = ptr::read(kv.reborrow().into_kv().1); + (kv.next_back_leaf_edge(), (k, v)) + }) + } } } diff --git a/src/liballoc/collections/btree/node.rs b/src/liballoc/collections/btree/node.rs index 1132ffdaf8005..a4b6cf12a23bd 100644 --- a/src/liballoc/collections/btree/node.rs +++ b/src/liballoc/collections/btree/node.rs @@ -44,34 +44,7 @@ const B: usize = 6; pub const MIN_LEN: usize = B - 1; pub const CAPACITY: usize = 2 * B - 1; -/// The underlying representation of leaf nodes. Note that it is often unsafe to actually store -/// these, since only the first `len` keys and values are assumed to be initialized. As such, -/// these should always be put behind pointers, and specifically behind `BoxedNode` in the owned -/// case. -/// -/// We have a separate type for the header and rely on it matching the prefix of `LeafNode`, in -/// order to statically allocate a single dummy node to avoid allocations. This struct is -/// `repr(C)` to prevent them from being reordered. `LeafNode` does not just contain a -/// `NodeHeader` because we do not want unnecessary padding between `len` and the keys. -/// Crucially, `NodeHeader` can be safely transmuted to different K and V. (This is exploited -/// by `as_header`.) -#[repr(C)] -struct NodeHeader { - /// We use `*const` as opposed to `*mut` so as to be covariant in `K` and `V`. - /// This either points to an actual node or is null. - parent: *const InternalNode, - - /// This node's index into the parent node's `edges` array. - /// `*node.parent.edges[node.parent_idx]` should be the same thing as `node`. - /// This is only guaranteed to be initialized when `parent` is non-null. - parent_idx: MaybeUninit, - - /// The number of keys and values this node stores. - /// - /// This next to `parent_idx` to encourage the compiler to join `len` and - /// `parent_idx` into the same 32-bit word, reducing space overhead. - len: u16, -} +/// The underlying representation of leaf nodes. #[repr(C)] struct LeafNode { /// We use `*const` as opposed to `*mut` so as to be covariant in `K` and `V`. @@ -111,21 +84,6 @@ impl LeafNode { } } -impl NodeHeader { - fn is_shared_root(&self) -> bool { - ptr::eq(self, &EMPTY_ROOT_NODE as *const _ as *const _) - } -} - -// We need to implement Sync here in order to make a static instance. -unsafe impl Sync for NodeHeader<(), ()> {} - -// An empty node used as a placeholder for the root node, to avoid allocations. -// We use just a header in order to save space, since no operation on an empty tree will -// ever take a pointer past the first key. -static EMPTY_ROOT_NODE: NodeHeader<(), ()> = - NodeHeader { parent: ptr::null(), parent_idx: MaybeUninit::uninit(), len: 0 }; - /// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden /// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an /// `InternalNode` can be directly casted to a pointer to the underlying `LeafNode` portion of the @@ -149,17 +107,14 @@ impl InternalNode { /// `len` of 0), there must be one initialized and valid edge. This function does not set up /// such an edge. unsafe fn new() -> Self { - InternalNode { data: LeafNode::new(), edges: [MaybeUninit::UNINIT; 2 * B] } + InternalNode { data: unsafe { LeafNode::new() }, edges: [MaybeUninit::UNINIT; 2 * B] } } } /// A managed, non-null pointer to a node. This is either an owned pointer to -/// `LeafNode`, an owned pointer to `InternalNode`, or a (not owned) -/// pointer to `NodeHeader<(), ()` (more specifically, the pointer to EMPTY_ROOT_NODE). -/// All of these types have a `NodeHeader` prefix, meaning that they have at -/// least the same size as `NodeHeader` and store the same kinds of data at the same -/// offsets; and they have a pointer alignment at least as large as `NodeHeader`'s. -/// However, `BoxedNode` contains no information as to which of the three types +/// `LeafNode` or an owned pointer to `InternalNode`. +/// +/// However, `BoxedNode` contains no information as to which of the two types /// of nodes it actually contains, and, partially due to this lack of information, /// has no destructor. struct BoxedNode { @@ -176,7 +131,7 @@ impl BoxedNode { } unsafe fn from_ptr(ptr: NonNull>) -> Self { - BoxedNode { ptr: Unique::from(ptr) } + BoxedNode { ptr: unsafe { Unique::new_unchecked(ptr.as_ptr()) } } } fn as_ptr(&self) -> NonNull> { @@ -184,8 +139,9 @@ impl BoxedNode { } } -/// Either an owned tree or a shared, empty tree. Note that this does not have a destructor, -/// and must be cleaned up manually if it is an owned tree. +/// An owned tree. +/// +/// Note that this does not have a destructor, and must be cleaned up manually. pub struct Root { node: BoxedNode, /// The number of levels below the root node. @@ -196,20 +152,6 @@ unsafe impl Sync for Root {} unsafe impl Send for Root {} impl Root { - /// Whether the instance of `Root` wraps a shared, empty root node. If not, - /// the entire tree is uniquely owned by the owner of the `Root` instance. - pub fn is_shared_root(&self) -> bool { - self.as_ref().is_shared_root() - } - - /// Returns a shared tree, wrapping a shared root node that is eternally empty. - pub fn shared_empty_root() -> Self { - Root { - node: unsafe { BoxedNode::from_ptr(NonNull::from(&EMPTY_ROOT_NODE).cast()) }, - height: 0, - } - } - /// Returns a new owned tree, with its own root node that is initially empty. pub fn new_leaf() -> Self { Root { node: BoxedNode::from_leaf(Box::new(unsafe { LeafNode::new() })), height: 0 } @@ -219,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, } } @@ -237,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, } } @@ -245,7 +187,6 @@ impl Root { /// Adds a new internal node with a single edge, pointing to the previous root, and make that /// new node the root. This increases the height by 1 and is the opposite of `pop_level`. pub fn push_level(&mut self) -> NodeRef, K, V, marker::Internal> { - debug_assert!(!self.is_shared_root()); let mut new_node = Box::new(unsafe { InternalNode::new() }); new_node.edges[0].write(unsafe { BoxedNode::from_ptr(self.node.as_ptr()) }); @@ -308,11 +249,6 @@ impl Root { /// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the /// `NodeRef` points to an internal node, and when this is `LeafOrInternal` the /// `NodeRef` could be pointing to either type of node. -/// Note that in case of a leaf node, this might still be the shared root! -/// Only turn this into a `LeafNode` reference if you know it is not the shared root! -/// Shared references must be dereferenceable *for the entire size of their pointee*, -/// so '&LeafNode` or `&InternalNode` pointing to the shared root is undefined behavior. -/// Turning this into a `NodeHeader` reference is always safe. pub struct NodeRef { /// The number of levels below the node. height: usize, @@ -354,7 +290,7 @@ impl NodeRef { /// Note that, despite being safe, calling this function can have the side effect /// of invalidating mutable references that unsafe code has created. pub fn len(&self) -> usize { - self.as_header().len as usize + self.as_leaf().len as usize } /// Returns the height of this node in the whole tree. Zero height denotes the @@ -374,35 +310,24 @@ impl NodeRef { NodeRef { height: self.height, node: self.node, root: self.root, _marker: PhantomData } } - /// Exposes the leaf "portion" of any leaf or internal node that is not the shared root. + /// Exposes the leaf "portion" of any leaf or internal node. /// If the node is a leaf, this function simply opens up its data. /// If the node is an internal node, so not a leaf, it does have all the data a leaf has /// (header, keys and values), and this function exposes that. - /// Unsafe because the node must not be the shared root. For more information, - /// see the `NodeRef` comments. - unsafe fn as_leaf(&self) -> &LeafNode { - debug_assert!(!self.is_shared_root()); - self.node.as_ref() - } - - fn as_header(&self) -> &NodeHeader { - unsafe { &*(self.node.as_ptr() as *const NodeHeader) } - } - - /// Returns whether the node is the shared, empty root. - pub fn is_shared_root(&self) -> bool { - self.as_header().is_shared_root() + fn as_leaf(&self) -> &LeafNode { + // The node must be valid for at least the LeafNode portion. + // This is not a reference in the NodeRef type because we don't know if + // it should be unique or shared. + unsafe { self.node.as_ref() } } /// Borrows a view into the keys stored in the node. - /// Unsafe because the caller must ensure that the node is not the shared root. - pub unsafe fn keys(&self) -> &[K] { + pub fn keys(&self) -> &[K] { self.reborrow().into_key_slice() } /// Borrows a view into the values stored in the node. - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn vals(&self) -> &[V] { + fn vals(&self) -> &[V] { self.reborrow().into_val_slice() } @@ -416,7 +341,7 @@ impl NodeRef { pub fn ascend( self, ) -> Result, marker::Edge>, Self> { - let parent_as_leaf = self.as_header().parent as *const LeafNode; + let parent_as_leaf = self.as_leaf().parent as *const LeafNode; if let Some(non_zero) = NonNull::new(parent_as_leaf as *mut _) { Ok(Handle { node: NodeRef { @@ -425,7 +350,7 @@ impl NodeRef { root: self.root, _marker: PhantomData, }, - idx: unsafe { usize::from(*self.as_header().parent_idx.as_ptr()) }, + idx: unsafe { usize::from(*self.as_leaf().parent_idx.as_ptr()) }, _marker: PhantomData, }) } else { @@ -464,18 +389,19 @@ impl NodeRef { pub unsafe fn deallocate_and_ascend( self, ) -> Option, marker::Edge>> { - assert!(!self.is_shared_root()); let height = self.height; let node = self.node; let ret = self.ascend().ok(); - Global.dealloc( - node.cast(), - if height > 0 { - Layout::new::>() - } else { - Layout::new::>() - }, - ); + unsafe { + Global.dealloc( + node.cast(), + if height > 0 { + Layout::new::>() + } else { + Layout::new::>() + }, + ); + } ret } } @@ -507,41 +433,37 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { /// (header, keys and values), and this function exposes that. /// /// Returns a raw ptr to avoid asserting exclusive access to the entire node. - /// This also implies you can invoke this member on the shared root, but the resulting pointer - /// might not be properly aligned and definitely would not allow accessing keys and values. fn as_leaf_mut(&mut self) -> *mut LeafNode { self.node.as_ptr() } - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn keys_mut(&mut self) -> &mut [K] { - self.reborrow_mut().into_key_slice_mut() + fn keys_mut(&mut self) -> &mut [K] { + // SAFETY: the caller will not be able to call further methods on self + // until the key slice reference is dropped, as we have unique access + // for the lifetime of the borrow. + unsafe { self.reborrow_mut().into_key_slice_mut() } } - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn vals_mut(&mut self) -> &mut [V] { - self.reborrow_mut().into_val_slice_mut() + fn vals_mut(&mut self) -> &mut [V] { + // SAFETY: the caller will not be able to call further methods on self + // until the value slice reference is dropped, as we have unique access + // for the lifetime of the borrow. + unsafe { self.reborrow_mut().into_val_slice_mut() } } } impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn into_key_slice(self) -> &'a [K] { - debug_assert!(!self.is_shared_root()); - // We cannot be the shared root, so `as_leaf` is okay. - slice::from_raw_parts(MaybeUninit::first_ptr(&self.as_leaf().keys), self.len()) + fn into_key_slice(self) -> &'a [K] { + unsafe { slice::from_raw_parts(MaybeUninit::first_ptr(&self.as_leaf().keys), self.len()) } } - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn into_val_slice(self) -> &'a [V] { - debug_assert!(!self.is_shared_root()); - // We cannot be the shared root, so `as_leaf` is okay. - slice::from_raw_parts(MaybeUninit::first_ptr(&self.as_leaf().vals), self.len()) + fn into_val_slice(self) -> &'a [V] { + unsafe { slice::from_raw_parts(MaybeUninit::first_ptr(&self.as_leaf().vals), self.len()) } } - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn into_slices(self) -> (&'a [K], &'a [V]) { - let k = ptr::read(&self); + fn into_slices(self) -> (&'a [K], &'a [V]) { + // SAFETY: equivalent to reborrow() except not requiring Type: 'a + let k = unsafe { ptr::read(&self) }; (k.into_key_slice(), self.into_val_slice()) } } @@ -553,28 +475,27 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { unsafe { &mut *(self.root as *mut Root) } } - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn into_key_slice_mut(mut self) -> &'a mut [K] { - debug_assert!(!self.is_shared_root()); - // We cannot be the shared root, so `as_leaf_mut` is okay. - slice::from_raw_parts_mut( - MaybeUninit::first_ptr_mut(&mut (*self.as_leaf_mut()).keys), - self.len(), - ) + fn into_key_slice_mut(mut self) -> &'a mut [K] { + // SAFETY: The keys of a node must always be initialized up to length. + unsafe { + slice::from_raw_parts_mut( + MaybeUninit::first_ptr_mut(&mut (*self.as_leaf_mut()).keys), + self.len(), + ) + } } - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn into_val_slice_mut(mut self) -> &'a mut [V] { - debug_assert!(!self.is_shared_root()); - slice::from_raw_parts_mut( - MaybeUninit::first_ptr_mut(&mut (*self.as_leaf_mut()).vals), - self.len(), - ) + fn into_val_slice_mut(mut self) -> &'a mut [V] { + // SAFETY: The values of a node must always be initialized up to length. + unsafe { + slice::from_raw_parts_mut( + MaybeUninit::first_ptr_mut(&mut (*self.as_leaf_mut()).vals), + self.len(), + ) + } } - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn into_slices_mut(mut self) -> (&'a mut [K], &'a mut [V]) { - debug_assert!(!self.is_shared_root()); + fn into_slices_mut(mut self) -> (&'a mut [K], &'a mut [V]) { // We cannot use the getters here, because calling the second one // invalidates the reference returned by the first. // More precisely, it is the call to `len` that is the culprit, @@ -582,8 +503,13 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { // overlap with the keys (and even the values, for ZST keys). let len = self.len(); let leaf = self.as_leaf_mut(); - let keys = slice::from_raw_parts_mut(MaybeUninit::first_ptr_mut(&mut (*leaf).keys), len); - let vals = slice::from_raw_parts_mut(MaybeUninit::first_ptr_mut(&mut (*leaf).vals), len); + // SAFETY: The keys and values of a node must always be initialized up to length. + let keys = unsafe { + slice::from_raw_parts_mut(MaybeUninit::first_ptr_mut(&mut (*leaf).keys), len) + }; + let vals = unsafe { + slice::from_raw_parts_mut(MaybeUninit::first_ptr_mut(&mut (*leaf).vals), len) + }; (keys, vals) } } @@ -592,7 +518,6 @@ impl<'a, K, V> NodeRef, K, V, marker::Leaf> { /// Adds a key/value pair the end of the node. pub fn push(&mut self, key: K, val: V) { assert!(self.len() < CAPACITY); - debug_assert!(!self.is_shared_root()); let idx = self.len(); @@ -607,7 +532,6 @@ impl<'a, K, V> NodeRef, K, V, marker::Leaf> { /// Adds a key/value pair to the beginning of the node. pub fn push_front(&mut self, key: K, val: V) { assert!(self.len() < CAPACITY); - debug_assert!(!self.is_shared_root()); unsafe { slice_insert(self.keys_mut(), 0, key); @@ -624,7 +548,6 @@ impl<'a, K, V> NodeRef, K, V, marker::Internal> { pub fn push(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); assert!(self.len() < CAPACITY); - debug_assert!(!self.is_shared_root()); let idx = self.len(); @@ -644,7 +567,7 @@ impl<'a, K, V> NodeRef, K, V, marker::Internal> { debug_assert!(first <= self.len()); debug_assert!(after_last <= self.len() + 1); for i in first..after_last { - Handle::new_edge(self.reborrow_mut(), i).correct_parent_link(); + unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link(); } } @@ -658,7 +581,6 @@ impl<'a, K, V> NodeRef, K, V, marker::Internal> { pub fn push_front(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); assert!(self.len() < CAPACITY); - debug_assert!(!self.is_shared_root()); unsafe { slice_insert(self.keys_mut(), 0, key); @@ -744,8 +666,7 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { } } - /// Unsafe because the caller must ensure that the node is not the shared root. - unsafe fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) { + fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) { (self.keys_mut().as_mut_ptr(), self.vals_mut().as_mut_ptr()) } } @@ -804,6 +725,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> { @@ -865,7 +791,7 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT &mut self, ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type - Handle { node: self.node.reborrow_mut(), idx: self.idx, _marker: PhantomData } + Handle { node: unsafe { self.node.reborrow_mut() }, idx: self.idx, _marker: PhantomData } } } @@ -904,7 +830,6 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge fn insert_fit(&mut self, key: K, val: V) -> *mut V { // Necessary for correctness, but in a private module debug_assert!(self.node.len() < CAPACITY); - debug_assert!(!self.node.is_shared_root()); unsafe { slice_insert(self.node.keys_mut(), self.idx, key); @@ -962,7 +887,7 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: unsafe fn cast_unchecked( &mut self, ) -> Handle, K, V, NewType>, marker::Edge> { - Handle::new_edge(self.node.cast_unchecked(), self.idx) + unsafe { Handle::new_edge(self.node.cast_unchecked(), self.idx) } } /// Inserts a new key/value pair and an edge that will go to the right of that new pair @@ -1081,7 +1006,6 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::KV> /// - All the key/value pairs to the right of this handle are put into a newly /// allocated node. pub fn split(mut self) -> (NodeRef, K, V, marker::Leaf>, K, V, Root) { - assert!(!self.node.is_shared_root()); unsafe { let mut new_node = Box::new(LeafNode::new()); @@ -1113,7 +1037,6 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::KV> pub fn remove( mut self, ) -> (Handle, K, V, marker::Leaf>, marker::Edge>, K, V) { - assert!(!self.node.is_shared_root()); unsafe { let k = slice_remove(self.node.keys_mut(), self.idx); let v = slice_remove(self.node.vals_mut(), self.idx); @@ -1226,7 +1149,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 @@ -1243,10 +1166,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) } @@ -1408,8 +1332,10 @@ unsafe fn move_kv( dest_offset: usize, count: usize, ) { - ptr::copy_nonoverlapping(source.0.add(source_offset), dest.0.add(dest_offset), count); - ptr::copy_nonoverlapping(source.1.add(source_offset), dest.1.add(dest_offset), count); + unsafe { + ptr::copy_nonoverlapping(source.0.add(source_offset), dest.0.add(dest_offset), count); + ptr::copy_nonoverlapping(source.1.add(source_offset), dest.1.add(dest_offset), count); + } } // Source and destination must have the same height. @@ -1422,8 +1348,10 @@ unsafe fn move_edges( ) { let source_ptr = source.as_internal_mut().edges.as_mut_ptr(); let dest_ptr = dest.as_internal_mut().edges.as_mut_ptr(); - ptr::copy_nonoverlapping(source_ptr.add(source_offset), dest_ptr.add(dest_offset), count); - dest.correct_childrens_parent_links(dest_offset, dest_offset + count); + unsafe { + ptr::copy_nonoverlapping(source_ptr.add(source_offset), dest_ptr.add(dest_offset), count); + dest.correct_childrens_parent_links(dest_offset, dest_offset + count); + } } impl Handle, marker::Edge> { @@ -1537,12 +1465,16 @@ pub mod marker { } unsafe fn slice_insert(slice: &mut [T], idx: usize, val: T) { - ptr::copy(slice.as_ptr().add(idx), slice.as_mut_ptr().add(idx + 1), slice.len() - idx); - ptr::write(slice.get_unchecked_mut(idx), val); + unsafe { + ptr::copy(slice.as_ptr().add(idx), slice.as_mut_ptr().add(idx + 1), slice.len() - idx); + ptr::write(slice.get_unchecked_mut(idx), val); + } } unsafe fn slice_remove(slice: &mut [T], idx: usize) -> T { - let ret = ptr::read(slice.get_unchecked(idx)); - ptr::copy(slice.as_ptr().add(idx + 1), slice.as_mut_ptr().add(idx), slice.len() - idx - 1); - ret + unsafe { + let ret = ptr::read(slice.get_unchecked(idx)); + ptr::copy(slice.as_ptr().add(idx + 1), slice.as_mut_ptr().add(idx), slice.len() - idx - 1); + ret + } } diff --git a/src/liballoc/collections/btree/search.rs b/src/liballoc/collections/btree/search.rs index 2ba5cebbdee74..4e80f7f21ebff 100644 --- a/src/liballoc/collections/btree/search.rs +++ b/src/liballoc/collections/btree/search.rs @@ -67,19 +67,16 @@ where Q: Ord, K: Borrow, { - // This function is defined over all borrow types (immutable, mutable, owned), - // and may be called on the shared root in each case. + // This function is defined over all borrow types (immutable, mutable, owned). // Using `keys()` is fine here even if BorrowType is mutable, as all we return // is an index -- not a reference. let len = node.len(); - if len > 0 { - let keys = unsafe { node.keys() }; // safe because a non-empty node cannot be the shared root - for (i, k) in keys.iter().enumerate() { - match key.cmp(k.borrow()) { - Ordering::Greater => {} - Ordering::Equal => return (i, true), - Ordering::Less => return (i, false), - } + let keys = node.keys(); + for (i, k) in keys.iter().enumerate() { + match key.cmp(k.borrow()) { + Ordering::Greater => {} + Ordering::Equal => return (i, true), + Ordering::Less => return (i, false), } } (len, false) diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs index b100ce754caad..525ef38c32fa2 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] @@ -1063,6 +1152,11 @@ impl Extend for BTreeSet { self.insert(elem); }); } + + #[inline] + fn extend_one(&mut self, elem: T) { + self.insert(elem); + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -1070,6 +1164,11 @@ impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BTreeSet { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &elem: &'a T) { + self.insert(elem); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/collections/linked_list.rs b/src/liballoc/collections/linked_list.rs index 53d4f7239b76e..36b5785fdf6c5 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, @@ -225,17 +225,17 @@ impl LinkedList { /// maintain validity of aliasing pointers. #[inline] unsafe fn unlink_node(&mut self, mut node: NonNull>) { - let node = node.as_mut(); // this one is ours now, we can create an &mut. + let node = unsafe { node.as_mut() }; // this one is ours now, we can create an &mut. // Not creating new mutable (unique!) references overlapping `element`. match node.prev { - Some(prev) => (*prev.as_ptr()).next = node.next, + Some(prev) => unsafe { (*prev.as_ptr()).next = node.next }, // this node is the head node None => self.head = node.next, }; match node.next { - Some(next) => (*next.as_ptr()).prev = node.prev, + Some(next) => unsafe { (*next.as_ptr()).prev = node.prev }, // this node is the tail node None => self.tail = node.prev, }; @@ -258,17 +258,23 @@ impl LinkedList { // This method takes care not to create multiple mutable references to whole nodes at the same time, // to maintain validity of aliasing pointers into `element`. if let Some(mut existing_prev) = existing_prev { - existing_prev.as_mut().next = Some(splice_start); + unsafe { + existing_prev.as_mut().next = Some(splice_start); + } } else { self.head = Some(splice_start); } if let Some(mut existing_next) = existing_next { - existing_next.as_mut().prev = Some(splice_end); + unsafe { + existing_next.as_mut().prev = Some(splice_end); + } } else { self.tail = Some(splice_end); } - splice_start.as_mut().prev = existing_prev; - splice_end.as_mut().next = existing_next; + unsafe { + splice_start.as_mut().prev = existing_prev; + splice_end.as_mut().next = existing_next; + } self.len += splice_length; } @@ -297,9 +303,13 @@ impl LinkedList { if let Some(mut split_node) = split_node { let first_part_head; let first_part_tail; - first_part_tail = split_node.as_mut().prev.take(); + unsafe { + first_part_tail = split_node.as_mut().prev.take(); + } if let Some(mut tail) = first_part_tail { - tail.as_mut().next = None; + unsafe { + tail.as_mut().next = None; + } first_part_head = self.head; } else { first_part_head = None; @@ -333,9 +343,13 @@ impl LinkedList { if let Some(mut split_node) = split_node { let second_part_head; let second_part_tail; - second_part_head = split_node.as_mut().next.take(); + unsafe { + second_part_head = split_node.as_mut().next.take(); + } if let Some(mut head) = second_part_head { - head.as_mut().prev = None; + unsafe { + head.as_mut().prev = None; + } second_part_tail = self.tail; } else { second_part_tail = None; @@ -390,7 +404,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 +561,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 +582,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 +608,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 +751,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 +774,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 +797,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 +817,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 +838,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 +894,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 +986,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 +1147,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 +1209,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 +1462,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 +1482,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 +1510,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 @@ -1717,6 +1762,11 @@ impl Extend for LinkedList { fn extend>(&mut self, iter: I) { >::spec_extend(self, iter); } + + #[inline] + fn extend_one(&mut self, elem: T) { + self.push_back(elem); + } } impl SpecExtend for LinkedList { @@ -1736,6 +1786,11 @@ impl<'a, T: 'a + Copy> Extend<&'a T> for LinkedList { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &elem: &'a T) { + self.push_back(elem); + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1835,3 +1890,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..15f3a94ca2d6a 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -7,12 +7,14 @@ #![stable(feature = "rust1", since = "1.0.0")] +// ignore-tidy-filelength + use core::array::LengthAtMost32; use core::cmp::{self, Ordering}; use core::fmt; use core::hash::{Hash, Hasher}; use core::iter::{once, repeat_with, FromIterator, FusedIterator}; -use core::mem::{self, replace}; +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 +52,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 +75,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. @@ -200,25 +203,27 @@ impl VecDeque { /// Turn ptr into a slice #[inline] unsafe fn buffer_as_slice(&self) -> &[T] { - slice::from_raw_parts(self.ptr(), self.cap()) + unsafe { slice::from_raw_parts(self.ptr(), self.cap()) } } /// Turn ptr into a mut slice #[inline] unsafe fn buffer_as_mut_slice(&mut self) -> &mut [T] { - slice::from_raw_parts_mut(self.ptr(), self.cap()) + unsafe { slice::from_raw_parts_mut(self.ptr(), self.cap()) } } /// Moves an element out of the buffer #[inline] unsafe fn buffer_read(&mut self, off: usize) -> T { - ptr::read(self.ptr().add(off)) + unsafe { ptr::read(self.ptr().add(off)) } } /// Writes an element into the buffer, moving it. #[inline] unsafe fn buffer_write(&mut self, off: usize, value: T) { - ptr::write(self.ptr().add(off), value); + unsafe { + ptr::write(self.ptr().add(off), value); + } } /// Returns `true` if the buffer is at full capacity. @@ -267,7 +272,9 @@ impl VecDeque { len, self.cap() ); - ptr::copy(self.ptr().add(src), self.ptr().add(dst), len); + unsafe { + ptr::copy(self.ptr().add(src), self.ptr().add(dst), len); + } } /// Copies a contiguous block of memory len long from src to dst @@ -289,7 +296,9 @@ impl VecDeque { len, self.cap() ); - ptr::copy_nonoverlapping(self.ptr().add(src), self.ptr().add(dst), len); + unsafe { + ptr::copy_nonoverlapping(self.ptr().add(src), self.ptr().add(dst), len); + } } /// Copies a potentially wrapping block of memory len long from src to dest. @@ -329,7 +338,9 @@ impl VecDeque { // 2 [_ _ A A A A B B _] // D . . . // - self.copy(dst, src, len); + unsafe { + self.copy(dst, src, len); + } } (false, false, true) => { // dst before src, src doesn't wrap, dst wraps @@ -340,8 +351,10 @@ impl VecDeque { // 3 [B B B B _ _ _ A A] // . . D . // - self.copy(dst, src, dst_pre_wrap_len); - self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len); + unsafe { + self.copy(dst, src, dst_pre_wrap_len); + self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len); + } } (true, false, true) => { // src before dst, src doesn't wrap, dst wraps @@ -352,8 +365,10 @@ impl VecDeque { // 3 [B B _ _ _ A A A A] // . . D . // - self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len); - self.copy(dst, src, dst_pre_wrap_len); + unsafe { + self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len); + self.copy(dst, src, dst_pre_wrap_len); + } } (false, true, false) => { // dst before src, src wraps, dst doesn't wrap @@ -364,8 +379,10 @@ impl VecDeque { // 3 [C C _ _ _ B B C C] // D . . . // - self.copy(dst, src, src_pre_wrap_len); - self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len); + unsafe { + self.copy(dst, src, src_pre_wrap_len); + self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len); + } } (true, true, false) => { // src before dst, src wraps, dst doesn't wrap @@ -376,8 +393,10 @@ impl VecDeque { // 3 [C C A A _ _ _ C C] // D . . . // - self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len); - self.copy(dst, src, src_pre_wrap_len); + unsafe { + self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len); + self.copy(dst, src, src_pre_wrap_len); + } } (false, true, true) => { // dst before src, src wraps, dst wraps @@ -391,9 +410,11 @@ impl VecDeque { // debug_assert!(dst_pre_wrap_len > src_pre_wrap_len); let delta = dst_pre_wrap_len - src_pre_wrap_len; - self.copy(dst, src, src_pre_wrap_len); - self.copy(dst + src_pre_wrap_len, 0, delta); - self.copy(0, delta, len - dst_pre_wrap_len); + unsafe { + self.copy(dst, src, src_pre_wrap_len); + self.copy(dst + src_pre_wrap_len, 0, delta); + self.copy(0, delta, len - dst_pre_wrap_len); + } } (true, true, true) => { // src before dst, src wraps, dst wraps @@ -407,9 +428,11 @@ impl VecDeque { // debug_assert!(src_pre_wrap_len > dst_pre_wrap_len); let delta = src_pre_wrap_len - dst_pre_wrap_len; - self.copy(delta, 0, len - src_pre_wrap_len); - self.copy(0, self.cap() - delta, delta); - self.copy(dst, src, dst_pre_wrap_len); + unsafe { + self.copy(delta, 0, len - src_pre_wrap_len); + self.copy(0, self.cap() - delta, delta); + self.copy(dst, src, dst_pre_wrap_len); + } } } } @@ -439,13 +462,17 @@ impl VecDeque { // Nop } else if self.head < old_capacity - self.tail { // B - self.copy_nonoverlapping(old_capacity, 0, self.head); + unsafe { + self.copy_nonoverlapping(old_capacity, 0, self.head); + } self.head += old_capacity; debug_assert!(self.head > self.tail); } else { // C let new_tail = new_capacity - (old_capacity - self.tail); - self.copy_nonoverlapping(new_tail, self.tail, old_capacity - self.tail); + unsafe { + self.copy_nonoverlapping(new_tail, self.tail, old_capacity - self.tail); + } self.tail = new_tail; debug_assert!(self.head < self.tail); } @@ -488,7 +515,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 +540,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 +678,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 +689,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 +705,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 +727,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 +986,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 +1019,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 +1380,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 +1405,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 +1422,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 +1457,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 +1515,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 +1915,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 +2035,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 +2085,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, @@ -2139,7 +2323,9 @@ impl VecDeque { unsafe fn rotate_left_inner(&mut self, mid: usize) { debug_assert!(mid * 2 <= self.len()); - self.wrap_copy(self.head, self.tail, mid); + unsafe { + self.wrap_copy(self.head, self.tail, mid); + } self.head = self.wrap_add(self.head, mid); self.tail = self.wrap_add(self.tail, mid); } @@ -2148,7 +2334,9 @@ impl VecDeque { debug_assert!(k * 2 <= self.len()); self.head = self.wrap_sub(self.head, k); self.tail = self.wrap_sub(self.tail, k); - self.wrap_copy(self.tail, self.head, k); + unsafe { + self.wrap_copy(self.tail, self.head, k); + } } } @@ -2723,6 +2911,16 @@ impl Extend for VecDeque { } } } + + #[inline] + fn extend_one(&mut self, elem: A) { + self.push_back(elem); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -2730,6 +2928,16 @@ impl<'a, T: 'a + Copy> Extend<&'a T> for VecDeque { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &elem: &T) { + self.push_back(elem); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2749,12 +2957,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 +2986,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 +3010,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..960af4bfda053 100644 --- a/src/liballoc/collections/vec_deque/tests.rs +++ b/src/liballoc/collections/vec_deque/tests.rs @@ -1,9 +1,7 @@ use super::*; -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 +14,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 +27,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 +41,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 +128,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 +384,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 ffa4176cc7969..41c2b221704e6 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -72,16 +72,18 @@ #![deny(intra_doc_link_resolution_failure)] // rustdoc is run without -D warnings #![allow(explicit_outlives_requirements)] #![allow(incomplete_features)] +#![deny(unsafe_op_in_unsafe_fn)] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(test))] #![feature(allocator_api)] #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] -#![feature(box_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)] @@ -92,20 +94,24 @@ #![feature(container_error_extra)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] +#![feature(extend_one)] #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(fundamental)] #![feature(internal_uninit_const)] #![feature(lang_items)] #![feature(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)] @@ -113,6 +119,7 @@ #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(unicode_internals)] +#![feature(unsafe_block_in_unsafe_fn)] #![feature(unsize)] #![feature(unsized_locals)] #![feature(allocator_internals)] @@ -160,6 +167,8 @@ pub mod str; pub mod string; #[cfg(target_has_atomic = "ptr")] pub mod sync; +#[cfg(target_has_atomic = "ptr")] +pub mod task; #[cfg(test)] mod tests; pub mod vec; 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..67ebdcc9f33b1 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, + 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 { @@ -109,7 +60,7 @@ impl RawVec { /// `#[rustc_force_min_const_fn]` attribute which requires conformance /// with `min_const_fn` but does not necessarily allow calling it in /// `stable(...) const fn` / user code not enabling `foo` when - /// `#[rustc_const_unstable(feature = "foo", ..)]` is present. + /// `#[rustc_const_unstable(feature = "foo", issue = "01234")]` is present. pub const NEW: Self = Self::new(); /// Creates the biggest possible `RawVec` (on the system heap) @@ -138,54 +89,116 @@ 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 } + unsafe { 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()) + } + } + + /// Converts the entire buffer into `Box<[MaybeUninit]>` with the specified `len`. + /// + /// Note that this will correctly reconstitute any `cap` changes + /// that may have been performed. (See description of type for details.) + /// + /// # Safety + /// + /// * `len` must be greater than or equal to the most recently requested capacity, and + /// * `len` must be less than or equal to `self.capacity()`. + /// + /// Note, that the requested capacity and `self.capacity()` could differ, as + /// an allocator could overallocate and return a greater memory block than requested. + pub 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()`" + ); + + let me = ManuallyDrop::new(self); + unsafe { + let slice = slice::from_raw_parts_mut(me.ptr() as *mut MaybeUninit, len); + Box::from_raw(slice) } } } 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: unsafe { 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 +209,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,226 +231,19 @@ impl RawVec { unsafe { let align = mem::align_of::(); let size = mem::size_of::() * self.cap; - Some(Layout::from_size_align_unchecked(size, align)) - } - } - } - - /// 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, + let layout = Layout::from_size_align_unchecked(size, align); + Some((self.ptr.cast().into(), layout)) } } } - /// 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. + /// Ensures that the buffer contains at least enough space to hold `len + + /// additional` elements. If it doesn't already have enough capacity, will + /// reallocate enough space plus comfortable slack space to get amortized + /// `O(1)` behavior. Will limit this behavior if it would needlessly cause + /// itself to panic. /// - /// If `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 - /// space to get amortized `O(1)` behavior. Will limit this behavior - /// if it would needlessly cause itself to panic. - /// - /// If `used_capacity` exceeds `self.capacity()`, this may fail to actually allocate + /// If `len` exceeds `self.capacity()`, this may fail to actually allocate /// the requested space. This is not really unsafe, but the unsafe /// code *you* write that relies on the behavior of this function may break. /// @@ -483,73 +289,59 @@ impl RawVec { /// # vector.push_all(&[1, 3, 5, 7, 9]); /// # } /// ``` - pub fn reserve(&mut self, used_capacity: usize, needed_extra_capacity: usize) { - match self.reserve_internal(used_capacity, needed_extra_capacity, Infallible, Amortized) { + pub fn reserve(&mut self, len: usize, additional: usize) { + match self.try_reserve(len, additional) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocError { .. }) => unreachable!(), + Err(AllocError { layout, .. }) => handle_alloc_error(layout), Ok(()) => { /* yay */ } } } - /// 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 - /// space to get amortized `O(1)` behavior. Will limit this behaviour - /// if it would needlessly cause itself to panic. - /// - /// 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. + + /// The same as `reserve`, but returns on errors instead of panicking or aborting. + pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + if self.needs_to_grow(len, additional) { + self.grow_amortized(len, additional) + } else { + Ok(()) + } + } + + /// Ensures that the buffer contains at least enough space to hold `len + + /// additional` elements. If it doesn't already, will reallocate the + /// minimum possible amount of memory necessary. Generally this will be + /// exactly the amount of memory necessary, but in principle the allocator + /// is free to give back more than we asked for. /// - /// Returns `true` if the reallocation attempt has succeeded. + /// If `len` exceeds `self.capacity()`, this may fail to actually allocate + /// the requested space. This is not really unsafe, but the unsafe code + /// *you* write that relies on the behavior of this function may break. /// /// # Panics /// /// * Panics if the requested capacity exceeds `usize::MAX` bytes. /// * 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; - } - - 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, - } + /// + /// # Aborts + /// + /// Aborts on OOM. + pub fn reserve_exact(&mut self, len: usize, additional: usize) { + match self.try_reserve_exact(len, additional) { + 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, + len: usize, + additional: usize, + ) -> Result<(), TryReserveError> { + if self.needs_to_grow(len, additional) { self.grow_exact(len, additional) } else { Ok(()) } + } + /// Shrinks the allocation down to the specified amount. If the given amount /// is 0, actually completely deallocates. /// @@ -561,166 +353,154 @@ 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; - } - - // 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"); - - 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. - - 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; + match self.shrink(amount, MayMove) { + Err(CapacityOverflow) => capacity_overflow(), + Err(AllocError { layout, .. }) => handle_alloc_error(layout), + Ok(()) => { /* yay */ } } } } -enum Fallibility { - Fallible, - Infallible, -} +impl RawVec { + /// Returns if the buffer needs to grow to fulfill the needed extra capacity. + /// Mainly used to make inlining reserve-calls possible without inlining `grow`. + fn needs_to_grow(&self, len: usize, additional: usize) -> bool { + additional > self.capacity().wrapping_sub(len) + } -use Fallibility::*; + fn capacity_from_bytes(excess: usize) -> usize { + debug_assert_ne!(mem::size_of::(), 0); + excess / mem::size_of::() + } -enum ReserveStrategy { - Exact, - Amortized, -} + 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); + } -use ReserveStrategy::*; + // This method is usually instantiated many times. So we want it to be as + // small as possible, to improve compile times. But we also want as much of + // its contents to be statically computable as possible, to make the + // generated code run faster. Therefore, this method is carefully written + // so that all of the code that depends on `T` is within it, while as much + // of the code that doesn't depend on `T` as possible is in functions that + // are non-generic over `T`. + fn grow_amortized(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + // This is ensured by the calling contexts. + debug_assert!(additional > 0); + + 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); + } -impl RawVec { - fn reserve_internal( - &mut self, - used_capacity: usize, - needed_extra_capacity: usize, - fallibility: Fallibility, - strategy: ReserveStrategy, - ) -> Result<(), TryReserveError> { + // Nothing we can really do about these checks, sadly. + let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?; + + // This guarantees exponential growth. The doubling cannot overflow + // because `cap <= isize::MAX` and the type of `cap` is `usize`. + let cap = cmp::max(self.cap * 2, required_cap); + + // 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); - 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 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)?; - - alloc_guard(new_layout.size())?; + // `finish_grow` is non-generic over `T`. + let memory = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; + self.set_memory(memory); + Ok(()) + } - 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), - }; + // The constraints on this method are much the same as those on + // `grow_amortized`, but this method is usually instantiated less often so + // it's less critical. + fn grow_exact(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + if 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); + } - self.ptr = ptr.cast().into(); - self.cap = new_cap; + let cap = len.checked_add(additional).ok_or(CapacityOverflow)?; + let new_layout = Layout::array::(cap); - Ok(()) - } + // `finish_grow` is non-generic over `T`. + let memory = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; + self.set_memory(memory); + Ok(()) } -} -impl RawVec { - /// Converts the entire buffer into `Box<[T]>`. - /// - /// 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 + fn shrink( + &mut self, + amount: usize, + placement: ReallocPlacement, + ) -> Result<(), TryReserveError> { + assert!(amount <= self.capacity(), "Tried to shrink to a larger capacity"); + + let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) }; + let new_size = amount * mem::size_of::(); + + let memory = unsafe { + self.alloc.shrink(ptr, layout, new_size, placement).map_err(|_| { + TryReserveError::AllocError { + layout: Layout::from_size_align_unchecked(new_size, layout.align()), + non_exhaustive: (), + } + })? + }; + self.set_memory(memory); + Ok(()) } } -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); - } - } +// 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, + 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(), MayMove, Uninitialized) } + } else { + alloc.alloc(new_layout, Uninitialized) } + .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?; + + Ok(memory) } 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 +516,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..6418c4a9823f2 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 @@ -34,15 +35,15 @@ fn allocator_param() { } } unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - Global.dealloc(ptr, layout) + unsafe { Global.dealloc(ptr, layout) } } } 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 9c286298aa65c..4d50ae9efca95 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -249,16 +249,20 @@ 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; #[cfg(test)] mod tests; +// This is repr(C) to future-proof against possible field-reordering, which +// would interfere with otherwise safe [into|from]_raw() of transmutable +// inner types. +#[repr(C)] struct RcBox { strong: Cell, weak: Cell, @@ -276,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>, @@ -300,7 +304,7 @@ impl Rc { } unsafe fn from_ptr(ptr: *mut RcBox) -> Self { - Self::from_inner(NonNull::new_unchecked(ptr)) + Self::from_inner(unsafe { NonNull::new_unchecked(ptr) }) } } @@ -320,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. @@ -542,7 +544,7 @@ impl Rc<[mem::MaybeUninit]> { #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub unsafe fn assume_init(self) -> Rc<[T]> { - Rc::from_ptr(mem::ManuallyDrop::new(self).ptr.as_ptr() as _) + unsafe { Rc::from_ptr(mem::ManuallyDrop::new(self).ptr.as_ptr() as _) } } } @@ -565,9 +567,31 @@ 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 + /// + /// ``` + /// 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"); + /// ``` + #[stable(feature = "weak_into_raw", since = "1.45.0")] + 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. @@ -580,15 +604,24 @@ impl Rc { } } - /// Constructs an `Rc` from a raw pointer. + /// Constructs an `Rc` from a raw pointer. + /// + /// The raw pointer must have been previously returned by a call to + /// [`Rc::into_raw`][into_raw] where `U` must have the same size + /// and alignment as `T`. This is trivially true if `U` is `T`. + /// Note that if `U` is not `T` but has the same size and alignment, this is + /// basically like transmuting references of different types. See + /// [`mem::transmute`][transmute] for more information on what + /// restrictions apply in this case. /// - /// The raw pointer must have been previously returned by a call to a - /// [`Rc::into_raw`][into_raw]. + /// The user of `from_raw` has to make sure a specific value of `T` is only + /// dropped once. /// - /// This function is unsafe because improper use may lead to memory problems. For example, a - /// double-free may occur if the function is called twice on the same raw pointer. + /// This function is unsafe because improper use may lead to memory unsafety, + /// even if the returned `Rc` is never accessed. /// /// [into_raw]: struct.Rc.html#method.into_raw + /// [transmute]: ../../std/mem/fn.transmute.html /// /// # Examples /// @@ -610,13 +643,13 @@ impl Rc { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - let offset = data_offset(ptr); + let offset = unsafe { data_offset(ptr) }; // Reverse the offset to find the original RcBox. let fake_ptr = ptr as *mut RcBox; - let rc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + let rc_ptr = unsafe { set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)) }; - Self::from_ptr(rc_ptr) + unsafe { Self::from_ptr(rc_ptr) } } /// Consumes the `Rc`, returning the wrapped pointer as `NonNull`. @@ -625,6 +658,7 @@ impl Rc { /// /// ``` /// #![feature(rc_into_raw_non_null)] + /// #![allow(deprecated)] /// /// use std::rc::Rc; /// @@ -634,6 +668,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 @@ -770,7 +805,7 @@ impl Rc { #[inline] #[unstable(feature = "get_mut_unchecked", issue = "63292")] pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T { - &mut this.ptr.as_mut().value + unsafe { &mut this.ptr.as_mut().value } } #[inline] @@ -923,14 +958,18 @@ 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()); - debug_assert_eq!(Layout::for_value(&*inner), layout); + let inner = mem_to_rcbox(mem.ptr.as_ptr()); + unsafe { + debug_assert_eq!(Layout::for_value(&*inner), layout); - ptr::write(&mut (*inner).strong, Cell::new(1)); - ptr::write(&mut (*inner).weak, Cell::new(1)); + ptr::write(&mut (*inner).strong, Cell::new(1)); + ptr::write(&mut (*inner).weak, Cell::new(1)); + } inner } @@ -938,9 +977,11 @@ impl Rc { /// Allocates an `RcBox` with sufficient space for an unsized inner value unsafe fn allocate_for_ptr(ptr: *const T) -> *mut RcBox { // Allocate for the `RcBox` using the given value. - Self::allocate_for_layout(Layout::for_value(&*ptr), |mem| { - set_data_ptr(ptr as *mut T, mem) as *mut RcBox - }) + unsafe { + Self::allocate_for_layout(Layout::for_value(&*ptr), |mem| { + set_data_ptr(ptr as *mut T, mem) as *mut RcBox + }) + } } fn from_box(v: Box) -> Rc { @@ -969,9 +1010,11 @@ impl Rc { impl Rc<[T]> { /// Allocates an `RcBox<[T]>` with the given length. unsafe fn allocate_for_slice(len: usize) -> *mut RcBox<[T]> { - Self::allocate_for_layout(Layout::array::(len).unwrap(), |mem| { - ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut RcBox<[T]> - }) + unsafe { + Self::allocate_for_layout(Layout::array::(len).unwrap(), |mem| { + ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut RcBox<[T]> + }) + } } } @@ -980,20 +1023,22 @@ impl Rc<[T]> { /// For a slice/trait object, this sets the `data` field and leaves the rest /// unchanged. For a sized raw pointer, this simply sets the pointer. unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { - ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); + unsafe { + ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); + } ptr } 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]> { - let ptr = Self::allocate_for_slice(v.len()); - - ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).value as *mut [T] as *mut T, v.len()); - - Self::from_ptr(ptr) + unsafe { + let ptr = Self::allocate_for_slice(v.len()); + ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).value as *mut [T] as *mut T, v.len()); + Self::from_ptr(ptr) + } } /// Constructs an `Rc<[T]>` from an iterator known to be of a certain size. @@ -1021,25 +1066,27 @@ impl Rc<[T]> { } } - let ptr = Self::allocate_for_slice(len); + unsafe { + let ptr = Self::allocate_for_slice(len); - let mem = ptr as *mut _ as *mut u8; - let layout = Layout::for_value(&*ptr); + let mem = ptr as *mut _ as *mut u8; + let layout = Layout::for_value(&*ptr); - // Pointer to first element - let elems = &mut (*ptr).value as *mut [T] as *mut T; + // Pointer to first element + let elems = &mut (*ptr).value as *mut [T] as *mut T; - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; + let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; - for (i, item) in iter.enumerate() { - ptr::write(elems.add(i), item); - guard.n_elems += 1; - } + for (i, item) in iter.enumerate() { + ptr::write(elems.add(i), item); + guard.n_elems += 1; + } - // All clear. Forget the guard so it doesn't free the new RcBox. - forget(guard); + // All clear. Forget the guard so it doesn't free the new RcBox. + forget(guard); - Self::from_ptr(ptr) + Self::from_ptr(ptr) + } } } @@ -1183,6 +1230,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 @@ -1191,7 +1244,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 @@ -1453,6 +1506,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 @@ -1510,25 +1578,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, @@ -1539,29 +1607,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`]`>`. @@ -1629,45 +1683,34 @@ 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 /// /// ``` - /// #![feature(weak_into_raw)] - /// /// use std::rc::Rc; /// use std::ptr; /// /// 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 - } - } + #[stable(feature = "weak_into_raw", since = "1.45.0")] + 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. @@ -1676,13 +1719,11 @@ 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 /// /// ``` - /// #![feature(weak_into_raw)] - /// /// use std::rc::{Rc, Weak}; /// /// let strong = Rc::new("hello".to_owned()); @@ -1697,10 +1738,10 @@ impl Weak { /// ``` /// /// [`from_raw`]: struct.Weak.html#method.from_raw - /// [`as_raw`]: struct.Weak.html#method.as_raw - #[unstable(feature = "weak_into_raw", issue = "60728")] + /// [`as_ptr`]: struct.Weak.html#method.as_ptr + #[stable(feature = "weak_into_raw", since = "1.45.0")] pub fn into_raw(self) -> *const T { - let result = self.as_raw(); + let result = self.as_ptr(); mem::forget(self); result } @@ -1715,9 +1756,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 @@ -1726,8 +1766,6 @@ impl Weak { /// # Examples /// /// ``` - /// #![feature(weak_into_raw)] - /// /// use std::rc::{Rc, Weak}; /// /// let strong = Rc::new("hello".to_owned()); @@ -1750,19 +1788,20 @@ 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")] + #[stable(feature = "weak_into_raw", since = "1.45.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { if ptr.is_null() { Self::new() } else { // See Rc::from_raw for details - let offset = data_offset(ptr); - let fake_ptr = ptr as *mut RcBox; - let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") } + unsafe { + let offset = data_offset(ptr); + let fake_ptr = ptr as *mut RcBox; + let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") } + } } } } @@ -2007,10 +2046,8 @@ trait RcBoxPtr { // The reference count will never be zero when this is called; // nevertheless, we insert an abort here to hint LLVM at // an otherwise missed optimization. - if strong == 0 || strong == usize::max_value() { - unsafe { - abort(); - } + if strong == 0 || strong == usize::MAX { + abort(); } self.inner().strong.set(strong + 1); } @@ -2033,10 +2070,8 @@ trait RcBoxPtr { // The reference count will never be zero when this is called; // nevertheless, we insert an abort here to hint LLVM at // an otherwise missed optimization. - if weak == 0 || weak == usize::max_value() { - unsafe { - abort(); - } + if weak == 0 || weak == usize::MAX { + abort(); } self.inner().weak.set(weak + 1); } @@ -2083,7 +2118,7 @@ unsafe fn data_offset(ptr: *const T) -> isize { // Because it is ?Sized, it will always be the last field in memory. // Note: This is a detail of the current implementation of the compiler, // and is not a guaranteed language detail. Do not rely on it outside of std. - data_offset_align(align_of_val(&*ptr)) + unsafe { data_offset_align(align_of_val(&*ptr)) } } /// Computes the offset of the data field within `RcBox`. diff --git a/src/liballoc/rc/tests.rs b/src/liballoc/rc/tests.rs index 56788bb56d550..e88385faf4fd4 100644 --- a/src/liballoc/rc/tests.rs +++ b/src/liballoc/rc/tests.rs @@ -407,14 +407,14 @@ fn test_from_vec() { fn test_downcast() { use std::any::Any; - let r1: Rc = Rc::new(i32::max_value()); + let r1: Rc = Rc::new(i32::MAX); let r2: Rc = Rc::new("abc"); assert!(r1.clone().downcast::().is_err()); let r1i32 = r1.downcast::(); assert!(r1i32.is_ok()); - assert_eq!(r1i32.unwrap(), Rc::new(i32::max_value())); + assert_eq!(r1i32.unwrap(), Rc::new(i32::MAX)); assert!(r2.clone().downcast::().is_err()); diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 7b83658fca60d..d7dc2174d665f 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,12 +140,14 @@ 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(); let b = Box::into_raw(b); - let xs = Vec::from_raw_parts(b as *mut T, len, len); - xs + Vec::from_raw_parts(b as *mut T, len, len) } } @@ -166,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. @@ -201,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 @@ -255,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 @@ -298,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 @@ -433,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 @@ -735,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); } } @@ -830,8 +831,7 @@ where { let len = v.len(); let v = v.as_mut_ptr(); - let v_mid = v.add(mid); - let v_end = v.add(len); + let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) }; // The merge process first copies the shorter run into `buf`. Then it traces the newly copied // run and the longer run forwards (or backwards), comparing their next unconsumed elements and @@ -854,8 +854,10 @@ where if mid <= len - mid { // The left run is shorter. - ptr::copy_nonoverlapping(v, buf, mid); - hole = MergeHole { start: buf, end: buf.add(mid), dest: v }; + unsafe { + ptr::copy_nonoverlapping(v, buf, mid); + hole = MergeHole { start: buf, end: buf.add(mid), dest: v }; + } // Initially, these pointers point to the beginnings of their arrays. let left = &mut hole.start; @@ -865,17 +867,21 @@ where while *left < hole.end && right < v_end { // Consume the lesser side. // If equal, prefer the left run to maintain stability. - let to_copy = if is_less(&*right, &**left) { - get_and_increment(&mut right) - } else { - get_and_increment(left) - }; - ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1); + unsafe { + let to_copy = if is_less(&*right, &**left) { + get_and_increment(&mut right) + } else { + get_and_increment(left) + }; + ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1); + } } } else { // The right run is shorter. - ptr::copy_nonoverlapping(v_mid, buf, len - mid); - hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid }; + unsafe { + ptr::copy_nonoverlapping(v_mid, buf, len - mid); + hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid }; + } // Initially, these pointers point past the ends of their arrays. let left = &mut hole.dest; @@ -885,12 +891,14 @@ where while v < *left && buf < *right { // Consume the greater side. // If equal, prefer the right run to maintain stability. - let to_copy = if is_less(&*right.offset(-1), &*left.offset(-1)) { - decrement_and_get(left) - } else { - decrement_and_get(right) - }; - ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1); + unsafe { + let to_copy = if is_less(&*right.offset(-1), &*left.offset(-1)) { + decrement_and_get(left) + } else { + decrement_and_get(right) + }; + ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1); + } } } // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of @@ -898,12 +906,12 @@ where unsafe fn get_and_increment(ptr: &mut *mut T) -> *mut T { let old = *ptr; - *ptr = ptr.offset(1); + *ptr = unsafe { ptr.offset(1) }; old } unsafe fn decrement_and_get(ptr: &mut *mut T) -> *mut T { - *ptr = ptr.offset(-1); + *ptr = unsafe { ptr.offset(-1) }; *ptr } @@ -936,7 +944,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..57927c688479b 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 { @@ -583,5 +583,5 @@ impl str { #[stable(feature = "str_box_extras", since = "1.20.0")] #[inline] pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { - Box::from_raw(Box::into_raw(v) as *mut str) + unsafe { Box::from_raw(Box::into_raw(v) as *mut str) } } diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 0e48f1548e6d1..64d9692244dde 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 @@ -722,7 +724,7 @@ impl String { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String { - String { vec: Vec::from_raw_parts(buf, length, capacity) } + unsafe { String { vec: Vec::from_raw_parts(buf, length, capacity) } } } /// Converts a vector of bytes to a `String` without checking that the @@ -1327,9 +1329,11 @@ impl String { let amt = bytes.len(); self.vec.reserve(amt); - ptr::copy(self.vec.as_ptr().add(idx), self.vec.as_mut_ptr().add(idx + amt), len - idx); - ptr::copy(bytes.as_ptr(), self.vec.as_mut_ptr().add(idx), amt); - self.vec.set_len(len + amt); + unsafe { + ptr::copy(self.vec.as_ptr().add(idx), self.vec.as_mut_ptr().add(idx + amt), len - idx); + ptr::copy(bytes.as_ptr(), self.vec.as_mut_ptr().add(idx), amt); + self.vec.set_len(len + amt); + } } /// Inserts a string slice into this `String` at a byte position. @@ -1461,6 +1465,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); @@ -1796,6 +1801,16 @@ impl Extend for String { self.reserve(lower_bound); iterator.for_each(move |c| self.push(c)); } + + #[inline] + fn extend_one(&mut self, c: char) { + self.push(c); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -1803,6 +1818,16 @@ impl<'a> Extend<&'a char> for String { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &c: &'a char) { + self.push(c); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1810,6 +1835,11 @@ impl<'a> Extend<&'a str> for String { fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |s| self.push_str(s)); } + + #[inline] + fn extend_one(&mut self, s: &'a str) { + self.push_str(s); + } } #[stable(feature = "extend_string", since = "1.4.0")] @@ -1817,6 +1847,11 @@ impl Extend for String { fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |s| self.push_str(&s)); } + + #[inline] + fn extend_one(&mut self, s: String) { + self.push_str(&s); + } } #[stable(feature = "herd_cows", since = "1.19.0")] @@ -1824,9 +1859,20 @@ impl<'a> Extend> for String { fn extend>>(&mut self, iter: I) { iter.into_iter().for_each(move |s| self.push_str(&s)); } + + #[inline] + fn extend_one(&mut self, s: Cow<'a, str>) { + self.push_str(&s); + } } -/// A convenience impl that delegates to the impl for `&str` +/// 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 +1894,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")] @@ -2169,6 +2230,14 @@ impl ToString for T { } } +#[stable(feature = "char_to_string_specialization", since = "1.46.0")] +impl ToString for char { + #[inline] + fn to_string(&self) -> String { + String::from(self.encode_utf8(&mut [0; 4])) + } +} + #[stable(feature = "str_to_string_specialization", since = "1.9.0")] impl ToString for str { #[inline] diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 4a0cf2984edd9..826f0c8fa833f 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; @@ -40,6 +40,23 @@ mod tests; /// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. const MAX_REFCOUNT: usize = (isize::MAX) as usize; +#[cfg(not(sanitize = "thread"))] +macro_rules! acquire { + ($x:expr) => { + atomic::fence(Acquire) + }; +} + +// ThreadSanitizer does not support memory fences. To avoid false positive +// reports in Arc / Weak implementation use atomic loads for synchronization +// instead. +#[cfg(sanitize = "thread")] +macro_rules! acquire { + ($x:expr) => { + $x.load(Acquire) + }; +} + /// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically /// Reference Counted'. /// @@ -191,7 +208,7 @@ const MAX_REFCOUNT: usize = (isize::MAX) as usize; /// 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>, @@ -215,7 +232,7 @@ impl Arc { } unsafe fn from_ptr(ptr: *mut ArcInner) -> Self { - Self::from_inner(NonNull::new_unchecked(ptr)) + unsafe { Self::from_inner(NonNull::new_unchecked(ptr)) } } } @@ -270,6 +287,10 @@ impl fmt::Debug for Weak { } } +// This is repr(C) to future-proof against possible field-reordering, which +// would interfere with otherwise safe [into|from]_raw() of transmutable +// inner types. +#[repr(C)] struct ArcInner { strong: atomic::AtomicUsize, @@ -304,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. @@ -402,7 +423,7 @@ impl Arc { return Err(this); } - atomic::fence(Acquire); + acquire!(this.inner().strong); unsafe { let elem = ptr::read(&this.ptr.as_ref().data); @@ -522,7 +543,7 @@ impl Arc<[mem::MaybeUninit]> { #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub unsafe fn assume_init(self) -> Arc<[T]> { - Arc::from_ptr(mem::ManuallyDrop::new(self).ptr.as_ptr() as _) + unsafe { Arc::from_ptr(mem::ManuallyDrop::new(self).ptr.as_ptr() as _) } } } @@ -545,9 +566,31 @@ 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 + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new("hello".to_owned()); + /// let y = Arc::clone(&x); + /// let x_ptr = Arc::as_ptr(&x); + /// assert_eq!(x_ptr, Arc::as_ptr(&y)); + /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// ``` + #[stable(feature = "weak_into_raw", since = "1.45.0")] + 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. @@ -560,15 +603,24 @@ impl Arc { } } - /// Constructs an `Arc` from a raw pointer. + /// Constructs an `Arc` from a raw pointer. + /// + /// The raw pointer must have been previously returned by a call to + /// [`Arc::into_raw`][into_raw] where `U` must have the same size and + /// alignment as `T`. This is trivially true if `U` is `T`. + /// Note that if `U` is not `T` but has the same size and alignment, this is + /// basically like transmuting references of different types. See + /// [`mem::transmute`][transmute] for more information on what + /// restrictions apply in this case. /// - /// The raw pointer must have been previously returned by a call to a - /// [`Arc::into_raw`][into_raw]. + /// The user of `from_raw` has to make sure a specific value of `T` is only + /// dropped once. /// - /// This function is unsafe because improper use may lead to memory problems. For example, a - /// double-free may occur if the function is called twice on the same raw pointer. + /// This function is unsafe because improper use may lead to memory unsafety, + /// even if the returned `Arc` is never accessed. /// /// [into_raw]: struct.Arc.html#method.into_raw + /// [transmute]: ../../std/mem/fn.transmute.html /// /// # Examples /// @@ -590,13 +642,15 @@ impl Arc { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - let offset = data_offset(ptr); + unsafe { + let offset = data_offset(ptr); - // Reverse the offset to find the original ArcInner. - let fake_ptr = ptr as *mut ArcInner; - let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + // Reverse the offset to find the original ArcInner. + let fake_ptr = ptr as *mut ArcInner; + let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - Self::from_ptr(arc_ptr) + Self::from_ptr(arc_ptr) + } } /// Consumes the `Arc`, returning the wrapped pointer as `NonNull`. @@ -605,6 +659,7 @@ impl Arc { /// /// ``` /// #![feature(rc_into_raw_non_null)] + /// #![allow(deprecated)] /// /// use std::sync::Arc; /// @@ -614,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 @@ -721,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 = unsafe { mem::ManuallyDrop::new(Arc::::from_raw(ptr)) }; + // Now increase refcount, but don't drop new refcount either + let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); + } + + /// Decrements the strong reference count on the `Arc` associated with the + /// provided pointer by one. + /// + /// # Safety + /// + /// The pointer must have been obtained through `Arc::into_raw`, and the + /// associated `Arc` instance must be valid (i.e. the strong count must be at + /// least 1) when invoking this method. This method can be used to release the final + /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been + /// released. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_mutate_strong_count)] + /// + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// unsafe { + /// let ptr = Arc::into_raw(five); + /// Arc::incr_strong_count(ptr); + /// + /// // Those assertions are deterministic because we haven't shared + /// // the `Arc` between threads. + /// let five = Arc::from_raw(ptr); + /// assert_eq!(2, Arc::strong_count(&five)); + /// Arc::decr_strong_count(ptr); + /// assert_eq!(1, Arc::strong_count(&five)); + /// } + /// ``` + #[inline] + #[unstable(feature = "arc_mutate_strong_count", issue = "71983")] + pub unsafe fn decr_strong_count(ptr: *const T) { + unsafe { mem::drop(Arc::from_raw(ptr)) }; + } + #[inline] fn inner(&self) -> &ArcInner { // This unsafety is ok because while this arc is alive we're guaranteed @@ -736,12 +867,10 @@ impl Arc { unsafe fn drop_slow(&mut self) { // Destroy the data at this time, even though we may not free the box // allocation itself (there may still be weak pointers lying around). - ptr::drop_in_place(&mut self.ptr.as_mut().data); + unsafe { ptr::drop_in_place(Self::get_mut_unchecked(self)) }; - if self.inner().weak.fetch_sub(1, Release) == 1 { - atomic::fence(Acquire); - Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) - } + // Drop the weak ref collectively held by all strong references + drop(Weak { ptr: self.ptr }); } #[inline] @@ -784,14 +913,18 @@ 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()); - debug_assert_eq!(Layout::for_value(&*inner), layout); + let inner = mem_to_arcinner(mem.ptr.as_ptr()); + debug_assert_eq!(unsafe { Layout::for_value(&*inner) }, layout); - ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); - ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); + unsafe { + ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); + ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); + } inner } @@ -799,9 +932,11 @@ impl Arc { /// Allocates an `ArcInner` with sufficient space for an unsized inner value. unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner { // Allocate for the `ArcInner` using the given value. - Self::allocate_for_layout(Layout::for_value(&*ptr), |mem| { - set_data_ptr(ptr as *mut T, mem) as *mut ArcInner - }) + unsafe { + Self::allocate_for_layout(Layout::for_value(&*ptr), |mem| { + set_data_ptr(ptr as *mut T, mem) as *mut ArcInner + }) + } } fn from_box(v: Box) -> Arc { @@ -830,9 +965,11 @@ impl Arc { impl Arc<[T]> { /// Allocates an `ArcInner<[T]>` with the given length. unsafe fn allocate_for_slice(len: usize) -> *mut ArcInner<[T]> { - Self::allocate_for_layout(Layout::array::(len).unwrap(), |mem| { - ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut ArcInner<[T]> - }) + unsafe { + Self::allocate_for_layout(Layout::array::(len).unwrap(), |mem| { + ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut ArcInner<[T]> + }) + } } } @@ -841,20 +978,24 @@ impl Arc<[T]> { /// For a slice/trait object, this sets the `data` field and leaves the rest /// unchanged. For a sized raw pointer, this simply sets the pointer. unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { - ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); + unsafe { + ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); + } ptr } impl Arc<[T]> { - /// Copy elements from slice into newly allocated Arc<[T]> + /// 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]> { - let ptr = Self::allocate_for_slice(v.len()); + unsafe { + let ptr = Self::allocate_for_slice(v.len()); - ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).data as *mut [T] as *mut T, v.len()); + ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).data as *mut [T] as *mut T, v.len()); - Self::from_ptr(ptr) + Self::from_ptr(ptr) + } } /// Constructs an `Arc<[T]>` from an iterator known to be of a certain size. @@ -882,25 +1023,27 @@ impl Arc<[T]> { } } - let ptr = Self::allocate_for_slice(len); + unsafe { + let ptr = Self::allocate_for_slice(len); - let mem = ptr as *mut _ as *mut u8; - let layout = Layout::for_value(&*ptr); + let mem = ptr as *mut _ as *mut u8; + let layout = Layout::for_value(&*ptr); - // Pointer to first element - let elems = &mut (*ptr).data as *mut [T] as *mut T; + // Pointer to first element + let elems = &mut (*ptr).data as *mut [T] as *mut T; - let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; + let mut guard = Guard { mem: NonNull::new_unchecked(mem), elems, layout, n_elems: 0 }; - for (i, item) in iter.enumerate() { - ptr::write(elems.add(i), item); - guard.n_elems += 1; - } + for (i, item) in iter.enumerate() { + ptr::write(elems.add(i), item); + guard.n_elems += 1; + } - // All clear. Forget the guard so it doesn't free the new ArcInner. - mem::forget(guard); + // All clear. Forget the guard so it doesn't free the new ArcInner. + mem::forget(guard); - Self::from_ptr(ptr) + Self::from_ptr(ptr) + } } } @@ -964,9 +1107,7 @@ 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 { - unsafe { - abort(); - } + abort(); } Self::from_inner(self.ptr) @@ -1069,7 +1210,7 @@ impl Arc { // As with `get_mut()`, the unsafety is ok because our reference was // either unique to begin with, or became one upon cloning the contents. - unsafe { &mut this.ptr.as_mut().data } + unsafe { Self::get_mut_unchecked(this) } } } @@ -1145,7 +1286,9 @@ impl Arc { #[inline] #[unstable(feature = "get_mut_unchecked", issue = "63292")] pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T { - &mut this.ptr.as_mut().data + // We are careful to *not* create a reference covering the "count" fields, as + // this would alias with concurrent access to the reference counts (e.g. by `Weak`). + unsafe { &mut (*this.ptr.as_ptr()).data } } /// Determine whether this is the unique reference (including weak refs) to @@ -1243,7 +1386,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { // // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) // [2]: (https://github.com/rust-lang/rust/pull/41714) - atomic::fence(Acquire); + acquire!(self.inner().strong); unsafe { self.drop_slow(); @@ -1308,45 +1451,34 @@ 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 /// /// ``` - /// #![feature(weak_into_raw)] - /// /// use std::sync::Arc; /// use std::ptr; /// /// let strong = Arc::new("hello".to_owned()); /// let weak = Arc::downgrade(&strong); /// // Both point to the same object - /// assert!(ptr::eq(&*strong, weak.as_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 - } - } + #[stable(feature = "weak_into_raw", since = "1.45.0")] + 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. @@ -1355,13 +1487,11 @@ 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 /// /// ``` - /// #![feature(weak_into_raw)] - /// /// use std::sync::{Arc, Weak}; /// /// let strong = Arc::new("hello".to_owned()); @@ -1376,10 +1506,10 @@ impl Weak { /// ``` /// /// [`from_raw`]: struct.Weak.html#method.from_raw - /// [`as_raw`]: struct.Weak.html#method.as_raw - #[unstable(feature = "weak_into_raw", issue = "60728")] + /// [`as_ptr`]: struct.Weak.html#method.as_ptr + #[stable(feature = "weak_into_raw", since = "1.45.0")] pub fn into_raw(self) -> *const T { - let result = self.as_raw(); + let result = self.as_ptr(); mem::forget(self); result } @@ -1395,9 +1525,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 @@ -1406,8 +1535,6 @@ impl Weak { /// # Examples /// /// ``` - /// #![feature(weak_into_raw)] - /// /// use std::sync::{Arc, Weak}; /// /// let strong = Arc::new("hello".to_owned()); @@ -1426,27 +1553,35 @@ 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 /// [`Weak`]: struct.Weak.html /// [`Arc`]: struct.Arc.html /// [`forget`]: ../../std/mem/fn.forget.html - #[unstable(feature = "weak_into_raw", issue = "60728")] + #[stable(feature = "weak_into_raw", since = "1.45.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { if ptr.is_null() { Self::new() } else { // See Arc::from_raw for details - let offset = data_offset(ptr); - let fake_ptr = ptr as *mut ArcInner; - let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") } + unsafe { + let offset = data_offset(ptr); + let fake_ptr = ptr as *mut ArcInner; + let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") } + } } } } +/// Helper type to allow accessing the reference counts without +/// making any assertions about the data field. +struct WeakInner<'a> { + weak: &'a atomic::AtomicUsize, + strong: &'a atomic::AtomicUsize, +} + impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying /// dropping of the inner value if successful. @@ -1493,9 +1628,7 @@ impl Weak { // See comments in `Arc::clone` for why we do this (for `mem::forget`). if n > MAX_REFCOUNT { - unsafe { - abort(); - } + abort(); } // Relaxed is valid for the same reason it is on Arc's Clone impl @@ -1552,8 +1685,18 @@ impl Weak { /// Returns `None` when the pointer is dangling and there is no allocated `ArcInner`, /// (i.e., when this `Weak` was created by `Weak::new`). #[inline] - fn inner(&self) -> Option<&ArcInner> { - if is_dangling(self.ptr) { None } else { Some(unsafe { self.ptr.as_ref() }) } + fn inner(&self) -> Option> { + if is_dangling(self.ptr) { + None + } else { + // We are careful to *not* create a reference covering the "data" field, as + // the field may be mutated concurrently (for example, if the last `Arc` + // is dropped, the data field will be dropped in-place). + Some(unsafe { + let ptr = self.ptr.as_ptr(); + WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak } + }) + } } /// Returns `true` if the two `Weak`s point to the same allocation (similar to @@ -1632,9 +1775,7 @@ impl Clone for Weak { // See comments in Arc::clone() for why we do this (for mem::forget). if old_size > MAX_REFCOUNT { - unsafe { - abort(); - } + abort(); } Weak { ptr: self.ptr } @@ -1701,7 +1842,7 @@ impl Drop for Weak { let inner = if let Some(inner) = self.inner() { inner } else { return }; if inner.weak.fetch_sub(1, Release) == 1 { - atomic::fence(Acquire); + acquire!(inner.weak); unsafe { Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) } } } @@ -1733,7 +1874,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 @@ -2002,6 +2143,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 @@ -2059,25 +2215,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, @@ -2088,29 +2244,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 { @@ -2134,7 +2276,7 @@ unsafe fn data_offset(ptr: *const T) -> isize { // Because it is `?Sized`, it will always be the last field in memory. // Note: This is a detail of the current implementation of the compiler, // and is not a guaranteed language detail. Do not rely on it outside of std. - data_offset_align(align_of_val(&*ptr)) + unsafe { data_offset_align(align_of_val(&*ptr)) } } /// Computes the offset of the data field within `ArcInner`. diff --git a/src/liballoc/sync/tests.rs b/src/liballoc/sync/tests.rs index edc2820ee22f1..6f08cd7f123be 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(); } @@ -463,14 +465,14 @@ fn test_from_vec() { fn test_downcast() { use std::any::Any; - let r1: Arc = Arc::new(i32::max_value()); + let r1: Arc = Arc::new(i32::MAX); let r2: Arc = Arc::new("abc"); assert!(r1.clone().downcast::().is_err()); let r1i32 = r1.downcast::(); assert!(r1i32.is_ok()); - assert_eq!(r1i32.unwrap(), Arc::new(i32::max_value())); + assert_eq!(r1i32.unwrap(), Arc::new(i32::MAX)); assert!(r2.clone().downcast::().is_err()); diff --git a/src/liballoc/task.rs b/src/liballoc/task.rs new file mode 100644 index 0000000000000..0d1cc99df47c5 --- /dev/null +++ b/src/liballoc/task.rs @@ -0,0 +1,92 @@ +#![unstable(feature = "wake_trait", issue = "69912")] +//! Types and Traits for working with asynchronous tasks. +use core::mem::ManuallyDrop; +use core::task::{RawWaker, RawWakerVTable, Waker}; + +use crate::sync::Arc; + +/// The implementation of waking a task on an executor. +/// +/// This trait can be used to create a [`Waker`]. An executor can define an +/// implementation of this trait, and use that to construct a Waker to pass +/// 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`][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. + #[unstable(feature = "wake_trait", issue = "69912")] + fn wake(self: Arc); + + /// Wake this task without consuming the waker. + /// + /// If an executor supports a cheaper way to wake without consuming the + /// waker, it should override this method. By default, it clones the + /// [`Arc`] and calls `wake` on the clone. + #[unstable(feature = "wake_trait", issue = "69912")] + fn wake_by_ref(self: &Arc) { + self.clone().wake(); + } +} + +#[unstable(feature = "wake_trait", issue = "69912")] +impl From> for Waker { + fn from(waker: Arc) -> Waker { + // SAFETY: This is safe because raw_waker safely constructs + // a RawWaker from Arc. + unsafe { Waker::from_raw(raw_waker(waker)) } + } +} + +#[unstable(feature = "wake_trait", issue = "69912")] +impl From> for RawWaker { + fn from(waker: Arc) -> RawWaker { + raw_waker(waker) + } +} + +// NB: This private function for constructing a RawWaker is used, rather than +// inlining this into the `From> for RawWaker` impl, to ensure that +// the safety of `From> for Waker` does not depend on the correct +// trait dispatch - instead both impls call this function directly and +// explicitly. +#[inline(always)] +fn raw_waker(waker: Arc) -> RawWaker { + // Increment the reference count of the arc to clone it. + unsafe fn clone_waker(waker: *const ()) -> RawWaker { + unsafe { 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 + unsafe fn wake(waker: *const ()) { + let waker: Arc = unsafe { Arc::from_raw(waker as *const W) }; + ::wake(waker); + } + + // Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it + unsafe fn wake_by_ref(waker: *const ()) { + let waker: ManuallyDrop> = + unsafe { ManuallyDrop::new(Arc::from_raw(waker as *const W)) }; + ::wake_by_ref(&waker); + } + + // Decrement the reference count of the Arc on drop + unsafe fn drop_waker(waker: *const ()) { + unsafe { Arc::decr_strong_count(waker as *const W) }; + } + + RawWaker::new( + Arc::into_raw(waker) as *const (), + &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), + ) +} 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/boxed.rs b/src/liballoc/tests/boxed.rs index 66782ecbeb7f6..5377485da8f3b 100644 --- a/src/liballoc/tests/boxed.rs +++ b/src/liballoc/tests/boxed.rs @@ -16,3 +16,36 @@ fn unitialized_zero_size_box() { NonNull::>::dangling().as_ptr(), ); } + +#[derive(Clone, PartialEq, Eq, Debug)] +struct Dummy { + _data: u8, +} + +#[test] +fn box_clone_and_clone_from_equivalence() { + for size in (0..8).map(|i| 2usize.pow(i)) { + let control = vec![Dummy { _data: 42 }; size].into_boxed_slice(); + let clone = control.clone(); + let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice(); + copy.clone_from(&control); + assert_eq!(control, clone); + assert_eq!(control, copy); + } +} + +/// This test might give a false positive in case the box realocates, but the alocator keeps the +/// original pointer. +/// +/// On the other hand it won't give a false negative, if it fails than the memory was definitly not +/// reused +#[test] +fn box_clone_from_ptr_stability() { + for size in (0..8).map(|i| 2usize.pow(i)) { + let control = vec![Dummy { _data: 42 }; size].into_boxed_slice(); + let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice(); + let copy_raw = copy.as_ptr() as usize; + copy.clone_from(&control); + assert_eq!(copy.as_ptr() as usize, copy_raw); + } +} diff --git a/src/liballoc/tests/btree/map.rs b/src/liballoc/tests/btree/map.rs index d05eec19346de..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 { @@ -67,7 +79,7 @@ fn test_basic_large() { #[test] fn test_basic_small() { let mut map = BTreeMap::new(); - // Empty, shared root: + // Empty, root is absent (None): assert_eq!(map.remove(&1), None); assert_eq!(map.len(), 0); assert_eq!(map.get(&1), None); @@ -123,7 +135,7 @@ fn test_basic_small() { assert_eq!(map.values().collect::>(), vec![&4]); assert_eq!(map.remove(&2), Some(4)); - // Empty but private root: + // Empty but root is owned (Some(...)): assert_eq!(map.len(), 0); assert_eq!(map.get(&1), None); assert_eq!(map.get_mut(&1), None); @@ -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,37 +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); -} - -#[test] -fn test_into_key_slice_with_shared_root_past_bounds() { - let mut map: BTreeMap = BTreeMap::new(); - assert_eq!(map.get(&Align32(1)), None); - assert_eq!(map.get_mut(&Align32(1)), None); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_1); + do_test_iter_mut_mutation::(MIN_INSERTS_HEIGHT_2); } #[test] @@ -286,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(); @@ -383,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]); @@ -473,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)]); @@ -523,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>) { @@ -564,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) { @@ -587,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) { @@ -607,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 @@ -762,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 { @@ -790,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 { @@ -1005,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(); @@ -1021,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; @@ -1047,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..e2dc816b01526 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)] @@ -11,14 +12,15 @@ #![feature(associated_type_bounds)] #![feature(binary_heap_into_iter_sorted)] #![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/str.rs b/src/liballoc/tests/str.rs index b703df6f3cb7d..eee98d4534042 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -566,13 +566,13 @@ mod slice_index { data: "hello"; // note: using 0 specifically ensures that the result of overflowing is 0..0, // so that `get` doesn't simply return None for the wrong reason. - bad: data[0..=usize::max_value()]; + bad: data[0..=usize::MAX]; message: "maximum usize"; } in mod rangetoinclusive { data: "hello"; - bad: data[..=usize::max_value()]; + bad: data[..=usize::MAX]; message: "maximum usize"; } } 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..ffff543b07fe5 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -1,9 +1,9 @@ use std::borrow::Cow; use std::collections::TryReserveError::*; +use std::fmt::Debug; use std::mem::size_of; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::vec::{Drain, IntoIter}; -use std::{isize, usize}; struct DropCounter<'a> { count: &'a mut u32, @@ -17,7 +17,7 @@ impl Drop for DropCounter<'_> { #[test] fn test_small_vec_struct() { - assert!(size_of::>() == size_of::() * 3); + assert_eq!(size_of::>(), size_of::() * 3); } #[test] @@ -69,7 +69,7 @@ fn test_reserve() { #[test] fn test_zst_capacity() { - assert_eq!(Vec::<()>::new().capacity(), usize::max_value()); + assert_eq!(Vec::<()>::new().capacity(), usize::MAX); } #[test] @@ -132,21 +132,6 @@ fn test_extend_ref() { assert_eq!(v, [1, 2, 3, 4, 5, 6, 7]); } -#[test] -fn test_remove_item() { - let mut v = vec![1, 2, 3]; - v.remove_item(&1); - - assert_eq!(v.len(), 2); - assert_eq!(v, [2, 3]); - - let mut w = vec![1, 2, 3]; - w.remove_item(&4); - - assert_eq!(w.len(), 3); - w.remove_item(&4); -} - #[test] fn test_slice_from_mut() { let mut values = vec![1, 2, 3, 4, 5]; @@ -564,19 +549,19 @@ fn test_drain_inclusive_range() { #[test] fn test_drain_max_vec_size() { - let mut v = Vec::<()>::with_capacity(usize::max_value()); + let mut v = Vec::<()>::with_capacity(usize::MAX); unsafe { - v.set_len(usize::max_value()); + v.set_len(usize::MAX); } - for _ in v.drain(usize::max_value() - 1..) {} - assert_eq!(v.len(), usize::max_value() - 1); + for _ in v.drain(usize::MAX - 1..) {} + assert_eq!(v.len(), usize::MAX - 1); - let mut v = Vec::<()>::with_capacity(usize::max_value()); + let mut v = Vec::<()>::with_capacity(usize::MAX); unsafe { - v.set_len(usize::max_value()); + v.set_len(usize::MAX); } - for _ in v.drain(usize::max_value() - 1..=usize::max_value() - 1) {} - assert_eq!(v.len(), usize::max_value() - 1); + for _ in v.drain(usize::MAX - 1..=usize::MAX - 1) {} + assert_eq!(v.len(), usize::MAX - 1); } #[test] @@ -1138,6 +1123,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 +1241,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 +1338,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 +1365,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 +1459,171 @@ 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); + } + } +} + +macro_rules! generate_assert_eq_vec_and_prim { + ($name:ident<$B:ident>($type:ty)) => { + fn $name + Debug, $B: Debug>(a: Vec, b: $type) { + assert!(a == b); + assert_eq!(a, b); + } + }; +} + +generate_assert_eq_vec_and_prim! { assert_eq_vec_and_slice (&[B]) } +generate_assert_eq_vec_and_prim! { assert_eq_vec_and_array_3([B; 3]) } + +#[test] +fn partialeq_vec_and_prim() { + assert_eq_vec_and_slice(vec![1, 2, 3], &[1, 2, 3]); + assert_eq_vec_and_array_3(vec![1, 2, 3], [1, 2, 3]); +} + +macro_rules! assert_partial_eq_valid { + ($a2:ident, $a3:ident; $b2:ident, $b3: ident) => { + assert!($a2 == $b2); + assert!($a2 != $b3); + assert!($a3 != $b2); + assert!($a3 == $b3); + assert_eq!($a2, $b2); + assert_ne!($a2, $b3); + assert_ne!($a3, $b2); + assert_eq!($a3, $b3); + }; +} + +#[test] +fn partialeq_vec_full() { + let vec2: Vec<_> = vec![1, 2]; + let vec3: Vec<_> = vec![1, 2, 3]; + let slice2: &[_] = &[1, 2]; + let slice3: &[_] = &[1, 2, 3]; + let slicemut2: &[_] = &mut [1, 2]; + let slicemut3: &[_] = &mut [1, 2, 3]; + let array2: [_; 2] = [1, 2]; + let array3: [_; 3] = [1, 2, 3]; + let arrayref2: &[_; 2] = &[1, 2]; + let arrayref3: &[_; 3] = &[1, 2, 3]; + + assert_partial_eq_valid!(vec2,vec3; vec2,vec3); + assert_partial_eq_valid!(vec2,vec3; slice2,slice3); + assert_partial_eq_valid!(vec2,vec3; slicemut2,slicemut3); + assert_partial_eq_valid!(slice2,slice3; vec2,vec3); + assert_partial_eq_valid!(slicemut2,slicemut3; vec2,vec3); + assert_partial_eq_valid!(vec2,vec3; array2,array3); + assert_partial_eq_valid!(vec2,vec3; arrayref2,arrayref3); +} diff --git a/src/liballoc/tests/vec_deque.rs b/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 d1956270f135f..1265d0e56b576 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`. //! @@ -61,11 +62,11 @@ use core::array::LengthAtMost32; use core::cmp::{self, Ordering}; use core::fmt; -use core::hash::{self, Hash}; +use core::hash::{Hash, Hasher}; 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}; @@ -342,14 +343,19 @@ impl Vec { /// /// // The vector contains no items, even though it has capacity for more /// assert_eq!(vec.len(), 0); + /// assert_eq!(vec.capacity(), 10); /// /// // These are all done without reallocating... /// for i in 0..10 { /// vec.push(i); /// } + /// assert_eq!(vec.len(), 10); + /// assert_eq!(vec.capacity(), 10); /// /// // ...but this may make the vector reallocate /// vec.push(11); + /// assert_eq!(vec.len(), 11); + /// assert!(vec.capacity() >= 11); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -391,7 +397,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()) } @@ -459,7 +465,7 @@ impl Vec { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Vec { - Vec { buf: RawVec::from_raw_parts(ptr, capacity), len: length } + unsafe { Vec { buf: RawVec::from_raw_parts(ptr, capacity), len: length } } } /// Returns the number of elements the vector can hold without @@ -677,9 +683,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 +745,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 +969,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 = self.as_mut_ptr().add(index); + self.set_len(len - 1); ptr::replace(hole, last) } } @@ -990,8 +1008,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 +1056,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 +1232,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()))) } } } @@ -1230,10 +1264,10 @@ impl Vec { /// Appends elements to `Self` from other buffer. #[inline] unsafe fn append_elements(&mut self, other: *const [T]) { - let count = (*other).len(); + let count = unsafe { (*other).len() }; self.reserve(count); let len = self.len(); - ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count); + unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) }; self.len += count; } @@ -1289,8 +1323,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 @@ -1377,9 +1428,18 @@ impl Vec { /// assert_eq!(vec2, [2, 3]); /// ``` #[inline] + #[must_use = "use `.truncate()` if you don't need the other half"] #[stable(feature = "split_off", since = "1.4.0")] pub fn split_off(&mut self, at: usize) -> Self { - 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); @@ -1564,8 +1624,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) { @@ -1579,7 +1639,7 @@ impl Vec { } } -// This code generalises `extend_with_{element,default}`. +// This code generalizes `extend_with_{element,default}`. trait ExtendWith { fn next(&mut self) -> T; fn last(self) -> T; @@ -1700,17 +1760,15 @@ impl Vec { impl Vec { /// Removes the first instance of `item` from the vector if the item exists. /// - /// # Examples - /// - /// ``` - /// # #![feature(vec_remove_item)] - /// let mut vec = vec![1, 2, 3, 1]; - /// - /// vec.remove_item(&1); - /// - /// assert_eq!(vec, vec![2, 3, 1]); - /// ``` + /// This method will be removed soon. #[unstable(feature = "vec_remove_item", reason = "recently added", issue = "40062")] + #[rustc_deprecated( + reason = "Removing the first item equal to a needle is already easily possible \ + with iterators and the current Vec methods. Furthermore, having a method for \ + one particular case of removal (linear search, only the first item, no swap remove) \ + but not for others is inconsistent. This method will be removed soon.", + since = "1.46.0" + )] pub fn remove_item(&mut self, item: &V) -> Option where T: PartialEq, @@ -1770,13 +1828,14 @@ impl SpecFromElem for T { } } +#[rustc_specialization_trait] unsafe trait IsZero { /// Whether this value is zero fn is_zero(&self) -> bool; } macro_rules! impl_is_zero { - ($t: ty, $is_zero: expr) => { + ($t:ty, $is_zero:expr) => { unsafe impl IsZero for $t { #[inline] fn is_zero(&self) -> bool { @@ -1819,9 +1878,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] @@ -1830,13 +1892,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 { @@ -1848,6 +1903,22 @@ unsafe impl IsZero for Option> { // Common trait implementations for Vec //////////////////////////////////////////////////////////////////////////////// +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::Deref for Vec { + type Target = [T]; + + fn deref(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ops::DerefMut for Vec { + fn deref_mut(&mut self) -> &mut [T] { + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Vec { #[cfg(not(test))] @@ -1872,7 +1943,7 @@ impl Clone for Vec { #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Vec { #[inline] - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { Hash::hash(&**self, state) } } @@ -1903,22 +1974,6 @@ impl> IndexMut for Vec { } } -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::Deref for Vec { - type Target = [T]; - - fn deref(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ops::DerefMut for Vec { - fn deref_mut(&mut self) -> &mut [T] { - unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl FromIterator for Vec { #[inline] @@ -1946,16 +2001,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, @@ -1993,6 +2048,16 @@ impl Extend for Vec { fn extend>(&mut self, iter: I) { >::spec_extend(self, iter.into_iter()) } + + #[inline] + fn extend_one(&mut self, item: T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } // Specialization trait used for Vec::from_iter and Vec::extend @@ -2017,7 +2082,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 @@ -2078,9 +2143,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(); @@ -2120,8 +2184,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); } } } @@ -2142,7 +2207,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); } @@ -2264,15 +2329,25 @@ impl<'a, T: 'a + Copy> Extend<&'a T> for Vec { fn extend>(&mut self, iter: I) { self.spec_extend(iter.into_iter()) } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.reserve(additional); + } } macro_rules! __impl_slice_eq1 { - ([$($vars:tt)*] $lhs:ty, $rhs:ty, $($constraints:tt)*) => { - #[stable(feature = "rust1", since = "1.0.0")] + ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?, #[$stability:meta]) => { + #[$stability] impl PartialEq<$rhs> for $lhs where A: PartialEq, - $($constraints)* + $($ty: $bound)? { #[inline] fn eq(&self, other: &$rhs) -> bool { self[..] == other[..] } @@ -2282,18 +2357,23 @@ macro_rules! __impl_slice_eq1 { } } -__impl_slice_eq1! { [] Vec, Vec, } -__impl_slice_eq1! { [] Vec, &[B], } -__impl_slice_eq1! { [] Vec, &mut [B], } -__impl_slice_eq1! { [] Cow<'_, [A]>, &[B], A: Clone } -__impl_slice_eq1! { [] Cow<'_, [A]>, &mut [B], A: Clone } -__impl_slice_eq1! { [] Cow<'_, [A]>, Vec, A: Clone } -__impl_slice_eq1! { [const N: usize] Vec, [B; N], [B; N]: LengthAtMost32 } -__impl_slice_eq1! { [const N: usize] Vec, &[B; N], [B; N]: LengthAtMost32 } +__impl_slice_eq1! { [] Vec, Vec, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Vec, &[B], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Vec, &mut [B], #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] &[A], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } +__impl_slice_eq1! { [] &mut [A], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } +__impl_slice_eq1! { [] Cow<'_, [A]>, Vec where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Cow<'_, [A]>, &[B] where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [] Cow<'_, [A]>, &mut [B] where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [const N: usize] Vec, [B; N] where [B; N]: LengthAtMost32, #[stable(feature = "rust1", since = "1.0.0")] } +__impl_slice_eq1! { [const N: usize] Vec, &[B; N] where [B; N]: LengthAtMost32, #[stable(feature = "rust1", since = "1.0.0")] } // NOTE: some less important impls are omitted to reduce code bloat // FIXME(Centril): Reconsider this? //__impl_slice_eq1! { [const N: usize] Vec, &mut [B; N], [B; N]: LengthAtMost32 } +//__impl_slice_eq1! { [const N: usize] [A; N], Vec, [A; N]: LengthAtMost32 } +//__impl_slice_eq1! { [const N: usize] &[A; N], Vec, [A; N]: LengthAtMost32 } +//__impl_slice_eq1! { [const N: usize] &mut [A; N], Vec, [A; N]: LengthAtMost32 } //__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, [B; N], [B; N]: LengthAtMost32 } //__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &[B; N], [B; N]: LengthAtMost32 } //__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &mut [B; N], [B; N]: LengthAtMost32 } @@ -2324,7 +2404,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 } @@ -2397,6 +2479,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 @@ -2526,7 +2623,18 @@ 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()) + } +} + +#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")] +impl AsRef<[T]> for IntoIter { + fn as_ref(&self) -> &[T] { + self.as_slice() } } @@ -2638,7 +2746,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 } @@ -2674,19 +2782,25 @@ impl<'a, T> Drain<'a, T> { /// # Examples /// /// ``` - /// # #![feature(vec_drain_as_slice)] /// let mut vec = vec!['a', 'b', 'c']; /// let mut drain = vec.drain(..); /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); /// let _ = drain.next().unwrap(); /// assert_eq!(drain.as_slice(), &['b', 'c']); /// ``` - #[unstable(feature = "vec_drain_as_slice", reason = "recently added", issue = "58957")] + #[stable(feature = "vec_drain_as_slice", since = "1.46.0")] pub fn as_slice(&self) -> &[T] { self.iter.as_slice() } } +#[stable(feature = "vec_drain_as_slice", since = "1.46.0")] +impl<'a, T> AsRef<[T]> for Drain<'a, T> { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + #[stable(feature = "drain", since = "1.6.0")] unsafe impl Sync for Drain<'_, T> {} #[stable(feature = "drain", since = "1.6.0")] @@ -2854,15 +2968,16 @@ impl Drain<'_, T> { /// Fill that range as much as possible with new elements from the `replace_with` iterator. /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { - let vec = self.vec.as_mut(); + let vec = unsafe { self.vec.as_mut() }; let range_start = vec.len; let range_end = self.tail_start; - let range_slice = - slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start); + let range_slice = unsafe { + slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start) + }; for place in range_slice { if let Some(new_item) = replace_with.next() { - ptr::write(place, new_item); + unsafe { ptr::write(place, new_item) }; vec.len += 1; } else { return false; @@ -2872,15 +2987,17 @@ impl Drain<'_, T> { } /// Makes room for inserting more elements before the tail. - unsafe fn move_tail(&mut self, extra_capacity: usize) { - let vec = self.vec.as_mut(); - let used_capacity = self.tail_start + self.tail_len; - vec.buf.reserve(used_capacity, extra_capacity); - - let new_tail_start = self.tail_start + extra_capacity; - let src = vec.as_ptr().add(self.tail_start); - let dst = vec.as_mut_ptr().add(new_tail_start); - ptr::copy(src, dst, self.tail_len); + unsafe fn move_tail(&mut self, additional: usize) { + let vec = unsafe { self.vec.as_mut() }; + let len = self.tail_start + self.tail_len; + vec.buf.reserve(len, additional); + + let new_tail_start = self.tail_start + additional; + unsafe { + let src = vec.as_ptr().add(self.tail_start); + let dst = vec.as_mut_ptr().add(new_tail_start); + ptr::copy(src, dst, self.tail_len); + } self.tail_start = new_tail_start; } } diff --git a/src/libarena/Cargo.toml b/src/libarena/Cargo.toml deleted file mode 100644 index 5158aab8b7dc5..0000000000000 --- a/src/libarena/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "arena" -version = "0.0.0" -edition = "2018" - -[lib] -name = "arena" -path = "lib.rs" - -[dependencies] -rustc_data_structures = { path = "../librustc_data_structures" } -smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs deleted file mode 100644 index 2a3d92edc4956..0000000000000 --- a/src/libarena/lib.rs +++ /dev/null @@ -1,492 +0,0 @@ -//! The arena, a fast but limited type of allocator. -//! -//! Arenas are a type of allocator that destroy the objects within, all at -//! once, once the arena itself is destroyed. They do not support deallocation -//! of individual objects while the arena itself is still alive. The benefit -//! of an arena is very fast allocation; just a pointer bump. -//! -//! This crate implements `TypedArena`, a simple arena that can only hold -//! objects of a single type. - -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - test(no_crate_inject, attr(deny(warnings))) -)] -#![feature(core_intrinsics)] -#![feature(dropck_eyepatch)] -#![feature(raw_vec_internals)] -#![cfg_attr(test, feature(test))] -#![allow(deprecated)] - -extern crate alloc; - -use rustc_data_structures::cold_path; -use smallvec::SmallVec; - -use std::cell::{Cell, RefCell}; -use std::cmp; -use std::intrinsics; -use std::marker::{PhantomData, Send}; -use std::mem; -use std::ptr; -use std::slice; - -use alloc::raw_vec::RawVec; - -/// An arena that can hold objects of only one type. -pub struct TypedArena { - /// A pointer to the next object to be allocated. - ptr: Cell<*mut T>, - - /// A pointer to the end of the allocated area. When this pointer is - /// reached, a new chunk is allocated. - end: Cell<*mut T>, - - /// A vector of arena chunks. - chunks: RefCell>>, - - /// Marker indicating that dropping the arena causes its owned - /// instances of `T` to be dropped. - _own: PhantomData, -} - -struct TypedArenaChunk { - /// The raw storage for the arena chunk. - storage: RawVec, - /// The number of valid entries in the chunk. - entries: usize, -} - -impl TypedArenaChunk { - #[inline] - unsafe fn new(capacity: usize) -> TypedArenaChunk { - TypedArenaChunk { storage: RawVec::with_capacity(capacity), entries: 0 } - } - - /// Destroys this arena chunk. - #[inline] - unsafe fn destroy(&mut self, len: usize) { - // The branch on needs_drop() is an -O1 performance optimization. - // Without the branch, dropping TypedArena takes linear time. - if mem::needs_drop::() { - let mut start = self.start(); - // Destroy all allocated objects. - for _ in 0..len { - ptr::drop_in_place(start); - start = start.offset(1); - } - } - } - - // Returns a pointer to the first allocated object. - #[inline] - fn start(&self) -> *mut T { - self.storage.ptr() - } - - // Returns a pointer to the end of the allocated space. - #[inline] - fn end(&self) -> *mut T { - unsafe { - if mem::size_of::() == 0 { - // A pointer as large as possible for zero-sized elements. - !0 as *mut T - } else { - self.start().add(self.storage.capacity()) - } - } - } -} - -const PAGE: usize = 4096; - -impl Default for TypedArena { - /// Creates a new `TypedArena`. - fn default() -> TypedArena { - TypedArena { - // We set both `ptr` and `end` to 0 so that the first call to - // alloc() will trigger a grow(). - ptr: Cell::new(ptr::null_mut()), - end: Cell::new(ptr::null_mut()), - chunks: RefCell::new(vec![]), - _own: PhantomData, - } - } -} - -impl TypedArena { - /// Allocates an object in the `TypedArena`, returning a reference to it. - #[inline] - pub fn alloc(&self, object: T) -> &mut T { - if self.ptr == self.end { - self.grow(1) - } - - unsafe { - if mem::size_of::() == 0 { - self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T); - let ptr = mem::align_of::() as *mut T; - // Don't drop the object. This `write` is equivalent to `forget`. - ptr::write(ptr, object); - &mut *ptr - } else { - let ptr = self.ptr.get(); - // Advance the pointer. - self.ptr.set(self.ptr.get().offset(1)); - // Write into uninitialized memory. - ptr::write(ptr, object); - &mut *ptr - } - } - } - - #[inline] - fn can_allocate(&self, len: usize) -> bool { - let available_capacity_bytes = self.end.get() as usize - self.ptr.get() as usize; - let at_least_bytes = len.checked_mul(mem::size_of::()).unwrap(); - available_capacity_bytes >= at_least_bytes - } - - /// Ensures there's enough space in the current chunk to fit `len` objects. - #[inline] - fn ensure_capacity(&self, len: usize) { - if !self.can_allocate(len) { - self.grow(len); - debug_assert!(self.can_allocate(len)); - } - } - - #[inline] - unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T { - assert!(mem::size_of::() != 0); - assert!(len != 0); - - self.ensure_capacity(len); - - let start_ptr = self.ptr.get(); - self.ptr.set(start_ptr.add(len)); - start_ptr - } - - /// Allocates a slice of objects that are copied into the `TypedArena`, returning a mutable - /// reference to it. Will panic if passed a zero-sized types. - /// - /// Panics: - /// - /// - Zero-sized types - /// - Zero-length slices - #[inline] - pub fn alloc_slice(&self, slice: &[T]) -> &mut [T] - where - T: Copy, - { - unsafe { - let len = slice.len(); - let start_ptr = self.alloc_raw_slice(len); - slice.as_ptr().copy_to_nonoverlapping(start_ptr, len); - slice::from_raw_parts_mut(start_ptr, len) - } - } - - #[inline] - pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { - assert!(mem::size_of::() != 0); - let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); - if vec.is_empty() { - return &mut []; - } - // Move the content to the arena by copying it and then forgetting - // the content of the SmallVec - unsafe { - let len = vec.len(); - let start_ptr = self.alloc_raw_slice(len); - vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); - vec.set_len(0); - slice::from_raw_parts_mut(start_ptr, len) - } - } - - /// Grows the arena. - #[inline(never)] - #[cold] - fn grow(&self, n: usize) { - unsafe { - let mut chunks = self.chunks.borrow_mut(); - let (chunk, mut new_capacity); - if let Some(last_chunk) = chunks.last_mut() { - let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize; - let currently_used_cap = used_bytes / mem::size_of::(); - last_chunk.entries = currently_used_cap; - if last_chunk.storage.reserve_in_place(currently_used_cap, n) { - self.end.set(last_chunk.end()); - return; - } else { - new_capacity = last_chunk.storage.capacity(); - loop { - 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); - } - chunk = TypedArenaChunk::::new(new_capacity); - self.ptr.set(chunk.start()); - self.end.set(chunk.end()); - chunks.push(chunk); - } - } - - /// Clears the arena. Deallocates all but the longest chunk which may be reused. - pub fn clear(&mut self) { - unsafe { - // Clear the last chunk, which is partially filled. - let mut chunks_borrow = self.chunks.borrow_mut(); - if let Some(mut last_chunk) = chunks_borrow.last_mut() { - self.clear_last_chunk(&mut last_chunk); - let len = chunks_borrow.len(); - // If `T` is ZST, code below has no effect. - for mut chunk in chunks_borrow.drain(..len - 1) { - chunk.destroy(chunk.entries); - } - } - } - } - - // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other - // chunks. - fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk) { - // Determine how much was filled. - let start = last_chunk.start() as usize; - // We obtain the value of the pointer to the first uninitialized element. - let end = self.ptr.get() as usize; - // We then calculate the number of elements to be dropped in the last chunk, - // which is the filled area's length. - let diff = if mem::size_of::() == 0 { - // `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get - // the number of zero-sized values in the last and only chunk, just out of caution. - // Recall that `end` was incremented for each allocated value. - end - start - } else { - (end - start) / mem::size_of::() - }; - // Pass that to the `destroy` method. - unsafe { - last_chunk.destroy(diff); - } - // Reset the chunk. - self.ptr.set(last_chunk.start()); - } -} - -unsafe impl<#[may_dangle] T> Drop for TypedArena { - fn drop(&mut self) { - unsafe { - // Determine how much was filled. - let mut chunks_borrow = self.chunks.borrow_mut(); - if let Some(mut last_chunk) = chunks_borrow.pop() { - // Drop the contents of the last chunk. - self.clear_last_chunk(&mut last_chunk); - // The last chunk will be dropped. Destroy all other chunks. - for chunk in chunks_borrow.iter_mut() { - chunk.destroy(chunk.entries); - } - } - // RawVec handles deallocation of `last_chunk` and `self.chunks`. - } - } -} - -unsafe impl Send for TypedArena {} - -pub struct DroplessArena { - /// A pointer to the next object to be allocated. - ptr: Cell<*mut u8>, - - /// A pointer to the end of the allocated area. When this pointer is - /// reached, a new chunk is allocated. - end: Cell<*mut u8>, - - /// A vector of arena chunks. - chunks: RefCell>>, -} - -unsafe impl Send for DroplessArena {} - -impl Default for DroplessArena { - #[inline] - fn default() -> DroplessArena { - DroplessArena { - ptr: Cell::new(ptr::null_mut()), - end: Cell::new(ptr::null_mut()), - chunks: Default::default(), - } - } -} - -impl DroplessArena { - #[inline] - fn align(&self, align: usize) { - let final_address = ((self.ptr.get() as usize) + align - 1) & !(align - 1); - self.ptr.set(final_address as *mut u8); - assert!(self.ptr <= self.end); - } - - #[inline(never)] - #[cold] - fn grow(&self, needed_bytes: usize) { - unsafe { - let mut chunks = self.chunks.borrow_mut(); - let (chunk, mut new_capacity); - if let Some(last_chunk) = chunks.last_mut() { - let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize; - if last_chunk.storage.reserve_in_place(used_bytes, needed_bytes) { - self.end.set(last_chunk.end()); - return; - } else { - new_capacity = last_chunk.storage.capacity(); - loop { - new_capacity = new_capacity.checked_mul(2).unwrap(); - if new_capacity >= used_bytes + needed_bytes { - break; - } - } - } - } else { - new_capacity = cmp::max(needed_bytes, PAGE); - } - chunk = TypedArenaChunk::::new(new_capacity); - self.ptr.set(chunk.start()); - self.end.set(chunk.end()); - chunks.push(chunk); - } - } - - #[inline] - pub fn alloc_raw(&self, bytes: usize, align: usize) -> &mut [u8] { - unsafe { - assert!(bytes != 0); - - self.align(align); - - let future_end = intrinsics::arith_offset(self.ptr.get(), bytes as isize); - if (future_end as *mut u8) >= self.end.get() { - self.grow(bytes); - } - - let ptr = self.ptr.get(); - // Set the pointer past ourselves - self.ptr.set(intrinsics::arith_offset(self.ptr.get(), bytes as isize) as *mut u8); - slice::from_raw_parts_mut(ptr, bytes) - } - } - - #[inline] - pub fn alloc(&self, object: T) -> &mut T { - assert!(!mem::needs_drop::()); - - let mem = self.alloc_raw(mem::size_of::(), mem::align_of::()) as *mut _ as *mut T; - - unsafe { - // Write into uninitialized memory. - ptr::write(mem, object); - &mut *mem - } - } - - /// Allocates a slice of objects that are copied into the `DroplessArena`, returning a mutable - /// reference to it. Will panic if passed a zero-sized type. - /// - /// Panics: - /// - /// - Zero-sized types - /// - Zero-length slices - #[inline] - pub fn alloc_slice(&self, slice: &[T]) -> &mut [T] - where - T: Copy, - { - assert!(!mem::needs_drop::()); - assert!(mem::size_of::() != 0); - assert!(!slice.is_empty()); - - let mem = self.alloc_raw(slice.len() * mem::size_of::(), mem::align_of::()) as *mut _ - as *mut T; - - unsafe { - let arena_slice = slice::from_raw_parts_mut(mem, slice.len()); - arena_slice.copy_from_slice(slice); - arena_slice - } - } - - #[inline] - unsafe fn write_from_iter>( - &self, - mut iter: I, - len: usize, - mem: *mut T, - ) -> &mut [T] { - let mut i = 0; - // Use a manual loop since LLVM manages to optimize it better for - // slice iterators - loop { - let value = iter.next(); - if i >= len || value.is_none() { - // We only return as many items as the iterator gave us, even - // though it was supposed to give us `len` - return slice::from_raw_parts_mut(mem, i); - } - ptr::write(mem.add(i), value.unwrap()); - i += 1; - } - } - - #[inline] - pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { - let iter = iter.into_iter(); - assert!(mem::size_of::() != 0); - assert!(!mem::needs_drop::()); - - let size_hint = iter.size_hint(); - - match size_hint { - (min, Some(max)) if min == max => { - // We know the exact number of elements the iterator will produce here - let len = min; - - if len == 0 { - return &mut []; - } - let size = len.checked_mul(mem::size_of::()).unwrap(); - let mem = self.alloc_raw(size, mem::align_of::()) as *mut _ as *mut T; - unsafe { self.write_from_iter(iter, len, mem) } - } - (_, _) => { - cold_path(move || -> &mut [T] { - let mut vec: SmallVec<[_; 8]> = iter.collect(); - if vec.is_empty() { - return &mut []; - } - // Move the content to the arena by copying it and then forgetting - // the content of the SmallVec - unsafe { - let len = vec.len(); - let start_ptr = self - .alloc_raw(len * mem::size_of::(), mem::align_of::()) - as *mut _ as *mut T; - vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); - vec.set_len(0); - slice::from_raw_parts_mut(start_ptr, len) - } - }) - } - } - } -} - -#[cfg(test)] -mod tests; diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml index ac07ffb14febd..42c555cafac86 100644 --- a/src/libcore/Cargo.toml +++ b/src/libcore/Cargo.toml @@ -19,6 +19,7 @@ path = "../libcore/tests/lib.rs" [[bench]] name = "corebenches" path = "../libcore/benches/lib.rs" +test = true [dev-dependencies] rand = "0.7" diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs deleted file mode 100644 index d2a513451ccb6..0000000000000 --- a/src/libcore/alloc.rs +++ /dev/null @@ -1,1007 +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) } - } - - /// 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 97ef513cbcc63..79b6304958d51 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -164,7 +164,7 @@ impl dyn Any { // Get `TypeId` of the type this function is instantiated with. let t = TypeId::of::(); - // Get `TypeId` of the type in the trait object. + // Get `TypeId` of the type in the trait object (`self`). let concrete = self.type_id(); // Compare both `TypeId`s on equality. @@ -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/lib.rs b/src/libcore/benches/lib.rs index 570fc4ab93390..de4ef7949f344 100644 --- a/src/libcore/benches/lib.rs +++ b/src/libcore/benches/lib.rs @@ -1,3 +1,5 @@ +// wasm32 does not support benches (no time). +#![cfg(not(target_arch = "wasm32"))] #![feature(flt2dec)] #![feature(test)] 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 dbf073cd4792e..c4293ed7bcfe2 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() )); //! } //! } //! @@ -779,18 +778,13 @@ impl RefCell { /// /// An example of panic: /// - /// ``` + /// ```should_panic /// use std::cell::RefCell; - /// use std::thread; - /// - /// let result = thread::spawn(move || { - /// let c = RefCell::new(5); - /// let m = c.borrow_mut(); /// - /// let b = c.borrow(); // this causes a panic - /// }).join(); + /// let c = RefCell::new(5); /// - /// assert!(result.is_err()); + /// let m = c.borrow_mut(); + /// let b = c.borrow(); // this causes a panic /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -850,27 +844,22 @@ impl RefCell { /// ``` /// use std::cell::RefCell; /// - /// let c = RefCell::new(5); + /// let c = RefCell::new("hello".to_owned()); /// - /// *c.borrow_mut() = 7; + /// *c.borrow_mut() = "bonjour".to_owned(); /// - /// assert_eq!(*c.borrow(), 7); + /// assert_eq!(&*c.borrow(), "bonjour"); /// ``` /// /// An example of panic: /// - /// ``` + /// ```should_panic /// use std::cell::RefCell; - /// use std::thread; - /// - /// let result = thread::spawn(move || { - /// let c = RefCell::new(5); - /// let m = c.borrow(); /// - /// let b = c.borrow_mut(); // this causes a panic - /// }).join(); + /// let c = RefCell::new(5); + /// let m = c.borrow(); /// - /// assert!(result.is_err()); + /// let b = c.borrow_mut(); // this causes a panic /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -1023,6 +1012,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 {} @@ -1139,8 +1153,8 @@ impl<'b> BorrowRef<'b> { // Incrementing borrow can result in a non-reading value (<= 0) in these cases: // 1. It was < 0, i.e. there are writing borrows, so we can't allow a read borrow // due to Rust's reference aliasing rules - // 2. It was isize::max_value() (the max amount of reading borrows) and it overflowed - // into isize::min_value() (the max amount of writing borrows) so we can't allow + // 2. It was isize::MAX (the max amount of reading borrows) and it overflowed + // into isize::MIN (the max amount of writing borrows) so we can't allow // an additional read borrow because isize can't represent so many read borrows // (this can only happen if you mem::forget more than a small constant amount of // `Ref`s, which is not good practice) @@ -1148,7 +1162,7 @@ impl<'b> BorrowRef<'b> { } else { // Incrementing borrow can result in a reading value (> 0) in these cases: // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read borrow - // 2. It was > 0 and < isize::max_value(), i.e. there were read borrows, and isize + // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize // is large enough to represent having one more read borrow borrow.set(b); Some(BorrowRef { borrow }) @@ -1174,7 +1188,7 @@ impl Clone for BorrowRef<'_> { debug_assert!(is_reading(borrow)); // Prevent the borrow counter from overflowing into // a writing borrow. - assert!(borrow != isize::max_value()); + assert!(borrow != isize::MAX); self.borrow.set(borrow + 1); BorrowRef { borrow: self.borrow } } @@ -1465,7 +1479,7 @@ impl<'b> BorrowRefMut<'b> { let borrow = self.borrow.get(); debug_assert!(is_writing(borrow)); // Prevent the borrow counter from underflowing. - assert!(borrow != isize::min_value()); + assert!(borrow != isize::MIN); self.borrow.set(borrow - 1); BorrowRefMut { borrow: self.borrow } } @@ -1569,7 +1583,7 @@ impl fmt::Display for RefMut<'_, T> { #[lang = "unsafe_cell"] #[stable(feature = "rust1", since = "1.0.0")] #[repr(transparent)] -#[cfg_attr(not(bootstrap), repr(no_niche))] // rust-lang/rust#68303. +#[repr(no_niche)] // rust-lang/rust#68303. pub struct UnsafeCell { value: T, } diff --git a/src/libcore/char/convert.rs b/src/libcore/char/convert.rs index 315020bac5850..d7e39946148ed 100644 --- a/src/libcore/char/convert.rs +++ b/src/libcore/char/convert.rs @@ -99,7 +99,7 @@ pub fn from_u32(i: u32) -> Option { #[inline] #[stable(feature = "char_from_unchecked", since = "1.5.0")] pub unsafe fn from_u32_unchecked(i: u32) -> char { - transmute(i) + if cfg!(debug_assertions) { char::from_u32(i).unwrap() } else { transmute(i) } } #[stable(feature = "char_convert", since = "1.13.0")] @@ -218,7 +218,7 @@ impl TryFrom for char { Err(CharTryFromError(())) } else { // SAFETY: checked that it's a legal unicode value - Ok(unsafe { from_u32_unchecked(i) }) + Ok(unsafe { transmute(i) }) } } } @@ -278,16 +278,11 @@ impl fmt::Display for CharTryFromError { /// /// Passing a large radix, causing a panic: /// -/// ``` -/// use std::thread; +/// ```should_panic /// use std::char; /// -/// let result = thread::spawn(|| { -/// // this panics -/// let c = char::from_digit(1, 37); -/// }).join(); -/// -/// assert!(result.is_err()); +/// // this panics +/// let c = char::from_digit(1, 37); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/char/methods.rs b/src/libcore/char/methods.rs index 302400744e25d..dd2f01c679f73 100644 --- a/src/libcore/char/methods.rs +++ b/src/libcore/char/methods.rs @@ -9,6 +9,238 @@ 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: + /// + /// ```should_panic + /// use std::char; + /// + /// // this panics + /// char::from_digit(1, 37); + /// ``` + #[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 @@ -45,15 +277,9 @@ impl char { /// /// Passing a large radix, causing a panic: /// - /// ``` - /// use std::thread; - /// - /// let result = thread::spawn(|| { - /// // this panics - /// '1'.is_digit(37); - /// }).join(); - /// - /// assert!(result.is_err()); + /// ```should_panic + /// // this panics + /// '1'.is_digit(37); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -100,14 +326,9 @@ impl char { /// /// Passing a large radix, causing a panic: /// - /// ``` - /// use std::thread; - /// - /// let result = thread::spawn(|| { - /// '1'.to_digit(37); - /// }).join(); - /// - /// assert!(result.is_err()); + /// ```should_panic + /// // this panics + /// '1'.to_digit(37); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -356,16 +577,7 @@ impl char { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn len_utf8(self) -> usize { - let code = self as u32; - if code < MAX_ONE_B { - 1 - } else if code < MAX_TWO_B { - 2 - } else if code < MAX_THREE_B { - 3 - } else { - 4 - } + len_utf8(self as u32) } /// Returns the number of 16-bit code units this `char` would need if @@ -418,51 +630,17 @@ impl char { /// /// A buffer that's too small: /// - /// ``` - /// use std::thread; - /// - /// let result = thread::spawn(|| { - /// let mut b = [0; 1]; + /// ```should_panic + /// let mut b = [0; 1]; /// - /// // this panics - /// 'ß'.encode_utf8(&mut b); - /// }).join(); - /// - /// assert!(result.is_err()); + /// // this panics + /// 'ß'.encode_utf8(&mut b); /// ``` #[stable(feature = "unicode_encode_char", since = "1.15.0")] #[inline] pub fn encode_utf8(self, dst: &mut [u8]) -> &mut str { - let code = self as u32; - let len = self.len_utf8(); - match (len, &mut dst[..]) { - (1, [a, ..]) => { - *a = code as u8; - } - (2, [a, b, ..]) => { - *a = (code >> 6 & 0x1F) as u8 | TAG_TWO_B; - *b = (code & 0x3F) as u8 | TAG_CONT; - } - (3, [a, b, c, ..]) => { - *a = (code >> 12 & 0x0F) as u8 | TAG_THREE_B; - *b = (code >> 6 & 0x3F) as u8 | TAG_CONT; - *c = (code & 0x3F) as u8 | TAG_CONT; - } - (4, [a, b, c, d, ..]) => { - *a = (code >> 18 & 0x07) as u8 | TAG_FOUR_B; - *b = (code >> 12 & 0x3F) as u8 | TAG_CONT; - *c = (code >> 6 & 0x3F) as u8 | TAG_CONT; - *d = (code & 0x3F) as u8 | TAG_CONT; - } - _ => panic!( - "encode_utf8: need {} bytes to encode U+{:X}, but the buffer has {}", - len, - code, - dst.len(), - ), - }; - // SAFETY: We just wrote UTF-8 content in, so converting to str is fine. - unsafe { from_utf8_unchecked_mut(&mut dst[..len]) } + // SAFETY: `char` is not a surrogate, so this is valid UTF-8. + unsafe { from_utf8_unchecked_mut(encode_utf8_raw(self as u32, dst)) } } /// Encodes this character as UTF-16 into the provided `u16` buffer, @@ -487,43 +665,16 @@ impl char { /// /// A buffer that's too small: /// - /// ``` - /// use std::thread; - /// - /// let result = thread::spawn(|| { - /// let mut b = [0; 1]; - /// - /// // this panics - /// '𝕊'.encode_utf16(&mut b); - /// }).join(); + /// ```should_panic + /// let mut b = [0; 1]; /// - /// assert!(result.is_err()); + /// // this panics + /// '𝕊'.encode_utf16(&mut b); /// ``` #[stable(feature = "unicode_encode_char", since = "1.15.0")] #[inline] pub fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] { - let mut code = self as u32; - // SAFETY: each arm checks whether there are enough bits to write into - unsafe { - if (code & 0xFFFF) == code && !dst.is_empty() { - // The BMP falls through (assuming non-surrogate, as it should) - *dst.get_unchecked_mut(0) = code as u16; - slice::from_raw_parts_mut(dst.as_mut_ptr(), 1) - } else if dst.len() >= 2 { - // Supplementary planes break into surrogates. - code -= 0x1_0000; - *dst.get_unchecked_mut(0) = 0xD800 | ((code >> 10) as u16); - *dst.get_unchecked_mut(1) = 0xDC00 | ((code as u16) & 0x3FF); - slice::from_raw_parts_mut(dst.as_mut_ptr(), 2) - } else { - panic!( - "encode_utf16: need {} units to encode U+{:X}, but the buffer has {}", - from_u32_unchecked(code).len_utf16(), - code, - dst.len(), - ) - } - } + encode_utf16_raw(self as u32, dst) } /// Returns `true` if this `char` has the `Alphabetic` property. @@ -575,8 +726,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 +758,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] @@ -1434,3 +1587,100 @@ impl char { } } } + +#[inline] +fn len_utf8(code: u32) -> usize { + if code < MAX_ONE_B { + 1 + } else if code < MAX_TWO_B { + 2 + } else if code < MAX_THREE_B { + 3 + } else { + 4 + } +} + +/// Encodes a raw u32 value as UTF-8 into the provided byte buffer, +/// and then returns the subslice of the buffer that contains the encoded character. +/// +/// Unlike `char::encode_utf8`, this method also handles codepoints in the surrogate range. +/// (Creating a `char` in the surrogate range is UB.) +/// The result is valid [generalized UTF-8] but not valid UTF-8. +/// +/// [generalized UTF-8]: https://simonsapin.github.io/wtf-8/#generalized-utf8 +/// +/// # Panics +/// +/// Panics if the buffer is not large enough. +/// A buffer of length four is large enough to encode any `char`. +#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +#[doc(hidden)] +#[inline] +pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { + let len = len_utf8(code); + match (len, &mut dst[..]) { + (1, [a, ..]) => { + *a = code as u8; + } + (2, [a, b, ..]) => { + *a = (code >> 6 & 0x1F) as u8 | TAG_TWO_B; + *b = (code & 0x3F) as u8 | TAG_CONT; + } + (3, [a, b, c, ..]) => { + *a = (code >> 12 & 0x0F) as u8 | TAG_THREE_B; + *b = (code >> 6 & 0x3F) as u8 | TAG_CONT; + *c = (code & 0x3F) as u8 | TAG_CONT; + } + (4, [a, b, c, d, ..]) => { + *a = (code >> 18 & 0x07) as u8 | TAG_FOUR_B; + *b = (code >> 12 & 0x3F) as u8 | TAG_CONT; + *c = (code >> 6 & 0x3F) as u8 | TAG_CONT; + *d = (code & 0x3F) as u8 | TAG_CONT; + } + _ => panic!( + "encode_utf8: need {} bytes to encode U+{:X}, but the buffer has {}", + len, + code, + dst.len(), + ), + }; + &mut dst[..len] +} + +/// Encodes a raw u32 value as UTF-16 into the provided `u16` buffer, +/// and then returns the subslice of the buffer that contains the encoded character. +/// +/// Unlike `char::encode_utf16`, this method also handles codepoints in the surrogate range. +/// (Creating a `char` in the surrogate range is UB.) +/// +/// # Panics +/// +/// Panics if the buffer is not large enough. +/// A buffer of length 2 is large enough to encode any `char`. +#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +#[doc(hidden)] +#[inline] +pub fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { + // SAFETY: each arm checks whether there are enough bits to write into + unsafe { + if (code & 0xFFFF) == code && !dst.is_empty() { + // The BMP falls through + *dst.get_unchecked_mut(0) = code as u16; + slice::from_raw_parts_mut(dst.as_mut_ptr(), 1) + } else if dst.len() >= 2 { + // Supplementary planes break into surrogates. + code -= 0x1_0000; + *dst.get_unchecked_mut(0) = 0xD800 | ((code >> 10) as u16); + *dst.get_unchecked_mut(1) = 0xDC00 | ((code as u16) & 0x3FF); + slice::from_raw_parts_mut(dst.as_mut_ptr(), 2) + } else { + panic!( + "encode_utf16: need {} units to encode U+{:X}, but the buffer has {}", + from_u32_unchecked(code).len_utf16(), + code, + dst.len(), + ) + } + } +} diff --git a/src/libcore/char/mod.rs b/src/libcore/char/mod.rs index cf5576e549cdf..1b4e906e4e475 100644 --- a/src/libcore/char/mod.rs +++ b/src/libcore/char/mod.rs @@ -34,13 +34,15 @@ 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; +// perma-unstable re-exports +#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::methods::encode_utf16_raw; +#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::methods::encode_utf8_raw; + use crate::fmt::{self, Write}; use crate::iter::FusedIterator; @@ -96,7 +98,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 +106,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..8ff1ced53b071 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; @@ -373,6 +374,7 @@ pub trait Into: Sized { /// [`Into`]: trait.Into.html /// [`from`]: trait.From.html#tymethod.from /// [book]: ../../book/ch09-00-error-handling.html +#[rustc_diagnostic_item = "from_trait"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented(on( all(_Self = "&str", T = "std::string::String"), @@ -746,3 +748,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..46ba0a279b7ff 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) } } )+ @@ -217,7 +217,7 @@ macro_rules! try_from_upper_bounded { /// is outside of the range of the target type. #[inline] fn try_from(u: $source) -> Result { - if u > (Self::max_value() as $source) { + if u > (Self::MAX as $source) { Err(TryFromIntError(())) } else { Ok(u as Self) @@ -239,8 +239,8 @@ macro_rules! try_from_both_bounded { /// is outside of the range of the target type. #[inline] fn try_from(u: $source) -> Result { - let min = Self::min_value() as $source; - let max = Self::max_value() as $source; + let min = Self::MIN as $source; + let max = Self::MAX as $source; if u < min || u > max { Err(TryFromIntError(())) } else { @@ -445,3 +445,42 @@ nzint_impl_from! { NonZeroU16, NonZeroI128, #[stable(feature = "nz_int_conv", si nzint_impl_from! { NonZeroU32, NonZeroI64, #[stable(feature = "nz_int_conv", since = "1.41.0")] } nzint_impl_from! { NonZeroU32, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] } nzint_impl_from! { NonZeroU64, NonZeroI128, #[stable(feature = "nz_int_conv", since = "1.41.0")] } + +macro_rules! nzint_impl_try_from_int { + ($Int: ty, $NonZeroInt: ty, #[$attr:meta], $doc: expr) => { + #[$attr] + #[doc = $doc] + impl TryFrom<$Int> for $NonZeroInt { + type Error = TryFromIntError; + + #[inline] + fn try_from(value: $Int) -> Result { + Self::new(value).ok_or(TryFromIntError(())) + } + } + }; + ($Int: ty, $NonZeroInt: ty, #[$attr:meta]) => { + nzint_impl_try_from_int!($Int, + $NonZeroInt, + #[$attr], + concat!("Attempts to convert `", + stringify!($Int), + "` to `", + stringify!($NonZeroInt), + "`.")); + } +} + +// Int -> Non-zero Int +nzint_impl_try_from_int! { u8, NonZeroU8, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { u16, NonZeroU16, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { u32, NonZeroU32, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { u64, NonZeroU64, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { u128, NonZeroU128, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { usize, NonZeroUsize, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { i8, NonZeroI8, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { i16, NonZeroI16, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { i32, NonZeroI32, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { i64, NonZeroI64, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { i128, NonZeroI128, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } +nzint_impl_try_from_int! { isize, NonZeroIsize, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } diff --git a/src/libcore/default.rs b/src/libcore/default.rs index 15ac3aea8b7ba..9a8d65cd4e06b 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: /// /// ``` @@ -115,6 +115,50 @@ pub trait Default: Sized { fn default() -> Self; } +/// Return the default value of a type according to the `Default` trait. +/// +/// The type to return is inferred from context; this is equivalent to +/// `Default::default()` but shorter to type. +/// +/// For example: +/// ``` +/// #![feature(default_free_fn)] +/// +/// use std::default::default; +/// +/// #[derive(Default)] +/// struct AppConfig { +/// foo: FooConfig, +/// bar: BarConfig, +/// } +/// +/// #[derive(Default)] +/// struct FooConfig { +/// foo: i32, +/// } +/// +/// #[derive(Default)] +/// struct BarConfig { +/// bar: f32, +/// baz: u8, +/// } +/// +/// fn main() { +/// let options = AppConfig { +/// foo: default(), +/// bar: BarConfig { +/// bar: 10.1, +/// ..default() +/// }, +/// }; +/// } +/// ``` +#[unstable(feature = "default_free_fn", issue = "73014")] +#[inline] +pub fn default() -> T { + Default::default() +} + /// Derive macro generating an impl of the trait `Default`. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] 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 a93b34fc46cb9..9c5dbb5e6f356 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -282,10 +282,10 @@ impl<'a> ArgumentV1<'a> { // SAFETY: `mem::transmute(x)` is safe because // 1. `&'b T` keeps the lifetime it originated with `'b` // (so as to not have an unbounded lifetime) - // 2. `&'b T` and `&'b Void` have the same memory layout + // 2. `&'b T` and `&'b Opaque` have the same memory layout // (when `T` is `Sized`, as it is here) // `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result` - // and `fn(&Void, &mut Formatter<'_>) -> Result` have the same ABI + // and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI // (as long as `T` is `Sized`) unsafe { ArgumentV1 { formatter: mem::transmute(f), value: mem::transmute(x) } } } @@ -441,6 +441,13 @@ impl Display for Arguments<'_> { /// `enum`s, it will use the name of the variant and, if applicable, `(`, then the /// `Debug` values of the fields, then `)`. /// +/// # Stability +/// +/// Derived `Debug` formats are not stable, and so may change with future Rust +/// versions. Additionally, `Debug` implementations of types provided by the +/// standard library (`libstd`, `libcore`, `liballoc`, etc.) are not stable, and +/// may also change with future Rust versions. +/// /// # Examples /// /// Deriving an implementation: @@ -852,7 +859,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"); /// @@ -1611,7 +1618,8 @@ impl<'a> Formatter<'a> { self.width } - /// Optionally specified precision for numeric types. + /// Optionally specified precision for numeric types. Alternatively, the + /// maximum width for string types. /// /// # Examples /// diff --git a/src/libcore/future/into_future.rs b/src/libcore/future/into_future.rs new file mode 100644 index 0000000000000..4020c254446e3 --- /dev/null +++ b/src/libcore/future/into_future.rs @@ -0,0 +1,27 @@ +use crate::future::Future; + +/// Conversion into a `Future`. +#[unstable(feature = "into_future", issue = "67644")] +pub trait IntoFuture { + /// The output that the future will produce on completion. + #[unstable(feature = "into_future", issue = "67644")] + type Output; + + /// Which kind of future are we turning this into? + #[unstable(feature = "into_future", issue = "67644")] + type Future: Future; + + /// Creates a future from a value. + #[unstable(feature = "into_future", issue = "67644")] + fn into_future(self) -> Self::Future; +} + +#[unstable(feature = "into_future", issue = "67644")] +impl IntoFuture for F { + type Output = F::Output; + type Future = F; + + fn into_future(self) -> Self::Future { + self + } +} diff --git a/src/libcore/future/mod.rs b/src/libcore/future/mod.rs index 89ea4713cfdaa..9dbc23f5c04c5 100644 --- a/src/libcore/future/mod.rs +++ b/src/libcore/future/mod.rs @@ -2,6 +2,88 @@ //! Asynchronous values. +use crate::{ + ops::{Generator, GeneratorState}, + pin::Pin, + ptr::NonNull, + task::{Context, Poll}, +}; + mod future; +mod into_future; +mod pending; +mod ready; + #[stable(feature = "futures_api", since = "1.36.0")] pub use self::future::Future; + +#[unstable(feature = "into_future", issue = "67644")] +pub use into_future::IntoFuture; + +#[unstable(feature = "future_readiness_fns", issue = "70921")] +pub use pending::{pending, Pending}; +#[unstable(feature = "future_readiness_fns", issue = "70921")] +pub use ready::{ready, Ready}; + +/// This type is needed because: +/// +/// a) Generators cannot implement `for<'a, 'b> Generator<&'a mut Context<'b>>`, so we need to pass +/// a raw pointer (see https://github.com/rust-lang/rust/issues/68923). +/// b) Raw pointers and `NonNull` aren't `Send` or `Sync`, so that would make every single future +/// non-Send/Sync as well, and we don't want that. +/// +/// It also simplifies the HIR lowering of `.await`. +#[doc(hidden)] +#[unstable(feature = "gen_future", issue = "50547")] +#[derive(Debug, Copy, Clone)] +pub struct ResumeTy(NonNull>); + +#[unstable(feature = "gen_future", issue = "50547")] +unsafe impl Send for ResumeTy {} + +#[unstable(feature = "gen_future", issue = "50547")] +unsafe impl Sync for ResumeTy {} + +/// Wrap a generator in a future. +/// +/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give +/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). +// This is `const` to avoid extra errors after we recover from `const async fn` +#[doc(hidden)] +#[unstable(feature = "gen_future", issue = "50547")] +#[inline] +pub const fn from_generator(gen: T) -> impl Future +where + T: Generator, +{ + #[rustc_diagnostic_item = "gen_future"] + struct GenFuture>(T); + + // We rely on the fact that async/await futures are immovable in order to create + // self-referential borrows in the underlying generator. + impl> !Unpin for GenFuture {} + + impl> Future for GenFuture { + type Output = T::Return; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // Safety: Safe because we're !Unpin + !Drop, and this is just a field projection. + let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) }; + + // Resume the generator, turning the `&mut Context` into a `NonNull` raw pointer. The + // `.await` lowering will safely cast that back to a `&mut Context`. + match gen.resume(ResumeTy(NonNull::from(cx).cast::>())) { + GeneratorState::Yielded(()) => Poll::Pending, + GeneratorState::Complete(x) => Poll::Ready(x), + } + } + } + + GenFuture(gen) +} + +#[doc(hidden)] +#[unstable(feature = "gen_future", issue = "50547")] +#[inline] +pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> { + &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 f4fb9ab1757cd..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)); - return dummy; + llvm_asm!("" : : "r"(&dummy)); + dummy } } diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index d722406b82b4a..2298958b88101 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -54,6 +54,7 @@ )] #![allow(missing_docs)] +use crate::marker::DiscriminantKind; use crate::mem; #[stable(feature = "drop_in_place", since = "1.8.0")] @@ -76,7 +77,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 +87,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 +99,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 +111,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 +121,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 +133,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 +145,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 +157,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 +169,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 +180,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 +190,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 +202,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 +214,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 +224,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 +236,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 +248,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 +260,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 +272,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 +281,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 +289,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 +297,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 +307,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 +315,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 +323,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 +333,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 +341,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 +349,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 +357,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 +365,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 +374,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 +382,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 +390,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 +398,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 +406,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 +415,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 +423,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 +431,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 +439,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 +447,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 +456,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 +464,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 +472,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 +480,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 +488,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 +497,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 +505,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 +513,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 +521,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 +529,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 +538,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 +546,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 +554,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 +562,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 +570,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 +579,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 +587,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 +595,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 +603,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 +611,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 +620,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 +628,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 +636,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 +644,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 +652,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 +661,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 +669,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 +677,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 +685,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 +693,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 +702,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 +710,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 +718,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 +726,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 +734,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 +743,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 +751,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 +759,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 +767,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 +775,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 +783,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 +793,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 +803,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 +813,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 +913,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 +942,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 +991,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,18 +1001,18 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`std::mem::size_of_val`](../../std/mem/fn.size_of_val.html). - pub fn size_of_val(_: &T) -> usize; - /// The minimum alignment of the type of the value that `val` points to. + pub fn size_of_val(_: *const T) -> usize; + /// The required alignment of the referenced value. /// /// The stabilized version of this intrinsic is - /// [`std::mem::min_align_of_val`](../../std/mem/fn.min_align_of_val.html). - pub fn min_align_of_val(_: &T) -> usize; + /// [`std::mem::align_of_val`](../../std/mem/fn.align_of_val.html). + pub fn min_align_of_val(_: *const T) -> usize; /// Gets a static string slice containing the name of a type. /// /// The stabilized version of this intrinsic is /// [`std::any::type_name`](../../std/any/fn.type_name.html) - #[rustc_const_unstable(feature = "const_type_name", issue = "none")] + #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub fn type_name() -> &'static str; /// Gets an identifier which is globally unique to the specified type. This @@ -1000,73 +1021,38 @@ extern "rust-intrinsic" { /// /// The stabilized version of this intrinsic is /// [`std::any::TypeId::of`](../../std/any/struct.TypeId.html#method.of) - #[rustc_const_unstable(feature = "const_type_id", issue = "none")] + #[rustc_const_unstable(feature = "const_type_id", issue = "41875")] pub fn type_id() -> u64; /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: /// This will statically either panic, or do nothing. - #[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>; - /// Creates a value initialized to zero. - /// - /// `init` is unsafe because it returns a zeroed-out datum, - /// which is unsafe unless `T` is `Copy`. Also, even if T is - /// `Copy`, an all-zero value may not correspond to any legitimate - /// state for the type in question. - /// - /// The stabilized version of this intrinsic is - /// [`std::mem::zeroed`](../../std/mem/fn.zeroed.html). - #[unstable( - feature = "core_intrinsics", - reason = "intrinsics are unlikely to ever be stabilized, instead \ - they should be used through stabilized interfaces \ - in the rest of the standard library", - issue = "none" - )] - #[rustc_deprecated(reason = "superseded by MaybeUninit, removal planned", since = "1.38.0")] - pub fn init() -> T; - - /// Creates an uninitialized value. - /// - /// `uninit` is unsafe because there is no guarantee of what its - /// contents are. In particular its drop-flag may be set to any - /// state, which means it may claim either dropped or - /// undropped. In the general case one must use `ptr::write` to - /// initialize memory previous set to the result of `uninit`. - /// - /// The stabilized version of this intrinsic is - /// [`std::mem::MaybeUninit`](../../std/mem/union.MaybeUninit.html). - #[unstable( - feature = "core_intrinsics", - reason = "intrinsics are unlikely to ever be stabilized, instead \ - they should be used through stabilized interfaces \ - in the rest of the standard library", - issue = "none" - )] - #[rustc_deprecated(reason = "superseded by MaybeUninit, removal planned", since = "1.38.0")] - pub fn uninit() -> T; - /// Moves a value out of scope without running drop glue. + /// + /// This exists solely for [`mem::forget_unsized`](../../std/mem/fn.forget_unsized.html); + /// normal `forget` uses `ManuallyDrop` instead. pub fn forget(_: T); /// Reinterprets the bits of a value of one type as another type. @@ -1127,6 +1113,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`: /// /// ``` @@ -1287,7 +1291,7 @@ extern "rust-intrinsic" { /// implements `Copy`. /// /// If the actual type neither requires drop glue nor implements - /// `Copy`, then may return `true` or `false`. + /// `Copy`, then the return value of this function is unspecified. /// /// The stabilized version of this intrinsic is /// [`std::mem::needs_drop`](../../std/mem/fn.needs_drop.html). @@ -1308,6 +1312,8 @@ 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"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] pub fn offset(dst: *const T, offset: isize) -> *const T; /// Calculates the offset from a pointer, potentially wrapping. @@ -1324,6 +1330,8 @@ 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"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] pub fn arith_offset(dst: *const T, offset: isize) -> *const T; /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with @@ -1332,6 +1340,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 @@ -1339,6 +1349,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 @@ -1346,6 +1358,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. @@ -1361,9 +1375,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` @@ -1571,8 +1589,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. @@ -1588,28 +1610,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` /// @@ -1617,7 +1651,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`. /// @@ -1649,11 +1683,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 /// /// ``` @@ -1666,7 +1702,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`. /// @@ -1698,11 +1734,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 /// /// ``` @@ -1715,7 +1753,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`. /// @@ -1723,7 +1761,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`. /// @@ -1731,7 +1769,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. /// @@ -1739,7 +1777,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 /// @@ -1747,7 +1785,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 /// @@ -1755,60 +1793,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. /// @@ -1816,7 +1862,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. /// @@ -1824,7 +1870,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. /// @@ -1832,21 +1878,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. /// @@ -1854,14 +1900,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. @@ -1869,7 +1915,7 @@ 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")] - pub fn discriminant_value(v: &T) -> u64; + pub fn discriminant_value(v: &T) -> ::Discriminant; /// Rust's "try catch" construct which invokes the function pointer `try_fn` /// with the data pointer `data`. @@ -1878,17 +1924,14 @@ 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. pub fn nontemporal_store(ptr: *mut T, val: T); /// See documentation of `<*const T>::offset_from` for details. - #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "none")] + #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "41079")] pub fn ptr_offset_from(ptr: *const T, base: *const T) -> isize; /// Internal hook used by Miri to implement unwinding. @@ -1898,6 +1941,23 @@ extern "rust-intrinsic" { /// /// Perma-unstable: do not use. pub fn miri_start_panic(payload: *mut u8) -> !; + + /// Internal placeholder for injecting code coverage counters when the "instrument-coverage" + /// option is enabled. The placeholder is replaced with `llvm.instrprof.increment` during code + /// generation. + #[cfg(not(bootstrap))] + #[lang = "count_code_region"] + pub fn count_code_region(index: u32); + + /// See documentation of `<*const T>::guaranteed_eq` for details. + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[cfg(not(bootstrap))] + pub fn ptr_guaranteed_eq(ptr: *const T, other: *const T) -> bool; + + /// See documentation of `<*const T>::guaranteed_ne` for details. + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[cfg(not(bootstrap))] + pub fn ptr_guaranteed_ne(ptr: *const T, other: *const T) -> bool; } // Some functions are defined here because they accidentally got made @@ -2014,9 +2074,14 @@ pub unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize) { fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); } - debug_assert!(is_aligned_and_not_null(src), "attempt to copy from unaligned or null pointer"); - debug_assert!(is_aligned_and_not_null(dst), "attempt to copy to unaligned or null pointer"); - debug_assert!(is_nonoverlapping(src, dst, count), "attempt to copy to overlapping memory"); + if cfg!(debug_assertions) + && !(is_aligned_and_not_null(src) + && is_aligned_and_not_null(dst) + && is_nonoverlapping(src, dst, count)) + { + // Not panicking to keep codegen impact smaller. + abort(); + } copy_nonoverlapping(src, dst, count) } @@ -2079,8 +2144,10 @@ pub unsafe fn copy(src: *const T, dst: *mut T, count: usize) { fn copy(src: *const T, dst: *mut T, count: usize); } - debug_assert!(is_aligned_and_not_null(src), "attempt to copy from unaligned or null pointer"); - debug_assert!(is_aligned_and_not_null(dst), "attempt to copy to unaligned or null pointer"); + if cfg!(debug_assertions) && !(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)) { + // Not panicking to keep codegen impact smaller. + abort(); + } copy(src, dst, count) } 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/flatten.rs b/src/libcore/iter/adapters/flatten.rs index 0a7a9f26f8912..4202e52448dcf 100644 --- a/src/libcore/iter/adapters/flatten.rs +++ b/src/libcore/iter/adapters/flatten.rs @@ -1,7 +1,7 @@ use crate::fmt; use crate::ops::Try; -use super::super::{DoubleEndedIterator, FusedIterator, Iterator}; +use super::super::{DoubleEndedIterator, Fuse, FusedIterator, Iterator}; use super::Map; /// An iterator that maps each element to an iterator, and yields the elements @@ -239,14 +239,17 @@ where /// this type. #[derive(Clone, Debug)] struct FlattenCompat { - iter: I, + iter: Fuse, frontiter: Option, backiter: Option, } -impl FlattenCompat { +impl FlattenCompat +where + I: Iterator, +{ /// Adapts an iterator by flattening it, for use in `flatten()` and `flat_map()`. fn new(iter: I) -> FlattenCompat { - FlattenCompat { iter, frontiter: None, backiter: None } + FlattenCompat { iter: iter.fuse(), frontiter: None, backiter: None } } } @@ -261,8 +264,9 @@ where fn next(&mut self) -> Option { loop { if let Some(ref mut inner) = self.frontiter { - if let elt @ Some(_) = inner.next() { - return elt; + match inner.next() { + None => self.frontiter = None, + elt @ Some(_) => return elt, } } match self.iter.next() { @@ -348,8 +352,9 @@ where fn next_back(&mut self) -> Option { loop { if let Some(ref mut inner) = self.backiter { - if let elt @ Some(_) = inner.next_back() { - return elt; + match inner.next_back() { + None => self.backiter = None, + elt @ Some(_) => return elt, } } match self.iter.next_back() { 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 +3848,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 +3858,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 +3872,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 +3882,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 +3911,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 +3936,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 +4061,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 +4071,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 +4084,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 +4124,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 +4158,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 @@ -5622,7 +5736,8 @@ unsafe impl<'a, T> TrustedRandomAccess for RChunksExactMut<'a, T> { /// and it must be properly aligned. This means in particular: /// /// * The entire memory range of this slice must be contained within a single allocated object! -/// Slices can never span across multiple allocated objects. +/// Slices can never span across multiple allocated objects. See [below](#incorrect-usage) +/// for an example incorrectly not taking this into account. /// * `data` must be non-null and aligned even for zero-length slices. One /// reason for this is that enum layout optimizations may rely on references /// (including slices of any length) being aligned and non-null to distinguish @@ -5655,6 +5770,34 @@ unsafe impl<'a, T> TrustedRandomAccess for RChunksExactMut<'a, T> { /// assert_eq!(slice[0], 42); /// ``` /// +/// ### Incorrect usage +/// +/// The following `join_slices` function is **unsound** ⚠️ +/// +/// ```rust,no_run +/// use std::slice; +/// +/// fn join_slices<'a, T>(fst: &'a [T], snd: &'a [T]) -> &'a [T] { +/// let fst_end = fst.as_ptr().wrapping_add(fst.len()); +/// let snd_start = snd.as_ptr(); +/// assert_eq!(fst_end, snd_start, "Slices must be contiguous!"); +/// unsafe { +/// // The assertion above ensures `fst` and `snd` are contiguous, but they might +/// // still be contained within _different allocated objects_, in which case +/// // creating this slice is undefined behavior. +/// slice::from_raw_parts(fst.as_ptr(), fst.len() + snd.len()) +/// } +/// } +/// +/// fn main() { +/// // `a` and `b` are different allocated objects... +/// let a = 42; +/// let b = 27; +/// // ... which may nevertheless be laid out contiguously in memory: | a | b | +/// let _ = join_slices(slice::from_ref(&a), slice::from_ref(&b)); // UB +/// } +/// ``` +/// /// [valid]: ../../std/ptr/index.html#safety /// [`NonNull::dangling()`]: ../../std/ptr/struct.NonNull.html#method.dangling /// [`pointer::offset`]: ../../std/primitive.pointer.html#method.offset @@ -5813,10 +5956,18 @@ where return false; } + #[cfg(bootstrap)] if self.as_ptr() == other.as_ptr() { return true; } + // While performance would suffer if `guaranteed_eq` just returned `false` + // for all arguments, correctness and return value of this function are not affected. + #[cfg(not(bootstrap))] + if self.as_ptr().guaranteed_eq(other.as_ptr()) { + return true; + } + self.iter().zip(other.iter()).all(|(x, y)| x == y) } } @@ -5830,9 +5981,18 @@ where if self.len() != other.len() { return false; } + + #[cfg(bootstrap)] if self.as_ptr() == other.as_ptr() { return true; } + + // While performance would suffer if `guaranteed_eq` just returned `false` + // for all arguments, correctness and return value of this function are not affected. + #[cfg(not(bootstrap))] + if self.as_ptr().guaranteed_eq(other.as_ptr()) { + return true; + } unsafe { let size = mem::size_of_val(self); memcmp(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0 diff --git a/src/libcore/slice/sort.rs b/src/libcore/slice/sort.rs index 019832e16f89c..8b2ac294764ff 100644 --- a/src/libcore/slice/sort.rs +++ b/src/libcore/slice/sort.rs @@ -1,6 +1,6 @@ //! Slice sorting //! -//! This module contains an sort algorithm based on Orson Peters' pattern-defeating quicksort, +//! This module contains a sorting algorithm based on Orson Peters' pattern-defeating quicksort, //! published at: https://github.com/orlp/pdqsort //! //! Unstable sorting is compatible with libcore because it doesn't allocate memory, unlike our @@ -20,6 +20,9 @@ struct CopyOnDrop { impl Drop for CopyOnDrop { fn drop(&mut self) { + // SAFETY: This is a helper class. + // Please refer to its usage for correctness. + // Namely, one must be sure that `src` and `dst` does not overlap as required by `ptr::copy_nonoverlapping`. unsafe { ptr::copy_nonoverlapping(self.src, self.dest, 1); } @@ -32,6 +35,21 @@ where F: FnMut(&T, &T) -> bool, { let len = v.len(); + // SAFETY: The unsafe operations below involves indexing without a bound check (`get_unchecked` and `get_unchecked_mut`) + // and copying memory (`ptr::copy_nonoverlapping`). + // + // a. Indexing: + // 1. We checked the size of the array to >=2. + // 2. All the indexing that we will do is always between {0 <= index < len} at most. + // + // b. Memory copying + // 1. We are obtaining pointers to references which are guaranteed to be valid. + // 2. They cannot overlap because we obtain pointers to difference indices of the slice. + // Namely, `i` and `i-1`. + // 3. If the slice is properly aligned, the elements are properly aligned. + // It is the caller's responsibility to make sure the slice is properly aligned. + // + // See comments below for further detail. unsafe { // If the first two elements are out-of-order... if len >= 2 && is_less(v.get_unchecked(1), v.get_unchecked(0)) { @@ -62,6 +80,21 @@ where F: FnMut(&T, &T) -> bool, { let len = v.len(); + // SAFETY: The unsafe operations below involves indexing without a bound check (`get_unchecked` and `get_unchecked_mut`) + // and copying memory (`ptr::copy_nonoverlapping`). + // + // a. Indexing: + // 1. We checked the size of the array to >= 2. + // 2. All the indexing that we will do is always between `0 <= index < len-1` at most. + // + // b. Memory copying + // 1. We are obtaining pointers to references which are guaranteed to be valid. + // 2. They cannot overlap because we obtain pointers to difference indices of the slice. + // Namely, `i` and `i+1`. + // 3. If the slice is properly aligned, the elements are properly aligned. + // It is the caller's responsibility to make sure the slice is properly aligned. + // + // See comments below for further detail. unsafe { // If the last two elements are out-of-order... if len >= 2 && is_less(v.get_unchecked(len - 1), v.get_unchecked(len - 2)) { @@ -103,6 +136,8 @@ where let mut i = 1; for _ in 0..MAX_STEPS { + // SAFETY: We already explicitly did the bound checking with `i < len`. + // All our subsequent indexing is only in the range `0 <= index < len` unsafe { // Find the next pair of adjacent out-of-order elements. while i < len && !is_less(v.get_unchecked(i), v.get_unchecked(i - 1)) { @@ -143,7 +178,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 @@ -220,6 +255,7 @@ where let mut offsets_l = [MaybeUninit::::uninit(); BLOCK]; // The current block on the right side (from `r.sub(block_r)` to `r`). + // SAFETY: The documentation for .add() specifically mention that `vec.as_ptr().add(vec.len())` is always safe` let mut r = unsafe { l.add(v.len()) }; let mut block_r = BLOCK; let mut start_r = ptr::null_mut(); @@ -268,6 +304,16 @@ where let mut elem = l; for i in 0..block_l { + // SAFETY: The unsafety operations below involve the usage of the `offset`. + // According to the conditions required by the function, we satisfy them because: + // 1. `offsets_l` is stack-allocated, and thus considered separate allocated object. + // 2. The function `is_less` returns a `bool`. + // Casting a `bool` will never overflow `isize`. + // 3. We have guaranteed that `block_l` will be `<= BLOCK`. + // Plus, `end_l` was initially set to the begin pointer of `offsets_` which was declared on the stack. + // Thus, we know that even in the worst case (all invocations of `is_less` returns false) we will only be at most 1 byte pass the end. + // Another unsafety operation here is dereferencing `elem`. + // However, `elem` was initially the begin pointer to the slice which is always valid. unsafe { // Branchless comparison. *end_l = i as u8; @@ -284,6 +330,17 @@ where let mut elem = r; for i in 0..block_r { + // SAFETY: The unsafety operations below involve the usage of the `offset`. + // According to the conditions required by the function, we satisfy them because: + // 1. `offsets_r` is stack-allocated, and thus considered separate allocated object. + // 2. The function `is_less` returns a `bool`. + // Casting a `bool` will never overflow `isize`. + // 3. We have guaranteed that `block_r` will be `<= BLOCK`. + // Plus, `end_r` was initially set to the begin pointer of `offsets_` which was declared on the stack. + // Thus, we know that even in the worst case (all invocations of `is_less` returns true) we will only be at most 1 byte pass the end. + // Another unsafety operation here is dereferencing `elem`. + // However, `elem` was initially `1 * sizeof(T)` past the end and we decrement it by `1 * sizeof(T)` before accessing it. + // Plus, `block_r` was asserted to be less than `BLOCK` and `elem` will therefore at most be pointing to the beginning of the slice. unsafe { // Branchless comparison. elem = elem.offset(-1); @@ -404,8 +461,13 @@ where // Find the first pair of out-of-order elements. let mut l = 0; let mut r = v.len(); + + // SAFETY: The unsafety below involves indexing an array. + // For the first one: We already do the bounds checking here with `l < r`. + // For the second one: We initially have `l == 0` and `r == v.len()` and we checked that `l < r` at every indexing operation. + // From here we know that `r` must be at least `r == l` which was shown to be valid from the first one. unsafe { - // Find the first element greater then or equal to the pivot. + // Find the first element greater than or equal to the pivot. while l < r && is_less(v.get_unchecked(l), pivot) { l += 1; } @@ -444,6 +506,7 @@ where // Read the pivot into a stack-allocated variable for efficiency. If a following comparison // operation panics, the pivot will be automatically written back into the slice. + // SAFETY: The pointer here is valid because it is obtained from a reference to a slice. let mut tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); let _pivot_guard = CopyOnDrop { src: &mut *tmp, dest: pivot }; let pivot = &*tmp; @@ -452,8 +515,12 @@ where let mut l = 0; let mut r = v.len(); loop { + // SAFETY: The unsafety below involves indexing an array. + // For the first one: We already do the bounds checking here with `l < r`. + // For the second one: We initially have `l == 0` and `r == v.len()` and we checked that `l < r` at every indexing operation. + // From here we know that `r` must be at least `r == l` which was shown to be valid from the first one. unsafe { - // Find the first element greater that the pivot. + // Find the first element greater than the pivot. while l < r && !is_less(pivot, v.get_unchecked(l)) { l += 1; } @@ -621,7 +688,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 +751,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..6c4b28499a60b 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}; @@ -1651,7 +1651,7 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { // Ascii case, try to skip forward quickly. // When the pointer is aligned, read 2 words of data per iteration // until we find a word containing a non-ascii byte. - if align != usize::max_value() && align.wrapping_sub(index) % usize_bytes == 0 { + if align != usize::MAX && align.wrapping_sub(index) % usize_bytes == 0 { let ptr = v.as_ptr(); while index < blocks_end { // SAFETY: since `align - index` and `ascii_block_size` are @@ -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"); } @@ -2082,7 +2083,7 @@ mod traits { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) @@ -2090,7 +2091,7 @@ mod traits { } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get_mut(slice) @@ -2106,14 +2107,14 @@ mod traits { } #[inline] fn index(self, slice: &str) -> &Self::Output { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { str_index_overflow_fail(); } (*self.start()..self.end() + 1).index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { str_index_overflow_fail(); } (*self.start()..self.end() + 1).index_mut(slice) @@ -2139,11 +2140,11 @@ mod traits { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if self.end == usize::max_value() { None } else { (..self.end + 1).get(slice) } + if self.end == usize::MAX { None } else { (..self.end + 1).get(slice) } } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if self.end == usize::max_value() { None } else { (..self.end + 1).get_mut(slice) } + if self.end == usize::MAX { None } else { (..self.end + 1).get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: &str) -> &Self::Output { @@ -2155,14 +2156,14 @@ mod traits { } #[inline] fn index(self, slice: &str) -> &Self::Output { - if self.end == usize::max_value() { + if self.end == usize::MAX { str_index_overflow_fail(); } (..self.end + 1).index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if self.end == usize::max_value() { + if self.end == usize::MAX { str_index_overflow_fail(); } (..self.end + 1).index_mut(slice) @@ -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); @@ -2268,12 +2270,11 @@ impl str { self.len() == 0 } - /// Checks that `index`-th byte lies at the start and/or end of a - /// UTF-8 code point sequence. + /// Checks that `index`-th byte is the first byte in a UTF-8 code point + /// sequence or the end of the string. /// /// The start and end of the string (when `index == self.len()`) are - /// considered to be - /// boundaries. + /// considered to be boundaries. /// /// Returns `false` if `index` is greater than `self.len()`. /// @@ -2640,7 +2641,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 +2682,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 +3009,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 +3036,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 +3062,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 +3091,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 +3140,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 +3187,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 +3309,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 +3334,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 +3349,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 +3403,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 +3450,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 +3499,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 +3552,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 +3600,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 +3639,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 +3683,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 +3728,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 +3949,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 +3997,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 +4042,24 @@ 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")] + #[stable(feature = "str_strip", since = "1.45.0")] 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,42 +4070,38 @@ 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, \ without modifying the original"] - #[unstable(feature = "str_strip", reason = "newly added", issue = "67302")] + #[stable(feature = "str_strip", since = "1.45.0")] pub fn strip_suffix<'a, P>(&'a self, suffix: P) -> Option<&'a str> where P: Pattern<'a>,

>::Searcher: ReverseSearcher<'a>, { - 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 +4146,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 +4183,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 +4571,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 +4664,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 +4674,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 +4682,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 +4699,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..14f1f293d40d6 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 @@ -27,6 +60,43 @@ use crate::usize; /// The trait itself acts as a builder for an associated /// `Searcher` type, which does the actual work of finding /// occurrences of the pattern in a string. +/// +/// Depending on the type of the pattern, the behaviour of methods like +/// [`str::find`] and [`str::contains`] can change. The table below describes +/// some of those behaviours. +/// +/// | Pattern type | Match condition | +/// |--------------------------|-------------------------------------------| +/// | `&str` | is substring | +/// | `char` | is contained in string | +/// | `&[char]` | any char in slice is contained in string | +/// | `F: FnMut(char) -> bool` | `F` returns `true` for a char in string | +/// | `&&str` | is substring | +/// | `&String` | is substring | +/// +/// # Examples +/// ``` +/// // &str +/// assert_eq!("abaaa".find("ba"), Some(1)); +/// assert_eq!("abaaa".find("bac"), None); +/// +/// // char +/// assert_eq!("abaaa".find('a'), Some(0)); +/// assert_eq!("abaaa".find('b'), Some(1)); +/// assert_eq!("abaaa".find('c'), None); +/// +/// // &[char] +/// assert_eq!("ab".find(&['b', 'a'][..]), Some(0)); +/// assert_eq!("abaaa".find(&['a', 'z'][..]), Some(0)); +/// assert_eq!("abaaa".find(&['c', 'd'][..]), None); +/// +/// // FnMut(char) -> bool +/// assert_eq!("abcdef_z".find(|ch| ch > 'd' && ch < 'y'), Some(4)); +/// assert_eq!("abcddd_z".find(|ch| ch > 'd' && ch < 'y'), None); +/// ``` +/// +/// [`str::find`]: ../../../std/primitive.str.html#method.find +/// [`str::contains`]: ../../../std/primitive.str.html#method.contains pub trait Pattern<'a>: Sized { /// Associated searcher for this pattern type Searcher: Searcher<'a>; @@ -55,6 +125,42 @@ pub trait Pattern<'a>: Sized { { matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j) } + + /// Removes the pattern from the front of haystack, if it matches. + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + 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 + } + } + + /// 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 +521,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 +560,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 +572,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 +694,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 +706,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 +772,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 +821,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 +853,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 +867,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/sync/atomic.rs b/src/libcore/sync/atomic.rs index 3ba15968f8933..1cd68f2881b7c 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -153,6 +153,9 @@ pub fn spin_loop_hint() { /// /// This type has the same in-memory representation as a [`bool`]. /// +/// **Note**: This type is only available on platforms that support atomic +/// loads and stores of `u8`. +/// /// [`bool`]: ../../../std/primitive.bool.html #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "rust1", since = "1.0.0")] @@ -178,6 +181,9 @@ unsafe impl Sync for AtomicBool {} /// A raw pointer type which can be safely shared between threads. /// /// This type has the same in-memory representation as a `*mut T`. +/// +/// **Note**: This type is only available on platforms that support atomic +/// loads and stores of pointers. Its size depends on the target pointer's size. #[cfg(target_has_atomic_load_store = "ptr")] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(target_pointer_width = "16", repr(C, align(2)))] @@ -447,6 +453,9 @@ impl AtomicBool { /// [`Acquire`] makes the store part of this operation [`Relaxed`], and /// using [`Release`] makes the load part [`Relaxed`]. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// /// [`Ordering`]: enum.Ordering.html /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [`Release`]: enum.Ordering.html#variant.Release @@ -481,6 +490,9 @@ impl AtomicBool { /// Using [`Acquire`] makes the store part of this operation [`Relaxed`] if it /// happens, and using [`Release`] makes the load part [`Relaxed`]. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// /// [`Ordering`]: enum.Ordering.html /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [`Release`]: enum.Ordering.html#variant.Release @@ -524,6 +536,8 @@ impl AtomicBool { /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] /// and must be equivalent to or weaker than the success ordering. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. /// /// [`bool`]: ../../../std/primitive.bool.html /// [`Ordering`]: enum.Ordering.html @@ -586,6 +600,9 @@ impl AtomicBool { /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] /// and must be equivalent to or weaker than the success ordering. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// /// [`bool`]: ../../../std/primitive.bool.html /// [`compare_exchange`]: #method.compare_exchange /// [`Ordering`]: enum.Ordering.html @@ -646,6 +663,9 @@ impl AtomicBool { /// [`Release`]: enum.Ordering.html#variant.Release /// [`Acquire`]: enum.Ordering.html#variant.Acquire /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// /// # Examples /// /// ``` @@ -683,6 +703,9 @@ impl AtomicBool { /// [`Acquire`] makes the store part of this operation [`Relaxed`], and /// using [`Release`] makes the load part [`Relaxed`]. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// /// [`Ordering`]: enum.Ordering.html /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [`Release`]: enum.Ordering.html#variant.Release @@ -737,6 +760,9 @@ impl AtomicBool { /// [`Acquire`] makes the store part of this operation [`Relaxed`], and /// using [`Release`] makes the load part [`Relaxed`]. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// /// [`Ordering`]: enum.Ordering.html /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [`Release`]: enum.Ordering.html#variant.Release @@ -779,6 +805,9 @@ impl AtomicBool { /// [`Acquire`] makes the store part of this operation [`Relaxed`], and /// using [`Release`] makes the load part [`Relaxed`]. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// /// [`Ordering`]: enum.Ordering.html /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [`Release`]: enum.Ordering.html#variant.Release @@ -981,6 +1010,9 @@ impl AtomicPtr { /// [`Acquire`] makes the store part of this operation [`Relaxed`], and /// using [`Release`] makes the load part [`Relaxed`]. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// /// [`Ordering`]: enum.Ordering.html /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [`Release`]: enum.Ordering.html#variant.Release @@ -1017,6 +1049,9 @@ impl AtomicPtr { /// Using [`Acquire`] makes the store part of this operation [`Relaxed`] if it /// happens, and using [`Release`] makes the load part [`Relaxed`]. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// /// [`Ordering`]: enum.Ordering.html /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [`Release`]: enum.Ordering.html#variant.Release @@ -1058,6 +1093,9 @@ impl AtomicPtr { /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] /// and must be equivalent to or weaker than the success ordering. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// /// [`Ordering`]: enum.Ordering.html /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed /// [`Release`]: enum.Ordering.html#variant.Release @@ -1118,6 +1156,9 @@ impl AtomicPtr { /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] /// and must be equivalent to or weaker than the success ordering. /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// /// [`compare_exchange`]: #method.compare_exchange /// [`Ordering`]: enum.Ordering.html /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed @@ -1223,6 +1264,13 @@ macro_rules! atomic_int { /// non-atomic types as well as information about the portability of /// this type, please see the [module-level documentation]. /// + /// **Note:** This type is only available on platforms that support + /// atomic loads and stores of [` + #[doc = $s_int_type] + /// `]( + #[doc = $int_ref] + /// ). + /// /// [module-level documentation]: index.html #[$stable] #[repr(C, align($align))] @@ -1408,6 +1456,9 @@ of this operation. All ordering modes are possible. Note that using [`Acquire`] makes the store part of this operation [`Relaxed`], and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1444,6 +1495,9 @@ might fail and hence just perform an `Acquire` load, but not have `Release` sema Using [`Acquire`] makes the store part of this operation [`Relaxed`] if it happens, and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1496,6 +1550,9 @@ of this operation [`Relaxed`], and using [`Release`] makes the successful load [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] and must be equivalent to or weaker than the success ordering. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1558,6 +1615,9 @@ and must be equivalent to or weaker than the success ordering. [`Acquire`]: enum.Ordering.html#variant.Acquire [`SeqCst`]: enum.Ordering.html#variant.SeqCst +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + # Examples ``` @@ -1599,6 +1659,9 @@ of this operation. All ordering modes are possible. Note that using [`Acquire`] makes the store part of this operation [`Relaxed`], and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1632,6 +1695,9 @@ of this operation. All ordering modes are possible. Note that using [`Acquire`] makes the store part of this operation [`Relaxed`], and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1668,6 +1734,9 @@ of this operation. All ordering modes are possible. Note that using [`Acquire`] makes the store part of this operation [`Relaxed`], and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1704,6 +1773,9 @@ of this operation. All ordering modes are possible. Note that using [`Acquire`] makes the store part of this operation [`Relaxed`], and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1741,6 +1813,9 @@ of this operation. All ordering modes are possible. Note that using [`Acquire`] makes the store part of this operation [`Relaxed`], and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1777,6 +1852,9 @@ of this operation. All ordering modes are possible. Note that using [`Acquire`] makes the store part of this operation [`Relaxed`], and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1807,19 +1885,21 @@ new value. Returns a `Result` of `Ok(previous_value)` if the function returned ` Note: This may call the function multiple times if the value has been changed from other threads in the meantime, as long as the function returns `Some(_)`, but the function will have been applied -but once to the stored value. +only once to the stored value. -`fetch_update` takes two [`Ordering`] arguments to describe the memory -ordering of this operation. The first describes the required ordering for loads -and failed updates while the second describes the required ordering when the -operation finally succeeds. Beware that this is different from the two -modes in [`compare_exchange`]! +`fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. +The first describes the required ordering for when the operation finally succeeds while the second +describes the required ordering for loads. These correspond to the success and failure orderings of +[`compare_exchange`] respectively. Using [`Acquire`] as success ordering makes the store part of this operation [`Relaxed`], and using [`Release`] makes the final successful load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`] and must be equivalent to or weaker than the success ordering. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`bool`]: ../../../std/primitive.bool.html [`compare_exchange`]: #method.compare_exchange [`Ordering`]: enum.Ordering.html @@ -1831,24 +1911,21 @@ and must be equivalent to or weaker than the success ordering. # Examples ```rust -#![feature(no_more_cas)] ", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; let x = ", stringify!($atomic_type), "::new(7); -assert_eq!(x.fetch_update(|_| None, Ordering::SeqCst, Ordering::SeqCst), Err(7)); -assert_eq!(x.fetch_update(|x| Some(x + 1), Ordering::SeqCst, Ordering::SeqCst), Ok(7)); -assert_eq!(x.fetch_update(|x| Some(x + 1), Ordering::SeqCst, Ordering::SeqCst), Ok(8)); +assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); +assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); +assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); assert_eq!(x.load(Ordering::SeqCst), 9); ```"), #[inline] - #[unstable(feature = "no_more_cas", - reason = "no more CAS loops in user code", - issue = "48655")] + #[stable(feature = "no_more_cas", since = "1.45.0")] #[$cfg_cas] pub fn fetch_update(&self, - mut f: F, + set_order: Ordering, fetch_order: Ordering, - set_order: Ordering) -> Result<$int_type, $int_type> + mut f: F) -> Result<$int_type, $int_type> where F: FnMut($int_type) -> Option<$int_type> { let mut prev = self.load(fetch_order); while let Some(next) = f(prev) { @@ -1874,6 +1951,9 @@ of this operation. All ordering modes are possible. Note that using [`Acquire`] makes the store part of this operation [`Relaxed`], and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1882,7 +1962,6 @@ using [`Release`] makes the load part [`Relaxed`]. # Examples ``` -#![feature(atomic_min_max)] ", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; let foo = ", stringify!($atomic_type), "::new(23); @@ -1893,7 +1972,6 @@ assert_eq!(foo.load(Ordering::SeqCst), 42); If you want to obtain the maximum value in one step, you can use the following: ``` -#![feature(atomic_min_max)] ", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; let foo = ", stringify!($atomic_type), "::new(23); @@ -1902,9 +1980,7 @@ let max_foo = foo.fetch_max(bar, Ordering::SeqCst).max(bar); assert!(max_foo == 42); ```"), #[inline] - #[unstable(feature = "atomic_min_max", - reason = "easier and faster min/max than writing manual CAS loop", - issue = "48655")] + #[stable(feature = "atomic_min_max", since = "1.45.0")] #[$cfg_cas] pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. @@ -1925,6 +2001,9 @@ of this operation. All ordering modes are possible. Note that using [`Acquire`] makes the store part of this operation [`Relaxed`], and using [`Release`] makes the load part [`Relaxed`]. +**Note**: This method is only available on platforms that support atomic +operations on [`", $s_int_type, "`](", $int_ref, "). + [`Ordering`]: enum.Ordering.html [`Relaxed`]: enum.Ordering.html#variant.Relaxed [`Release`]: enum.Ordering.html#variant.Release @@ -1933,7 +2012,6 @@ using [`Release`] makes the load part [`Relaxed`]. # Examples ``` -#![feature(atomic_min_max)] ", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; let foo = ", stringify!($atomic_type), "::new(23); @@ -1946,7 +2024,6 @@ assert_eq!(foo.load(Ordering::Relaxed), 22); If you want to obtain the minimum value in one step, you can use the following: ``` -#![feature(atomic_min_max)] ", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering}; let foo = ", stringify!($atomic_type), "::new(23); @@ -1955,9 +2032,7 @@ let min_foo = foo.fetch_min(bar, Ordering::SeqCst).min(bar); assert_eq!(min_foo, 12); ```"), #[inline] - #[unstable(feature = "atomic_min_max", - reason = "easier and faster min/max than writing manual CAS loop", - issue = "48655")] + #[stable(feature = "atomic_min_max", since = "1.45.0")] #[$cfg_cas] pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type { // SAFETY: data races are prevented by atomic intrinsics. @@ -2259,7 +2334,7 @@ fn strongest_failure_ordering(order: Ordering) -> Ordering { } #[inline] -unsafe fn atomic_store(dst: *mut T, val: T, order: Ordering) { +unsafe fn atomic_store(dst: *mut T, val: T, order: Ordering) { match order { Release => intrinsics::atomic_store_rel(dst, val), Relaxed => intrinsics::atomic_store_relaxed(dst, val), @@ -2270,7 +2345,7 @@ unsafe fn atomic_store(dst: *mut T, val: T, order: Ordering) { } #[inline] -unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { +unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_load_acq(dst), Relaxed => intrinsics::atomic_load_relaxed(dst), @@ -2282,7 +2357,7 @@ unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_swap(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_swap(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xchg_acq(dst, val), Release => intrinsics::atomic_xchg_rel(dst, val), @@ -2295,7 +2370,7 @@ unsafe fn atomic_swap(dst: *mut T, val: T, order: Ordering) -> T { /// Returns the previous value (like __sync_fetch_and_add). #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_add(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_add(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xadd_acq(dst, val), Release => intrinsics::atomic_xadd_rel(dst, val), @@ -2308,7 +2383,7 @@ unsafe fn atomic_add(dst: *mut T, val: T, order: Ordering) -> T { /// Returns the previous value (like __sync_fetch_and_sub). #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_sub(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_sub(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xsub_acq(dst, val), Release => intrinsics::atomic_xsub_rel(dst, val), @@ -2320,7 +2395,7 @@ unsafe fn atomic_sub(dst: *mut T, val: T, order: Ordering) -> T { #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_compare_exchange( +unsafe fn atomic_compare_exchange( dst: *mut T, old: T, new: T, @@ -2346,7 +2421,7 @@ unsafe fn atomic_compare_exchange( #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_compare_exchange_weak( +unsafe fn atomic_compare_exchange_weak( dst: *mut T, old: T, new: T, @@ -2372,7 +2447,7 @@ unsafe fn atomic_compare_exchange_weak( #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_and(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_and(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_and_acq(dst, val), Release => intrinsics::atomic_and_rel(dst, val), @@ -2384,7 +2459,7 @@ unsafe fn atomic_and(dst: *mut T, val: T, order: Ordering) -> T { #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_nand(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_nand(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_nand_acq(dst, val), Release => intrinsics::atomic_nand_rel(dst, val), @@ -2396,7 +2471,7 @@ unsafe fn atomic_nand(dst: *mut T, val: T, order: Ordering) -> T { #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_or(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_or(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_or_acq(dst, val), Release => intrinsics::atomic_or_rel(dst, val), @@ -2408,7 +2483,7 @@ unsafe fn atomic_or(dst: *mut T, val: T, order: Ordering) -> T { #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_xor(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_xor(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xor_acq(dst, val), Release => intrinsics::atomic_xor_rel(dst, val), @@ -2421,7 +2496,7 @@ unsafe fn atomic_xor(dst: *mut T, val: T, order: Ordering) -> T { /// returns the max value (signed comparison) #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_max(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_max(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_max_acq(dst, val), Release => intrinsics::atomic_max_rel(dst, val), @@ -2434,7 +2509,7 @@ unsafe fn atomic_max(dst: *mut T, val: T, order: Ordering) -> T { /// returns the min value (signed comparison) #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_min(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_min(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_min_acq(dst, val), Release => intrinsics::atomic_min_rel(dst, val), @@ -2447,7 +2522,7 @@ unsafe fn atomic_min(dst: *mut T, val: T, order: Ordering) -> T { /// returns the max value (unsigned comparison) #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_umax(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_umax(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_umax_acq(dst, val), Release => intrinsics::atomic_umax_rel(dst, val), @@ -2460,7 +2535,7 @@ unsafe fn atomic_umax(dst: *mut T, val: T, order: Ordering) -> T { /// returns the min value (unsigned comparison) #[inline] #[cfg(target_has_atomic = "8")] -unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { +unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_umin_acq(dst, val), Release => intrinsics::atomic_umin_rel(dst, val), @@ -2548,15 +2623,7 @@ unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(target_arch = "wasm32", allow(unused_variables))] pub fn fence(order: Ordering) { - // On wasm32 it looks like fences aren't implemented in LLVM yet in that - // they will cause LLVM to abort. The wasm instruction set doesn't have - // fences right now. There's discussion online about the best way for tools - // to conventionally implement fences at - // https://github.com/WebAssembly/tool-conventions/issues/59. We should - // follow that discussion and implement a solution when one comes about! - #[cfg(not(target_arch = "wasm32"))] // SAFETY: using an atomic fence is safe. unsafe { match order { diff --git a/src/libcore/tests/array.rs b/src/libcore/tests/array.rs index c2a816f0a7d90..4bc44e98fc802 100644 --- a/src/libcore/tests/array.rs +++ b/src/libcore/tests/array.rs @@ -241,3 +241,52 @@ fn iterator_drops() { } assert_eq!(i.get(), 5); } + +// This test does not work on targets without panic=unwind support. +// To work around this problem, test is marked is should_panic, so it will +// be automagically skipped on unsuitable targets, such as +// wasm32-unknown-unkown. +// +// It means that we use panic for indicating success. +#[test] +#[should_panic(expected = "test succeeded")] +fn array_default_impl_avoids_leaks_on_panic() { + use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + static COUNTER: AtomicUsize = AtomicUsize::new(0); + #[derive(Debug)] + struct Bomb(usize); + + impl Default for Bomb { + fn default() -> Bomb { + if COUNTER.load(Relaxed) == 3 { + panic!("bomb limit exceeded"); + } + + COUNTER.fetch_add(1, Relaxed); + Bomb(COUNTER.load(Relaxed)) + } + } + + impl Drop for Bomb { + fn drop(&mut self) { + COUNTER.fetch_sub(1, Relaxed); + } + } + + let res = std::panic::catch_unwind(|| <[Bomb; 5]>::default()); + let panic_msg = match res { + Ok(_) => unreachable!(), + Err(p) => p.downcast::<&'static str>().unwrap(), + }; + assert_eq!(*panic_msg, "bomb limit exceeded"); + // check that all bombs are successfully dropped + assert_eq!(COUNTER.load(Relaxed), 0); + panic!("test succeeded") +} + +#[test] +fn empty_array_is_always_default() { + struct DoesNotImplDefault; + + let _arr = <[DoesNotImplDefault; 0]>::default(); +} 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 5b41ef350657f..3b854b56c320d 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -1,8 +1,8 @@ +// ignore-tidy-filelength + use core::cell::Cell; use core::convert::TryFrom; use core::iter::*; -use core::usize; -use core::{i16, i8, isize}; #[test] fn test_lt() { @@ -76,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(); @@ -208,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]; @@ -800,6 +813,30 @@ fn test_iterator_peekable_rfold() { assert_eq!(i, xs.len()); } +#[test] +fn test_iterator_peekable_next_if_eq() { + // first, try on references + let xs = vec!["Heart", "of", "Gold"]; + let mut it = xs.into_iter().peekable(); + // try before `peek()` + assert_eq!(it.next_if_eq(&"trillian"), None); + assert_eq!(it.next_if_eq(&"Heart"), Some("Heart")); + // try after peek() + assert_eq!(it.peek(), Some(&"of")); + assert_eq!(it.next_if_eq(&"of"), Some("of")); + assert_eq!(it.next_if_eq(&"zaphod"), None); + // make sure `next()` still behaves + assert_eq!(it.next(), Some("Gold")); + + // make sure comparison works for owned values + let xs = vec![String::from("Ludicrous"), "speed".into()]; + let mut it = xs.into_iter().peekable(); + // make sure basic functionality works + assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into())); + assert_eq!(it.next_if_eq("speed"), Some("speed".into())); + assert_eq!(it.next_if_eq(""), None); +} + /// This is an iterator that follows the Iterator contract, /// but it is not fused. After having returned None once, it will start /// producing elements if .next() is called again. @@ -1919,6 +1956,21 @@ fn test_range() { ); } +#[test] +fn test_char_range() { + use std::char; + // Miri is too slow + let from = if cfg!(miri) { char::from_u32(0xD800 - 10).unwrap() } else { '\0' }; + let to = if cfg!(miri) { char::from_u32(0xDFFF + 10).unwrap() } else { char::MAX }; + assert!((from..=to).eq((from as u32..=to as u32).filter_map(char::from_u32))); + assert!((from..=to).rev().eq((from as u32..=to as u32).filter_map(char::from_u32).rev())); + + assert_eq!(('\u{D7FF}'..='\u{E000}').count(), 2); + assert_eq!(('\u{D7FF}'..='\u{E000}').size_hint(), (2, Some(2))); + assert_eq!(('\u{D7FF}'..'\u{E000}').count(), 1); + assert_eq!(('\u{D7FF}'..'\u{E000}').size_hint(), (1, Some(1))); +} + #[test] fn test_range_exhaustion() { let mut r = 10..10; @@ -2126,6 +2178,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)] @@ -2249,62 +2319,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] @@ -2500,42 +2566,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); + + 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); - x = 5; - let y = x.replace_one(); - assert_eq!(x, 1); - assert_eq!(y, 5); + 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); + + 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); - x = 5; - let y = x.replace_one(); - assert_eq!(x, 1); - assert_eq!(y, 5); + 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] @@ -2898,7 +3013,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()); @@ -2940,3 +3055,73 @@ fn test_partition() { check(xs, |&x| x < 3, 3); // small check(xs, |&x| x > 6, 3); // large } + +/// An iterator that panics whenever `next` or next_back` is called +/// after `None` has already been returned. This does not violate +/// `Iterator`'s contract. Used to test that iterator adaptors don't +/// poll their inner iterators after exhausting them. +struct NonFused { + iter: I, + done: bool, +} + +impl NonFused { + fn new(iter: I) -> Self { + Self { iter, done: false } + } +} + +impl Iterator for NonFused +where + I: Iterator, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + assert!(!self.done, "this iterator has already returned None"); + self.iter.next().or_else(|| { + self.done = true; + None + }) + } +} + +impl DoubleEndedIterator for NonFused +where + I: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + assert!(!self.done, "this iterator has already returned None"); + self.iter.next_back().or_else(|| { + self.done = true; + None + }) + } +} + +#[test] +fn test_peekable_non_fused() { + let mut iter = NonFused::new(empty::()).peekable(); + + assert_eq!(iter.peek(), None); + assert_eq!(iter.next_back(), None); +} + +#[test] +fn test_flatten_non_fused_outer() { + let mut iter = NonFused::new(once(0..2)).flatten(); + + assert_eq!(iter.next_back(), Some(1)); + assert_eq!(iter.next(), Some(0)); + assert_eq!(iter.next(), None); +} + +#[test] +fn test_flatten_non_fused_inner() { + let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new); + + assert_eq!(iter.next_back(), Some(2)); + assert_eq!(iter.next(), Some(0)); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), None); +} diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 05f958cbe81fe..4e55452a4c31b 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(min_specialization)] #![feature(step_trait)] +#![feature(step_trait_ext)] #![feature(str_internals)] #![feature(test)] #![feature(trusted_len)] @@ -42,6 +42,8 @@ #![feature(unwrap_infallible)] #![feature(leading_trailing_ones)] #![feature(const_forget)] +#![feature(option_unwrap_none)] +#![feature(peekable_next_if)] extern crate test; diff --git a/src/libcore/tests/mem.rs b/src/libcore/tests/mem.rs index 8337ab103419f..59588d97787b7 100644 --- a/src/libcore/tests/mem.rs +++ b/src/libcore/tests/mem.rs @@ -129,21 +129,3 @@ fn test_discriminant_send_sync() { is_send_sync::>(); is_send_sync::>(); } - -#[test] -fn test_const_forget() { - const _: () = forget(0i32); - const _: () = forget(Vec::>>::new()); - - // Writing this function signature without const-forget - // triggers compiler errors: - // 1) That we use a non-const fn inside a const fn - // 2) without the forget, it complains about the destructor of Box - const fn const_forget_box(x: Box) { - forget(x); - } - - // Call the forget_box at runtime, - // as we can't const-construct a box yet. - const_forget_box(Box::new(0i32)); -} diff --git a/src/libcore/tests/nonzero.rs b/src/libcore/tests/nonzero.rs index 6c5d19845e406..48aec6d718d3d 100644 --- a/src/libcore/tests/nonzero.rs +++ b/src/libcore/tests/nonzero.rs @@ -1,3 +1,4 @@ +use core::convert::TryFrom; use core::num::{IntErrorKind, NonZeroI32, NonZeroI8, NonZeroU32, NonZeroU8}; use core::option::Option::{self, None, Some}; use std::mem::size_of; @@ -141,3 +142,56 @@ 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); +} + +#[test] +fn test_nonzero_from_int_on_success() { + assert_eq!(NonZeroU8::try_from(5), Ok(NonZeroU8::new(5).unwrap())); + assert_eq!(NonZeroU32::try_from(5), Ok(NonZeroU32::new(5).unwrap())); + + assert_eq!(NonZeroI8::try_from(-5), Ok(NonZeroI8::new(-5).unwrap())); + assert_eq!(NonZeroI32::try_from(-5), Ok(NonZeroI32::new(-5).unwrap())); +} + +#[test] +fn test_nonzero_from_int_on_err() { + assert!(NonZeroU8::try_from(0).is_err()); + assert!(NonZeroU32::try_from(0).is_err()); + + assert!(NonZeroI8::try_from(0).is_err()); + assert!(NonZeroI32::try_from(0).is_err()); +} 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..939f1325c8499 100644 --- a/src/libcore/tests/num/mod.rs +++ b/src/libcore/tests/num/mod.rs @@ -140,8 +140,8 @@ macro_rules! test_impl_from { ($fn_name: ident, $Small: ty, $Large: ty) => { #[test] fn $fn_name() { - let small_max = <$Small>::max_value(); - let small_min = <$Small>::min_value(); + let small_max = <$Small>::MAX; + let small_min = <$Small>::MIN; let large_max: $Large = small_max.into(); let large_min: $Large = small_min.into(); assert_eq!(large_max as $Small, small_max); @@ -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()); @@ -250,8 +248,8 @@ macro_rules! test_impl_try_from_always_ok { ($fn_name:ident, $source:ty, $target: ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; assert_eq!(<$target as TryFrom<$source>>::try_from(max).unwrap(), max as $target); assert_eq!(<$target as TryFrom<$source>>::try_from(min).unwrap(), min as $target); @@ -363,8 +361,8 @@ macro_rules! test_impl_try_from_signed_to_unsigned_upper_ok { ($fn_name:ident, $source:ty, $target:ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; let neg_one: $source = -1; assert_eq!(<$target as TryFrom<$source>>::try_from(max).unwrap(), max as $target); @@ -428,8 +426,8 @@ macro_rules! test_impl_try_from_unsigned_to_signed_upper_err { ($fn_name:ident, $source:ty, $target:ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); assert_eq!(<$target as TryFrom<$source>>::try_from(min).unwrap(), min as $target); @@ -489,11 +487,11 @@ macro_rules! test_impl_try_from_same_sign_err { ($fn_name:ident, $source:ty, $target:ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; - let t_max = <$target>::max_value(); - let t_min = <$target>::min_value(); + let t_max = <$target>::MAX; + let t_min = <$target>::MIN; assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); if min != 0 { assert!(<$target as TryFrom<$source>>::try_from(min).is_err()); @@ -578,11 +576,11 @@ macro_rules! test_impl_try_from_signed_to_unsigned_err { ($fn_name:ident, $source:ty, $target:ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; - let t_max = <$target>::max_value(); - let t_min = <$target>::min_value(); + let t_max = <$target>::MAX; + let t_min = <$target>::MIN; assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); assert!(<$target as TryFrom<$source>>::try_from(min).is_err()); assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target); @@ -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/ptr.rs b/src/libcore/tests/ptr.rs index a008b3319f39a..9fea34d668fcc 100644 --- a/src/libcore/tests/ptr.rs +++ b/src/libcore/tests/ptr.rs @@ -357,7 +357,7 @@ fn align_offset_weird_strides() { unsafe fn test_weird_stride(ptr: *const T, align: usize) -> bool { let numptr = ptr as usize; - let mut expected = usize::max_value(); + let mut expected = usize::MAX; // Naive but definitely correct way to find the *first* aligned element of stride::. for el in 0..align { if (numptr + el * ::std::mem::size_of::()) % align == 0 { diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index dbab433e33f48..cd46117f76322 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]; @@ -1697,8 +1691,8 @@ fn test_copy_within_panics_src_inverted() { #[should_panic(expected = "attempted to index slice up to maximum usize")] fn test_copy_within_panics_src_out_of_bounds() { let mut bytes = *b"Hello, World!"; - // an inclusive range ending at usize::max_value() would make src_end overflow - bytes.copy_within(usize::max_value()..=usize::max_value(), 0); + // an inclusive range ending at usize::MAX would make src_end overflow + bytes.copy_within(usize::MAX..=usize::MAX, 0); } #[test] @@ -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..3b6dafeee2540 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; @@ -31,7 +31,7 @@ const MICROS_PER_SEC: u64 = 1_000_000; /// the number of nanoseconds. /// /// `Duration`s implement many common traits, including [`Add`], [`Sub`], and other -/// [`ops`] traits. +/// [`ops`] traits. It implements `Default` by returning a zero-length `Duration`. /// /// [`Add`]: ../../std/ops/trait.Add.html /// [`Sub`]: ../../std/ops/trait.Sub.html @@ -138,6 +138,24 @@ impl Duration { Duration { secs, nanos } } + /// Creates a new `Duration` that spans no time. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_zero)] + /// use std::time::Duration; + /// + /// let duration = Duration::zero(); + /// assert!(duration.is_zero()); + /// assert_eq!(duration.as_nanos(), 0); + /// ``` + #[unstable(feature = "duration_zero", issue = "73544")] + #[inline] + pub const fn zero() -> Duration { + Duration { secs: 0, nanos: 0 } + } + /// Creates a new `Duration` from the specified number of whole seconds. /// /// # Examples @@ -152,7 +170,6 @@ impl Duration { /// ``` #[stable(feature = "duration", since = "1.3.0")] #[inline] - #[rustc_promotable] #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] pub const fn from_secs(secs: u64) -> Duration { Duration { secs, nanos: 0 } @@ -224,6 +241,29 @@ impl Duration { } } + /// Returns true if this `Duration` spans no time. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_zero)] + /// use std::time::Duration; + /// + /// assert!(Duration::zero().is_zero()); + /// assert!(Duration::new(0, 0).is_zero()); + /// assert!(Duration::from_nanos(0).is_zero()); + /// assert!(Duration::from_secs(0).is_zero()); + /// + /// assert!(!Duration::new(1, 1).is_zero()); + /// assert!(!Duration::from_nanos(1).is_zero()); + /// assert!(!Duration::from_secs(1).is_zero()); + /// ``` + #[unstable(feature = "duration_zero", issue = "73544")] + #[inline] + pub const fn is_zero(&self) -> bool { + self.secs == 0 && self.nanos == 0 + } + /// Returns the number of _whole_ seconds contained by this `Duration`. /// /// The returned value does not include the fractional (nanosecond) part of the @@ -389,7 +429,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 +500,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/printable.rs b/src/libcore/unicode/printable.rs index eee9ea52ef0d2..9680aa14d3b54 100644 --- a/src/libcore/unicode/printable.rs +++ b/src/libcore/unicode/printable.rs @@ -44,7 +44,7 @@ pub(crate) fn is_printable(x: char) -> bool { } else if x < 0x20000 { check(lower, SINGLETONS1U, SINGLETONS1L, NORMAL1) } else { - if 0x2a6d7 <= x && x < 0x2a700 { + if 0x2a6de <= x && x < 0x2a700 { return false; } if 0x2b735 <= x && x < 0x2b740 { @@ -59,7 +59,10 @@ pub(crate) fn is_printable(x: char) -> bool { if 0x2ebe1 <= x && x < 0x2f800 { return false; } - if 0x2fa1e <= x && x < 0xe0100 { + if 0x2fa1e <= x && x < 0x30000 { + return false; + } + if 0x3134b <= x && x < 0xe0100 { return false; } if 0xe01f0 <= x && x < 0x110000 { @@ -81,7 +84,7 @@ const SINGLETONS0U: &[(u8, u8)] = &[ (0x0a, 28), (0x0b, 25), (0x0c, 20), - (0x0d, 18), + (0x0d, 16), (0x0e, 13), (0x0f, 4), (0x10, 3), @@ -96,7 +99,7 @@ const SINGLETONS0U: &[(u8, u8)] = &[ (0x1d, 1), (0x1f, 22), (0x20, 3), - (0x2b, 4), + (0x2b, 3), (0x2c, 2), (0x2d, 11), (0x2e, 1), @@ -129,29 +132,29 @@ const SINGLETONS0L: &[u8] = &[ 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, 0x91, 0xa9, 0xb4, 0xba, - 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x04, - 0x0d, 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x81, - 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, 0xd7, 0xf0, - 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, - 0xc5, 0xc7, 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, - 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, 0x4e, 0x4f, - 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, - 0xb6, 0xb7, 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, - 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, 0xfe, 0xff, - 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, - 0x1f, 0x6e, 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, - 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, 0x17, 0x1e, - 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, - 0x5e, 0x7e, 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, - 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, - 0x96, 0x97, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, - 0xaf, 0xb7, 0xbf, 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, - 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, 0xc1, - 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, - 0x0f, 0x10, 0x27, 0x2f, 0xee, 0xef, 0x6e, 0x6f, - 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, 0xfe, - 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, - 0xd8, 0xd9, 0xe7, 0xfe, 0xff, + 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, + 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, + 0xbc, 0xbe, 0xbf, 0xd5, 0xd7, 0xf0, 0xf1, 0x83, + 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, + 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, + 0xc6, 0xce, 0xcf, 0x49, 0x4e, 0x4f, 0x57, 0x59, + 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, + 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, + 0x5b, 0x5c, 0xf6, 0xf7, 0xfe, 0xff, 0x80, 0x0d, + 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, + 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, + 0xbb, 0xbc, 0xfa, 0x16, 0x17, 0x1e, 0x1f, 0x46, + 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, + 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, + 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x96, 0x2f, + 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, + 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, + 0x30, 0x8f, 0x1f, 0xc0, 0xc1, 0xce, 0xff, 0x4e, + 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, + 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, + 0x42, 0x45, 0x90, 0x91, 0xfe, 0xff, 0x53, 0x67, + 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, + 0xfe, 0xff, ]; #[rustfmt::skip] const SINGLETONS1U: &[(u8, u8)] = &[ @@ -163,14 +166,15 @@ const SINGLETONS1U: &[(u8, u8)] = &[ (0x09, 2), (0x0a, 5), (0x0b, 2), + (0x0e, 4), (0x10, 1), - (0x11, 4), + (0x11, 2), (0x12, 5), (0x13, 17), - (0x14, 2), + (0x14, 1), (0x15, 2), (0x17, 2), - (0x19, 4), + (0x19, 13), (0x1c, 5), (0x1d, 8), (0x24, 1), @@ -188,32 +192,35 @@ const SINGLETONS1U: &[(u8, u8)] = &[ (0xe8, 2), (0xee, 32), (0xf0, 4), - (0xf9, 6), + (0xf8, 2), + (0xf9, 2), (0xfa, 2), + (0xfb, 1), ]; #[rustfmt::skip] const SINGLETONS1L: &[u8] = &[ 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, - 0x37, 0x56, 0x57, 0xbd, 0x35, 0xce, 0xcf, 0xe0, - 0x12, 0x87, 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, - 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, 0x45, 0x46, - 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5a, 0x5c, - 0xb6, 0xb7, 0x1b, 0x1c, 0xa8, 0xa9, 0xd8, 0xd9, - 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, - 0x3e, 0x66, 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, - 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, 0x28, 0x55, - 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, - 0xba, 0xbc, 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, - 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, 0xcd, - 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, - 0xc5, 0xc6, 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, - 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, 0x50, 0x53, - 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, - 0x65, 0x66, 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, - 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, 0x0c, 0x72, - 0xa3, 0xa4, 0xcb, 0xcc, 0x6e, 0x6f, + 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, + 0x35, 0xe0, 0x12, 0x87, 0x89, 0x8e, 0x9e, 0x04, + 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, + 0x5c, 0xb6, 0xb7, 0x1b, 0x1c, 0x07, 0x08, 0x0a, + 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, + 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, + 0x0a, 0x3b, 0x3e, 0x66, 0x69, 0x8f, 0x92, 0x6f, + 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, + 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, + 0xa8, 0xad, 0xba, 0xbc, 0xc4, 0x06, 0x0b, 0x0c, + 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, + 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, + 0x3e, 0x3f, 0xc5, 0xc6, 0x04, 0x20, 0x23, 0x25, + 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, + 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, + 0x60, 0x63, 0x65, 0x66, 0x6b, 0x73, 0x78, 0x7d, + 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, + 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, ]; #[rustfmt::skip] const NORMAL0: &[u8] = &[ @@ -225,7 +232,7 @@ const NORMAL0: &[u8] = &[ 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, - 0x1e, 0x15, + 0x28, 0x0b, 0x80, 0xe0, 0x03, 0x19, 0x08, 0x01, 0x04, @@ -237,8 +244,8 @@ const NORMAL0: &[u8] = &[ 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, - 0x55, 0x08, - 0x02, 0x04, + 0x55, 0x07, + 0x03, 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, @@ -292,7 +299,7 @@ const NORMAL0: &[u8] = &[ 0x0b, 0x03, 0x80, 0xac, 0x06, 0x0a, 0x06, - 0x1f, 0x41, + 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, @@ -315,21 +322,19 @@ const NORMAL0: &[u8] = &[ 0x3b, 0x07, 0x02, 0x0e, 0x18, 0x09, - 0x80, 0xb0, 0x30, + 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, - 0x80, 0xb6, 0x05, - 0x24, 0x0c, - 0x9b, 0xc6, 0x0a, - 0xd2, 0x30, 0x10, + 0x80, 0xdf, 0x0c, + 0xee, 0x0d, 0x03, 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, - 0x80, 0xc7, 0x30, - 0x35, 0x04, + 0x80, 0xcb, 0x2a, + 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, @@ -341,7 +346,7 @@ const NORMAL0: &[u8] = &[ 0x80, 0x83, 0x18, 0x1c, 0x0a, 0x16, 0x09, - 0x48, 0x08, + 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, 0x04, @@ -365,7 +370,7 @@ const NORMAL1: &[u8] = &[ 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, - 0x65, 0x04, + 0x66, 0x03, 0x01, 0x2f, 0x2e, 0x80, 0x82, 0x1d, 0x03, @@ -410,16 +415,17 @@ const NORMAL1: &[u8] = &[ 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, - 0x1f, 0x80, 0x81, + 0x52, 0x4e, 0x28, 0x08, - 0x2a, 0x80, 0x86, + 0x2a, 0x56, + 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, 0x19, 0x07, 0x0a, 0x06, - 0x47, 0x09, + 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, @@ -430,7 +436,7 @@ const NORMAL1: &[u8] = &[ 0x01, 0x05, 0x10, 0x03, 0x05, 0x80, 0x8b, - 0x60, 0x20, + 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, @@ -443,7 +449,8 @@ const NORMAL1: &[u8] = &[ 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, - 0x01, 0x80, 0xa0, + 0x48, 0x09, + 0x0a, 0x46, 0x45, 0x1b, 0x48, 0x08, 0x53, 0x1d, @@ -456,7 +463,8 @@ const NORMAL1: &[u8] = &[ 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, 0x36, - 0x19, 0x80, 0xc7, + 0x19, 0x80, 0xb7, + 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, 0x0b, @@ -474,9 +482,11 @@ const NORMAL1: &[u8] = &[ 0x4b, 0x04, 0x39, 0x07, 0x11, 0x40, - 0x04, 0x1c, + 0x05, 0x0b, + 0x02, 0x0e, 0x97, 0xf8, 0x08, - 0x82, 0xf3, 0xa5, 0x0d, + 0x84, 0xd6, 0x2a, + 0x09, 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, @@ -515,17 +525,15 @@ const NORMAL1: &[u8] = &[ 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, - 0x0d, 0x03, - 0x5d, 0x03, - 0x3d, 0x39, + 0x80, 0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, 0x9a, - 0x83, 0xd6, 0x0a, + 0x83, 0xd8, 0x08, + 0x0d, 0x03, 0x0d, 0x03, - 0x0b, 0x05, 0x74, 0x0c, 0x59, 0x07, 0x0c, 0x14, @@ -533,12 +541,15 @@ const NORMAL1: &[u8] = &[ 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, - 0x1e, 0x52, - 0x77, 0x03, - 0x31, 0x03, - 0x80, 0xa6, 0x0c, - 0x14, 0x04, + 0x22, 0x4e, + 0x81, 0x54, 0x0c, + 0x15, 0x03, 0x03, 0x05, + 0x07, 0x09, + 0x19, 0x07, + 0x07, 0x09, 0x03, 0x0d, - 0x06, 0x85, 0x6a, + 0x07, 0x29, + 0x80, 0xcb, 0x25, + 0x0a, 0x84, 0x06, ]; diff --git a/src/libcore/unicode/unicode_data.rs b/src/libcore/unicode/unicode_data.rs index da4cd4e9b1da1..9c92a8ba28ae4 100644 --- a/src/libcore/unicode/unicode_data.rs +++ b/src/libcore/unicode/unicode_data.rs @@ -1,614 +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) = (12, 1, 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) = (190, 37); - static BITSET_CHUNKS_MAP: [u8; 187] = [ - 6, 32, 10, 18, 19, 23, 21, 12, 7, 5, 0, 20, 14, 49, 49, 49, 49, 49, 49, 36, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 47, 49, 30, 8, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 45, 0, 0, 0, 0, 0, 0, 0, 0, 4, 35, 17, 31, 16, 25, 24, 26, 13, 15, - 44, 27, 0, 0, 49, 11, 0, 0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 0, 38, 1, 49, 49, 49, 49, 49, 48, - 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 28, 0, 0, 0, 0, 0, 29, 0, 0, 9, 0, 33, 2, 3, 0, 0, - 0, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 41, 49, 49, 49, - 43, 22, 49, 49, 49, 49, 40, 49, 49, 49, 49, 49, 49, 46, + 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]; 50] = [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 0, 0, 248, 241, 38, 40], - [0, 0, 0, 0, 0, 0, 0, 0, 108, 133, 110, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 190, 200, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 248, 248, 248, 248, 248, 205, 248, 23, 134, 245, 68, 237], - [0, 0, 179, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 103, 99, 176, 248, 248, 248, 248, 248, 248, 248, 61, 0, 151, 217, 178], - [0, 145, 28, 0, 168, 221, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [48, 77, 248, 165, 201, 120, 184, 137, 91, 175, 143, 83, 206, 196, 248, 56], - [53, 0, 0, 0, 126, 15, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0], - [59, 54, 127, 199, 167, 186, 157, 114, 154, 84, 160, 115, 158, 66, 155, 21], - [62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [91, 129, 164, 101, 248, 248, 248, 79, 248, 248, 248, 248, 230, 128, 135, 117], - [97, 0, 220, 144, 0, 0, 212, 44, 142, 240, 30, 97, 0, 0, 0, 0], - [116, 247, 219, 171, 188, 248, 104, 190, 0, 0, 0, 0, 0, 0, 0, 0], - [141, 185, 88, 0, 149, 213, 22, 0, 0, 0, 0, 89, 0, 0, 0, 0], - [147, 90, 35, 82, 98, 0, 153, 0, 85, 119, 29, 45, 86, 71, 18, 0], - [150, 32, 248, 107, 0, 81, 0, 0, 0, 0, 227, 17, 211, 105, 231, 19], - [162, 41, 161, 69, 163, 173, 123, 73, 106, 14, 124, 37, 1, 187, 121, 0], - [172, 240, 228, 170, 248, 248, 248, 248, 248, 229, 138, 235, 234, 24, 222, 125], - [208, 233, 248, 74, 204, 64, 140, 232, 63, 0, 0, 0, 0, 0, 0, 0], - [220, 97, 202, 86, 94, 78, 203, 9, 226, 80, 46, 0, 183, 11, 174, 67], - [231, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248], - [247, 248, 248, 248, 248, 248, 248, 248, 248, 209, 225, 95, 76, 75, 180, 25], - [248, 5, 96, 50, 72, 87, 248, 26, 132, 0, 198, 51, 159, 42, 0, 0], - [248, 8, 72, 72, 49, 0, 0, 0, 0, 0, 0, 0, 194, 5, 0, 89], - [248, 36, 248, 7, 0, 0, 139, 31, 143, 3, 93, 0, 55, 0, 0, 0], - [248, 62, 248, 248, 248, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [248, 118, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [248, 236, 166, 246, 136, 239, 248, 248, 248, 248, 215, 169, 182, 207, 214, 12], - [248, 248, 13, 130, 248, 248, 248, 248, 57, 146, 248, 65, 218, 248, 243, 177], - [248, 248, 191, 111, 197, 43, 0, 0, 248, 248, 248, 248, 91, 47, 0, 0], - [248, 248, 244, 248, 189, 223, 152, 70, 224, 210, 248, 148, 240, 242, 68, 100], - [248, 248, 248, 4, 248, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [248, 248, 248, 248, 35, 195, 248, 248, 248, 248, 248, 113, 0, 0, 0, 0], - [248, 248, 248, 248, 131, 240, 238, 109, 0, 181, 248, 122, 102, 216, 143, 27], - [248, 248, 248, 248, 248, 248, 86, 0, 248, 248, 248, 248, 248, 248, 248, 248], - [248, 248, 248, 248, 248, 248, 248, 248, 33, 0, 0, 0, 0, 0, 0, 0], - [248, 248, 248, 248, 248, 248, 248, 248, 97, 35, 0, 60, 65, 156, 16, 0], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 6, 0, 0, 0, 0, 0, 0], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 192, 248, 248, 248, 248, 248], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 20, 248, 248, 248, 248], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 72, 0, 0, 0, 0], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 81, 248, 248, 248], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 23, 0], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 193, 112], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 39], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 65], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 92], - [248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248], + 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; 249] = [ - 0, 1, 15, 17, 31, 63, 127, 179, 511, 1023, 2191, 4079, 4087, 8191, 8319, 16384, 65535, - 131071, 262143, 4128527, 8388607, 8461767, 24870911, 67108863, 134217727, 276824575, - 335544350, 486341884, 536805376, 536870911, 553648127, 1056964608, 1073692671, 1073741823, - 1140785663, 2147483647, 2147485627, 4026540127, 4294934783, 8589934591, 47244640256, - 64548249055, 68191066527, 68719476735, 115913785343, 137438953215, 549755813888, - 1095220854783, 1099511627711, 1099511627775, 2199023190016, 2199023255551, 4398046511103, - 8641373536127, 8791831609343, 8795690369023, 8796093022207, 13198434443263, 17592186044415, - 35184321757183, 70368744112128, 88094074470339, 140737488355327, 140737488355328, - 141836999983103, 281474976710655, 563017343310239, 1125625028935679, 1125899906842623, - 1688915364814303, 2119858418286774, 2251795522912255, 2251799813685247, 3377704004976767, - 3509778554814463, 3905461007941631, 4503595333443583, 4503599627370495, 8796093022142464, - 9006649498927104, 9007192812290047, 9007199254740991, 15762594400829440, 17169970223906821, - 17732925109967239, 18014398491652207, 18014398509481983, 20266198323101808, - 36027697507139583, 36028792723996672, 36028792728190975, 36028797018963967, - 72057594037927935, 90071992547409919, 143851303137705983, 144053615424700415, - 144115188075855868, 144115188075855871, 288230371860938751, 297241973452963840, - 301749971126844416, 319718190147960832, 576460743713488896, 576460743847706622, - 576460748008488959, 576460752303359999, 576460752303423486, 576460752303423487, - 790380184120328175, 1152640029630136575, 1152917029519358975, 1152921504591118335, - 1152921504606845055, 1152921504606846975, 1153765996922689951, 2161727885562420159, - 2251241253188403424, 2295745090394464220, 2305570330330005503, 2305843004918726656, - 2305843004919250943, 2305843009196916483, 2305843009213693951, 3457638613854978028, - 4323455298678290390, 4557642822898941951, 4575692405780512767, 4602678814877679616, - 4611686017001275199, 4611686018360336384, 4611686018427322368, 4611686018427387903, - 4656722014700830719, 6843210385291930244, 6881498031078244479, 6908521828386340863, - 8935141660164089791, 8935423131384840192, 9168765891372858879, 9169328841326329855, - 9187201948305063935, 9187343239835811327, 9216616637413720063, 9223372036854775807, - 9223372041149743103, 9223934986808197120, 10371930679322607615, 10502394331027995967, - 11241233151490523135, 13006395723845991295, 13258596753222922239, 13609596598936928288, - 13834776580305453567, 13907115649320091647, 14082190885810440174, 14123225865944680428, - 16212958624174047247, 16412803692974677999, 16424062692043104238, 16424062692043104239, - 16424062692043243502, 16424625641996804079, 16429129241624174575, 16717361816799141871, - 16717361816799216127, 16788293510930366511, 17005555242810474495, 17293822569102704639, - 17581979622616071300, 17870283321271910397, 17870283321406070975, 17870283321406128127, - 17978369712463020031, 18158513764145585631, 18158781978395017215, 18194542490281852927, - 18410715276682199039, 18410715276690587772, 18428729675200069631, 18428729675200069632, - 18433233274827440127, 18437455399478099968, 18437736874452713471, 18442240474082181119, - 18444492273895866367, 18445618173802708993, 18446181192473632767, 18446216308128218879, - 18446462598732840928, 18446462598732840959, 18446462598732840960, 18446462599806582783, - 18446462615912710143, 18446462667452317695, 18446463149025525759, 18446463629525450752, - 18446463698110251007, 18446463698244468735, 18446464796682337663, 18446466966713532416, - 18446466996779287551, 18446471394825862144, 18446471394825863167, 18446480190918885375, - 18446498607738650623, 18446532967477018623, 18446602782178705022, 18446603336221163519, - 18446603336221196287, 18446638520593285119, 18446673709243564031, 18446708893632430079, - 18446740770879700992, 18446741595513422027, 18446741874686295551, 18446743249075830783, - 18446743798965862398, 18446744056529672000, 18446744060816261120, 18446744068886102015, - 18446744069414584320, 18446744069414601696, 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, 19, 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, 20, 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, 164], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 47, 52], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 171, 2], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 88, 134, 38], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 102, 6, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 76, 26, 0, 146, 136, 79, 43, 117], - [0, 0, 0, 0, 0, 0, 0, 0, 152, 0, 0, 58, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 165, 97, 75, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 48, 0, 114, 0, 0], - [0, 0, 0, 0, 0, 170, 68, 0, 0, 7, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0], - [0, 0, 0, 28, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 133, 0, 0, 0, 0, 15, 160, 45, 84, 51, 78, 12, 109], - [0, 0, 11, 0, 0, 30, 161, 90, 35, 80, 0, 69, 173, 13, 81, 129], - [0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 131, 0, 85, 0, 148, 0, 175, 73, 0, 0, 0, 0, 0, 0, 0], - [20, 4, 62, 0, 118, 0, 0, 0, 32, 154, 145, 0, 124, 89, 67, 86], - [25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [59, 0, 0, 150, 70, 24, 132, 60, 100, 122, 163, 99, 0, 46, 0, 66], - [63, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0], - [71, 33, 0, 178, 123, 83, 120, 137, 121, 98, 121, 167, 153, 55, 3, 18], - [72, 149, 36, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [104, 133, 0, 110, 174, 105, 177, 166, 0, 0, 0, 0, 0, 0, 155, 139], - [107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [111, 50, 106, 0, 0, 0, 0, 0, 0, 0, 172, 179, 179, 112, 9, 0], - [113, 0, 0, 0, 0, 0, 0, 49, 142, 34, 31, 0, 0, 0, 0, 0], - [116, 0, 42, 141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [140, 93, 37, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0], - [159, 0, 101, 0, 158, 10, 29, 0, 0, 0, 0, 91, 0, 0, 0, 0], - [162, 56, 153, 54, 125, 53, 0, 27, 115, 21, 126, 19, 108, 144, 127, 8], - [168, 41, 151, 5, 0, 0, 157, 39, 156, 1, 103, 0, 65, 0, 0, 0], - [169, 147, 130, 17, 96, 87, 143, 16, 138, 0, 0, 64, 125, 95, 0, 0], - [176, 179, 0, 0, 179, 179, 179, 77, 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; 180] = [ - 0, 1, 3, 4, 8, 13, 15, 28, 64, 176, 191, 1016, 1792, 2047, 4080, 4096, 7680, 8192, 8193, - 16192, 30720, 32704, 32768, 131008, 262016, 2097152, 2359296, 6030336, 8323072, 10682368, - 33554432, 58719232, 159383552, 234881024, 243138688, 402587711, 536805376, 536879204, - 546307648, 805306369, 1073741824, 1073741916, 2113929216, 3221225472, 3758096384, - 4026531840, 4160749568, 4294934528, 4294967296, 4512022528, 5368709120, 17179869183, - 47244640256, 51539615774, 51539619904, 51543810078, 51545914817, 66035122176, 412316860416, - 412316862532, 412316893184, 1030792151040, 2199023255648, 8641373536127, 8763880767488, - 17303886364672, 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, 6917775322003857411, - 7421334051581067264, 8070450532247928832, 8788774672813524990, 9205357638345293827, - 9222809086901354496, 9223091111633879040, 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, 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, 504403158265495676, - 576460743713488896, 576460743847706622, 1152921504591118335, 2295745090394464220, - 4557642822898941951, 4611686017001275199, 6908521828386340863, 8935141660164089791, - 9223934986808197120, 13605092999309557792, 16717361816799216127, 16717361816799223999, - 17005555242810474495, 17446871633794956420, 17870283321271910397, 17870283321406128127, - 18410715276682199039, 18428729675200069631, 18428729675200069632, 18437736874452713471, - 18446462598732840959, 18446462598732840960, 18446463698110251007, 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, 19, 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, 20, 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, 15, 18, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 70, 102, 29], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 62, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 83, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 103, 35, 66, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 35, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 117, 0, 0, 45, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 130, 78, 60, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 37, 0, 90, 0, 0], - [0, 0, 0, 0, 0, 129, 54, 0, 0, 3, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0], - [0, 0, 0, 19, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 67, 0, 114, 0, 137, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 7, 0, 0, 0, 125, 5, 24, 63, 0, 55, 135, 9, 64, 100], - [0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [10, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [12, 0, 48, 0, 92, 0, 0, 0, 25, 119, 113, 0, 96, 71, 53, 68], - [46, 0, 0, 116, 57, 17, 101, 44, 81, 94, 127, 80, 0, 0, 0, 52], - [49, 0, 0, 0, 83, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 0], - [56, 26, 0, 136, 95, 43, 107, 105, 93, 79, 93, 132, 128, 42, 104, 20], - [59, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [85, 0, 0, 87, 0, 0, 0, 131, 0, 0, 0, 0, 0, 0, 0, 0], - [89, 0, 0, 0, 0, 0, 0, 38, 110, 27, 22, 0, 0, 0, 0, 0], - [109, 74, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0], - [124, 0, 82, 0, 123, 6, 21, 0, 0, 0, 0, 72, 0, 0, 0, 0], - [126, 40, 118, 39, 108, 41, 0, 34, 91, 14, 97, 13, 86, 112, 98, 4], - [133, 32, 120, 2, 0, 0, 122, 30, 121, 1, 84, 0, 51, 0, 0, 0], - [134, 115, 88, 0, 77, 69, 111, 11, 106, 0, 0, 50, 108, 76, 0, 0], - [137, 138, 0, 0, 138, 138, 138, 62, 0, 0, 0, 0, 0, 0, 0, 0], - ]; - static BITSET: [u64; 139] = [ - 0, 1, 13, 28, 64, 182, 191, 1016, 2032, 2047, 4096, 7680, 14336, 16128, 32640, 32768, - 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, 51552198686, 51556262398, 137438953472, 412316860416, - 412316862532, 1030792151040, 2199023255648, 8641373536127, 8763880767488, 17303886364672, - 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, 7421334051581067264, - 8788774672813524990, 9205357638345293827, 9222809086901354496, 9223090561878065152, - 9223372036854775808, 9223372036854775935, 9224497932466651184, 9727775195120332910, - 10376293541461622786, 11526998316797657088, 11959590285459062784, 12103423998558208000, - 12699165786766311424, 13005832773892571136, 13798747783286489088, 13835058055282032640, - 13835058055282163729, 13951307220663664640, 14987979559889010690, 17872468738205286400, - 17906312118425092095, 18158513697557839871, 18158513749097456062, 18374686479671623680, - 18374686479671623682, 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, 504403158265495560, 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, - 18446463698110251007, 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) = (124, 11); - static BITSET_CHUNKS_MAP: [u8; 124] = [ - 30, 7, 10, 24, 18, 3, 28, 20, 23, 27, 0, 15, 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, 8, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 12, 17, 25, 16, 22, 19, 14, 21, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 6, 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, - 4, 1, 0, 0, 9, 0, 13, 26, + 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]; 33] = [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71], - [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, 32, 0, 0, 0, 48], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 42, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 21, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 46, 0, 0, 0, 2], - [0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 30, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 30, 0, 44, 0, 30, 0, 30, 0, 40, 0, 33], - [0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 36, 43, 4, 0, 0, 0, 0, 51, 22, 3, 0, 12], - [0, 0, 0, 6, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 34, 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, 0], - [0, 0, 0, 61, 46, 0, 0, 0, 0, 59, 0, 0, 23, 9, 0, 0], - [0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 2, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0], - [0, 14, 0, 14, 0, 0, 0, 0, 0, 14, 0, 2, 50, 0, 0, 0], - [0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 25, 0, 0, 0, 14, 24, 0, 0, 0, 0, 0, 0, 0, 0, 10], - [0, 31, 0, 46, 64, 0, 0, 38, 0, 0, 0, 46, 0, 0, 0, 0], - [0, 45, 2, 0, 0, 70, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 58, 0, 30, 0, 41, 0, 30, 0, 14, 0, 14, 35, 0, 0, 0], - [0, 62, 29, 60, 17, 0, 54, 69, 0, 56, 19, 27, 0, 63, 28, 0], - [0, 65, 37, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 68, 18, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 8, 0], - [14, 0, 0, 0, 0, 7, 0, 16, 0, 0, 15, 0, 0, 14, 46, 0], - [39, 0, 0, 14, 2, 0, 0, 47, 0, 14, 0, 0, 0, 0, 0, 46], - [46, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [49, 0, 0, 0, 0, 0, 11, 0, 24, 20, 66, 0, 0, 0, 0, 0], - [72, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - ]; - static BITSET: [u64; 73] = [ - 0, 999, 1023, 1026, 3072, 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, 13, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 10, 0, 38, 46, 44, 2], - [0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 51, 24, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 60, 62, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 27, 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, 23, 34, 35], - [0, 0, 57, 7, 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, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 30], - [0, 11, 0, 12, 50, 37, 36, 45, 47, 6, 0, 0, 0, 49, 18, 53], - [15, 0, 60, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [22, 52, 43, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [25, 39, 42, 41, 59, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [58, 65, 29, 17, 48, 63, 31, 20, 55, 61, 64, 32, 28, 21, 16, 4], + [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, 116, 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, 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, ) } } @@ -1202,20 +1135,21 @@ pub mod conversions { ('\u{a7ba}', ['\u{a7bb}', '\u{0}', '\u{0}']), ('\u{a7bc}', ['\u{a7bd}', '\u{0}', '\u{0}']), ('\u{a7be}', ['\u{a7bf}', '\u{0}', '\u{0}']), ('\u{a7c2}', ['\u{a7c3}', '\u{0}', '\u{0}']), ('\u{a7c4}', ['\u{a794}', '\u{0}', '\u{0}']), ('\u{a7c5}', ['\u{282}', '\u{0}', '\u{0}']), - ('\u{a7c6}', ['\u{1d8e}', '\u{0}', '\u{0}']), ('\u{ff21}', ['\u{ff41}', '\u{0}', '\u{0}']), - ('\u{ff22}', ['\u{ff42}', '\u{0}', '\u{0}']), ('\u{ff23}', ['\u{ff43}', '\u{0}', '\u{0}']), - ('\u{ff24}', ['\u{ff44}', '\u{0}', '\u{0}']), ('\u{ff25}', ['\u{ff45}', '\u{0}', '\u{0}']), - ('\u{ff26}', ['\u{ff46}', '\u{0}', '\u{0}']), ('\u{ff27}', ['\u{ff47}', '\u{0}', '\u{0}']), - ('\u{ff28}', ['\u{ff48}', '\u{0}', '\u{0}']), ('\u{ff29}', ['\u{ff49}', '\u{0}', '\u{0}']), - ('\u{ff2a}', ['\u{ff4a}', '\u{0}', '\u{0}']), ('\u{ff2b}', ['\u{ff4b}', '\u{0}', '\u{0}']), - ('\u{ff2c}', ['\u{ff4c}', '\u{0}', '\u{0}']), ('\u{ff2d}', ['\u{ff4d}', '\u{0}', '\u{0}']), - ('\u{ff2e}', ['\u{ff4e}', '\u{0}', '\u{0}']), ('\u{ff2f}', ['\u{ff4f}', '\u{0}', '\u{0}']), - ('\u{ff30}', ['\u{ff50}', '\u{0}', '\u{0}']), ('\u{ff31}', ['\u{ff51}', '\u{0}', '\u{0}']), - ('\u{ff32}', ['\u{ff52}', '\u{0}', '\u{0}']), ('\u{ff33}', ['\u{ff53}', '\u{0}', '\u{0}']), - ('\u{ff34}', ['\u{ff54}', '\u{0}', '\u{0}']), ('\u{ff35}', ['\u{ff55}', '\u{0}', '\u{0}']), - ('\u{ff36}', ['\u{ff56}', '\u{0}', '\u{0}']), ('\u{ff37}', ['\u{ff57}', '\u{0}', '\u{0}']), - ('\u{ff38}', ['\u{ff58}', '\u{0}', '\u{0}']), ('\u{ff39}', ['\u{ff59}', '\u{0}', '\u{0}']), - ('\u{ff3a}', ['\u{ff5a}', '\u{0}', '\u{0}']), + ('\u{a7c6}', ['\u{1d8e}', '\u{0}', '\u{0}']), ('\u{a7c7}', ['\u{a7c8}', '\u{0}', '\u{0}']), + ('\u{a7c9}', ['\u{a7ca}', '\u{0}', '\u{0}']), ('\u{a7f5}', ['\u{a7f6}', '\u{0}', '\u{0}']), + ('\u{ff21}', ['\u{ff41}', '\u{0}', '\u{0}']), ('\u{ff22}', ['\u{ff42}', '\u{0}', '\u{0}']), + ('\u{ff23}', ['\u{ff43}', '\u{0}', '\u{0}']), ('\u{ff24}', ['\u{ff44}', '\u{0}', '\u{0}']), + ('\u{ff25}', ['\u{ff45}', '\u{0}', '\u{0}']), ('\u{ff26}', ['\u{ff46}', '\u{0}', '\u{0}']), + ('\u{ff27}', ['\u{ff47}', '\u{0}', '\u{0}']), ('\u{ff28}', ['\u{ff48}', '\u{0}', '\u{0}']), + ('\u{ff29}', ['\u{ff49}', '\u{0}', '\u{0}']), ('\u{ff2a}', ['\u{ff4a}', '\u{0}', '\u{0}']), + ('\u{ff2b}', ['\u{ff4b}', '\u{0}', '\u{0}']), ('\u{ff2c}', ['\u{ff4c}', '\u{0}', '\u{0}']), + ('\u{ff2d}', ['\u{ff4d}', '\u{0}', '\u{0}']), ('\u{ff2e}', ['\u{ff4e}', '\u{0}', '\u{0}']), + ('\u{ff2f}', ['\u{ff4f}', '\u{0}', '\u{0}']), ('\u{ff30}', ['\u{ff50}', '\u{0}', '\u{0}']), + ('\u{ff31}', ['\u{ff51}', '\u{0}', '\u{0}']), ('\u{ff32}', ['\u{ff52}', '\u{0}', '\u{0}']), + ('\u{ff33}', ['\u{ff53}', '\u{0}', '\u{0}']), ('\u{ff34}', ['\u{ff54}', '\u{0}', '\u{0}']), + ('\u{ff35}', ['\u{ff55}', '\u{0}', '\u{0}']), ('\u{ff36}', ['\u{ff56}', '\u{0}', '\u{0}']), + ('\u{ff37}', ['\u{ff57}', '\u{0}', '\u{0}']), ('\u{ff38}', ['\u{ff58}', '\u{0}', '\u{0}']), + ('\u{ff39}', ['\u{ff59}', '\u{0}', '\u{0}']), ('\u{ff3a}', ['\u{ff5a}', '\u{0}', '\u{0}']), ('\u{10400}', ['\u{10428}', '\u{0}', '\u{0}']), ('\u{10401}', ['\u{10429}', '\u{0}', '\u{0}']), ('\u{10402}', ['\u{1042a}', '\u{0}', '\u{0}']), @@ -2052,51 +1986,52 @@ pub mod conversions { ('\u{a7b7}', ['\u{a7b6}', '\u{0}', '\u{0}']), ('\u{a7b9}', ['\u{a7b8}', '\u{0}', '\u{0}']), ('\u{a7bb}', ['\u{a7ba}', '\u{0}', '\u{0}']), ('\u{a7bd}', ['\u{a7bc}', '\u{0}', '\u{0}']), ('\u{a7bf}', ['\u{a7be}', '\u{0}', '\u{0}']), ('\u{a7c3}', ['\u{a7c2}', '\u{0}', '\u{0}']), - ('\u{ab53}', ['\u{a7b3}', '\u{0}', '\u{0}']), ('\u{ab70}', ['\u{13a0}', '\u{0}', '\u{0}']), - ('\u{ab71}', ['\u{13a1}', '\u{0}', '\u{0}']), ('\u{ab72}', ['\u{13a2}', '\u{0}', '\u{0}']), - ('\u{ab73}', ['\u{13a3}', '\u{0}', '\u{0}']), ('\u{ab74}', ['\u{13a4}', '\u{0}', '\u{0}']), - ('\u{ab75}', ['\u{13a5}', '\u{0}', '\u{0}']), ('\u{ab76}', ['\u{13a6}', '\u{0}', '\u{0}']), - ('\u{ab77}', ['\u{13a7}', '\u{0}', '\u{0}']), ('\u{ab78}', ['\u{13a8}', '\u{0}', '\u{0}']), - ('\u{ab79}', ['\u{13a9}', '\u{0}', '\u{0}']), ('\u{ab7a}', ['\u{13aa}', '\u{0}', '\u{0}']), - ('\u{ab7b}', ['\u{13ab}', '\u{0}', '\u{0}']), ('\u{ab7c}', ['\u{13ac}', '\u{0}', '\u{0}']), - ('\u{ab7d}', ['\u{13ad}', '\u{0}', '\u{0}']), ('\u{ab7e}', ['\u{13ae}', '\u{0}', '\u{0}']), - ('\u{ab7f}', ['\u{13af}', '\u{0}', '\u{0}']), ('\u{ab80}', ['\u{13b0}', '\u{0}', '\u{0}']), - ('\u{ab81}', ['\u{13b1}', '\u{0}', '\u{0}']), ('\u{ab82}', ['\u{13b2}', '\u{0}', '\u{0}']), - ('\u{ab83}', ['\u{13b3}', '\u{0}', '\u{0}']), ('\u{ab84}', ['\u{13b4}', '\u{0}', '\u{0}']), - ('\u{ab85}', ['\u{13b5}', '\u{0}', '\u{0}']), ('\u{ab86}', ['\u{13b6}', '\u{0}', '\u{0}']), - ('\u{ab87}', ['\u{13b7}', '\u{0}', '\u{0}']), ('\u{ab88}', ['\u{13b8}', '\u{0}', '\u{0}']), - ('\u{ab89}', ['\u{13b9}', '\u{0}', '\u{0}']), ('\u{ab8a}', ['\u{13ba}', '\u{0}', '\u{0}']), - ('\u{ab8b}', ['\u{13bb}', '\u{0}', '\u{0}']), ('\u{ab8c}', ['\u{13bc}', '\u{0}', '\u{0}']), - ('\u{ab8d}', ['\u{13bd}', '\u{0}', '\u{0}']), ('\u{ab8e}', ['\u{13be}', '\u{0}', '\u{0}']), - ('\u{ab8f}', ['\u{13bf}', '\u{0}', '\u{0}']), ('\u{ab90}', ['\u{13c0}', '\u{0}', '\u{0}']), - ('\u{ab91}', ['\u{13c1}', '\u{0}', '\u{0}']), ('\u{ab92}', ['\u{13c2}', '\u{0}', '\u{0}']), - ('\u{ab93}', ['\u{13c3}', '\u{0}', '\u{0}']), ('\u{ab94}', ['\u{13c4}', '\u{0}', '\u{0}']), - ('\u{ab95}', ['\u{13c5}', '\u{0}', '\u{0}']), ('\u{ab96}', ['\u{13c6}', '\u{0}', '\u{0}']), - ('\u{ab97}', ['\u{13c7}', '\u{0}', '\u{0}']), ('\u{ab98}', ['\u{13c8}', '\u{0}', '\u{0}']), - ('\u{ab99}', ['\u{13c9}', '\u{0}', '\u{0}']), ('\u{ab9a}', ['\u{13ca}', '\u{0}', '\u{0}']), - ('\u{ab9b}', ['\u{13cb}', '\u{0}', '\u{0}']), ('\u{ab9c}', ['\u{13cc}', '\u{0}', '\u{0}']), - ('\u{ab9d}', ['\u{13cd}', '\u{0}', '\u{0}']), ('\u{ab9e}', ['\u{13ce}', '\u{0}', '\u{0}']), - ('\u{ab9f}', ['\u{13cf}', '\u{0}', '\u{0}']), ('\u{aba0}', ['\u{13d0}', '\u{0}', '\u{0}']), - ('\u{aba1}', ['\u{13d1}', '\u{0}', '\u{0}']), ('\u{aba2}', ['\u{13d2}', '\u{0}', '\u{0}']), - ('\u{aba3}', ['\u{13d3}', '\u{0}', '\u{0}']), ('\u{aba4}', ['\u{13d4}', '\u{0}', '\u{0}']), - ('\u{aba5}', ['\u{13d5}', '\u{0}', '\u{0}']), ('\u{aba6}', ['\u{13d6}', '\u{0}', '\u{0}']), - ('\u{aba7}', ['\u{13d7}', '\u{0}', '\u{0}']), ('\u{aba8}', ['\u{13d8}', '\u{0}', '\u{0}']), - ('\u{aba9}', ['\u{13d9}', '\u{0}', '\u{0}']), ('\u{abaa}', ['\u{13da}', '\u{0}', '\u{0}']), - ('\u{abab}', ['\u{13db}', '\u{0}', '\u{0}']), ('\u{abac}', ['\u{13dc}', '\u{0}', '\u{0}']), - ('\u{abad}', ['\u{13dd}', '\u{0}', '\u{0}']), ('\u{abae}', ['\u{13de}', '\u{0}', '\u{0}']), - ('\u{abaf}', ['\u{13df}', '\u{0}', '\u{0}']), ('\u{abb0}', ['\u{13e0}', '\u{0}', '\u{0}']), - ('\u{abb1}', ['\u{13e1}', '\u{0}', '\u{0}']), ('\u{abb2}', ['\u{13e2}', '\u{0}', '\u{0}']), - ('\u{abb3}', ['\u{13e3}', '\u{0}', '\u{0}']), ('\u{abb4}', ['\u{13e4}', '\u{0}', '\u{0}']), - ('\u{abb5}', ['\u{13e5}', '\u{0}', '\u{0}']), ('\u{abb6}', ['\u{13e6}', '\u{0}', '\u{0}']), - ('\u{abb7}', ['\u{13e7}', '\u{0}', '\u{0}']), ('\u{abb8}', ['\u{13e8}', '\u{0}', '\u{0}']), - ('\u{abb9}', ['\u{13e9}', '\u{0}', '\u{0}']), ('\u{abba}', ['\u{13ea}', '\u{0}', '\u{0}']), - ('\u{abbb}', ['\u{13eb}', '\u{0}', '\u{0}']), ('\u{abbc}', ['\u{13ec}', '\u{0}', '\u{0}']), - ('\u{abbd}', ['\u{13ed}', '\u{0}', '\u{0}']), ('\u{abbe}', ['\u{13ee}', '\u{0}', '\u{0}']), - ('\u{abbf}', ['\u{13ef}', '\u{0}', '\u{0}']), ('\u{fb00}', ['F', 'F', '\u{0}']), - ('\u{fb01}', ['F', 'I', '\u{0}']), ('\u{fb02}', ['F', 'L', '\u{0}']), - ('\u{fb03}', ['F', 'F', 'I']), ('\u{fb04}', ['F', 'F', 'L']), - ('\u{fb05}', ['S', 'T', '\u{0}']), ('\u{fb06}', ['S', 'T', '\u{0}']), - ('\u{fb13}', ['\u{544}', '\u{546}', '\u{0}']), + ('\u{a7c8}', ['\u{a7c7}', '\u{0}', '\u{0}']), ('\u{a7ca}', ['\u{a7c9}', '\u{0}', '\u{0}']), + ('\u{a7f6}', ['\u{a7f5}', '\u{0}', '\u{0}']), ('\u{ab53}', ['\u{a7b3}', '\u{0}', '\u{0}']), + ('\u{ab70}', ['\u{13a0}', '\u{0}', '\u{0}']), ('\u{ab71}', ['\u{13a1}', '\u{0}', '\u{0}']), + ('\u{ab72}', ['\u{13a2}', '\u{0}', '\u{0}']), ('\u{ab73}', ['\u{13a3}', '\u{0}', '\u{0}']), + ('\u{ab74}', ['\u{13a4}', '\u{0}', '\u{0}']), ('\u{ab75}', ['\u{13a5}', '\u{0}', '\u{0}']), + ('\u{ab76}', ['\u{13a6}', '\u{0}', '\u{0}']), ('\u{ab77}', ['\u{13a7}', '\u{0}', '\u{0}']), + ('\u{ab78}', ['\u{13a8}', '\u{0}', '\u{0}']), ('\u{ab79}', ['\u{13a9}', '\u{0}', '\u{0}']), + ('\u{ab7a}', ['\u{13aa}', '\u{0}', '\u{0}']), ('\u{ab7b}', ['\u{13ab}', '\u{0}', '\u{0}']), + ('\u{ab7c}', ['\u{13ac}', '\u{0}', '\u{0}']), ('\u{ab7d}', ['\u{13ad}', '\u{0}', '\u{0}']), + ('\u{ab7e}', ['\u{13ae}', '\u{0}', '\u{0}']), ('\u{ab7f}', ['\u{13af}', '\u{0}', '\u{0}']), + ('\u{ab80}', ['\u{13b0}', '\u{0}', '\u{0}']), ('\u{ab81}', ['\u{13b1}', '\u{0}', '\u{0}']), + ('\u{ab82}', ['\u{13b2}', '\u{0}', '\u{0}']), ('\u{ab83}', ['\u{13b3}', '\u{0}', '\u{0}']), + ('\u{ab84}', ['\u{13b4}', '\u{0}', '\u{0}']), ('\u{ab85}', ['\u{13b5}', '\u{0}', '\u{0}']), + ('\u{ab86}', ['\u{13b6}', '\u{0}', '\u{0}']), ('\u{ab87}', ['\u{13b7}', '\u{0}', '\u{0}']), + ('\u{ab88}', ['\u{13b8}', '\u{0}', '\u{0}']), ('\u{ab89}', ['\u{13b9}', '\u{0}', '\u{0}']), + ('\u{ab8a}', ['\u{13ba}', '\u{0}', '\u{0}']), ('\u{ab8b}', ['\u{13bb}', '\u{0}', '\u{0}']), + ('\u{ab8c}', ['\u{13bc}', '\u{0}', '\u{0}']), ('\u{ab8d}', ['\u{13bd}', '\u{0}', '\u{0}']), + ('\u{ab8e}', ['\u{13be}', '\u{0}', '\u{0}']), ('\u{ab8f}', ['\u{13bf}', '\u{0}', '\u{0}']), + ('\u{ab90}', ['\u{13c0}', '\u{0}', '\u{0}']), ('\u{ab91}', ['\u{13c1}', '\u{0}', '\u{0}']), + ('\u{ab92}', ['\u{13c2}', '\u{0}', '\u{0}']), ('\u{ab93}', ['\u{13c3}', '\u{0}', '\u{0}']), + ('\u{ab94}', ['\u{13c4}', '\u{0}', '\u{0}']), ('\u{ab95}', ['\u{13c5}', '\u{0}', '\u{0}']), + ('\u{ab96}', ['\u{13c6}', '\u{0}', '\u{0}']), ('\u{ab97}', ['\u{13c7}', '\u{0}', '\u{0}']), + ('\u{ab98}', ['\u{13c8}', '\u{0}', '\u{0}']), ('\u{ab99}', ['\u{13c9}', '\u{0}', '\u{0}']), + ('\u{ab9a}', ['\u{13ca}', '\u{0}', '\u{0}']), ('\u{ab9b}', ['\u{13cb}', '\u{0}', '\u{0}']), + ('\u{ab9c}', ['\u{13cc}', '\u{0}', '\u{0}']), ('\u{ab9d}', ['\u{13cd}', '\u{0}', '\u{0}']), + ('\u{ab9e}', ['\u{13ce}', '\u{0}', '\u{0}']), ('\u{ab9f}', ['\u{13cf}', '\u{0}', '\u{0}']), + ('\u{aba0}', ['\u{13d0}', '\u{0}', '\u{0}']), ('\u{aba1}', ['\u{13d1}', '\u{0}', '\u{0}']), + ('\u{aba2}', ['\u{13d2}', '\u{0}', '\u{0}']), ('\u{aba3}', ['\u{13d3}', '\u{0}', '\u{0}']), + ('\u{aba4}', ['\u{13d4}', '\u{0}', '\u{0}']), ('\u{aba5}', ['\u{13d5}', '\u{0}', '\u{0}']), + ('\u{aba6}', ['\u{13d6}', '\u{0}', '\u{0}']), ('\u{aba7}', ['\u{13d7}', '\u{0}', '\u{0}']), + ('\u{aba8}', ['\u{13d8}', '\u{0}', '\u{0}']), ('\u{aba9}', ['\u{13d9}', '\u{0}', '\u{0}']), + ('\u{abaa}', ['\u{13da}', '\u{0}', '\u{0}']), ('\u{abab}', ['\u{13db}', '\u{0}', '\u{0}']), + ('\u{abac}', ['\u{13dc}', '\u{0}', '\u{0}']), ('\u{abad}', ['\u{13dd}', '\u{0}', '\u{0}']), + ('\u{abae}', ['\u{13de}', '\u{0}', '\u{0}']), ('\u{abaf}', ['\u{13df}', '\u{0}', '\u{0}']), + ('\u{abb0}', ['\u{13e0}', '\u{0}', '\u{0}']), ('\u{abb1}', ['\u{13e1}', '\u{0}', '\u{0}']), + ('\u{abb2}', ['\u{13e2}', '\u{0}', '\u{0}']), ('\u{abb3}', ['\u{13e3}', '\u{0}', '\u{0}']), + ('\u{abb4}', ['\u{13e4}', '\u{0}', '\u{0}']), ('\u{abb5}', ['\u{13e5}', '\u{0}', '\u{0}']), + ('\u{abb6}', ['\u{13e6}', '\u{0}', '\u{0}']), ('\u{abb7}', ['\u{13e7}', '\u{0}', '\u{0}']), + ('\u{abb8}', ['\u{13e8}', '\u{0}', '\u{0}']), ('\u{abb9}', ['\u{13e9}', '\u{0}', '\u{0}']), + ('\u{abba}', ['\u{13ea}', '\u{0}', '\u{0}']), ('\u{abbb}', ['\u{13eb}', '\u{0}', '\u{0}']), + ('\u{abbc}', ['\u{13ec}', '\u{0}', '\u{0}']), ('\u{abbd}', ['\u{13ed}', '\u{0}', '\u{0}']), + ('\u{abbe}', ['\u{13ee}', '\u{0}', '\u{0}']), ('\u{abbf}', ['\u{13ef}', '\u{0}', '\u{0}']), + ('\u{fb00}', ['F', 'F', '\u{0}']), ('\u{fb01}', ['F', 'I', '\u{0}']), + ('\u{fb02}', ['F', 'L', '\u{0}']), ('\u{fb03}', ['F', 'F', 'I']), + ('\u{fb04}', ['F', 'F', 'L']), ('\u{fb05}', ['S', 'T', '\u{0}']), + ('\u{fb06}', ['S', 'T', '\u{0}']), ('\u{fb13}', ['\u{544}', '\u{546}', '\u{0}']), ('\u{fb14}', ['\u{544}', '\u{535}', '\u{0}']), ('\u{fb15}', ['\u{544}', '\u{53b}', '\u{0}']), ('\u{fb16}', ['\u{54e}', '\u{546}', '\u{0}']), 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/Cargo.toml b/src/libfmt_macros/Cargo.toml deleted file mode 100644 index 01608701a79dc..0000000000000 --- a/src/libfmt_macros/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "fmt_macros" -version = "0.0.0" -edition = "2018" - -[lib] -name = "fmt_macros" -path = "lib.rs" - -[dependencies] -rustc_span = { path = "../librustc_span" } -rustc_lexer = { path = "../librustc_lexer" } diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs deleted file mode 100644 index e138503b508d5..0000000000000 --- a/src/libfmt_macros/lib.rs +++ /dev/null @@ -1,655 +0,0 @@ -//! Macro support for format strings -//! -//! These structures are used when parsing format strings for the compiler. -//! Parsing does not happen at runtime: structures of `std::fmt::rt` are -//! generated instead. - -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/", - test(attr(deny(warnings))) -)] -#![feature(nll)] -#![feature(rustc_private)] -#![feature(unicode_internals)] -#![feature(bool_to_option)] - -pub use Alignment::*; -pub use Count::*; -pub use Flag::*; -pub use Piece::*; -pub use Position::*; - -use std::iter; -use std::str; -use std::string; - -use rustc_span::{InnerSpan, Symbol}; - -#[derive(Copy, Clone)] -struct InnerOffset(usize); - -impl InnerOffset { - fn to(self, end: InnerOffset) -> InnerSpan { - InnerSpan::new(self.0, end.0) - } -} - -/// A piece is a portion of the format string which represents the next part -/// to emit. These are emitted as a stream by the `Parser` class. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Piece<'a> { - /// A literal string which should directly be emitted - String(&'a str), - /// This describes that formatting should process the next argument (as - /// specified inside) for emission. - NextArgument(Argument<'a>), -} - -/// Representation of an argument specification. -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct Argument<'a> { - /// Where to find this argument - pub position: Position, - /// How to format the argument - pub format: FormatSpec<'a>, -} - -/// Specification for the formatting of an argument in the format string. -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct FormatSpec<'a> { - /// Optionally specified character to fill alignment with. - pub fill: Option, - /// Optionally specified alignment. - pub align: Alignment, - /// Packed version of various flags provided. - pub flags: u32, - /// The integer precision to use. - pub precision: Count, - /// The span of the precision formatting flag (for diagnostics). - pub precision_span: Option, - /// The string width requested for the resulting format. - pub width: Count, - /// The span of the width formatting flag (for diagnostics). - pub width_span: Option, - /// The descriptor string representing the name of the format desired for - /// this argument, this can be empty or any number of characters, although - /// it is required to be one word. - pub ty: &'a str, - /// The span of the descriptor string (for diagnostics). - pub ty_span: Option, -} - -/// Enum describing where an argument for a format can be located. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Position { - /// The argument is implied to be located at an index - ArgumentImplicitlyIs(usize), - /// The argument is located at a specific index given in the format - ArgumentIs(usize), - /// The argument has a name. - ArgumentNamed(Symbol), -} - -impl Position { - pub fn index(&self) -> Option { - match self { - ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i), - _ => None, - } - } -} - -/// Enum of alignments which are supported. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Alignment { - /// The value will be aligned to the left. - AlignLeft, - /// The value will be aligned to the right. - AlignRight, - /// The value will be aligned in the center. - AlignCenter, - /// The value will take on a default alignment. - AlignUnknown, -} - -/// Various flags which can be applied to format strings. The meaning of these -/// flags is defined by the formatters themselves. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Flag { - /// A `+` will be used to denote positive numbers. - FlagSignPlus, - /// A `-` will be used to denote negative numbers. This is the default. - FlagSignMinus, - /// An alternate form will be used for the value. In the case of numbers, - /// this means that the number will be prefixed with the supplied string. - FlagAlternate, - /// For numbers, this means that the number will be padded with zeroes, - /// and the sign (`+` or `-`) will precede them. - FlagSignAwareZeroPad, - /// For Debug / `?`, format integers in lower-case hexadecimal. - FlagDebugLowerHex, - /// For Debug / `?`, format integers in upper-case hexadecimal. - FlagDebugUpperHex, -} - -/// A count is used for the precision and width parameters of an integer, and -/// can reference either an argument or a literal integer. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Count { - /// The count is specified explicitly. - CountIs(usize), - /// The count is specified by the argument with the given name. - CountIsName(Symbol), - /// The count is specified by the argument at the given index. - CountIsParam(usize), - /// The count is implied and cannot be explicitly specified. - CountImplied, -} - -pub struct ParseError { - pub description: string::String, - pub note: Option, - pub label: string::String, - pub span: InnerSpan, - pub secondary_label: Option<(string::String, InnerSpan)>, -} - -/// The parser structure for interpreting the input format string. This is -/// modeled as an iterator over `Piece` structures to form a stream of tokens -/// being output. -/// -/// 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> { - input: &'a str, - cur: iter::Peekable>, - /// Error messages accumulated during parsing - pub errors: Vec, - /// Current position of implicit positional argument pointer - curarg: usize, - /// `Some(raw count)` when the string is "raw", used to position spans correctly - style: Option, - /// Start and end byte offset of every successfully parsed argument - pub arg_places: Vec, - /// Characters that need to be shifted - skips: Vec, - /// Span of the last opening brace seen, used for error reporting - last_opening_brace: Option, - /// Whether the source string is comes from `println!` as opposed to `format!` or `print!` - append_newline: bool, -} - -impl<'a> Iterator for Parser<'a> { - type Item = Piece<'a>; - - fn next(&mut self) -> Option> { - if let Some(&(pos, c)) = self.cur.peek() { - match c { - '{' => { - let curr_last_brace = self.last_opening_brace; - let byte_pos = self.to_span_index(pos); - self.last_opening_brace = Some(byte_pos.to(InnerOffset(byte_pos.0 + 1))); - self.cur.next(); - if self.consume('{') { - self.last_opening_brace = curr_last_brace; - - Some(String(self.string(pos + 1))) - } else { - let arg = self.argument(); - 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)); - } - Some(NextArgument(arg)) - } - } - '}' => { - self.cur.next(); - if self.consume('}') { - Some(String(self.string(pos + 1))) - } else { - let err_pos = self.to_span_index(pos); - self.err_with_note( - "unmatched `}` found", - "unmatched `}`", - "if you intended to print `}`, you can escape it using `}}`", - err_pos.to(err_pos), - ); - None - } - } - '\n' => Some(String(self.string(pos))), - _ => Some(String(self.string(pos))), - } - } else { - None - } - } -} - -impl<'a> Parser<'a> { - /// Creates a new parser for the given format string - pub fn new( - s: &'a str, - style: Option, - skips: Vec, - append_newline: bool, - ) -> Parser<'a> { - Parser { - input: s, - cur: s.char_indices().peekable(), - errors: vec![], - curarg: 0, - style, - arg_places: vec![], - skips, - last_opening_brace: None, - append_newline, - } - } - - /// Notifies of an error. The message doesn't actually need to be of type - /// String, but I think it does when this eventually uses conditions so it - /// might as well start using it now. - fn err, S2: Into>( - &mut self, - description: S1, - label: S2, - span: InnerSpan, - ) { - self.errors.push(ParseError { - description: description.into(), - note: None, - label: label.into(), - span, - secondary_label: None, - }); - } - - /// Notifies of an error. The message doesn't actually need to be of type - /// String, but I think it does when this eventually uses conditions so it - /// might as well start using it now. - fn err_with_note< - S1: Into, - S2: Into, - S3: Into, - >( - &mut self, - description: S1, - label: S2, - note: S3, - span: InnerSpan, - ) { - self.errors.push(ParseError { - description: description.into(), - note: Some(note.into()), - label: label.into(), - span, - secondary_label: None, - }); - } - - /// Optionally consumes the specified character. If the character is not at - /// the current position, then the current iterator isn't moved and `false` is - /// returned, otherwise the character is consumed and `true` is returned. - fn consume(&mut self, c: char) -> bool { - self.consume_pos(c).is_some() - } - - /// Optionally consumes the specified character. If the character is not at - /// the current position, then the current iterator isn't moved and `None` is - /// returned, otherwise the character is consumed and the current position is - /// returned. - fn consume_pos(&mut self, c: char) -> Option { - if let Some(&(pos, maybe)) = self.cur.peek() { - if c == maybe { - self.cur.next(); - return Some(pos); - } - } - None - } - - fn to_span_index(&self, pos: usize) -> InnerOffset { - let mut pos = pos; - // This handles the raw string case, the raw argument is the number of # - // in r###"..."### (we need to add one because of the `r`). - let raw = self.style.map(|raw| raw + 1).unwrap_or(0); - for skip in &self.skips { - if pos > *skip { - pos += 1; - } else if pos == *skip && raw == 0 { - pos += 1; - } else { - break; - } - } - InnerOffset(raw + pos + 1) - } - - /// Forces consumption of the specified character. If the character is not - /// found, an error is emitted. - fn must_consume(&mut self, c: char) -> Option { - self.ws(); - - if let Some(&(pos, maybe)) = self.cur.peek() { - if c == maybe { - self.cur.next(); - Some(pos) - } else { - let pos = self.to_span_index(pos); - let description = format!("expected `'}}'`, found `{:?}`", maybe); - let label = "expected `}`".to_owned(); - let (note, secondary_label) = if c == '}' { - ( - Some( - "if you intended to print `{`, you can escape it using `{{`".to_owned(), - ), - self.last_opening_brace - .map(|sp| ("because of this opening brace".to_owned(), sp)), - ) - } else { - (None, None) - }; - self.errors.push(ParseError { - description, - note, - label, - span: pos.to(pos), - secondary_label, - }); - None - } - } else { - let description = format!("expected `{:?}` but string was terminated", c); - // point at closing `"` - let pos = self.input.len() - if self.append_newline { 1 } else { 0 }; - let pos = self.to_span_index(pos); - if c == '}' { - let label = format!("expected `{:?}`", c); - let (note, secondary_label) = if c == '}' { - ( - Some( - "if you intended to print `{`, you can escape it using `{{`".to_owned(), - ), - self.last_opening_brace - .map(|sp| ("because of this opening brace".to_owned(), sp)), - ) - } else { - (None, None) - }; - self.errors.push(ParseError { - description, - note, - label, - span: pos.to(pos), - secondary_label, - }); - } else { - self.err(description, format!("expected `{:?}`", c), pos.to(pos)); - } - None - } - } - - /// Consumes all whitespace characters until the first non-whitespace character - fn ws(&mut self) { - while let Some(&(_, c)) = self.cur.peek() { - if c.is_whitespace() { - self.cur.next(); - } else { - break; - } - } - } - - /// Parses all of a string which is to be considered a "raw literal" in a - /// format string. This is everything outside of the braces. - fn string(&mut self, start: usize) -> &'a str { - // we may not consume the character, peek the iterator - while let Some(&(pos, c)) = self.cur.peek() { - match c { - '{' | '}' => { - return &self.input[start..pos]; - } - _ => { - self.cur.next(); - } - } - } - &self.input[start..self.input.len()] - } - - /// 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(); - - // Resolve position after parsing format spec. - let pos = match pos { - Some(position) => position, - None => { - let i = self.curarg; - self.curarg += 1; - ArgumentImplicitlyIs(i) - } - }; - - Argument { position: pos, format } - } - - /// Parses a positional argument for a format. This could either be an - /// integer index of an argument, a named argument, or a blank string. - /// Returns `Some(parsed_position)` if the position is not implicitly - /// consuming a macro argument, `None` if it's the case. - fn position(&mut self) -> Option { - if let Some(i) = self.integer() { - Some(ArgumentIs(i)) - } else { - match self.cur.peek() { - Some(&(_, c)) if rustc_lexer::is_id_start(c) => { - Some(ArgumentNamed(Symbol::intern(self.word()))) - } - - // This is an `ArgumentNext`. - // Record the fact and do the resolution after parsing the - // format spec, to make things like `{:.*}` work. - _ => None, - } - } - } - - /// Parses a format specifier at the current position, returning all of the - /// relevant information in the `FormatSpec` struct. - fn format(&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; - } - - // fill character - if let Some(&(_, c)) = self.cur.peek() { - match self.cur.clone().nth(1) { - Some((_, '>')) | Some((_, '<')) | Some((_, '^')) => { - spec.fill = Some(c); - self.cur.next(); - } - _ => {} - } - } - // Alignment - if self.consume('<') { - spec.align = AlignLeft; - } else if self.consume('>') { - spec.align = AlignRight; - } else if self.consume('^') { - spec.align = AlignCenter; - } - // Sign flags - if self.consume('+') { - spec.flags |= 1 << (FlagSignPlus as u32); - } else if self.consume('-') { - spec.flags |= 1 << (FlagSignMinus as u32); - } - // Alternate marker - if self.consume('#') { - spec.flags |= 1 << (FlagAlternate as u32); - } - // Width and precision - let mut havewidth = false; - - if self.consume('0') { - // small ambiguity with '0$' as a format string. In theory this is a - // '0' flag and then an ill-formatted format string with just a '$' - // and no count, but this is better if we instead interpret this as - // no '0' flag and '0$' as the width instead. - if self.consume('$') { - spec.width = CountIsParam(0); - havewidth = true; - } else { - spec.flags |= 1 << (FlagSignAwareZeroPad as u32); - } - } - if !havewidth { - let width_span_start = if let Some((pos, _)) = self.cur.peek() { *pos } else { 0 }; - let (w, sp) = self.count(width_span_start); - spec.width = w; - spec.width_span = sp; - } - if let Some(start) = self.consume_pos('.') { - if let Some(end) = self.consume_pos('*') { - // Resolve `CountIsNextParam`. - // We can do this immediately as `position` is resolved later. - let i = self.curarg; - self.curarg += 1; - spec.precision = CountIsParam(i); - spec.precision_span = - Some(self.to_span_index(start).to(self.to_span_index(end + 1))); - } else { - let (p, sp) = self.count(start); - spec.precision = p; - spec.precision_span = sp; - } - } - let ty_span_start = self.cur.peek().map(|(pos, _)| *pos); - // Optional radix followed by the actual format specifier - if self.consume('x') { - if self.consume('?') { - spec.flags |= 1 << (FlagDebugLowerHex as u32); - spec.ty = "?"; - } else { - spec.ty = "x"; - } - } else if self.consume('X') { - if self.consume('?') { - spec.flags |= 1 << (FlagDebugUpperHex as u32); - spec.ty = "?"; - } else { - spec.ty = "X"; - } - } else if self.consume('?') { - spec.ty = "?"; - } else { - 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. - fn count(&mut self, start: usize) -> (Count, Option) { - if let Some(i) = self.integer() { - if let Some(end) = self.consume_pos('$') { - let span = self.to_span_index(start).to(self.to_span_index(end + 1)); - (CountIsParam(i), Some(span)) - } else { - (CountIs(i), None) - } - } else { - let tmp = self.cur.clone(); - let word = self.word(); - if word.is_empty() { - self.cur = tmp; - (CountImplied, None) - } else if self.consume('$') { - (CountIsName(Symbol::intern(word)), None) - } else { - self.cur = tmp; - (CountImplied, None) - } - } - } - - /// Parses a word starting at the current position. A word is the same as - /// Rust identifier, except that it can't start with `_` character. - fn word(&mut self) -> &'a str { - let start = match self.cur.peek() { - Some(&(pos, c)) if rustc_lexer::is_id_start(c) => { - self.cur.next(); - pos - } - _ => { - return ""; - } - }; - let mut end = None; - while let Some(&(pos, c)) = self.cur.peek() { - if rustc_lexer::is_id_continue(c) { - self.cur.next(); - } else { - end = Some(pos); - break; - } - } - let end = end.unwrap_or(self.input.len()); - let word = &self.input[start..end]; - if word == "_" { - self.err_with_note( - "invalid argument name `_`", - "invalid argument name", - "argument name cannot be a single underscore", - self.to_span_index(start).to(self.to_span_index(end)), - ); - } - word - } - - /// Optionally parses an integer at the current position. This doesn't deal - /// with overflow at all, it's just accumulating digits. - fn integer(&mut self) -> Option { - let mut cur = 0; - let mut found = false; - while let Some(&(_, c)) = self.cur.peek() { - if let Some(i) = c.to_digit(10) { - cur = cur * 10 + i as usize; - found = true; - self.cur.next(); - } else { - break; - } - } - found.then_some(cur) - } -} - -#[cfg(test)] -mod tests; diff --git a/src/libfmt_macros/tests.rs b/src/libfmt_macros/tests.rs deleted file mode 100644 index 98c2a17f0dd54..0000000000000 --- a/src/libfmt_macros/tests.rs +++ /dev/null @@ -1,296 +0,0 @@ -use super::*; - -fn same(fmt: &'static str, p: &[Piece<'static>]) { - let parser = Parser::new(fmt, None, vec![], false); - assert_eq!(parser.collect::>>(), p); -} - -fn fmtdflt() -> FormatSpec<'static> { - return FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountImplied, - width: CountImplied, - precision_span: None, - width_span: None, - ty: "", - ty_span: None, - }; -} - -fn musterr(s: &str) { - let mut p = Parser::new(s, None, vec![], false); - p.next(); - assert!(!p.errors.is_empty()); -} - -#[test] -fn simple() { - same("asdf", &[String("asdf")]); - same("a{{b", &[String("a"), String("{b")]); - same("a}}b", &[String("a"), String("}b")]); - same("a}}", &[String("a"), String("}")]); - same("}}", &[String("}")]); - same("\\}}", &[String("\\"), String("}")]); -} - -#[test] -fn invalid01() { - musterr("{") -} -#[test] -fn invalid02() { - musterr("}") -} -#[test] -fn invalid04() { - musterr("{3a}") -} -#[test] -fn invalid05() { - musterr("{:|}") -} -#[test] -fn invalid06() { - musterr("{:>>>}") -} - -#[test] -fn format_nothing() { - same("{}", &[NextArgument(Argument { position: ArgumentImplicitlyIs(0), format: fmtdflt() })]); -} -#[test] -fn format_position() { - same("{3}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]); -} -#[test] -fn format_position_nothing_else() { - same("{3:}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]); -} -#[test] -fn format_type() { - same( - "{3:x}", - &[NextArgument(Argument { - position: ArgumentIs(3), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountImplied, - width: CountImplied, - precision_span: None, - width_span: None, - ty: "x", - ty_span: None, - }, - })], - ); -} -#[test] -fn format_align_fill() { - same( - "{3:>}", - &[NextArgument(Argument { - position: ArgumentIs(3), - format: FormatSpec { - fill: None, - align: AlignRight, - flags: 0, - precision: CountImplied, - width: CountImplied, - precision_span: None, - width_span: None, - ty: "", - ty_span: None, - }, - })], - ); - same( - "{3:0<}", - &[NextArgument(Argument { - position: ArgumentIs(3), - format: FormatSpec { - fill: Some('0'), - align: AlignLeft, - flags: 0, - precision: CountImplied, - width: CountImplied, - precision_span: None, - width_span: None, - ty: "", - ty_span: None, - }, - })], - ); - same( - "{3:*` to return -//! an owned vector or a borrowed slice as appropriate: we construct the -//! node vector from scratch, but borrow the edge list (rather than -//! constructing a copy of all the edges from scratch). -//! -//! The output from this example renders five nodes, with the first four -//! forming a diamond-shaped acyclic graph and then pointing to the fifth -//! which is cyclic. -//! -//! ```rust -//! #![feature(rustc_private)] -//! -//! use std::io::Write; -//! use graphviz as dot; -//! -//! type Nd = isize; -//! type Ed = (isize,isize); -//! struct Edges(Vec); -//! -//! pub fn render_to(output: &mut W) { -//! let edges = Edges(vec![(0,1), (0,2), (1,3), (2,3), (3,4), (4,4)]); -//! dot::render(&edges, output).unwrap() -//! } -//! -//! impl<'a> dot::Labeller<'a> for Edges { -//! type Node = Nd; -//! type Edge = Ed; -//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example1").unwrap() } -//! -//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> { -//! dot::Id::new(format!("N{}", *n)).unwrap() -//! } -//! } -//! -//! impl<'a> dot::GraphWalk<'a> for Edges { -//! type Node = Nd; -//! type Edge = Ed; -//! fn nodes(&self) -> dot::Nodes<'a,Nd> { -//! // (assumes that |N| \approxeq |E|) -//! let &Edges(ref v) = self; -//! let mut nodes = Vec::with_capacity(v.len()); -//! for &(s,t) in v { -//! nodes.push(s); nodes.push(t); -//! } -//! nodes.sort(); -//! nodes.dedup(); -//! nodes.into() -//! } -//! -//! fn edges(&'a self) -> dot::Edges<'a,Ed> { -//! let &Edges(ref edges) = self; -//! (&edges[..]).into() -//! } -//! -//! fn source(&self, e: &Ed) -> Nd { let &(s,_) = e; s } -//! -//! fn target(&self, e: &Ed) -> Nd { let &(_,t) = e; t } -//! } -//! -//! # pub fn main() { render_to(&mut Vec::new()) } -//! ``` -//! -//! ```no_run -//! # pub fn render_to(output: &mut W) { unimplemented!() } -//! pub fn main() { -//! use std::fs::File; -//! let mut f = File::create("example1.dot").unwrap(); -//! render_to(&mut f) -//! } -//! ``` -//! -//! Output from first example (in `example1.dot`): -//! -//! ```dot -//! digraph example1 { -//! N0[label="N0"]; -//! N1[label="N1"]; -//! N2[label="N2"]; -//! N3[label="N3"]; -//! N4[label="N4"]; -//! N0 -> N1[label=""]; -//! N0 -> N2[label=""]; -//! N1 -> N3[label=""]; -//! N2 -> N3[label=""]; -//! N3 -> N4[label=""]; -//! N4 -> N4[label=""]; -//! } -//! ``` -//! -//! The second example illustrates using `node_label` and `edge_label` to -//! add labels to the nodes and edges in the rendered graph. The graph -//! here carries both `nodes` (the label text to use for rendering a -//! particular node), and `edges` (again a list of `(source,target)` -//! indices). -//! -//! This example also illustrates how to use a type (in this case the edge -//! type) that shares substructure with the graph: the edge type here is a -//! direct reference to the `(source,target)` pair stored in the graph's -//! internal vector (rather than passing around a copy of the pair -//! itself). Note that this implies that `fn edges(&'a self)` must -//! construct a fresh `Vec<&'a (usize,usize)>` from the `Vec<(usize,usize)>` -//! edges stored in `self`. -//! -//! Since both the set of nodes and the set of edges are always -//! constructed from scratch via iterators, we use the `collect()` method -//! from the `Iterator` trait to collect the nodes and edges into freshly -//! constructed growable `Vec` values (rather than using `Cow` as in the -//! first example above). -//! -//! The output from this example renders four nodes that make up the -//! Hasse-diagram for the subsets of the set `{x, y}`. Each edge is -//! labeled with the ⊆ character (specified using the HTML character -//! entity `&sube`). -//! -//! ```rust -//! #![feature(rustc_private)] -//! -//! use std::io::Write; -//! use graphviz as dot; -//! -//! type Nd = usize; -//! type Ed<'a> = &'a (usize, usize); -//! struct Graph { nodes: Vec<&'static str>, edges: Vec<(usize,usize)> } -//! -//! pub fn render_to(output: &mut W) { -//! let nodes = vec!["{x,y}","{x}","{y}","{}"]; -//! let edges = vec![(0,1), (0,2), (1,3), (2,3)]; -//! let graph = Graph { nodes: nodes, edges: edges }; -//! -//! dot::render(&graph, output).unwrap() -//! } -//! -//! impl<'a> dot::Labeller<'a> for Graph { -//! type Node = Nd; -//! type Edge = Ed<'a>; -//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example2").unwrap() } -//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> { -//! dot::Id::new(format!("N{}", n)).unwrap() -//! } -//! fn node_label<'b>(&'b self, n: &Nd) -> dot::LabelText<'b> { -//! dot::LabelText::LabelStr(self.nodes[*n].into()) -//! } -//! fn edge_label<'b>(&'b self, _: &Ed) -> dot::LabelText<'b> { -//! dot::LabelText::LabelStr("⊆".into()) -//! } -//! } -//! -//! impl<'a> dot::GraphWalk<'a> for Graph { -//! type Node = Nd; -//! type Edge = Ed<'a>; -//! fn nodes(&self) -> dot::Nodes<'a,Nd> { (0..self.nodes.len()).collect() } -//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { self.edges.iter().collect() } -//! fn source(&self, e: &Ed) -> Nd { let & &(s,_) = e; s } -//! fn target(&self, e: &Ed) -> Nd { let & &(_,t) = e; t } -//! } -//! -//! # pub fn main() { render_to(&mut Vec::new()) } -//! ``` -//! -//! ```no_run -//! # pub fn render_to(output: &mut W) { unimplemented!() } -//! pub fn main() { -//! use std::fs::File; -//! let mut f = File::create("example2.dot").unwrap(); -//! render_to(&mut f) -//! } -//! ``` -//! -//! The third example is similar to the second, except now each node and -//! edge now carries a reference to the string label for each node as well -//! as that node's index. (This is another illustration of how to share -//! structure with the graph itself, and why one might want to do so.) -//! -//! The output from this example is the same as the second example: the -//! Hasse-diagram for the subsets of the set `{x, y}`. -//! -//! ```rust -//! #![feature(rustc_private)] -//! -//! use std::io::Write; -//! use graphviz as dot; -//! -//! type Nd<'a> = (usize, &'a str); -//! type Ed<'a> = (Nd<'a>, Nd<'a>); -//! struct Graph { nodes: Vec<&'static str>, edges: Vec<(usize,usize)> } -//! -//! pub fn render_to(output: &mut W) { -//! let nodes = vec!["{x,y}","{x}","{y}","{}"]; -//! let edges = vec![(0,1), (0,2), (1,3), (2,3)]; -//! let graph = Graph { nodes: nodes, edges: edges }; -//! -//! dot::render(&graph, output).unwrap() -//! } -//! -//! impl<'a> dot::Labeller<'a> for Graph { -//! type Node = Nd<'a>; -//! type Edge = Ed<'a>; -//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example3").unwrap() } -//! fn node_id(&'a self, n: &Nd<'a>) -> dot::Id<'a> { -//! dot::Id::new(format!("N{}", n.0)).unwrap() -//! } -//! fn node_label<'b>(&'b self, n: &Nd<'b>) -> dot::LabelText<'b> { -//! let &(i, _) = n; -//! dot::LabelText::LabelStr(self.nodes[i].into()) -//! } -//! fn edge_label<'b>(&'b self, _: &Ed<'b>) -> dot::LabelText<'b> { -//! dot::LabelText::LabelStr("⊆".into()) -//! } -//! } -//! -//! impl<'a> dot::GraphWalk<'a> for Graph { -//! type Node = Nd<'a>; -//! type Edge = Ed<'a>; -//! fn nodes(&'a self) -> dot::Nodes<'a,Nd<'a>> { -//! self.nodes.iter().map(|s| &s[..]).enumerate().collect() -//! } -//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { -//! self.edges.iter() -//! .map(|&(i,j)|((i, &self.nodes[i][..]), -//! (j, &self.nodes[j][..]))) -//! .collect() -//! } -//! fn source(&self, e: &Ed<'a>) -> Nd<'a> { let &(s,_) = e; s } -//! fn target(&self, e: &Ed<'a>) -> Nd<'a> { let &(_,t) = e; t } -//! } -//! -//! # pub fn main() { render_to(&mut Vec::new()) } -//! ``` -//! -//! ```no_run -//! # pub fn render_to(output: &mut W) { unimplemented!() } -//! pub fn main() { -//! use std::fs::File; -//! let mut f = File::create("example3.dot").unwrap(); -//! render_to(&mut f) -//! } -//! ``` -//! -//! # References -//! -//! * [Graphviz](http://www.graphviz.org/) -//! -//! * [DOT language](http://www.graphviz.org/doc/info/lang.html) - -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - test(attr(allow(unused_variables), deny(warnings))) -)] -#![feature(nll)] - -use LabelText::*; - -use std::borrow::Cow; -use std::io; -use std::io::prelude::*; - -/// The text for a graphviz label on a node or edge. -pub enum LabelText<'a> { - /// This kind of label preserves the text directly as is. - /// - /// Occurrences of backslashes (`\`) are escaped, and thus appear - /// as backslashes in the rendered label. - LabelStr(Cow<'a, str>), - - /// This kind of label uses the graphviz label escString type: - /// - /// - /// Occurrences of backslashes (`\`) are not escaped; instead they - /// are interpreted as initiating an escString escape sequence. - /// - /// Escape sequences of particular interest: in addition to `\n` - /// to break a line (centering the line preceding the `\n`), there - /// are also the escape sequences `\l` which left-justifies the - /// preceding line and `\r` which right-justifies it. - EscStr(Cow<'a, str>), - - /// This uses a graphviz [HTML string label][html]. The string is - /// printed exactly as given, but between `<` and `>`. **No - /// escaping is performed.** - /// - /// [html]: http://www.graphviz.org/content/node-shapes#html - HtmlStr(Cow<'a, str>), -} - -/// The style for a node or edge. -/// See for descriptions. -/// Note that some of these are not valid for edges. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Style { - None, - Solid, - Dashed, - Dotted, - Bold, - Rounded, - Diagonals, - Filled, - Striped, - Wedged, -} - -impl Style { - pub fn as_slice(self) -> &'static str { - match self { - Style::None => "", - Style::Solid => "solid", - Style::Dashed => "dashed", - Style::Dotted => "dotted", - Style::Bold => "bold", - Style::Rounded => "rounded", - Style::Diagonals => "diagonals", - Style::Filled => "filled", - Style::Striped => "striped", - Style::Wedged => "wedged", - } - } -} - -// There is a tension in the design of the labelling API. -// -// For example, I considered making a `Labeller` trait that -// provides labels for `T`, and then making the graph type `G` -// implement `Labeller` and `Labeller`. However, this is -// not possible without functional dependencies. (One could work -// around that, but I did not explore that avenue heavily.) -// -// Another approach that I actually used for a while was to make a -// `Label` trait that is implemented by the client-specific -// Node and Edge types (as well as an implementation on Graph itself -// for the overall name for the graph). The main disadvantage of this -// second approach (compared to having the `G` type parameter -// implement a Labelling service) that I have encountered is that it -// makes it impossible to use types outside of the current crate -// directly as Nodes/Edges; you need to wrap them in newtype'd -// structs. See e.g., the `No` and `Ed` structs in the examples. (In -// practice clients using a graph in some other crate would need to -// provide some sort of adapter shim over the graph anyway to -// interface with this library). -// -// Another approach would be to make a single `Labeller` trait -// that provides three methods (graph_label, node_label, edge_label), -// and then make `G` implement `Labeller`. At first this did not -// appeal to me, since I had thought I would need separate methods on -// each data variant for dot-internal identifiers versus user-visible -// labels. However, the identifier/label distinction only arises for -// nodes; graphs themselves only have identifiers, and edges only have -// labels. -// -// So in the end I decided to use the third approach described above. - -/// `Id` is a Graphviz `ID`. -pub struct Id<'a> { - name: Cow<'a, str>, -} - -impl<'a> Id<'a> { - /// Creates an `Id` named `name`. - /// - /// The caller must ensure that the input conforms to an - /// identifier format: it must be a non-empty string made up of - /// alphanumeric or underscore characters, not beginning with a - /// digit (i.e., the regular expression `[a-zA-Z_][a-zA-Z_0-9]*`). - /// - /// (Note: this format is a strict subset of the `ID` format - /// defined by the DOT language. This function may change in the - /// future to accept a broader subset, or the entirety, of DOT's - /// `ID` format.) - /// - /// Passing an invalid string (containing spaces, brackets, - /// quotes, ...) will return an empty `Err` value. - pub fn new>>(name: Name) -> Result, ()> { - let name = name.into(); - match name.chars().next() { - Some(c) if c.is_ascii_alphabetic() || c == '_' => {} - _ => return Err(()), - } - if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { - return Err(()); - } - - Ok(Id { name }) - } - - pub fn as_slice(&'a self) -> &'a str { - &*self.name - } - - pub fn name(self) -> Cow<'a, str> { - self.name - } -} - -/// Each instance of a type that implements `Label` maps to a -/// unique identifier with respect to `C`, which is used to identify -/// it in the generated .dot file. They can also provide more -/// elaborate (and non-unique) label text that is used in the graphviz -/// rendered output. - -/// The graph instance is responsible for providing the DOT compatible -/// identifiers for the nodes and (optionally) rendered labels for the nodes and -/// edges, as well as an identifier for the graph itself. -pub trait Labeller<'a> { - type Node; - type Edge; - - /// Must return a DOT compatible identifier naming the graph. - fn graph_id(&'a self) -> Id<'a>; - - /// Maps `n` to a unique identifier with respect to `self`. The - /// implementor is responsible for ensuring that the returned name - /// is a valid DOT identifier. - fn node_id(&'a self, n: &Self::Node) -> Id<'a>; - - /// Maps `n` to one of the [graphviz `shape` names][1]. If `None` - /// is returned, no `shape` attribute is specified. - /// - /// [1]: http://www.graphviz.org/content/node-shapes - fn node_shape(&'a self, _node: &Self::Node) -> Option> { - None - } - - /// Maps `n` to a label that will be used in the rendered output. - /// The label need not be unique, and may be the empty string; the - /// default is just the output from `node_id`. - fn node_label(&'a self, n: &Self::Node) -> LabelText<'a> { - LabelStr(self.node_id(n).name) - } - - /// Maps `e` to a label that will be used in the rendered output. - /// The label need not be unique, and may be the empty string; the - /// default is in fact the empty string. - fn edge_label(&'a self, _e: &Self::Edge) -> LabelText<'a> { - LabelStr("".into()) - } - - /// Maps `n` to a style that will be used in the rendered output. - fn node_style(&'a self, _n: &Self::Node) -> Style { - Style::None - } - - /// Maps `e` to a style that will be used in the rendered output. - fn edge_style(&'a self, _e: &Self::Edge) -> Style { - Style::None - } -} - -/// Escape tags in such a way that it is suitable for inclusion in a -/// Graphviz HTML label. -pub fn escape_html(s: &str) -> String { - s.replace("&", "&").replace("\"", """).replace("<", "<").replace(">", ">") -} - -impl<'a> LabelText<'a> { - pub fn label>>(s: S) -> LabelText<'a> { - LabelStr(s.into()) - } - - pub fn escaped>>(s: S) -> LabelText<'a> { - EscStr(s.into()) - } - - pub fn html>>(s: S) -> LabelText<'a> { - HtmlStr(s.into()) - } - - fn escape_char(c: char, mut f: F) - where - F: FnMut(char), - { - match c { - // not escaping \\, since Graphviz escString needs to - // interpret backslashes; see EscStr above. - '\\' => f(c), - _ => { - for c in c.escape_default() { - f(c) - } - } - } - } - fn escape_str(s: &str) -> String { - let mut out = String::with_capacity(s.len()); - for c in s.chars() { - LabelText::escape_char(c, |c| out.push(c)); - } - out - } - - /// Renders text as string suitable for a label in a .dot file. - /// This includes quotes or suitable delimiters. - pub fn to_dot_string(&self) -> String { - match *self { - LabelStr(ref s) => format!("\"{}\"", s.escape_default()), - EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s)), - HtmlStr(ref s) => format!("<{}>", s), - } - } - - /// Decomposes content into string suitable for making EscStr that - /// yields same content as self. The result obeys the law - /// render(`lt`) == render(`EscStr(lt.pre_escaped_content())`) for - /// all `lt: LabelText`. - fn pre_escaped_content(self) -> Cow<'a, str> { - match self { - EscStr(s) => s, - LabelStr(s) => { - if s.contains('\\') { - (&*s).escape_default().to_string().into() - } else { - s - } - } - HtmlStr(s) => s, - } - } - - /// Puts `prefix` on a line above this label, with a blank line separator. - pub fn prefix_line(self, prefix: LabelText<'_>) -> LabelText<'static> { - prefix.suffix_line(self) - } - - /// Puts `suffix` on a line below this label, with a blank line separator. - pub fn suffix_line(self, suffix: LabelText<'_>) -> LabelText<'static> { - let mut prefix = self.pre_escaped_content().into_owned(); - let suffix = suffix.pre_escaped_content(); - prefix.push_str(r"\n\n"); - prefix.push_str(&suffix); - EscStr(prefix.into()) - } -} - -pub type Nodes<'a, N> = Cow<'a, [N]>; -pub type Edges<'a, E> = Cow<'a, [E]>; - -// (The type parameters in GraphWalk should be associated items, -// when/if Rust supports such.) - -/// GraphWalk is an abstraction over a directed graph = (nodes,edges) -/// made up of node handles `N` and edge handles `E`, where each `E` -/// can be mapped to its source and target nodes. -/// -/// The lifetime parameter `'a` is exposed in this trait (rather than -/// introduced as a generic parameter on each method declaration) so -/// that a client impl can choose `N` and `E` that have substructure -/// that is bound by the self lifetime `'a`. -/// -/// The `nodes` and `edges` method each return instantiations of -/// `Cow<[T]>` to leave implementors the freedom to create -/// entirely new vectors or to pass back slices into internally owned -/// vectors. -pub trait GraphWalk<'a> { - type Node: Clone; - type Edge: Clone; - - /// Returns all the nodes in this graph. - fn nodes(&'a self) -> Nodes<'a, Self::Node>; - /// Returns all of the edges in this graph. - fn edges(&'a self) -> Edges<'a, Self::Edge>; - /// The source node for `edge`. - fn source(&'a self, edge: &Self::Edge) -> Self::Node; - /// The target node for `edge`. - fn target(&'a self, edge: &Self::Edge) -> Self::Node; -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum RenderOption { - NoEdgeLabels, - NoNodeLabels, - NoEdgeStyles, - NoNodeStyles, - - Monospace, -} - -/// Returns vec holding all the default render options. -pub fn default_options() -> Vec { - vec![] -} - -/// Renders directed graph `g` into the writer `w` in DOT syntax. -/// (Simple wrapper around `render_opts` that passes a default set of options.) -pub fn render<'a, N, E, G, W>(g: &'a G, w: &mut W) -> io::Result<()> -where - N: Clone + 'a, - E: Clone + 'a, - G: Labeller<'a, Node = N, Edge = E> + GraphWalk<'a, Node = N, Edge = E>, - W: Write, -{ - render_opts(g, w, &[]) -} - -/// Renders directed graph `g` into the writer `w` in DOT syntax. -/// (Main entry point for the library.) -pub fn render_opts<'a, N, E, G, W>(g: &'a G, w: &mut W, options: &[RenderOption]) -> io::Result<()> -where - N: Clone + 'a, - E: Clone + 'a, - G: Labeller<'a, Node = N, Edge = E> + GraphWalk<'a, Node = N, Edge = E>, - W: Write, -{ - writeln!(w, "digraph {} {{", g.graph_id().as_slice())?; - - // Global graph properties - if options.contains(&RenderOption::Monospace) { - writeln!(w, r#" graph[fontname="monospace"];"#)?; - writeln!(w, r#" node[fontname="monospace"];"#)?; - writeln!(w, r#" edge[fontname="monospace"];"#)?; - } - - for n in g.nodes().iter() { - write!(w, " ")?; - let id = g.node_id(n); - - let escaped = &g.node_label(n).to_dot_string(); - - let mut text = Vec::new(); - write!(text, "{}", id.as_slice()).unwrap(); - - if !options.contains(&RenderOption::NoNodeLabels) { - write!(text, "[label={}]", escaped).unwrap(); - } - - let style = g.node_style(n); - if !options.contains(&RenderOption::NoNodeStyles) && style != Style::None { - write!(text, "[style=\"{}\"]", style.as_slice()).unwrap(); - } - - if let Some(s) = g.node_shape(n) { - write!(text, "[shape={}]", &s.to_dot_string()).unwrap(); - } - - writeln!(text, ";").unwrap(); - w.write_all(&text[..])?; - } - - for e in g.edges().iter() { - let escaped_label = &g.edge_label(e).to_dot_string(); - write!(w, " ")?; - let source = g.source(e); - let target = g.target(e); - let source_id = g.node_id(&source); - let target_id = g.node_id(&target); - - let mut text = Vec::new(); - write!(text, "{} -> {}", source_id.as_slice(), target_id.as_slice()).unwrap(); - - if !options.contains(&RenderOption::NoEdgeLabels) { - write!(text, "[label={}]", escaped_label).unwrap(); - } - - let style = g.edge_style(e); - if !options.contains(&RenderOption::NoEdgeStyles) && style != Style::None { - write!(text, "[style=\"{}\"]", style.as_slice()).unwrap(); - } - - writeln!(text, ";").unwrap(); - w.write_all(&text[..])?; - } - - writeln!(w, "}}") -} - -#[cfg(test)] -mod tests; diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index f44a875c9d0d5..27056d5f934fd 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -21,6 +21,7 @@ use core::any::Any; #[rustc_std_internal_symbol] +#[cfg_attr(not(bootstrap), allow(improper_ctypes_definitions))] pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) { unreachable!() } @@ -106,15 +107,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/emcc.rs b/src/libpanic_unwind/emcc.rs index c7144fe16cdda..a0bdb1481c6b2 100644 --- a/src/libpanic_unwind/emcc.rs +++ b/src/libpanic_unwind/emcc.rs @@ -6,8 +6,6 @@ //! Emscripten's runtime always implements those APIs and does not //! implement libunwind. -#![allow(private_no_mangle_fns)] - use alloc::boxed::Box; use core::any::Any; use core::mem; diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs index 9c032b30341e9..f5d83c21da068 100644 --- a/src/libpanic_unwind/gcc.rs +++ b/src/libpanic_unwind/gcc.rs @@ -36,8 +36,6 @@ //! Once stack has been unwound down to the handler frame level, unwinding stops //! and the last personality routine transfers control to the catch block. -#![allow(private_no_mangle_fns)] - use alloc::boxed::Box; use core::any::Any; @@ -313,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/hermit.rs b/src/libpanic_unwind/hermit.rs index 6bded4dd499bd..69b9edb77c564 100644 --- a/src/libpanic_unwind/hermit.rs +++ b/src/libpanic_unwind/hermit.rs @@ -4,7 +4,6 @@ use alloc::boxed::Box; use core::any::Any; -use core::ptr; pub unsafe fn cleanup(_ptr: *mut u8) -> Box { extern "C" { diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index 0a2a0e9e045c6..f361354da2ac2 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; @@ -84,6 +81,7 @@ extern "C" { mod dwarf; #[rustc_std_internal_symbol] +#[cfg_attr(not(bootstrap), allow(improper_ctypes_definitions))] pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) { Box::into_raw(imp::cleanup(payload)) } diff --git a/src/libpanic_unwind/seh.rs b/src/libpanic_unwind/seh.rs index c294fe26327d7..1f812f8df6122 100644 --- a/src/libpanic_unwind/seh.rs +++ b/src/libpanic_unwind/seh.rs @@ -45,7 +45,6 @@ //! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions #![allow(nonstandard_style)] -#![allow(private_no_mangle_fns)] use alloc::boxed::Box; use core::any::Any; @@ -118,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; @@ -214,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(), @@ -329,5 +327,5 @@ pub unsafe fn cleanup(payload: *mut u8) -> Box { #[lang = "eh_personality"] #[cfg(not(test))] fn rust_eh_personality() { - unsafe { core::intrinsics::abort() } + 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 59ce14c97c0e6..2d5bd7e872bd5 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -24,9 +24,10 @@ #![feature(decl_macro)] #![feature(extern_types)] #![feature(in_band_lifetimes)] +#![feature(negative_impls)] #![feature(optin_builtin_traits)] #![feature(rustc_attrs)] -#![feature(specialization)] +#![feature(min_specialization)] #![recursion_limit = "256"] #[unstable(feature = "proc_macro_internals", issue = "27812")] @@ -38,11 +39,30 @@ mod diagnostic; #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] pub use diagnostic::{Diagnostic, Level, MultiSpan}; +use std::cmp::Ordering; use std::ops::{Bound, RangeBounds}; use std::path::PathBuf; use std::str::FromStr; use std::{error, fmt, iter, mem}; +/// Determines whether proc_macro has been made accessible to the currently +/// running program. +/// +/// The proc_macro crate is only intended for use inside the implementation of +/// procedural macros. All the functions in this crate panic if invoked from +/// outside of a procedural macro, such as from a build script or unit test or +/// ordinary Rust binary. +/// +/// With consideration for Rust libraries that are designed to support both +/// macro and non-macro use cases, `proc_macro::is_available()` provides a +/// non-panicking way to detect whether the infrastructure required to use the +/// API of proc_macro is presently available. Returns true if invoked from +/// inside of a procedural macro, false if invoked from any other binary. +#[unstable(feature = "proc_macro_is_available", issue = "71436")] +pub fn is_available() -> bool { + bridge::Bridge::is_available() +} + /// The main type provided by this crate, representing an abstract stream of /// tokens, or, more specifically, a sequence of token trees. /// The type provide interfaces for iterating over those token trees and, conversely, @@ -139,6 +159,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}; @@ -254,14 +281,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 { @@ -284,7 +311,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()) } @@ -332,14 +359,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) } @@ -394,6 +421,20 @@ impl !Send for LineColumn {} #[unstable(feature = "proc_macro_span", issue = "54725")] impl !Sync for LineColumn {} +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl Ord for LineColumn { + fn cmp(&self, other: &Self) -> Ordering { + self.line.cmp(&other.line).then(self.column.cmp(&other.column)) + } +} + +#[unstable(feature = "proc_macro_span", issue = "54725")] +impl PartialOrd for LineColumn { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + /// The source file of a given `Span`. #[unstable(feature = "proc_macro_span", issue = "54725")] #[derive(Clone)] @@ -1115,7 +1156,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/libproc_macro/tests/test.rs b/src/libproc_macro/tests/test.rs new file mode 100644 index 0000000000000..331b330cf29f0 --- /dev/null +++ b/src/libproc_macro/tests/test.rs @@ -0,0 +1,12 @@ +#![feature(proc_macro_span)] + +use proc_macro::LineColumn; + +#[test] +fn test_line_column_ord() { + let line0_column0 = LineColumn { line: 0, column: 0 }; + let line0_column1 = LineColumn { line: 0, column: 1 }; + let line1_column0 = LineColumn { line: 1, column: 0 }; + assert!(line0_column0 < line0_column1); + assert!(line0_column1 < line1_column0); +} diff --git a/src/libprofiler_builtins/build.rs b/src/libprofiler_builtins/build.rs index 8adcff67800fe..e23e2f2c1306f 100644 --- a/src/libprofiler_builtins/build.rs +++ b/src/libprofiler_builtins/build.rs @@ -41,7 +41,6 @@ fn main() { cfg.flag("-fno-builtin"); cfg.flag("-fvisibility=hidden"); cfg.flag("-fomit-frame-pointer"); - cfg.flag("-ffreestanding"); cfg.define("VISIBILITY_HIDDEN", None); if !target.contains("windows") { cfg.define("COMPILER_RT_HAS_UNAME", Some("1")); @@ -64,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 481d691b8e9b2..0000000000000 --- a/src/librustc/Cargo.toml +++ /dev/null @@ -1,38 +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_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 72258c0537a96..0000000000000 --- a/src/librustc/arena.rs +++ /dev/null @@ -1,358 +0,0 @@ -use arena::{DroplessArena, TypedArena}; -use smallvec::SmallVec; -use std::cell::RefCell; -use std::marker::PhantomData; -use std::mem; -use std::ptr; -use std::slice; - -/// 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 types - [few] hir_krate: rustc_hir::Crate<$tcx>, - [] arm: rustc_hir::Arm<$tcx>, - [] attribute: rustc_ast::ast::Attribute, - [] block: rustc_hir::Block<$tcx>, - [] bare_fn_ty: rustc_hir::BareFnTy<$tcx>, - [few] global_asm: rustc_hir::GlobalAsm, - [] generic_arg: rustc_hir::GenericArg<$tcx>, - [] generic_args: rustc_hir::GenericArgs<$tcx>, - [] generic_bound: rustc_hir::GenericBound<$tcx>, - [] generic_param: rustc_hir::GenericParam<$tcx>, - [] expr: rustc_hir::Expr<$tcx>, - [] field: rustc_hir::Field<$tcx>, - [] field_pat: rustc_hir::FieldPat<$tcx>, - [] fn_decl: rustc_hir::FnDecl<$tcx>, - [] foreign_item: rustc_hir::ForeignItem<$tcx>, - [] impl_item_ref: rustc_hir::ImplItemRef<$tcx>, - [] inline_asm: rustc_hir::InlineAsm<$tcx>, - [] local: rustc_hir::Local<$tcx>, - [few] macro_def: rustc_hir::MacroDef<$tcx>, - [] param: rustc_hir::Param<$tcx>, - [] pat: rustc_hir::Pat<$tcx>, - [] path: rustc_hir::Path<$tcx>, - [] path_segment: rustc_hir::PathSegment<$tcx>, - [] poly_trait_ref: rustc_hir::PolyTraitRef<$tcx>, - [] qpath: rustc_hir::QPath<$tcx>, - [] stmt: rustc_hir::Stmt<$tcx>, - [] struct_field: rustc_hir::StructField<$tcx>, - [] trait_item_ref: rustc_hir::TraitItemRef, - [] ty: rustc_hir::Ty<$tcx>, - [] type_binding: rustc_hir::TypeBinding<$tcx>, - [] variant: rustc_hir::Variant<$tcx>, - [] where_predicate: rustc_hir::WherePredicate<$tcx>, - - // HIR query types - [few] indexed_hir: rustc::hir::map::IndexedHir<$tcx>, - [few] hir_definitions: rustc::hir::map::definitions::Definitions, - [] hir_owner: rustc::hir::HirOwner<$tcx>, - [] hir_owner_items: rustc::hir::HirOwnerItems<$tcx>, - ], $tcx); - ) -} - -macro_rules! arena_for_type { - ([][$ty:ty]) => { - TypedArena<$ty> - }; - ([few $(, $attrs:ident)*][$ty:ty]) => { - PhantomData<$ty> - }; - ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { - arena_for_type!([$($attrs),*]$args) - }; -} - -macro_rules! declare_arena { - ([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => { - #[derive(Default)] - pub struct Arena<$tcx> { - pub dropless: DroplessArena, - drop: DropArena, - $($name: arena_for_type!($a[$ty]),)* - } - } -} - -macro_rules! which_arena_for_type { - ([][$arena:expr]) => { - Some($arena) - }; - ([few$(, $attrs:ident)*][$arena:expr]) => { - None - }; - ([$ignore:ident$(, $attrs:ident)*]$args:tt) => { - which_arena_for_type!([$($attrs),*]$args) - }; -} - -macro_rules! impl_arena_allocatable { - ([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => { - $( - impl ArenaAllocatable for $ty {} - unsafe impl<$tcx> ArenaField<$tcx> for $ty { - #[inline] - fn arena<'a>(_arena: &'a Arena<$tcx>) -> Option<&'a TypedArena> { - which_arena_for_type!($a[&_arena.$name]) - } - } - )* - } -} - -arena_types!(declare_arena, [], 'tcx); - -arena_types!(impl_arena_allocatable, [], 'tcx); - -#[marker] -pub trait ArenaAllocatable {} - -impl ArenaAllocatable for T {} - -unsafe trait ArenaField<'tcx>: Sized { - /// Returns a specific arena to allocate from. - /// If `None` is returned, the `DropArena` will be used. - fn arena<'a>(arena: &'a Arena<'tcx>) -> Option<&'a TypedArena>; -} - -unsafe impl<'tcx, T> ArenaField<'tcx> for T { - #[inline] - default fn arena<'a>(_: &'a Arena<'tcx>) -> Option<&'a TypedArena> { - panic!() - } -} - -impl<'tcx> Arena<'tcx> { - #[inline] - pub fn alloc(&self, value: T) -> &mut T { - if !mem::needs_drop::() { - return self.dropless.alloc(value); - } - match >::arena(self) { - Some(arena) => arena.alloc(value), - None => unsafe { self.drop.alloc(value) }, - } - } - - #[inline] - pub fn alloc_slice(&self, value: &[T]) -> &mut [T] { - if value.is_empty() { - return &mut []; - } - self.dropless.alloc_slice(value) - } - - pub fn alloc_from_iter>( - &'a self, - iter: I, - ) -> &'a mut [T] { - if !mem::needs_drop::() { - return self.dropless.alloc_from_iter(iter); - } - match >::arena(self) { - Some(arena) => arena.alloc_from_iter(iter), - None => unsafe { self.drop.alloc_from_iter(iter) }, - } - } -} - -/// Calls the destructor for an object when dropped. -struct DropType { - drop_fn: unsafe fn(*mut u8), - obj: *mut u8, -} - -unsafe fn drop_for_type(to_drop: *mut u8) { - std::ptr::drop_in_place(to_drop as *mut T) -} - -impl Drop for DropType { - fn drop(&mut self) { - unsafe { (self.drop_fn)(self.obj) } - } -} - -/// An arena which can be used to allocate any type. -/// Allocating in this arena is unsafe since the type system -/// doesn't know which types it contains. In order to -/// allocate safely, you must store a PhantomData -/// alongside this arena for each type T you allocate. -#[derive(Default)] -struct DropArena { - /// A list of destructors to run when the arena drops. - /// Ordered so `destructors` gets dropped before the arena - /// since its destructor can reference memory in the arena. - destructors: RefCell>, - arena: DroplessArena, -} - -impl DropArena { - #[inline] - unsafe fn alloc(&self, object: T) -> &mut T { - let mem = - self.arena.alloc_raw(mem::size_of::(), mem::align_of::()) as *mut _ as *mut T; - // Write into uninitialized memory. - ptr::write(mem, object); - let result = &mut *mem; - // Record the destructor after doing the allocation as that may panic - // and would cause `object`'s destuctor to run twice if it was recorded before - self.destructors - .borrow_mut() - .push(DropType { drop_fn: drop_for_type::, obj: result as *mut T as *mut u8 }); - result - } - - #[inline] - unsafe fn alloc_from_iter>(&self, iter: I) -> &mut [T] { - let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); - if vec.is_empty() { - return &mut []; - } - let len = vec.len(); - - let start_ptr = self - .arena - .alloc_raw(len.checked_mul(mem::size_of::()).unwrap(), mem::align_of::()) - as *mut _ as *mut T; - - let mut destructors = self.destructors.borrow_mut(); - // Reserve space for the destructors so we can't panic while adding them - destructors.reserve(len); - - // Move the content to the arena by copying it and then forgetting - // the content of the SmallVec - vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); - mem::forget(vec.drain(..)); - - // Record the destructors after doing the allocation as that may panic - // and would cause `object`'s destuctor to run twice if it was recorded before - for i in 0..len { - destructors.push(DropType { - drop_fn: drop_for_type::, - obj: start_ptr.offset(i as isize) as *mut u8, - }); - } - - slice::from_raw_parts_mut(start_ptr, len) - } -} diff --git a/src/librustc/dep_graph/debug.rs b/src/librustc/dep_graph/debug.rs deleted file mode 100644 index d44c54593a627..0000000000000 --- a/src/librustc/dep_graph/debug.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! Code for debugging the dep-graph. - -use super::dep_node::DepNode; -use std::error::Error; - -/// A dep-node filter goes from a user-defined string to a query over -/// nodes. Right now the format is like this: -/// -/// x & y & z -/// -/// where the format-string of the dep-node must contain `x`, `y`, and -/// `z`. -#[derive(Debug)] -pub struct DepNodeFilter { - text: String, -} - -impl DepNodeFilter { - pub fn new(text: &str) -> Self { - DepNodeFilter { text: text.trim().to_string() } - } - - /// Returns `true` if all nodes always pass the filter. - pub fn accepts_all(&self) -> bool { - self.text.is_empty() - } - - /// Tests whether `node` meets the filter, returning true if so. - pub fn test(&self, node: &DepNode) -> bool { - let debug_str = format!("{:?}", node); - self.text.split('&').map(|s| s.trim()).all(|f| debug_str.contains(f)) - } -} - -/// A filter like `F -> G` where `F` and `G` are valid dep-node -/// filters. This can be used to test the source/target independently. -pub struct EdgeFilter { - pub source: DepNodeFilter, - pub target: DepNodeFilter, -} - -impl EdgeFilter { - pub fn new(test: &str) -> Result> { - let parts: Vec<_> = test.split("->").collect(); - if parts.len() != 2 { - Err(format!("expected a filter like `a&b -> c&d`, not `{}`", test).into()) - } else { - Ok(EdgeFilter { - source: DepNodeFilter::new(parts[0]), - target: DepNodeFilter::new(parts[1]), - }) - } - } - - pub fn test(&self, source: &DepNode, target: &DepNode) -> bool { - self.source.test(source) && self.target.test(target) - } -} diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs deleted file mode 100644 index e3df9d5d04be1..0000000000000 --- a/src/librustc/dep_graph/dep_node.rs +++ /dev/null @@ -1,520 +0,0 @@ -//! This module defines the `DepNode` type which the compiler uses to represent -//! nodes in the dependency graph. A `DepNode` consists of a `DepKind` (which -//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc) -//! and a `Fingerprint`, a 128 bit hash value the exact meaning of which -//! depends on the node's `DepKind`. Together, the kind and the fingerprint -//! fully identify a dependency node, even across multiple compilation sessions. -//! In other words, the value of the fingerprint does not depend on anything -//! that is specific to a given compilation session, like an unpredictable -//! interning key (e.g., NodeId, DefId, Symbol) or the numeric value of a -//! pointer. The concept behind this could be compared to how git commit hashes -//! uniquely identify a given commit and has a few advantages: -//! -//! * A `DepNode` can simply be serialized to disk and loaded in another session -//! without the need to do any "rebasing (like we have to do for Spans and -//! NodeIds) or "retracing" like we had to do for `DefId` in earlier -//! implementations of the dependency graph. -//! * A `Fingerprint` is just a bunch of bits, which allows `DepNode` to -//! implement `Copy`, `Sync`, `Send`, `Freeze`, etc. -//! * Since we just have a bit pattern, `DepNode` can be mapped from disk into -//! memory without any post-processing (e.g., "abomination-style" pointer -//! reconstruction). -//! * Because a `DepNode` is self-contained, we can instantiate `DepNodes` that -//! refer to things that do not exist anymore. In previous implementations -//! `DepNode` contained a `DefId`. A `DepNode` referring to something that -//! had been removed between the previous and the current compilation session -//! could not be instantiated because the current compilation session -//! contained no `DefId` for thing that had been removed. -//! -//! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro -//! defines the `DepKind` enum and a corresponding `DepConstructor` enum. The -//! `DepConstructor` enum links a `DepKind` to the parameters that are needed at -//! runtime in order to construct a valid `DepNode` fingerprint. -//! -//! Because the macro sees what parameters a given `DepKind` requires, it can -//! "infer" some properties for each kind of `DepNode`: -//! -//! * Whether a `DepNode` of a given kind has any parameters at all. Some -//! `DepNode`s could represent global concepts with only one value. -//! * Whether it is possible, in principle, to reconstruct a query key from a -//! given `DepNode`. Many `DepKind`s only require a single `DefId` parameter, -//! in which case it is possible to map the node's fingerprint back to the -//! `DefId` it was computed from. In other cases, too much information gets -//! lost during fingerprint computation. -//! -//! The `DepConstructor` enum, together with `DepNode::new()` ensures that only -//! valid `DepNode` instances can be constructed. For example, the API does not -//! allow for constructing parameterless `DepNode`s with anything other -//! than a zeroed out fingerprint. More generally speaking, it relieves the -//! user of the `DepNode` API of having to know how to compute the expected -//! fingerprint for a given set of node parameters. - -use crate::hir::map::DefPathHash; -use crate::ich::{Fingerprint, StableHashingContext}; -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::subst::SubstsRef; -use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX}; -use rustc_hir::HirId; -use rustc_span::symbol::Symbol; -use std::fmt; -use std::hash::Hash; - -// erase!() just makes tokens go away. It's used to specify which macro argument -// is repeated (i.e., which sub-expression of the macro we are in) but don't need -// to actually use any of the arguments. -macro_rules! erase { - ($x:tt) => {{}}; -} - -macro_rules! is_anon_attr { - (anon) => { - true - }; - ($attr:ident) => { - false - }; -} - -macro_rules! is_eval_always_attr { - (eval_always) => { - true - }; - ($attr:ident) => { - false - }; -} - -macro_rules! contains_anon_attr { - ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_anon_attr!($attr) | )* false}); -} - -macro_rules! contains_eval_always_attr { - ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_eval_always_attr!($attr) | )* false}); -} - -macro_rules! define_dep_nodes { - (<$tcx:tt> - $( - [$($attrs:tt)*] - $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* - ,)* - ) => ( - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, - RustcEncodable, RustcDecodable)] - #[allow(non_camel_case_types)] - pub enum DepKind { - $($variant),* - } - - impl DepKind { - #[allow(unreachable_code)] - pub fn can_reconstruct_query_key<$tcx>(&self) -> bool { - match *self { - $( - DepKind :: $variant => { - if contains_anon_attr!($($attrs)*) { - return false; - } - - // tuple args - $({ - return <$tuple_arg_ty as DepNodeParams> - ::CAN_RECONSTRUCT_QUERY_KEY; - })* - - true - } - )* - } - } - - pub fn is_anon(&self) -> bool { - match *self { - $( - DepKind :: $variant => { contains_anon_attr!($($attrs)*) } - )* - } - } - - pub fn is_eval_always(&self) -> bool { - match *self { - $( - DepKind :: $variant => { contains_eval_always_attr!($($attrs)*) } - )* - } - } - - #[allow(unreachable_code)] - pub fn has_params(&self) -> bool { - match *self { - $( - DepKind :: $variant => { - // tuple args - $({ - erase!($tuple_arg_ty); - return true; - })* - - false - } - )* - } - } - } - - pub struct DepConstructor; - - #[allow(non_camel_case_types)] - impl DepConstructor { - $( - #[inline(always)] - #[allow(unreachable_code, non_snake_case)] - pub fn $variant(_tcx: TyCtxt<'_>, $(arg: $tuple_arg_ty)*) -> DepNode { - // tuple args - $({ - erase!($tuple_arg_ty); - 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; - })* - - DepNode { - kind: DepKind::$variant, - hash: Fingerprint::ZERO, - } - } - )* - } - - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, - RustcEncodable, RustcDecodable)] - pub struct DepNode { - pub kind: DepKind, - pub hash: Fingerprint, - } - - impl DepNode { - /// Construct a DepNode from the given DepKind and DefPathHash. This - /// method will assert that the given DepKind actually requires a - /// single DefId/DefPathHash parameter. - pub fn from_def_path_hash(def_path_hash: DefPathHash, - kind: DepKind) - -> DepNode { - debug_assert!(kind.can_reconstruct_query_key() && kind.has_params()); - DepNode { - kind, - hash: def_path_hash.0, - } - } - - /// Creates a new, parameterless DepNode. This method will assert - /// that the DepNode corresponding to the given DepKind actually - /// does not require any parameters. - pub fn new_no_params(kind: DepKind) -> DepNode { - debug_assert!(!kind.has_params()); - DepNode { - kind, - hash: Fingerprint::ZERO, - } - } - - /// Extracts the DefId corresponding to this DepNode. This will work - /// if two conditions are met: - /// - /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and - /// 2. the item that the DefPath refers to exists in the current tcx. - /// - /// Condition (1) is determined by the DepKind variant of the - /// DepNode. Condition (2) might not be fulfilled if a DepNode - /// refers to something from the previous compilation session that - /// has been removed. - pub fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option { - if self.kind.can_reconstruct_query_key() { - let def_path_hash = DefPathHash(self.hash); - tcx.def_path_hash_to_def_id.as_ref()? - .get(&def_path_hash).cloned() - } else { - None - } - } - - /// Used in testing - pub fn from_label_string(label: &str, - def_path_hash: DefPathHash) - -> Result { - let kind = match label { - $( - stringify!($variant) => DepKind::$variant, - )* - _ => return Err(()), - }; - - if !kind.can_reconstruct_query_key() { - return Err(()); - } - - if kind.has_params() { - Ok(DepNode::from_def_path_hash(def_path_hash, kind)) - } else { - Ok(DepNode::new_no_params(kind)) - } - } - - /// Used in testing - pub fn has_label_string(label: &str) -> bool { - match label { - $( - stringify!($variant) => true, - )* - _ => false, - } - } - } - - /// Contains variant => str representations for constructing - /// DepNode groups for tests. - #[allow(dead_code, non_upper_case_globals)] - pub mod label_strs { - $( - pub const $variant: &str = stringify!($variant); - )* - } - ); -} - -impl fmt::Debug for DepNode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.kind)?; - - if !self.kind.has_params() && !self.kind.is_anon() { - return Ok(()); - } - - write!(f, "(")?; - - crate::ty::tls::with_opt(|opt_tcx| { - if let Some(tcx) = opt_tcx { - if let Some(def_id) = self.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(*self) { - write!(f, "{}", s)?; - } else { - write!(f, "{}", self.hash)?; - } - } else { - write!(f, "{}", self.hash)?; - } - Ok(()) - })?; - - write!(f, ")") - } -} - -rustc_dep_node_append!([define_dep_nodes!][ <'tcx> - // We use this for most things when incr. comp. is turned off. - [] Null, - - // Represents metadata from an extern crate. - [eval_always] CrateMetadata(CrateNum), - - [anon] TraitSelect, - - [] CompileCodegenUnit(Symbol), -]); - -pub(crate) trait DepNodeParams<'tcx>: fmt::Debug + Sized { - const CAN_RECONSTRUCT_QUERY_KEY: bool; - - /// This method turns the parameters of a DepNodeConstructor into an opaque - /// Fingerprint to be used in DepNode. - /// Not all DepNodeParams support being turned into a Fingerprint (they - /// don't need to if the corresponding DepNode is anonymous). - fn to_fingerprint(&self, _: TyCtxt<'tcx>) -> Fingerprint { - panic!("Not implemented. Accidentally called on anonymous node?") - } - - fn to_debug_str(&self, _: TyCtxt<'tcx>) -> String { - format!("{:?}", self) - } - - /// This method tries to recover the query key from the given `DepNode`, - /// something which is needed when forcing `DepNode`s during red-green - /// evaluation. The query system will only call this method if - /// `CAN_RECONSTRUCT_QUERY_KEY` is `true`. - /// It is always valid to return `None` here, in which case incremental - /// compilation will treat the query as having changed instead of forcing it. - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option; -} - -impl<'tcx, T> DepNodeParams<'tcx> for T -where - T: HashStable> + fmt::Debug, -{ - default const CAN_RECONSTRUCT_QUERY_KEY: bool = false; - - default fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { - let mut hcx = tcx.create_stable_hashing_context(); - let mut hasher = StableHasher::new(); - - self.hash_stable(&mut hcx, &mut hasher); - - hasher.finish() - } - - default fn to_debug_str(&self, _: TyCtxt<'tcx>) -> String { - format!("{:?}", *self) - } - - default fn recover(_: TyCtxt<'tcx>, _: &DepNode) -> Option { - None - } -} - -impl<'tcx> DepNodeParams<'tcx> for DefId { - const CAN_RECONSTRUCT_QUERY_KEY: bool = true; - - fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint { - tcx.def_path_hash(*self).0 - } - - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - tcx.def_path_str(*self) - } - - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - dep_node.extract_def_id(tcx) - } -} - -impl<'tcx> DepNodeParams<'tcx> for DefIndex { - const CAN_RECONSTRUCT_QUERY_KEY: bool = true; - - fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint { - tcx.hir().definitions().def_path_hash(*self).0 - } - - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - tcx.def_path_str(DefId::local(*self)) - } - - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - dep_node.extract_def_id(tcx).map(|id| id.index) - } -} - -impl<'tcx> DepNodeParams<'tcx> for CrateNum { - const CAN_RECONSTRUCT_QUERY_KEY: bool = true; - - fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint { - let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; - tcx.def_path_hash(def_id).0 - } - - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - tcx.crate_name(*self).to_string() - } - - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - dep_node.extract_def_id(tcx).map(|id| id.krate) - } -} - -impl<'tcx> DepNodeParams<'tcx> for (DefId, DefId) { - const CAN_RECONSTRUCT_QUERY_KEY: bool = false; - - // We actually would not need to specialize the implementation of this - // method but it's faster to combine the hashes than to instantiate a full - // hashing context and stable-hashing state. - fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint { - let (def_id_0, def_id_1) = *self; - - let def_path_hash_0 = tcx.def_path_hash(def_id_0); - let def_path_hash_1 = tcx.def_path_hash(def_id_1); - - def_path_hash_0.0.combine(def_path_hash_1.0) - } - - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - let (def_id_0, def_id_1) = *self; - - format!("({}, {})", tcx.def_path_debug_str(def_id_0), tcx.def_path_debug_str(def_id_1)) - } -} - -impl<'tcx> DepNodeParams<'tcx> for HirId { - const CAN_RECONSTRUCT_QUERY_KEY: bool = false; - - // We actually would not need to specialize the implementation of this - // method but it's faster to combine the hashes than to instantiate a full - // hashing context and stable-hashing state. - fn to_fingerprint(&self, tcx: TyCtxt<'_>) -> Fingerprint { - let HirId { owner, local_id } = *self; - - let def_path_hash = tcx.def_path_hash(DefId::local(owner)); - let local_id = Fingerprint::from_smaller_hash(local_id.as_u32().into()); - - def_path_hash.0.combine(local_id) - } -} - -/// 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, - HashStable -)] -pub struct WorkProductId { - hash: Fingerprint, -} - -impl WorkProductId { - pub fn from_cgu_name(cgu_name: &str) -> WorkProductId { - let mut hasher = StableHasher::new(); - cgu_name.len().hash(&mut hasher); - cgu_name.hash(&mut hasher); - WorkProductId { hash: hasher.finish() } - } - - pub fn from_fingerprint(fingerprint: Fingerprint) -> WorkProductId { - WorkProductId { hash: fingerprint } - } -} diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs deleted file mode 100644 index 1fbd90743f402..0000000000000 --- a/src/librustc/dep_graph/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -pub mod debug; -mod dep_node; -mod graph; -mod prev; -mod query; -mod safe; -mod serialized; - -pub(crate) use self::dep_node::DepNodeParams; -pub use self::dep_node::{label_strs, DepConstructor, DepKind, DepNode, WorkProductId}; -pub use self::graph::WorkProductFileKind; -pub use self::graph::{hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, WorkProduct}; -pub use self::prev::PreviousDepGraph; -pub use self::query::DepGraphQuery; -pub use self::safe::AssertDepGraphSafe; -pub use self::safe::DepGraphSafe; -pub use self::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; diff --git a/src/librustc/dep_graph/prev.rs b/src/librustc/dep_graph/prev.rs deleted file mode 100644 index fbc8f7bc997e0..0000000000000 --- a/src/librustc/dep_graph/prev.rs +++ /dev/null @@ -1,55 +0,0 @@ -use super::dep_node::DepNode; -use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; -use crate::ich::Fingerprint; -use rustc_data_structures::fx::FxHashMap; - -#[derive(Debug, RustcEncodable, RustcDecodable, Default)] -pub struct PreviousDepGraph { - data: SerializedDepGraph, - index: FxHashMap, -} - -impl PreviousDepGraph { - pub fn new(data: SerializedDepGraph) -> PreviousDepGraph { - let index: FxHashMap<_, _> = - data.nodes.iter_enumerated().map(|(idx, &dep_node)| (dep_node, idx)).collect(); - PreviousDepGraph { data, index } - } - - #[inline] - pub fn edge_targets_from( - &self, - dep_node_index: SerializedDepNodeIndex, - ) -> &[SerializedDepNodeIndex] { - self.data.edge_targets_from(dep_node_index) - } - - #[inline] - pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> DepNode { - self.data.nodes[dep_node_index] - } - - #[inline] - pub fn node_to_index(&self, dep_node: &DepNode) -> SerializedDepNodeIndex { - self.index[dep_node] - } - - #[inline] - pub fn node_to_index_opt(&self, dep_node: &DepNode) -> Option { - self.index.get(dep_node).cloned() - } - - #[inline] - pub fn fingerprint_of(&self, dep_node: &DepNode) -> Option { - self.index.get(dep_node).map(|&node_index| self.data.fingerprints[node_index]) - } - - #[inline] - pub fn fingerprint_by_index(&self, dep_node_index: SerializedDepNodeIndex) -> Fingerprint { - self.data.fingerprints[dep_node_index] - } - - pub fn node_count(&self) -> usize { - self.index.len() - } -} diff --git a/src/librustc/dep_graph/query.rs b/src/librustc/dep_graph/query.rs deleted file mode 100644 index c71c11ed0ebdf..0000000000000 --- a/src/librustc/dep_graph/query.rs +++ /dev/null @@ -1,74 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::graph::implementation::{ - Direction, Graph, NodeIndex, INCOMING, OUTGOING, -}; - -use super::DepNode; - -pub struct DepGraphQuery { - pub graph: Graph, - pub indices: FxHashMap, -} - -impl DepGraphQuery { - pub fn new(nodes: &[DepNode], edges: &[(DepNode, DepNode)]) -> 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())); - } - - for &(ref source, ref target) in edges { - let source = indices[source]; - let target = indices[target]; - graph.add_edge(source, target, ()); - } - - DepGraphQuery { graph, indices } - } - - pub fn contains_node(&self, node: &DepNode) -> bool { - self.indices.contains_key(&node) - } - - pub fn nodes(&self) -> Vec<&DepNode> { - self.graph.all_nodes().iter().map(|n| &n.data).collect() - } - - pub fn edges(&self) -> Vec<(&DepNode, &DepNode)> { - self.graph - .all_edges() - .iter() - .map(|edge| (edge.source(), edge.target())) - .map(|(s, t)| (self.graph.node_data(s), self.graph.node_data(t))) - .collect() - } - - fn reachable_nodes(&self, node: &DepNode, direction: Direction) -> Vec<&DepNode> { - if let Some(&index) = self.indices.get(node) { - self.graph.depth_traverse(index, direction).map(|s| self.graph.node_data(s)).collect() - } else { - vec![] - } - } - - /// All nodes reachable from `node`. In other words, things that - /// will have to be recomputed if `node` changes. - pub fn transitive_successors(&self, node: &DepNode) -> Vec<&DepNode> { - self.reachable_nodes(node, OUTGOING) - } - - /// All nodes that can reach `node`. - pub fn transitive_predecessors(&self, node: &DepNode) -> Vec<&DepNode> { - self.reachable_nodes(node, INCOMING) - } - - /// Just the outgoing edges from `node`. - pub fn immediate_successors(&self, node: &DepNode) -> Vec<&DepNode> { - if let Some(&index) = self.indices.get(&node) { - self.graph.successor_nodes(index).map(|s| self.graph.node_data(s)).collect() - } else { - vec![] - } - } -} diff --git a/src/librustc/dep_graph/safe.rs b/src/librustc/dep_graph/safe.rs deleted file mode 100644 index 74e32867cdec1..0000000000000 --- a/src/librustc/dep_graph/safe.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! The `DepGraphSafe` trait - -use crate::ty::TyCtxt; - -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 {} - -/// 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> {} - -/// 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/dep_graph/serialized.rs b/src/librustc/dep_graph/serialized.rs deleted file mode 100644 index 45ef52dbf39c2..0000000000000 --- a/src/librustc/dep_graph/serialized.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! The data that we will serialize and deserialize. - -use crate::dep_graph::DepNode; -use crate::ich::Fingerprint; -use rustc_index::vec::IndexVec; - -rustc_index::newtype_index! { - pub struct SerializedDepNodeIndex { .. } -} - -/// Data for use when recompiling the **current crate**. -#[derive(Debug, RustcEncodable, RustcDecodable, Default)] -pub struct SerializedDepGraph { - /// The set of all DepNodes in the graph - pub nodes: IndexVec, - /// The set of all Fingerprints in the graph. Each Fingerprint corresponds to - /// the DepNode at the same index in the nodes vector. - pub fingerprints: IndexVec, - /// For each DepNode, stores the list of edges originating from that - /// DepNode. Encoded as a [start, end) pair indexing into edge_list_data, - /// which holds the actual DepNodeIndices of the target nodes. - pub edge_list_indices: IndexVec, - /// A flattened list of all edge targets in the graph. Edge sources are - /// implicit in edge_list_indices. - pub edge_list_data: Vec, -} - -impl SerializedDepGraph { - #[inline] - pub fn edge_targets_from(&self, source: SerializedDepNodeIndex) -> &[SerializedDepNodeIndex] { - let targets = self.edge_list_indices[source]; - &self.edge_list_data[targets.0 as usize..targets.1 as usize] - } -} diff --git a/src/librustc/hir/map/blocks.rs b/src/librustc/hir/map/blocks.rs deleted file mode 100644 index d9ffe4582e7d7..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::Method(..) => true, - _ => false, - } - } -} - -impl MaybeFnLike for hir::TraitItem<'_> { - fn is_fn_like(&self) -> bool { - match self.kind { - hir::TraitItemKind::Fn(_, hir::TraitMethod::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::TraitMethod::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::Method(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/definitions.rs b/src/librustc/hir/map/definitions.rs deleted file mode 100644 index 42ccf7e72504b..0000000000000 --- a/src/librustc/hir/map/definitions.rs +++ /dev/null @@ -1,552 +0,0 @@ -//! For each definition, we track the following data. A definition -//! here is defined somewhat circularly as "something with a `DefId`", -//! but it generally corresponds to things like structs, enums, etc. -//! There are also some rather random cases (like const initializer -//! expressions) that are mostly just leftovers. - -use rustc_ast::ast; -use rustc_ast::node_id::NodeMap; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::StableHasher; -use rustc_hir as hir; -use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_index::vec::IndexVec; -use rustc_session::CrateDisambiguator; -use rustc_span::hygiene::ExpnId; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::Span; - -use std::fmt::Write; -use std::hash::Hash; - -pub use rustc_hir::def_id::DefPathHash; - -/// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa. -/// Internally the `DefPathTable` holds a tree of `DefKey`s, where each `DefKey` -/// stores the `DefIndex` of its parent. -/// There is one `DefPathTable` for each crate. -#[derive(Clone, Default, RustcDecodable, RustcEncodable)] -pub struct DefPathTable { - index_to_key: IndexVec, - def_path_hashes: IndexVec, -} - -impl DefPathTable { - fn allocate(&mut self, key: DefKey, def_path_hash: DefPathHash) -> DefIndex { - let index = { - let index = DefIndex::from(self.index_to_key.len()); - debug!("DefPathTable::insert() - {:?} <-> {:?}", key, index); - self.index_to_key.push(key); - index - }; - self.def_path_hashes.push(def_path_hash); - debug_assert!(self.def_path_hashes.len() == self.index_to_key.len()); - index - } - - pub fn next_id(&self) -> DefIndex { - DefIndex::from(self.index_to_key.len()) - } - - #[inline(always)] - pub fn def_key(&self, index: DefIndex) -> DefKey { - self.index_to_key[index] - } - - #[inline(always)] - pub fn def_path_hash(&self, index: DefIndex) -> DefPathHash { - let hash = self.def_path_hashes[index]; - debug!("def_path_hash({:?}) = {:?}", index, hash); - hash - } - - pub fn add_def_path_hashes_to(&self, cnum: CrateNum, out: &mut FxHashMap) { - out.extend(self.def_path_hashes.iter().enumerate().map(|(index, &hash)| { - let def_id = DefId { krate: cnum, index: DefIndex::from(index) }; - (hash, def_id) - })); - } - - pub fn size(&self) -> usize { - self.index_to_key.len() - } -} - -/// The definition table containing node definitions. -/// It holds the `DefPathTable` for local `DefId`s/`DefPath`s and it also stores a -/// mapping from `NodeId`s to local `DefId`s. -#[derive(Clone, Default)] -pub struct Definitions { - table: DefPathTable, - node_to_def_index: NodeMap, - def_index_to_node: IndexVec, - - pub(super) node_to_hir_id: IndexVec, - /// The reverse mapping of `node_to_hir_id`. - pub(super) hir_to_node_id: FxHashMap, - - /// If `ExpnId` is an ID of some macro expansion, - /// then `DefId` is the normal module (`mod`) in which the expanded macro was defined. - parent_modules_of_macro_defs: FxHashMap, - /// Item with a given `DefIndex` was defined during macro expansion with ID `ExpnId`. - expansions_that_defined: FxHashMap, - next_disambiguator: FxHashMap<(DefIndex, DefPathData), u32>, - def_index_to_span: FxHashMap, - /// When collecting definitions from an AST fragment produced by a macro invocation `ExpnId` - /// we know what parent node that fragment should be attached to thanks to this table. - invocation_parents: FxHashMap, - /// Indices of unnamed struct or variant fields with unresolved attributes. - placeholder_field_indices: NodeMap, -} - -/// A unique identifier that we can use to lookup a definition -/// precisely. It combines the index of the definition's parent (if -/// any) with a `DisambiguatedDefPathData`. -#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] -pub struct DefKey { - /// The parent path. - pub parent: Option, - - /// The identifier of this node. - pub disambiguated_data: DisambiguatedDefPathData, -} - -impl DefKey { - fn compute_stable_hash(&self, parent_hash: DefPathHash) -> DefPathHash { - let mut hasher = StableHasher::new(); - - // We hash a `0u8` here to disambiguate between regular `DefPath` hashes, - // and the special "root_parent" below. - 0u8.hash(&mut hasher); - parent_hash.hash(&mut hasher); - - let DisambiguatedDefPathData { ref data, disambiguator } = self.disambiguated_data; - - ::std::mem::discriminant(data).hash(&mut hasher); - if let Some(name) = data.get_opt_name() { - // Get a stable hash by considering the symbol chars rather than - // the symbol index. - name.as_str().hash(&mut hasher); - } - - disambiguator.hash(&mut hasher); - - DefPathHash(hasher.finish()) - } - - fn root_parent_stable_hash( - crate_name: &str, - crate_disambiguator: CrateDisambiguator, - ) -> DefPathHash { - let mut hasher = StableHasher::new(); - // Disambiguate this from a regular `DefPath` hash; see `compute_stable_hash()` above. - 1u8.hash(&mut hasher); - crate_name.hash(&mut hasher); - crate_disambiguator.hash(&mut hasher); - DefPathHash(hasher.finish()) - } -} - -/// A pair of `DefPathData` and an integer disambiguator. The integer is -/// normally `0`, but in the event that there are multiple defs with the -/// same `parent` and `data`, we use this field to disambiguate -/// between them. This introduces some artificial ordering dependency -/// but means that if you have, e.g., two impls for the same type in -/// the same module, they do get distinct `DefId`s. -#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] -pub struct DisambiguatedDefPathData { - pub data: DefPathData, - pub disambiguator: u32, -} - -#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] -pub struct DefPath { - /// The path leading from the crate root to the item. - pub data: Vec, - - /// The crate root this path is relative to. - pub krate: CrateNum, -} - -impl DefPath { - pub fn is_local(&self) -> bool { - self.krate == LOCAL_CRATE - } - - pub fn make(krate: CrateNum, start_index: DefIndex, mut get_key: FN) -> DefPath - where - FN: FnMut(DefIndex) -> DefKey, - { - let mut data = vec![]; - let mut index = Some(start_index); - loop { - debug!("DefPath::make: krate={:?} index={:?}", krate, index); - let p = index.unwrap(); - let key = get_key(p); - debug!("DefPath::make: key={:?}", key); - match key.disambiguated_data.data { - DefPathData::CrateRoot => { - assert!(key.parent.is_none()); - break; - } - _ => { - data.push(key.disambiguated_data); - index = key.parent; - } - } - } - data.reverse(); - DefPath { data, krate } - } - - /// Returns a string representation of the `DefPath` without - /// the crate-prefix. This method is useful if you don't have - /// a `TyCtxt` available. - pub fn to_string_no_crate(&self) -> String { - let mut s = String::with_capacity(self.data.len() * 16); - - for component in &self.data { - write!(s, "::{}[{}]", component.data.as_symbol(), component.disambiguator).unwrap(); - } - - s - } - - /// Returns a filename-friendly string for the `DefPath`, with the - /// crate-prefix. - pub fn to_string_friendly(&self, crate_imported_name: F) -> String - where - F: FnOnce(CrateNum) -> Symbol, - { - let crate_name_str = crate_imported_name(self.krate).as_str(); - let mut s = String::with_capacity(crate_name_str.len() + self.data.len() * 16); - - write!(s, "::{}", crate_name_str).unwrap(); - - for component in &self.data { - if component.disambiguator == 0 { - write!(s, "::{}", component.data.as_symbol()).unwrap(); - } else { - write!(s, "{}[{}]", component.data.as_symbol(), component.disambiguator).unwrap(); - } - } - - s - } - - /// Returns a filename-friendly string of the `DefPath`, without - /// the crate-prefix. This method is useful if you don't have - /// a `TyCtxt` available. - pub fn to_filename_friendly_no_crate(&self) -> String { - let mut s = String::with_capacity(self.data.len() * 16); - - let mut opt_delimiter = None; - for component in &self.data { - opt_delimiter.map(|d| s.push(d)); - opt_delimiter = Some('-'); - if component.disambiguator == 0 { - write!(s, "{}", component.data.as_symbol()).unwrap(); - } else { - write!(s, "{}[{}]", component.data.as_symbol(), component.disambiguator).unwrap(); - } - } - s - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -pub enum DefPathData { - // Root: these should only be used for the root nodes, because - // they are treated specially by the `def_path` function. - /// The crate root (marker). - CrateRoot, - // Catch-all for random `DefId` things like `DUMMY_NODE_ID`. - Misc, - - // Different kinds of items and item-like things: - /// An impl. - Impl, - /// Something in the type namespace. - TypeNs(Symbol), - /// Something in the value namespace. - ValueNs(Symbol), - /// Something in the macro namespace. - MacroNs(Symbol), - /// Something in the lifetime namespace. - LifetimeNs(Symbol), - /// A closure expression. - ClosureExpr, - - // Subportions of items: - /// Implicit constructor for a unit or tuple-like struct or enum variant. - Ctor, - /// A constant expression (see `{ast,hir}::AnonConst`). - AnonConst, - /// An `impl Trait` type node. - ImplTrait, -} - -impl Definitions { - pub fn def_path_table(&self) -> &DefPathTable { - &self.table - } - - /// Gets the number of definitions. - pub fn def_index_count(&self) -> usize { - self.table.index_to_key.len() - } - - pub fn def_key(&self, index: DefIndex) -> DefKey { - self.table.def_key(index) - } - - #[inline(always)] - pub fn def_path_hash(&self, index: DefIndex) -> DefPathHash { - self.table.def_path_hash(index) - } - - /// Returns the path from the crate root to `index`. The root - /// nodes are not included in the path (i.e., this will be an - /// empty vector for the crate root). For an inlined item, this - /// will be the path of the item in the external crate (but the - /// path will begin with the path to the external crate). - pub fn def_path(&self, index: DefIndex) -> DefPath { - DefPath::make(LOCAL_CRATE, index, |p| self.def_key(p)) - } - - #[inline] - pub fn opt_def_index(&self, node: ast::NodeId) -> Option { - self.node_to_def_index.get(&node).copied() - } - - #[inline] - pub fn opt_local_def_id(&self, node: ast::NodeId) -> Option { - self.opt_def_index(node).map(DefId::local) - } - - #[inline] - pub fn local_def_id(&self, node: ast::NodeId) -> DefId { - self.opt_local_def_id(node).unwrap() - } - - #[inline] - pub fn as_local_node_id(&self, def_id: DefId) -> Option { - if def_id.krate == LOCAL_CRATE { - let node_id = self.def_index_to_node[def_id.index]; - if node_id != ast::DUMMY_NODE_ID { - return Some(node_id); - } - } - None - } - - #[inline] - pub fn as_local_hir_id(&self, def_id: DefId) -> Option { - if def_id.krate == LOCAL_CRATE { - let hir_id = self.def_index_to_hir_id(def_id.index); - if hir_id != hir::DUMMY_HIR_ID { Some(hir_id) } else { None } - } else { - None - } - } - - #[inline] - pub fn hir_to_node_id(&self, hir_id: hir::HirId) -> ast::NodeId { - self.hir_to_node_id[&hir_id] - } - - #[inline] - pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId { - self.node_to_hir_id[node_id] - } - - #[inline] - pub fn def_index_to_hir_id(&self, def_index: DefIndex) -> hir::HirId { - let node_id = self.def_index_to_node[def_index]; - self.node_to_hir_id[node_id] - } - - /// Retrieves the span of the given `DefId` if `DefId` is in the local crate, the span exists - /// and it's not `DUMMY_SP`. - #[inline] - pub fn opt_span(&self, def_id: DefId) -> Option { - if def_id.krate == LOCAL_CRATE { - self.def_index_to_span.get(&def_id.index).copied() - } else { - None - } - } - - /// Adds a root definition (no parent) and a few other reserved definitions. - pub fn create_root_def( - &mut self, - crate_name: &str, - crate_disambiguator: CrateDisambiguator, - ) -> DefIndex { - let key = DefKey { - parent: None, - disambiguated_data: DisambiguatedDefPathData { - data: DefPathData::CrateRoot, - disambiguator: 0, - }, - }; - - let parent_hash = DefKey::root_parent_stable_hash(crate_name, crate_disambiguator); - let def_path_hash = key.compute_stable_hash(parent_hash); - - // Create the definition. - let root_index = self.table.allocate(key, def_path_hash); - assert_eq!(root_index, CRATE_DEF_INDEX); - assert!(self.def_index_to_node.is_empty()); - self.def_index_to_node.push(ast::CRATE_NODE_ID); - self.node_to_def_index.insert(ast::CRATE_NODE_ID, root_index); - self.set_invocation_parent(ExpnId::root(), root_index); - - root_index - } - - /// Adds a definition with a parent definition. - pub fn create_def_with_parent( - &mut self, - parent: DefIndex, - node_id: ast::NodeId, - data: DefPathData, - expn_id: ExpnId, - span: Span, - ) -> DefIndex { - debug!( - "create_def_with_parent(parent={:?}, node_id={:?}, data={:?})", - parent, node_id, data - ); - - assert!( - !self.node_to_def_index.contains_key(&node_id), - "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}", - node_id, - data, - self.table.def_key(self.node_to_def_index[&node_id]) - ); - - // The root node must be created with `create_root_def()`. - assert!(data != DefPathData::CrateRoot); - - // Find the next free disambiguator for this key. - let disambiguator = { - let next_disamb = self.next_disambiguator.entry((parent, data)).or_insert(0); - let disambiguator = *next_disamb; - *next_disamb = next_disamb.checked_add(1).expect("disambiguator overflow"); - disambiguator - }; - - let key = DefKey { - parent: Some(parent), - disambiguated_data: DisambiguatedDefPathData { data, disambiguator }, - }; - - let parent_hash = self.table.def_path_hash(parent); - let def_path_hash = key.compute_stable_hash(parent_hash); - - debug!("create_def_with_parent: after disambiguation, key = {:?}", key); - - // Create the definition. - let index = self.table.allocate(key, def_path_hash); - assert_eq!(index.index(), self.def_index_to_node.len()); - self.def_index_to_node.push(node_id); - - // Some things for which we allocate `DefIndex`es don't correspond to - // anything in the AST, so they don't have a `NodeId`. For these cases - // we don't need a mapping from `NodeId` to `DefIndex`. - if node_id != ast::DUMMY_NODE_ID { - debug!("create_def_with_parent: def_index_to_node[{:?} <-> {:?}", index, node_id); - self.node_to_def_index.insert(node_id, index); - } - - if expn_id != ExpnId::root() { - self.expansions_that_defined.insert(index, expn_id); - } - - // The span is added if it isn't dummy. - if !span.is_dummy() { - self.def_index_to_span.insert(index, span); - } - - index - } - - /// 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) { - assert!( - self.node_to_hir_id.is_empty(), - "trying to initialize `NodeId` -> `HirId` mapping twice" - ); - self.node_to_hir_id = mapping; - - // Build the reverse mapping of `node_to_hir_id`. - self.hir_to_node_id = self - .node_to_hir_id - .iter_enumerated() - .map(|(node_id, &hir_id)| (hir_id, node_id)) - .collect(); - } - - pub fn expansion_that_defined(&self, index: DefIndex) -> ExpnId { - self.expansions_that_defined.get(&index).copied().unwrap_or(ExpnId::root()) - } - - pub fn parent_module_of_macro_def(&self, expn_id: ExpnId) -> DefId { - self.parent_modules_of_macro_defs[&expn_id] - } - - pub fn add_parent_module_of_macro_def(&mut self, expn_id: ExpnId, module: DefId) { - self.parent_modules_of_macro_defs.insert(expn_id, module); - } - - pub fn invocation_parent(&self, invoc_id: ExpnId) -> DefIndex { - self.invocation_parents[&invoc_id] - } - - pub fn set_invocation_parent(&mut self, invoc_id: ExpnId, parent: DefIndex) { - let old_parent = self.invocation_parents.insert(invoc_id, parent); - assert!(old_parent.is_none(), "parent `DefIndex` is reset for an invocation"); - } - - pub fn placeholder_field_index(&self, node_id: ast::NodeId) -> usize { - self.placeholder_field_indices[&node_id] - } - - pub fn set_placeholder_field_index(&mut self, node_id: ast::NodeId, index: usize) { - let old_index = self.placeholder_field_indices.insert(node_id, index); - assert!(old_index.is_none(), "placeholder field index is reset for a node ID"); - } -} - -impl DefPathData { - pub fn get_opt_name(&self) -> Option { - use self::DefPathData::*; - match *self { - TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name), - - Impl | CrateRoot | Misc | ClosureExpr | Ctor | AnonConst | ImplTrait => None, - } - } - - pub fn as_symbol(&self) -> Symbol { - use self::DefPathData::*; - match *self { - TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => name, - // Note that this does not show up in user print-outs. - CrateRoot => sym::double_braced_crate, - Impl => sym::double_braced_impl, - Misc => sym::double_braced_misc, - ClosureExpr => sym::double_braced_closure, - Ctor => sym::double_braced_constructor, - AnonConst => sym::double_braced_constant, - ImplTrait => sym::double_braced_opaque, - } - } - - pub fn to_string(&self) -> String { - self.as_symbol().to_string() - } -} diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs deleted file mode 100644 index bcbb6f3ec31e6..0000000000000 --- a/src/librustc/hir/map/mod.rs +++ /dev/null @@ -1,1168 +0,0 @@ -use self::collector::NodeCollector; -pub use self::definitions::{ - DefKey, DefPath, DefPathData, DefPathHash, Definitions, DisambiguatedDefPathData, -}; - -use crate::hir::{HirOwner, HirOwnerItems}; -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, DefIndex, LocalDefId, LOCAL_CRATE}; -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; -pub mod definitions; -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::Method(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::Method(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(_, TraitMethod::Provided(body)) => Some(body), - _ => None, - }, - - Node::ImplItem(item) => match item.kind { - ImplItemKind::Const(_, body) | ImplItemKind::Method(_, 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 HirOwner<'hir>>, - pub(super) with_bodies: Option<&'hir mut HirOwnerItems<'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: DefId) -> DefKey { - assert!(def_id.is_local()); - self.tcx.definitions.def_key(def_id.index) - } - - 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: DefId) -> DefPath { - assert!(def_id.is_local()); - self.tcx.definitions.def_path(def_id.index) - } - - #[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) - ) - }) - } - - #[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.tcx.definitions.opt_local_def_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: 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 def_index_to_hir_id(&self, def_index: DefIndex) -> HirId { - self.tcx.definitions.def_index_to_hir_id(def_index) - } - - #[inline] - pub fn local_def_id_to_hir_id(&self, def_id: LocalDefId) -> HirId { - self.tcx.definitions.def_index_to_hir_id(def_id.to_def_id().index) - } - - 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::Method(..) => 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> { - Some(self.get_entry(id)) - } - - fn get_entry(&self, id: HirId) -> Entry<'hir> { - if id.local_id == ItemLocalId::from_u32_const(0) { - let owner = self.tcx.hir_owner(id.owner_def_id()); - Entry { parent: owner.parent, node: owner.node } - } else { - let owner = self.tcx.hir_owner_items(id.owner_def_id()); - let item = owner.items[id.local_id].as_ref().unwrap(); - Entry { parent: HirId { owner: id.owner, local_id: item.parent }, node: item.node } - } - } - - 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_items(DefId::local(id.hir_id.owner)) - .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) -> 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::Method(..), .. }) => 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); - - 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> { - let node = self.get_entry(hir_id).node; - if let Node::Crate(..) = node { None } else { Some(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::Method(..) => 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 - } - - 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) - }; - - let map = tcx.arena.alloc(IndexedHir { crate_hash, map }); - - 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::Method(..) => 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 3b69fc8d8f2ac..0000000000000 --- a/src/librustc/hir/mod.rs +++ /dev/null @@ -1,86 +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::{DefId, 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 HirOwner<'tcx> { - parent: HirId, - node: Node<'tcx>, -} - -impl<'a, 'tcx> HashStable> for HirOwner<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let HirOwner { parent, node } = self; - hcx.while_hashing_hir_bodies(false, |hcx| { - parent.hash_stable(hcx, hasher); - node.hash_stable(hcx, hasher); - }); - } -} - -#[derive(Clone)] -pub struct HirItem<'tcx> { - parent: ItemLocalId, - node: Node<'tcx>, -} - -pub struct HirOwnerItems<'tcx> { - hash: Fingerprint, - items: IndexVec>>, - bodies: FxHashMap>, -} - -impl<'a, 'tcx> HashStable> for HirOwnerItems<'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - // We ignore the `items` and `bodies` fields since these refer to information included in - // `hash` which is hashed in the collector and used for the crate hash. - let HirOwnerItems { hash, items: _, 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) -> DefId { - self.parent_module_from_def_id(DefId::local(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).unwrap())) - }; - providers.hir_crate = |tcx, _| tcx.untracked_crate; - providers.index_hir = map::index_hir; - providers.hir_module_items = |tcx, id| { - assert_eq!(id.krate, LOCAL_CRATE); - let hir = tcx.hir(); - let module = hir.as_local_hir_id(id).unwrap(); - &tcx.untracked_crate.modules[&module] - }; - providers.hir_owner = |tcx, id| tcx.index_hir(id.krate).map[id.index].signature.unwrap(); - providers.hir_owner_items = |tcx, id| { - tcx.index_hir(id.krate).map[id.index].with_bodies.as_ref().map(|items| &**items).unwrap() - }; - 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 24237235e0c4a..0000000000000 --- a/src/librustc/lib.rs +++ /dev/null @@ -1,93 +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_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(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 use rustc_session as session; -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 dcc8dcbf21961..0000000000000 --- a/src/librustc/lint.rs +++ /dev/null @@ -1,394 +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::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId}; -use rustc_hir::HirId; -pub use rustc_session::lint::{builtin, Level, Lint, LintId, LintPass}; -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); - } - - return (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 - } -} - -pub fn add_elided_lifetime_in_path_suggestion( - sess: &Session, - db: &mut DiagnosticBuilder<'_>, - n: usize, - path_span: Span, - incl_angl_brckt: bool, - insertion_span: Span, - anon_lts: String, -) { - let (replace_span, suggestion) = if incl_angl_brckt { - (insertion_span, anon_lts) - } else { - // When possible, prefer a suggestion that replaces the whole - // `Path` expression with `Path<'_, T>`, rather than inserting `'_, ` - // at a point (which makes for an ugly/confusing label) - if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) { - // But our spans can get out of whack due to macros; if the place we think - // we want to insert `'_` isn't even within the path expression's span, we - // should bail out of making any suggestion rather than panicking on a - // subtract-with-overflow or string-slice-out-out-bounds (!) - // FIXME: can we do better? - if insertion_span.lo().0 < path_span.lo().0 { - return; - } - let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize; - if insertion_index > snippet.len() { - return; - } - let (before, after) = snippet.split_at(insertion_index); - (path_span, format!("{}{}{}", before, anon_lts, after)) - } else { - (insertion_span, anon_lts) - } - }; - db.span_suggestion( - replace_span, - &format!("indicate the anonymous lifetime{}", pluralize!(n)), - suggestion, - Applicability::MachineApplicable, - ); -} 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/middle/region.rs b/src/librustc/middle/region.rs deleted file mode 100644 index 758ba4a1ab013..0000000000000 --- a/src/librustc/middle/region.rs +++ /dev/null @@ -1,669 +0,0 @@ -//! This file declares the `ScopeTree` type, which describes -//! the parent links in the region hierarchy. -//! -//! For more information about how MIR-based region-checking works, -//! see the [rustc dev guide]. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/borrowck.html - -use crate::ich::{NodeIdHashingMode, StableHashingContext}; -use crate::ty::{self, DefIdTree, TyCtxt}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_hir::Node; - -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_macros::HashStable; -use rustc_span::{Span, DUMMY_SP}; - -use std::fmt; - -/// Represents a statically-describable scope that can be used to -/// bound the lifetime/region for values. -/// -/// `Node(node_id)`: Any AST node that has any scope at all has the -/// `Node(node_id)` scope. Other variants represent special cases not -/// immediately derivable from the abstract syntax tree structure. -/// -/// `DestructionScope(node_id)` represents the scope of destructors -/// implicitly-attached to `node_id` that run immediately after the -/// expression for `node_id` itself. Not every AST node carries a -/// `DestructionScope`, but those that are `terminating_scopes` do; -/// see discussion with `ScopeTree`. -/// -/// `Remainder { block, statement_index }` represents -/// the scope of user code running immediately after the initializer -/// expression for the indexed statement, until the end of the block. -/// -/// So: the following code can be broken down into the scopes beneath: -/// -/// ```text -/// let a = f().g( 'b: { let x = d(); let y = d(); x.h(y) } ) ; -/// -/// +-+ (D12.) -/// +-+ (D11.) -/// +---------+ (R10.) -/// +-+ (D9.) -/// +----------+ (M8.) -/// +----------------------+ (R7.) -/// +-+ (D6.) -/// +----------+ (M5.) -/// +-----------------------------------+ (M4.) -/// +--------------------------------------------------+ (M3.) -/// +--+ (M2.) -/// +-----------------------------------------------------------+ (M1.) -/// -/// (M1.): Node scope of the whole `let a = ...;` statement. -/// (M2.): Node scope of the `f()` expression. -/// (M3.): Node scope of the `f().g(..)` expression. -/// (M4.): Node scope of the block labeled `'b:`. -/// (M5.): Node scope of the `let x = d();` statement -/// (D6.): DestructionScope for temporaries created during M5. -/// (R7.): Remainder scope for block `'b:`, stmt 0 (let x = ...). -/// (M8.): Node scope of the `let y = d();` statement. -/// (D9.): DestructionScope for temporaries created during M8. -/// (R10.): Remainder scope for block `'b:`, stmt 1 (let y = ...). -/// (D11.): DestructionScope for temporaries and bindings from block `'b:`. -/// (D12.): DestructionScope for temporaries created during M1 (e.g., f()). -/// ``` -/// -/// Note that while the above picture shows the destruction scopes -/// as following their corresponding node scopes, in the internal -/// data structures of the compiler the destruction scopes are -/// represented as enclosing parents. This is sound because we use the -/// enclosing parent relationship just to ensure that referenced -/// values live long enough; phrased another way, the starting point -/// of each range is not really the important thing in the above -/// picture, but rather the ending point. -// -// FIXME(pnkfelix): this currently derives `PartialOrd` and `Ord` to -// placate the same deriving in `ty::FreeRegion`, but we may want to -// actually attach a more meaningful ordering to scopes than the one -// generated via deriving here. -#[derive( - Clone, - PartialEq, - PartialOrd, - Eq, - Ord, - Hash, - Copy, - RustcEncodable, - RustcDecodable, - HashStable -)] -pub struct Scope { - pub id: hir::ItemLocalId, - pub data: ScopeData, -} - -impl fmt::Debug for Scope { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.data { - ScopeData::Node => write!(fmt, "Node({:?})", self.id), - ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.id), - ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id), - ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id), - ScopeData::Remainder(fsi) => write!( - fmt, - "Remainder {{ block: {:?}, first_statement_index: {}}}", - self.id, - fsi.as_u32(), - ), - } - } -} - -#[derive( - Clone, - PartialEq, - PartialOrd, - Eq, - Ord, - Hash, - Debug, - Copy, - RustcEncodable, - RustcDecodable, - HashStable -)] -pub enum ScopeData { - Node, - - /// Scope of the call-site for a function or closure - /// (outlives the arguments as well as the body). - CallSite, - - /// Scope of arguments passed to a function or closure - /// (they outlive its body). - Arguments, - - /// Scope of destructors for temporaries of node-id. - Destruction, - - /// Scope following a `let id = expr;` binding in a block. - Remainder(FirstStatementIndex), -} - -rustc_index::newtype_index! { - /// Represents a subscope of `block` for a binding that is introduced - /// by `block.stmts[first_statement_index]`. Such subscopes represent - /// a suffix of the block. Note that each subscope does not include - /// the initializer expression, if any, for the statement indexed by - /// `first_statement_index`. - /// - /// For example, given `{ let (a, b) = EXPR_1; let c = EXPR_2; ... }`: - /// - /// * The subscope with `first_statement_index == 0` is scope of both - /// `a` and `b`; it does not include EXPR_1, but does include - /// everything after that first `let`. (If you want a scope that - /// includes EXPR_1 as well, then do not use `Scope::Remainder`, - /// but instead another `Scope` that encompasses the whole block, - /// e.g., `Scope::Node`. - /// - /// * The subscope with `first_statement_index == 1` is scope of `c`, - /// and thus does not include EXPR_2, but covers the `...`. - pub struct FirstStatementIndex { - derive [HashStable] - } -} - -// compilation error if size of `ScopeData` is not the same as a `u32` -static_assert_size!(ScopeData, 4); - -impl Scope { - /// Returns a item-local ID associated with this scope. - /// - /// N.B., likely to be replaced as API is refined; e.g., pnkfelix - /// anticipates `fn entry_node_id` and `fn each_exit_node_id`. - pub fn item_local_id(&self) -> hir::ItemLocalId { - self.id - } - - pub fn hir_id(&self, scope_tree: &ScopeTree) -> 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, - } - } - - /// 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 span = tcx.hir().span(hir_id); - if let ScopeData::Remainder(first_statement_index) = self.data { - if let Node::Block(ref blk) = tcx.hir().get(hir_id) { - // Want span for scope starting after the - // indexed statement and ending at end of - // `blk`; reuse span of `blk` and shift `lo` - // forward to end of indexed statement. - // - // (This is the special case aluded to in the - // doc-comment for this method) - - let stmt_span = blk.stmts[first_statement_index.index()].span; - - // To avoid issues with macro-generated spans, the span - // of the statement must be nested in that of the block. - if span.lo() <= stmt_span.lo() && stmt_span.lo() <= span.hi() { - return Span::new(stmt_span.lo(), span.hi(), span.ctxt()); - } - } - } - span - } -} - -pub type ScopeDepth = u32; - -/// The region scope tree encodes information about region relationships. -#[derive(Default, Debug)] -pub struct ScopeTree { - /// If not empty, this body is the root of this region hierarchy. - pub root_body: Option, - - /// The parent of the root body owner, if the latter is an - /// an associated const or method, as impls/traits can also - /// have lifetime parameters free in this body. - pub root_parent: Option, - - /// Maps from a scope ID to the enclosing scope id; - /// this is usually corresponding to the lexical nesting, though - /// in the case of closures the parent scope is the innermost - /// conditional expression or repeating block. (Note that the - /// enclosing scope ID for the block associated with a closure is - /// the closure itself.) - pub parent_map: FxHashMap, - - /// Maps from a variable or binding ID to the block in which that - /// variable is declared. - var_map: FxHashMap, - - /// Maps from a `NodeId` to the associated destruction scope (if any). - destruction_scopes: FxHashMap, - - /// `rvalue_scopes` includes entries for those expressions whose - /// cleanup scope is larger than the default. The map goes from the - /// expression ID to the cleanup scope id. For rvalues not present in - /// this table, the appropriate cleanup scope is the innermost - /// enclosing statement, conditional expression, or repeating - /// block (see `terminating_scopes`). - /// In constants, None is used to indicate that certain expressions - /// escape into 'static and should have no local cleanup scope. - rvalue_scopes: FxHashMap>, - - /// Encodes the hierarchy of fn bodies. Every fn body (including - /// closures) forms its own distinct region hierarchy, rooted in - /// the block that is the fn body. This map points from the ID of - /// that root block to the ID of the root block for the enclosing - /// fn, if any. Thus the map structures the fn bodies into a - /// hierarchy based on their lexical mapping. This is used to - /// handle the relationships between regions in a fn and in a - /// closure defined by that fn. See the "Modeling closures" - /// section of the README in infer::region_constraints for - /// more details. - closure_tree: FxHashMap, - - /// If there are any `yield` nested within a scope, this map - /// stores the `Span` of the last one and its index in the - /// postorder of the Visitor traversal on the HIR. - /// - /// HIR Visitor postorder indexes might seem like a peculiar - /// thing to care about. but it turns out that HIR bindings - /// and the temporary results of HIR expressions are never - /// storage-live at the end of HIR nodes with postorder indexes - /// lower than theirs, and therefore don't need to be suspended - /// at yield-points at these indexes. - /// - /// For an example, suppose we have some code such as: - /// ```rust,ignore (example) - /// foo(f(), yield y, bar(g())) - /// ``` - /// - /// With the HIR tree (calls numbered for expository purposes) - /// ``` - /// Call#0(foo, [Call#1(f), Yield(y), Call#2(bar, Call#3(g))]) - /// ``` - /// - /// Obviously, the result of `f()` was created before the yield - /// (and therefore needs to be kept valid over the yield) while - /// the result of `g()` occurs after the yield (and therefore - /// doesn't). If we want to infer that, we can look at the - /// postorder traversal: - /// ```plain,ignore - /// `foo` `f` Call#1 `y` Yield `bar` `g` Call#3 Call#2 Call#0 - /// ``` - /// - /// In which we can easily see that `Call#1` occurs before the yield, - /// and `Call#3` after it. - /// - /// To see that this method works, consider: - /// - /// Let `D` be our binding/temporary and `U` be our other HIR node, with - /// `HIR-postorder(U) < HIR-postorder(D)` (in our example, U would be - /// the yield and D would be one of the calls). Let's show that - /// `D` is storage-dead at `U`. - /// - /// Remember that storage-live/storage-dead refers to the state of - /// the *storage*, and does not consider moves/drop flags. - /// - /// Then: - /// 1. From the ordering guarantee of HIR visitors (see - /// `rustc::hir::intravisit`), `D` does not dominate `U`. - /// 2. Therefore, `D` is *potentially* storage-dead at `U` (because - /// we might visit `U` without ever getting to `D`). - /// 3. However, we guarantee that at each HIR point, each - /// binding/temporary is always either always storage-live - /// or always storage-dead. This is what is being guaranteed - /// by `terminating_scopes` including all blocks where the - /// count of executions is not guaranteed. - /// 4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`, - /// QED. - /// - /// This property ought to not on (3) in an essential way -- it - /// is probably still correct even if we have "unrestricted" terminating - /// scopes. However, why use the complicated proof when a simple one - /// works? - /// - /// A subtle thing: `box` expressions, such as `box (&x, yield 2, &y)`. It - /// might seem that a `box` expression creates a `Box` temporary - /// when it *starts* executing, at `HIR-preorder(BOX-EXPR)`. That might - /// be true in the MIR desugaring, but it is not important in the semantics. - /// - /// The reason is that semantically, until the `box` expression returns, - /// the values are still owned by their containing expressions. So - /// we'll see that `&x`. - pub yield_in_scope: FxHashMap, - - /// The number of visit_expr and visit_pat calls done in the body. - /// Used to sanity check visit_expr/visit_pat call count when - /// calculating generator interiors. - pub body_expr_count: FxHashMap, -} - -#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)] -pub struct YieldData { - /// The `Span` of the yield. - pub span: Span, - /// The number of expressions and patterns appearing before the `yield` in the body plus one. - pub expr_and_pat_count: usize, - pub source: hir::YieldSource, -} - -impl<'tcx> ScopeTree { - pub fn record_scope_parent(&mut self, child: Scope, parent: Option<(Scope, ScopeDepth)>) { - debug!("{:?}.parent = {:?}", child, parent); - - if let Some(p) = parent { - let prev = self.parent_map.insert(child, p); - assert!(prev.is_none()); - } - - // Record the destruction scopes for later so we can query them. - if let ScopeData::Destruction = child.data { - self.destruction_scopes.insert(child.item_local_id(), child); - } - } - - pub fn each_encl_scope(&self, mut e: E) - where - E: FnMut(Scope, Scope), - { - for (&child, &parent) in &self.parent_map { - e(child, parent.0) - } - } - - pub fn each_var_scope(&self, mut e: E) - where - E: FnMut(&hir::ItemLocalId, Scope), - { - for (child, &parent) in self.var_map.iter() { - e(child, parent) - } - } - - pub fn opt_destruction_scope(&self, n: hir::ItemLocalId) -> Option { - self.destruction_scopes.get(&n).cloned() - } - - /// Records that `sub_closure` is defined within `sup_closure`. These IDs - /// should be the ID of the block that is the fn body, which is - /// also the root of the region hierarchy for that fn. - pub fn record_closure_parent( - &mut self, - sub_closure: hir::ItemLocalId, - sup_closure: hir::ItemLocalId, - ) { - debug!( - "record_closure_parent(sub_closure={:?}, sup_closure={:?})", - sub_closure, sup_closure - ); - assert!(sub_closure != sup_closure); - let previous = self.closure_tree.insert(sub_closure, sup_closure); - assert!(previous.is_none()); - } - - pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { - debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); - assert!(var != lifetime.item_local_id()); - self.var_map.insert(var, lifetime); - } - - pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option) { - debug!("record_rvalue_scope(sub={:?}, sup={:?})", var, lifetime); - if let Some(lifetime) = lifetime { - assert!(var != lifetime.item_local_id()); - } - self.rvalue_scopes.insert(var, lifetime); - } - - /// Returns the narrowest scope that encloses `id`, if any. - pub fn opt_encl_scope(&self, id: Scope) -> Option { - self.parent_map.get(&id).cloned().map(|(p, _)| p) - } - - /// Returns the narrowest scope that encloses `id`, if any. - #[allow(dead_code)] // used in cfg - pub fn encl_scope(&self, id: Scope) -> Scope { - self.opt_encl_scope(id).unwrap() - } - - /// Returns the lifetime of the local variable `var_id` - pub fn var_scope(&self, var_id: hir::ItemLocalId) -> Scope { - self.var_map - .get(&var_id) - .cloned() - .unwrap_or_else(|| bug!("no enclosing scope for id {:?}", var_id)) - } - - /// Returns the scope when the temp created by `expr_id` will be cleaned up. - pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> Option { - // Check for a designated rvalue scope. - if let Some(&s) = self.rvalue_scopes.get(&expr_id) { - debug!("temporary_scope({:?}) = {:?} [custom]", expr_id, s); - return s; - } - - // Otherwise, locate the innermost terminating scope - // if there's one. Static items, for instance, won't - // have an enclosing scope, hence no scope will be - // returned. - let mut id = Scope { id: expr_id, data: ScopeData::Node }; - - while let Some(&(p, _)) = self.parent_map.get(&id) { - match p.data { - ScopeData::Destruction => { - debug!("temporary_scope({:?}) = {:?} [enclosing]", expr_id, id); - return Some(id); - } - _ => id = p, - } - } - - debug!("temporary_scope({:?}) = None", expr_id); - return None; - } - - /// Returns the lifetime of the variable `id`. - pub fn var_region(&self, id: hir::ItemLocalId) -> ty::RegionKind { - let scope = ty::ReScope(self.var_scope(id)); - debug!("var_region({:?}) = {:?}", id, scope); - scope - } - - pub fn scopes_intersect(&self, scope1: Scope, scope2: Scope) -> bool { - self.is_subscope_of(scope1, scope2) || self.is_subscope_of(scope2, scope1) - } - - /// Returns `true` if `subscope` is equal to or is lexically nested inside `superscope`, and - /// `false` otherwise. - pub fn is_subscope_of(&self, subscope: Scope, superscope: Scope) -> bool { - let mut s = subscope; - debug!("is_subscope_of({:?}, {:?})", subscope, superscope); - while superscope != s { - match self.opt_encl_scope(s) { - None => { - debug!("is_subscope_of({:?}, {:?}, s={:?})=false", subscope, superscope, s); - return false; - } - Some(scope) => s = scope, - } - } - - debug!("is_subscope_of({:?}, {:?})=true", subscope, superscope); - - return true; - } - - /// Returns the ID of the innermost containing body. - pub fn containing_body(&self, mut scope: Scope) -> Option { - loop { - if let ScopeData::CallSite = scope.data { - return Some(scope.item_local_id()); - } - - scope = self.opt_encl_scope(scope)?; - } - } - - /// Finds the nearest common ancestor of two scopes. That is, finds the - /// smallest scope which is greater than or equal to both `scope_a` and - /// `scope_b`. - pub fn nearest_common_ancestor(&self, scope_a: Scope, scope_b: Scope) -> Scope { - if scope_a == scope_b { - return scope_a; - } - - let mut a = scope_a; - let mut b = scope_b; - - // Get the depth of each scope's parent. If either scope has no parent, - // it must be the root, which means we can stop immediately because the - // root must be the nearest common ancestor. (In practice, this is - // moderately common.) - let (parent_a, parent_a_depth) = match self.parent_map.get(&a) { - Some(pd) => *pd, - None => return a, - }; - let (parent_b, parent_b_depth) = match self.parent_map.get(&b) { - Some(pd) => *pd, - None => return b, - }; - - if parent_a_depth > parent_b_depth { - // `a` is lower than `b`. Move `a` up until it's at the same depth - // as `b`. The first move up is trivial because we already found - // `parent_a` above; the loop does the remaining N-1 moves. - a = parent_a; - for _ in 0..(parent_a_depth - parent_b_depth - 1) { - a = self.parent_map.get(&a).unwrap().0; - } - } else if parent_b_depth > parent_a_depth { - // `b` is lower than `a`. - b = parent_b; - for _ in 0..(parent_b_depth - parent_a_depth - 1) { - b = self.parent_map.get(&b).unwrap().0; - } - } else { - // Both scopes are at the same depth, and we know they're not equal - // because that case was tested for at the top of this function. So - // we can trivially move them both up one level now. - assert!(parent_a_depth != 0); - a = parent_a; - b = parent_b; - } - - // Now both scopes are at the same level. We move upwards in lockstep - // until they match. In practice, this loop is almost always executed - // zero times because `a` is almost always a direct ancestor of `b` or - // vice versa. - while a != b { - a = self.parent_map.get(&a).unwrap().0; - b = self.parent_map.get(&b).unwrap().0; - } - - a - } - - /// Assuming that the provided region was defined within this `ScopeTree`, - /// returns the outermost `Scope` that the region outlives. - 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 scope = tcx - .hir() - .maybe_body_owned_by(param_owner_id) - .map(|body_id| tcx.hir().body(body_id).value.hir_id.local_id) - .unwrap_or_else(|| { - // The lifetime was defined on node that doesn't own a body, - // which in practice can only mean a trait or an impl, that - // is the parent of a method, and that is enforced below. - if Some(param_owner_id) != self.root_parent { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!( - "free_scope: {:?} not recognized by the \ - region scope tree for {:?} / {:?}", - param_owner, - self.root_parent.map(|id| tcx.hir().local_def_id(id)), - self.root_body.map(|hir_id| DefId::local(hir_id.owner)) - ), - ); - } - - // The trait/impl lifetime is in scope for the method's body. - self.root_body.unwrap().local_id - }); - - Scope { id: scope, data: ScopeData::CallSite } - } - - /// Assuming that the provided region was defined within this `ScopeTree`, - /// returns the outermost `Scope` that the region outlives. - pub fn free_scope(&self, tcx: TyCtxt<'tcx>, fr: &ty::FreeRegion) -> Scope { - let param_owner = match fr.bound_region { - ty::BoundRegion::BrNamed(def_id, _) => tcx.parent(def_id).unwrap(), - _ => fr.scope, - }; - - // Ensure that the named late-bound lifetimes were defined - // 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 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 } - } - - /// Checks whether the given scope contains a `yield`. If so, - /// returns `Some((span, expr_count))` with the span of a yield we found and - /// the number of expressions and patterns appearing before the `yield` in the body + 1. - /// If there a are multiple yields in a scope, the one with the highest number is returned. - pub fn yield_in_scope(&self, scope: Scope) -> Option { - self.yield_in_scope.get(&scope).cloned() - } - - /// Gives the number of expressions visited in a body. - /// Used to sanity check visit_expr call count when - /// calculating generator interiors. - pub fn body_expr_count(&self, body_id: hir::BodyId) -> Option { - self.body_expr_count.get(&body_id).copied() - } -} - -impl<'a> HashStable> for ScopeTree { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let ScopeTree { - root_body, - root_parent, - ref body_expr_count, - ref parent_map, - ref var_map, - ref destruction_scopes, - ref rvalue_scopes, - ref closure_tree, - ref yield_in_scope, - } = *self; - - hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { - root_body.hash_stable(hcx, hasher); - root_parent.hash_stable(hcx, hasher); - }); - - body_expr_count.hash_stable(hcx, hasher); - parent_map.hash_stable(hcx, hasher); - var_map.hash_stable(hcx, hasher); - destruction_scopes.hash_stable(hcx, hasher); - rvalue_scopes.hash_stable(hcx, hasher); - closure_tree.hash_stable(hcx, hasher); - yield_in_scope.hash_stable(hcx, hasher); - } -} diff --git a/src/librustc/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 0b33408edf02d..0000000000000 --- a/src/librustc/mir/interpret/error.rs +++ /dev/null @@ -1,640 +0,0 @@ -use super::{CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef}; - -use crate::hir::map::definitions::DefPathData; -use crate::mir; -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::{Pos, Span}; -use rustc_target::spec::abi::Abi; -use std::{any::Any, fmt}; - -#[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>), -} - -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), - } - } -} - -/// Error information for when the program caused Undefined Behavior. -pub enum UndefinedBehaviorInfo { - /// Free-form case. Only for errors that are never caught! - Ub(String), - /// Free-form case for experimental UB. Only for errors that are never caught! - UbExperimental(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), -} - -impl fmt::Debug for UndefinedBehaviorInfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use UndefinedBehaviorInfo::*; - match self { - Ub(msg) | UbExperimental(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), - } - } -} - -/// 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<'tcx> { - /// Free-form case. Only for errors that are never caught! - Unsupported(String), - - /// When const-prop encounters a situation it does not support, it raises this error. - /// This must not allocate for performance reasons (hence `str`, not `String`). - ConstPropUnsupported(&'static str), - - // -- Everything below is not categorized yet -- - FunctionAbiMismatch(Abi, Abi), - FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>), - FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>), - FunctionArgCountMismatch, - UnterminatedCString(Pointer), - DanglingPointerDeref, - DoubleFree, - InvalidMemoryAccess, - InvalidFunctionPointer, - InvalidBool, - PointerOutOfBounds { - ptr: Pointer, - msg: CheckInAllocMsg, - allocation_size: Size, - }, - InvalidNullPointerUsage, - ReadPointerAsBytes, - ReadBytesAsPointer, - ReadForeignStatic, - InvalidPointerMath, - ReadUndefBytes(Size), - DeadLocal, - InvalidBoolOp(mir::BinOp), - UnimplementedTraitSelection, - CalledClosureAsFunction, - NoMirFor(String), - DerefFunctionPointer, - ExecuteMemory, - InvalidChar(u128), - OutOfTls, - TlsOutOfBounds, - AlignmentCheckFailed { - required: Align, - has: Align, - }, - ValidationFailure(String), - VtableForArgumentlessMethod, - ModifiedConstantMemory, - ModifiedStatic, - TypeNotPrimitive(Ty<'tcx>), - ReallocatedWrongMemoryKind(String, String), - DeallocatedWrongMemoryKind(String, String), - ReallocateNonBasePtr, - DeallocateNonBasePtr, - IncorrectAllocationInformation(Size, Size, Align, Align), - HeapAllocZeroBytes, - HeapAllocNonPowerOfTwoAlignment(u64), - ReadFromReturnPointer, - PathNotFound(Vec), - TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), -} - -impl fmt::Debug for UnsupportedOpInfo<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use UnsupportedOpInfo::*; - match self { - PointerOutOfBounds { ptr, msg, allocation_size } => write!( - f, - "{} failed: pointer must be in-bounds at offset {}, \ - but is outside bounds of allocation {} which has size {}", - msg, - ptr.offset.bytes(), - ptr.alloc_id, - allocation_size.bytes() - ), - ValidationFailure(ref err) => write!(f, "type validation failed: {}", err), - NoMirFor(ref func) => write!(f, "no MIR for `{}`", func), - FunctionAbiMismatch(caller_abi, callee_abi) => write!( - f, - "tried to call a function with ABI {:?} using caller ABI {:?}", - callee_abi, caller_abi - ), - FunctionArgMismatch(caller_ty, callee_ty) => write!( - f, - "tried to call a function with argument of type {:?} \ - passing data of type {:?}", - callee_ty, caller_ty - ), - TransmuteSizeDiff(from_ty, to_ty) => write!( - f, - "tried to transmute from {:?} to {:?}, but their sizes differed", - from_ty, to_ty - ), - FunctionRetMismatch(caller_ty, callee_ty) => write!( - f, - "tried to call a function with return type {:?} \ - passing return place of type {:?}", - callee_ty, caller_ty - ), - FunctionArgCountMismatch => { - write!(f, "tried to call a function with incorrect number of arguments") - } - ReallocatedWrongMemoryKind(ref old, ref new) => { - write!(f, "tried to reallocate memory from `{}` to `{}`", old, new) - } - DeallocatedWrongMemoryKind(ref old, ref new) => { - write!(f, "tried to deallocate `{}` memory but gave `{}` as the kind", old, new) - } - InvalidChar(c) => { - write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c) - } - AlignmentCheckFailed { required, has } => write!( - f, - "tried to access memory with alignment {}, but alignment {} is required", - has.bytes(), - required.bytes() - ), - TypeNotPrimitive(ty) => write!(f, "expected primitive type, got {}", ty), - PathNotFound(ref path) => write!(f, "cannot find path {:?}", path), - IncorrectAllocationInformation(size, size2, align, align2) => write!( - f, - "incorrect alloc info: expected size {} and align {}, \ - got size {} and align {}", - size.bytes(), - align.bytes(), - size2.bytes(), - align2.bytes() - ), - InvalidMemoryAccess => write!(f, "tried to access memory through an invalid pointer"), - DanglingPointerDeref => write!(f, "dangling pointer was dereferenced"), - DoubleFree => write!(f, "tried to deallocate dangling pointer"), - InvalidFunctionPointer => { - write!(f, "tried to use a function pointer after offsetting it") - } - InvalidBool => write!(f, "invalid boolean value read"), - InvalidNullPointerUsage => write!(f, "invalid use of NULL pointer"), - ReadPointerAsBytes => write!( - f, - "a raw memory access tried to access part of a pointer value as raw \ - bytes" - ), - ReadBytesAsPointer => { - write!(f, "a memory access tried to interpret some bytes as a pointer") - } - ReadForeignStatic => write!(f, "tried to read from foreign (extern) static"), - InvalidPointerMath => write!( - f, - "attempted to do invalid arithmetic on pointers that would leak base \ - addresses, e.g., comparing pointers into different allocations" - ), - DeadLocal => write!(f, "tried to access a dead local variable"), - DerefFunctionPointer => write!(f, "tried to dereference a function pointer"), - ExecuteMemory => write!(f, "tried to treat a memory pointer as a function pointer"), - OutOfTls => write!(f, "reached the maximum number of representable TLS keys"), - TlsOutOfBounds => write!(f, "accessed an invalid (unallocated) TLS key"), - CalledClosureAsFunction => { - write!(f, "tried to call a closure through a function pointer") - } - VtableForArgumentlessMethod => { - write!(f, "tried to call a vtable function without arguments") - } - ModifiedConstantMemory => write!(f, "tried to modify constant memory"), - ModifiedStatic => write!( - f, - "tried to modify a static's initial value from another static's \ - initializer" - ), - ReallocateNonBasePtr => write!( - f, - "tried to reallocate with a pointer not to the beginning of an \ - existing object" - ), - DeallocateNonBasePtr => write!( - f, - "tried to deallocate with a pointer not to the beginning of an \ - existing object" - ), - HeapAllocZeroBytes => write!(f, "tried to re-, de- or allocate zero bytes on the heap"), - ReadFromReturnPointer => write!(f, "tried to read from the return pointer"), - UnimplementedTraitSelection => { - write!(f, "there were unresolved type arguments during trait selection") - } - InvalidBoolOp(_) => write!(f, "invalid boolean operation"), - UnterminatedCString(_) => write!( - f, - "attempted to get length of a null-terminated string, but no null \ - found before end of allocation" - ), - ReadUndefBytes(_) => write!(f, "attempted to read undefined bytes"), - HeapAllocNonPowerOfTwoAlignment(_) => write!( - f, - "tried to re-, de-, or allocate heap memory with alignment that is \ - not a power of two" - ), - Unsupported(ref msg) => write!(f, "{}", msg), - ConstPropUnsupported(ref msg) => { - write!(f, "Constant propagation encountered an unsupported situation: {}", msg) - } - } - } -} - -/// 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 into an infinite loop. - InfiniteLoop, -} - -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") - } - InfiniteLoop => write!( - f, - "duplicate interpreter state observed here, const evaluation will never \ - terminate" - ), - } - } -} - -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<'tcx>), - /// 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`. - write!(f, "{:?}", self) - } -} - -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(_) => bug!("unhandled MachineStop"), - } - } -} - -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 { - InterpError::MachineStop(_) - | InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::Unsupported(UnsupportedOpInfo::ValidationFailure(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) => true, - _ => false, - } - } -} diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs deleted file mode 100644 index 64c07b431db38..0000000000000 --- a/src/librustc/mir/interpret/mod.rs +++ /dev/null @@ -1,565 +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! throw_unsup { - ($($tt:tt)*) => { return Err(err_unsup!($($tt)*).into()) }; -} - -#[macro_export] -macro_rules! throw_unsup_format { - ($($tt:tt)*) => { throw_unsup!(Unsupported(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! throw_inval { - ($($tt:tt)*) => { return Err(err_inval!($($tt)*).into()) }; -} - -#[macro_export] -macro_rules! throw_ub { - ($($tt:tt)*) => { return Err(err_ub!($($tt)*).into()) }; -} - -#[macro_export] -macro_rules! throw_ub_format { - ($($tt:tt)*) => { throw_ub!(Ub(format!($($tt)*))) }; -} - -#[macro_export] -macro_rules! throw_exhaust { - ($($tt:tt)*) => { return Err(err_exhaust!($($tt)*).into()) }; -} - -#[macro_export] -macro_rules! throw_machine_stop { - ($($tt:tt)*) => { - return Err($crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)).into()) - }; -} - -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, 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 (`AstConv::ast_const_to_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 { - write!(fmt, "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) - } -} - -impl fmt::Display for AllocId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// 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 cc3c50b7899f3..0000000000000 --- a/src/librustc/mir/interpret/pointer.rs +++ /dev/null @@ -1,232 +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, - 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: () } - } - - /// Test if the pointer is "inbounds" of an allocation of the given size. - /// A pointer is "inbounds" even if its offset is equal to the size; this is - /// a "one-past-the-end" pointer. - #[inline(always)] - pub fn check_inbounds_alloc( - self, - allocation_size: Size, - msg: CheckInAllocMsg, - ) -> InterpResult<'tcx, ()> { - if self.offset > allocation_size { - throw_unsup!(PointerOutOfBounds { ptr: self.erase_tag(), msg, allocation_size }) - } else { - Ok(()) - } - } -} diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs deleted file mode 100644 index a26ff2ea7aa2f..0000000000000 --- a/src/librustc/mir/mod.rs +++ /dev/null @@ -1,2995 +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; -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, - HashStable, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord -)] -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, - 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>, u64), - - /// &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, b), - 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 ")?; - // FIXME make the default pretty printing of raw pointers more detailed. Here we output the - // debug representation of raw pointers, so that the raw pointers in the mir dump output are - // detailed and just not '{pointer}'. - if let ty::RawPtr(_) = self.literal.ty.kind { - write!(fmt, "{:?} : {}", self.literal.val, self.literal.ty) - } else { - write!(fmt, "{}", self.literal) - } - } -} - -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) - } - } -} - -/* - * `TypeFoldable` implementations for MIR types -*/ - -CloneTypeFoldableAndLiftImpls! { - BlockTailInfo, - MirPhase, - SourceInfo, - FakeReadCause, - RetagKind, - SourceScope, - SourceScopeData, - SourceScopeLocalData, - UserTypeAnnotationIndex, -} - -impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - use crate::mir::TerminatorKind::*; - - let kind = match self.kind { - Goto { target } => Goto { target }, - SwitchInt { ref discr, switch_ty, ref values, ref targets } => SwitchInt { - discr: discr.fold_with(folder), - switch_ty: switch_ty.fold_with(folder), - values: values.clone(), - targets: targets.clone(), - }, - Drop { ref location, target, unwind } => { - Drop { location: location.fold_with(folder), target, unwind } - } - DropAndReplace { ref location, ref value, target, unwind } => DropAndReplace { - location: location.fold_with(folder), - value: value.fold_with(folder), - target, - unwind, - }, - Yield { ref value, resume, ref resume_arg, drop } => Yield { - value: value.fold_with(folder), - resume, - resume_arg: resume_arg.fold_with(folder), - drop, - }, - Call { ref func, ref args, ref destination, cleanup, from_hir_call } => { - let dest = - destination.as_ref().map(|&(ref loc, dest)| (loc.fold_with(folder), dest)); - - Call { - func: func.fold_with(folder), - args: args.fold_with(folder), - destination: dest, - cleanup, - from_hir_call, - } - } - Assert { ref cond, expected, ref msg, target, cleanup } => { - use AssertKind::*; - let msg = match msg { - BoundsCheck { ref len, ref index } => { - BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder) } - } - Overflow(_) - | OverflowNeg - | DivisionByZero - | RemainderByZero - | ResumedAfterReturn(_) - | ResumedAfterPanic(_) => msg.clone(), - }; - Assert { cond: cond.fold_with(folder), expected, msg, target, cleanup } - } - GeneratorDrop => GeneratorDrop, - Resume => Resume, - Abort => Abort, - Return => Return, - Unreachable => Unreachable, - FalseEdges { real_target, imaginary_target } => { - FalseEdges { real_target, imaginary_target } - } - FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind }, - }; - Terminator { source_info: self.source_info, kind } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - use crate::mir::TerminatorKind::*; - - match self.kind { - SwitchInt { ref discr, switch_ty, .. } => { - discr.visit_with(visitor) || switch_ty.visit_with(visitor) - } - Drop { ref location, .. } => location.visit_with(visitor), - DropAndReplace { ref location, ref value, .. } => { - location.visit_with(visitor) || value.visit_with(visitor) - } - Yield { ref value, .. } => value.visit_with(visitor), - Call { ref func, ref args, ref destination, .. } => { - let dest = if let Some((ref loc, _)) = *destination { - loc.visit_with(visitor) - } else { - false - }; - dest || func.visit_with(visitor) || args.visit_with(visitor) - } - Assert { ref cond, ref msg, .. } => { - if cond.visit_with(visitor) { - use AssertKind::*; - match msg { - BoundsCheck { ref len, ref index } => { - len.visit_with(visitor) || index.visit_with(visitor) - } - Overflow(_) - | OverflowNeg - | DivisionByZero - | RemainderByZero - | ResumedAfterReturn(_) - | ResumedAfterPanic(_) => false, - } - } else { - false - } - } - Goto { .. } - | Resume - | Abort - | Return - | GeneratorDrop - | Unreachable - | FalseEdges { .. } - | FalseUnwind { .. } => false, - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for GeneratorKind { - fn super_fold_with>(&self, _: &mut F) -> Self { - *self - } - - fn super_visit_with>(&self, _: &mut V) -> bool { - false - } -} - -impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - Place { local: self.local.fold_with(folder), projection: self.projection.fold_with(folder) } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.local.visit_with(visitor) || self.projection.visit_with(visitor) - } -} - -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_place_elems(&v) - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) - } -} - -impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - use crate::mir::Rvalue::*; - match *self { - Use(ref op) => Use(op.fold_with(folder)), - Repeat(ref op, len) => Repeat(op.fold_with(folder), len), - Ref(region, bk, ref place) => { - Ref(region.fold_with(folder), bk, place.fold_with(folder)) - } - AddressOf(mutability, ref place) => AddressOf(mutability, place.fold_with(folder)), - Len(ref place) => Len(place.fold_with(folder)), - Cast(kind, ref op, ty) => Cast(kind, op.fold_with(folder), ty.fold_with(folder)), - BinaryOp(op, ref rhs, ref lhs) => { - BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)) - } - CheckedBinaryOp(op, ref rhs, ref lhs) => { - CheckedBinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)) - } - UnaryOp(op, ref val) => UnaryOp(op, val.fold_with(folder)), - Discriminant(ref place) => Discriminant(place.fold_with(folder)), - NullaryOp(op, ty) => NullaryOp(op, ty.fold_with(folder)), - Aggregate(ref kind, ref fields) => { - let kind = box match **kind { - AggregateKind::Array(ty) => AggregateKind::Array(ty.fold_with(folder)), - AggregateKind::Tuple => AggregateKind::Tuple, - AggregateKind::Adt(def, v, substs, user_ty, n) => AggregateKind::Adt( - def, - v, - substs.fold_with(folder), - user_ty.fold_with(folder), - n, - ), - AggregateKind::Closure(id, substs) => { - AggregateKind::Closure(id, substs.fold_with(folder)) - } - AggregateKind::Generator(id, substs, movablity) => { - AggregateKind::Generator(id, substs.fold_with(folder), movablity) - } - }; - Aggregate(kind, fields.fold_with(folder)) - } - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - use crate::mir::Rvalue::*; - match *self { - Use(ref op) => op.visit_with(visitor), - Repeat(ref op, _) => op.visit_with(visitor), - Ref(region, _, ref place) => region.visit_with(visitor) || place.visit_with(visitor), - AddressOf(_, ref place) => place.visit_with(visitor), - Len(ref place) => place.visit_with(visitor), - Cast(_, ref op, ty) => op.visit_with(visitor) || ty.visit_with(visitor), - BinaryOp(_, ref rhs, ref lhs) | CheckedBinaryOp(_, ref rhs, ref lhs) => { - rhs.visit_with(visitor) || lhs.visit_with(visitor) - } - UnaryOp(_, ref val) => val.visit_with(visitor), - Discriminant(ref place) => place.visit_with(visitor), - NullaryOp(_, ty) => ty.visit_with(visitor), - Aggregate(ref kind, ref fields) => { - (match **kind { - AggregateKind::Array(ty) => ty.visit_with(visitor), - AggregateKind::Tuple => false, - AggregateKind::Adt(_, _, substs, user_ty, _) => { - substs.visit_with(visitor) || user_ty.visit_with(visitor) - } - AggregateKind::Closure(_, substs) => substs.visit_with(visitor), - AggregateKind::Generator(_, substs, _) => substs.visit_with(visitor), - }) || fields.visit_with(visitor) - } - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - match *self { - Operand::Copy(ref place) => Operand::Copy(place.fold_with(folder)), - Operand::Move(ref place) => Operand::Move(place.fold_with(folder)), - Operand::Constant(ref c) => Operand::Constant(c.fold_with(folder)), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - Operand::Copy(ref place) | Operand::Move(ref place) => place.visit_with(visitor), - Operand::Constant(ref c) => c.visit_with(visitor), - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - use crate::mir::ProjectionElem::*; - - match *self { - Deref => Deref, - Field(f, ty) => Field(f, ty.fold_with(folder)), - Index(v) => Index(v.fold_with(folder)), - 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 }, - } - } - - fn super_visit_with>(&self, visitor: &mut Vs) -> bool { - use crate::mir::ProjectionElem::*; - - match self { - Field(_, ty) => ty.visit_with(visitor), - Index(v) => v.visit_with(visitor), - _ => false, - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for Field { - fn super_fold_with>(&self, _: &mut F) -> Self { - *self - } - fn super_visit_with>(&self, _: &mut V) -> bool { - false - } -} - -impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal { - fn super_fold_with>(&self, _: &mut F) -> Self { - *self - } - fn super_visit_with>(&self, _: &mut V) -> bool { - false - } -} - -impl<'tcx, R: Idx, C: Idx> TypeFoldable<'tcx> for BitMatrix { - fn super_fold_with>(&self, _: &mut F) -> Self { - self.clone() - } - fn super_visit_with>(&self, _: &mut V) -> bool { - false - } -} - -impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> { - fn super_fold_with>(&self, folder: &mut F) -> Self { - Constant { - span: self.span, - user_ty: self.user_ty.fold_with(folder), - literal: self.literal.fold_with(folder), - } - } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.literal.visit_with(visitor) - } -} diff --git a/src/librustc/mir/query.rs b/src/librustc/mir/query.rs deleted file mode 100644 index 824cdfe55bfb6..0000000000000 --- a/src/librustc/mir/query.rs +++ /dev/null @@ -1,229 +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 that we treat the free -/// regions in the closure's type "as if" they were erased, so their -/// precise identity is not important, only their position. -/// -/// Example: If type check produces a closure with the closure substs: -/// -/// ```text -/// ClosureSubsts = [ -/// i8, // the "closure kind" -/// for<'x> fn(&'a &'x u32) -> &'x u32, // the "closure signature" -/// &'a String, // some upvar -/// ] -/// ``` -/// -/// here, there is one unique free region (`'a`) but it appears -/// twice. We would "renumber" each occurrence to a unique vid, as follows: -/// -/// ```text -/// ClosureSubsts = [ -/// i8, // the "closure kind" -/// for<'x> fn(&'1 &'x u32) -> &'x u32, // the "closure signature" -/// &'2 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 as `ReClosureBound` -/// instances assigned one of these same indices. Those regions will -/// be substituted away by the creator. We use `ReClosureBound` in -/// that case because the regions must be allocated in the global -/// `TyCtxt`, and hence 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 ff3a82e53639e..0000000000000 --- a/src/librustc/query/mod.rs +++ /dev/null @@ -1,1256 +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, DefIndex}; - -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: DefId) -> &'tcx hir::ModuleItems { - eval_always - } - - // An HIR item with a `DefId` that can own other HIR items which do not themselves have - // a `DefId`. - // This can be conveniently accessed by methods on `tcx.hir()`. - // Avoid calling this query directly. - query hir_owner(key: DefId) -> &'tcx HirOwner<'tcx> { - eval_always - } - - // The HIR items which do not themselves have a `DefId` and are owned by another HIR item - // with a `DefId`. - // This can be conveniently accessed by methods on `tcx.hir()`. - // Avoid calling this query directly. - query hir_owner_items(key: DefId) -> &'tcx HirOwnerItems<'tcx> { - eval_always - } - - /// 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(_: DefId) -> DefId { - eval_always - } - } - - 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(_: DefIndex) - -> 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(_: DefIndex) -> - Option<&'tcx FxHashMap> { - desc { "looking up a named region" } - } - query is_late_bound_map(_: DefIndex) -> - Option<&'tcx FxHashSet> { - desc { "testing if a region is late bound" } - } - query object_lifetime_defaults_map(_: DefIndex) - -> 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 c90551826202e..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(def_id, ref substs) => { - substs.as_closure().upvar_tys(def_id, tcx).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/select.rs b/src/librustc/traits/select.rs deleted file mode 100644 index d316d7659e222..0000000000000 --- a/src/librustc/traits/select.rs +++ /dev/null @@ -1,331 +0,0 @@ -//! Candidate selection. See the [rustc dev guide] for more information on how this works. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection - -use self::EvaluationResult::*; - -use super::{SelectionError, SelectionResult}; - -use crate::dep_graph::DepNodeIndex; -use crate::ty::{self, TyCtxt}; - -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lock; -use rustc_hir::def_id::DefId; - -#[derive(Clone, Default)] -pub struct SelectionCache<'tcx> { - pub hashmap: Lock< - FxHashMap< - ty::ParamEnvAnd<'tcx, ty::TraitRef<'tcx>>, - WithDepNode>>, - >, - >, -} - -impl<'tcx> SelectionCache<'tcx> { - /// Actually frees the underlying memory in contrast to what stdlib containers do on `clear` - pub fn clear(&self) { - *self.hashmap.borrow_mut() = Default::default(); - } -} - -/// The selection process begins by considering all impls, where -/// 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 -/// list. But the other cases are considered *candidates*. -/// -/// For selection to succeed, there must be exactly one matching -/// candidate. If the obligation is fully known, this is guaranteed -/// by coherence. However, if the obligation contains type parameters -/// or variables, there may be multiple such impls. -/// -/// It is not a real problem if multiple matching impls exist because -/// of type variables - it just means the obligation isn't sufficiently -/// elaborated. In that case we report an ambiguity, and the caller can -/// try again after more type information has been gathered or report a -/// "type annotations needed" error. -/// -/// However, with type parameters, this can be a real problem - type -/// parameters don't unify with regular types, but they *can* unify -/// with variables from blanket impls, and (unless we know its bounds -/// 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)); } -/// -/// we can't just use the impl to resolve the `` obligation -/// -- a type from another crate (that doesn't implement `fmt::Debug`) could -/// implement `AsDebug`. -/// -/// Because where-clauses match the type exactly, multiple clauses can -/// only match if there are unresolved variables, and we can mostly just -/// report this ambiguity in that case. This is still a problem - we can't -/// *do anything* with ambiguities that involve only regions. This is issue -/// #21974. -/// -/// If a single where-clause matches and there are no inference -/// variables left, then it definitely matches and we can just select -/// it. -/// -/// In fact, we even select the where-clause when the obligation contains -/// 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 } } -/// -/// 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`, -/// so the program prints "false". However, if the where-clause is omitted, -/// the blanket impl is selected, we unify `$0=()`, and the program prints -/// "()". -/// -/// Exactly the same issues apply to projection and object candidates, except -/// that we can have both a projection candidate and a where-clause candidate -/// for the same obligation. In that case either would do (except that -/// different "leaps of logic" would occur if inference variables are -/// present), and we just pick the where-clause. This is, for example, -/// required for associated types to work in default impls, as the bounds -/// are visible both as projection bounds and as where-clauses from the -/// parameter environment. -#[derive(PartialEq, Eq, Debug, Clone, TypeFoldable)] -pub enum SelectionCandidate<'tcx> { - BuiltinCandidate { - /// `false` if there are no *further* obligations. - has_nested: bool, - }, - ParamCandidate(ty::PolyTraitRef<'tcx>), - ImplCandidate(DefId), - AutoImplCandidate(DefId), - - /// This is a trait matching with a projected type as `Self`, and - /// we found an applicable bound in the trait definition. - ProjectionCandidate, - - /// Implementation of a `Fn`-family trait by one of the anonymous types - /// generated for a `||` expression. - ClosureCandidate, - - /// Implementation of a `Generator` trait by one of the anonymous types - /// generated for a generator. - GeneratorCandidate, - - /// Implementation of a `Fn`-family trait by one of the anonymous - /// types generated for a fn pointer type (e.g., `fn(int) -> int`) - FnPointerCandidate, - - TraitAliasCandidate(DefId), - - ObjectCandidate, - - BuiltinObjectCandidate, - - BuiltinUnsizeCandidate, -} - -/// The result of trait evaluation. The order is important -/// here as the evaluation of a list is the maximum of the -/// evaluations. -/// -/// The evaluation results are ordered: -/// - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions` -/// implies `EvaluatedToAmbig` implies `EvaluatedToUnknown` -/// - `EvaluatedToErr` implies `EvaluatedToRecur` -/// - the "union" of evaluation results is equal to their maximum - -/// all the "potential success" candidates can potentially succeed, -/// so they are noops when unioned with a definite error, and within -/// the categories it's easy to see that the unions are correct. -#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, HashStable)] -pub enum EvaluationResult { - /// Evaluation successful. - EvaluatedToOk, - /// Evaluation successful, but there were unevaluated region obligations. - EvaluatedToOkModuloRegions, - /// Evaluation is known to be ambiguous -- it *might* hold for some - /// assignment of inference variables, but it might not. - /// - /// While this has the same meaning as `EvaluatedToUnknown` -- we can't - /// know whether this obligation holds or not -- it is the result we - /// would get with an empty stack, and therefore is cacheable. - EvaluatedToAmbig, - /// Evaluation failed because of recursion involving inference - /// variables. We are somewhat imprecise there, so we don't actually - /// know the real result. - /// - /// This can't be trivially cached for the same reason as `EvaluatedToRecur`. - EvaluatedToUnknown, - /// Evaluation failed because we encountered an obligation we are already - /// trying to prove on this branch. - /// - /// We know this branch can't be a part of a minimal proof-tree for - /// the "root" of our cycle, because then we could cut out the recursion - /// and maintain a valid proof tree. However, this does not mean - /// that all the obligations on this branch do not hold -- it's possible - /// that we entered this branch "speculatively", and that there - /// might be some other way to prove this obligation that does not - /// go through this cycle -- so we can't cache this as a failure. - /// - /// For example, suppose we have this: - /// - /// ```rust,ignore (pseudo-Rust) - /// pub trait Trait { fn xyz(); } - /// // This impl is "useless", but we can still have - /// // an `impl Trait for SomeUnsizedType` somewhere. - /// impl Trait for T { fn xyz() {} } - /// - /// pub fn foo() { - /// ::xyz(); - /// } - /// ``` - /// - /// When checking `foo`, we have to prove `T: Trait`. This basically - /// translates into this: - /// - /// ```plain,ignore - /// (T: Trait + Sized →_\impl T: Trait), T: Trait ⊢ T: Trait - /// ``` - /// - /// When we try to prove it, we first go the first option, which - /// recurses. This shows us that the impl is "useless" -- it won't - /// tell us that `T: Trait` unless it already implemented `Trait` - /// by some other means. However, that does not prevent `T: Trait` - /// does not hold, because of the bound (which can indeed be satisfied - /// by `SomeUnsizedType` from another crate). - // - // FIXME: when an `EvaluatedToRecur` goes past its parent root, we - // ought to convert it to an `EvaluatedToErr`, because we know - // there definitely isn't a proof tree for that obligation. Not - // doing so is still sound -- there isn't any proof tree, so the - // branch still can't be a part of a minimal one -- but does not re-enable caching. - EvaluatedToRecur, - /// Evaluation failed. - EvaluatedToErr, -} - -impl EvaluationResult { - /// Returns `true` if this evaluation result is known to apply, even - /// considering outlives constraints. - pub fn must_apply_considering_regions(self) -> bool { - self == EvaluatedToOk - } - - /// Returns `true` if this evaluation result is known to apply, ignoring - /// outlives constraints. - pub fn must_apply_modulo_regions(self) -> bool { - self <= EvaluatedToOkModuloRegions - } - - pub fn may_apply(self) -> bool { - match self { - EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToUnknown => { - true - } - - EvaluatedToErr | EvaluatedToRecur => false, - } - } - - pub fn is_stack_dependent(self) -> bool { - match self { - EvaluatedToUnknown | EvaluatedToRecur => true, - - EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToErr => false, - } - } -} - -/// Indicates that trait evaluation caused overflow. -#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable)] -pub struct OverflowError; - -impl<'tcx> From for SelectionError<'tcx> { - fn from(OverflowError: OverflowError) -> SelectionError<'tcx> { - SelectionError::Overflow - } -} - -#[derive(Clone, Default)] -pub struct EvaluationCache<'tcx> { - pub hashmap: Lock< - FxHashMap>, WithDepNode>, - >, -} - -impl<'tcx> EvaluationCache<'tcx> { - /// Actually frees the underlying memory in contrast to what stdlib containers do on `clear` - pub fn clear(&self) { - *self.hashmap.borrow_mut() = Default::default(); - } -} - -#[derive(Clone, Eq, PartialEq)] -pub struct WithDepNode { - dep_node: DepNodeIndex, - cached_value: T, -} - -impl WithDepNode { - pub fn new(dep_node: DepNodeIndex, cached_value: T) -> Self { - WithDepNode { dep_node, cached_value } - } - - pub fn get(&self, tcx: TyCtxt<'_>) -> T { - tcx.dep_graph.read_index(self.dep_node); - self.cached_value.clone() - } -} - -#[derive(Clone, Debug)] -pub enum IntercrateAmbiguityCause { - DownstreamCrate { trait_desc: String, self_desc: Option }, - UpstreamCrateUpdate { trait_desc: String, self_desc: Option }, - ReservationImpl { message: String }, -} - -impl IntercrateAmbiguityCause { - /// Emits notes when the overlap is caused by complex intercrate ambiguities. - /// See #23980 for details. - pub fn add_intercrate_ambiguity_hint(&self, err: &mut rustc_errors::DiagnosticBuilder<'_>) { - err.note(&self.intercrate_ambiguity_hint()); - } - - pub fn intercrate_ambiguity_hint(&self) -> String { - match self { - &IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { - format!(" for type `{}`", ty) - } else { - String::new() - }; - format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc) - } - &IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { - format!(" for type `{}`", ty) - } else { - String::new() - }; - format!( - "upstream crates may add a new impl of trait `{}`{} \ - in future versions", - trait_desc, self_desc - ) - } - &IntercrateAmbiguityCause::ReservationImpl { ref message } => message.clone(), - } - } -} diff --git a/src/librustc/traits/specialization_graph.rs b/src/librustc/traits/specialization_graph.rs deleted file mode 100644 index d481e578fc1dd..0000000000000 --- a/src/librustc/traits/specialization_graph.rs +++ /dev/null @@ -1,202 +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_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, -} - -impl Graph { - pub fn new() -> Graph { - Graph { parent: Default::default(), children: Default::default() } - } - - /// 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. -pub fn ancestors( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - start_from_impl: DefId, -) -> Ancestors<'tcx> { - let specialization_graph = tcx.specialization_graph_of(trait_def_id); - 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/codec.rs b/src/librustc/ty/codec.rs deleted file mode 100644 index c305999a64ba7..0000000000000 --- a/src/librustc/ty/codec.rs +++ /dev/null @@ -1,494 +0,0 @@ -// This module contains some shared code for encoding and decoding various -// things from the `ty` module, and in particular implements support for -// "shorthands" which allow to have pointers back into the already encoded -// stream instead of re-encoding the same thing twice. -// -// The functionality in here is shared between persisting to crate metadata and -// persisting to incr. comp. caches. - -use crate::arena::ArenaAllocatable; -use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; -use crate::mir::{self, interpret::Allocation}; -use crate::ty::subst::SubstsRef; -use crate::ty::{self, List, Ty, TyCtxt}; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; -use rustc_span::Span; -use std::hash::Hash; -use std::intrinsics; - -/// The shorthand encoding uses an enum's variant index `usize` -/// and is offset by this value so it never matches a real variant. -/// This offset is also chosen so that the first byte is never < 0x80. -pub const SHORTHAND_OFFSET: usize = 0x80; - -pub trait EncodableWithShorthand: Clone + Eq + Hash { - type Variant: Encodable; - fn variant(&self) -> &Self::Variant; -} - -#[allow(rustc::usage_of_ty_tykind)] -impl<'tcx> EncodableWithShorthand for Ty<'tcx> { - type Variant = ty::TyKind<'tcx>; - fn variant(&self) -> &Self::Variant { - &self.kind - } -} - -impl<'tcx> EncodableWithShorthand for ty::Predicate<'tcx> { - type Variant = ty::Predicate<'tcx>; - fn variant(&self) -> &Self::Variant { - self - } -} - -pub trait TyEncoder: Encoder { - fn position(&self) -> usize; -} - -impl TyEncoder for opaque::Encoder { - #[inline] - fn position(&self) -> usize { - self.position() - } -} - -/// Encode the given value or a previously cached shorthand. -pub fn encode_with_shorthand(encoder: &mut E, value: &T, cache: M) -> Result<(), E::Error> -where - E: TyEncoder, - M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, - T: EncodableWithShorthand, -{ - let existing_shorthand = cache(encoder).get(value).cloned(); - if let Some(shorthand) = existing_shorthand { - return encoder.emit_usize(shorthand); - } - - let variant = value.variant(); - - let start = encoder.position(); - variant.encode(encoder)?; - let len = encoder.position() - start; - - // The shorthand encoding uses the same usize as the - // discriminant, with an offset so they can't conflict. - let discriminant = intrinsics::discriminant_value(variant); - assert!(discriminant < SHORTHAND_OFFSET as u64); - let shorthand = start + SHORTHAND_OFFSET; - - // Get the number of bits that leb128 could fit - // in the same space as the fully encoded type. - let leb128_bits = len * 7; - - // Check that the shorthand is a not longer than the - // full encoding itself, i.e., it's an obvious win. - if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { - cache(encoder).insert(value.clone(), shorthand); - } - - Ok(()) -} - -pub fn encode_spanned_predicates<'tcx, E, C>( - encoder: &mut E, - predicates: &'tcx [(ty::Predicate<'tcx>, Span)], - cache: C, -) -> Result<(), E::Error> -where - E: TyEncoder, - C: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, usize>, -{ - predicates.len().encode(encoder)?; - for (predicate, span) in predicates { - encode_with_shorthand(encoder, predicate, &cache)?; - span.encode(encoder)?; - } - Ok(()) -} - -pub trait TyDecoder<'tcx>: Decoder { - fn tcx(&self) -> TyCtxt<'tcx>; - - fn peek_byte(&self) -> u8; - - fn position(&self) -> usize; - - fn cached_ty_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> - where - F: FnOnce(&mut Self) -> Result, Self::Error>; - - fn with_position(&mut self, pos: usize, f: F) -> R - where - F: FnOnce(&mut Self) -> R; - - fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum; - - fn positioned_at_shorthand(&self) -> bool { - (self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0 - } -} - -#[inline] -pub fn decode_arena_allocable( - decoder: &mut D, -) -> Result<&'tcx T, D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().arena.alloc(Decodable::decode(decoder)?)) -} - -#[inline] -pub fn decode_arena_allocable_slice( - decoder: &mut D, -) -> Result<&'tcx [T], D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().arena.alloc_from_iter( as Decodable>::decode(decoder)?)) -} - -#[inline] -pub fn decode_cnum(decoder: &mut D) -> Result -where - D: TyDecoder<'tcx>, -{ - let cnum = CrateNum::from_u32(u32::decode(decoder)?); - Ok(decoder.map_encoded_cnum_to_current(cnum)) -} - -#[allow(rustc::usage_of_ty_tykind)] -#[inline] -pub fn decode_ty(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - // Handle shorthands first, if we have an usize > 0x80. - if decoder.positioned_at_shorthand() { - let pos = decoder.read_usize()?; - assert!(pos >= SHORTHAND_OFFSET); - let shorthand = pos - SHORTHAND_OFFSET; - - decoder.cached_ty_for_shorthand(shorthand, |decoder| { - decoder.with_position(shorthand, Ty::decode) - }) - } else { - let tcx = decoder.tcx(); - Ok(tcx.mk_ty(ty::TyKind::decode(decoder)?)) - } -} - -#[inline] -pub fn decode_spanned_predicates( - decoder: &mut D, -) -> Result<&'tcx [(ty::Predicate<'tcx>, Span)], D::Error> -where - D: TyDecoder<'tcx>, -{ - let tcx = decoder.tcx(); - Ok(tcx.arena.alloc_from_iter( - (0..decoder.read_usize()?) - .map(|_| { - // Handle shorthands first, if we have an usize > 0x80. - let predicate = if decoder.positioned_at_shorthand() { - let pos = decoder.read_usize()?; - assert!(pos >= SHORTHAND_OFFSET); - let shorthand = pos - SHORTHAND_OFFSET; - - decoder.with_position(shorthand, ty::Predicate::decode) - } else { - ty::Predicate::decode(decoder) - }?; - Ok((predicate, Decodable::decode(decoder)?)) - }) - .collect::, _>>()?, - )) -} - -#[inline] -pub fn decode_substs(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - let len = decoder.read_usize()?; - let tcx = decoder.tcx(); - Ok(tcx.mk_substs((0..len).map(|_| Decodable::decode(decoder)))?) -} - -#[inline] -pub fn decode_place(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - let local: mir::Local = Decodable::decode(decoder)?; - let len = decoder.read_usize()?; - let projection: &'tcx List> = - decoder.tcx().mk_place_elems((0..len).map(|_| Decodable::decode(decoder)))?; - Ok(mir::Place { local, projection }) -} - -#[inline] -pub fn decode_region(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().mk_region(Decodable::decode(decoder)?)) -} - -#[inline] -pub fn decode_ty_slice(decoder: &mut D) -> Result<&'tcx ty::List>, D::Error> -where - D: TyDecoder<'tcx>, -{ - let len = decoder.read_usize()?; - Ok(decoder.tcx().mk_type_list((0..len).map(|_| Decodable::decode(decoder)))?) -} - -#[inline] -pub fn decode_adt_def(decoder: &mut D) -> Result<&'tcx ty::AdtDef, D::Error> -where - D: TyDecoder<'tcx>, -{ - let def_id = DefId::decode(decoder)?; - Ok(decoder.tcx().adt_def(def_id)) -} - -#[inline] -pub fn decode_existential_predicate_slice( - decoder: &mut D, -) -> Result<&'tcx ty::List>, D::Error> -where - D: TyDecoder<'tcx>, -{ - let len = decoder.read_usize()?; - Ok(decoder.tcx().mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) -} - -#[inline] -pub fn decode_canonical_var_infos(decoder: &mut D) -> Result, D::Error> -where - D: TyDecoder<'tcx>, -{ - let len = decoder.read_usize()?; - let interned: Result, _> = - (0..len).map(|_| Decodable::decode(decoder)).collect(); - Ok(decoder.tcx().intern_canonical_var_infos(interned?.as_slice())) -} - -#[inline] -pub fn decode_const(decoder: &mut D) -> Result<&'tcx ty::Const<'tcx>, D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?)) -} - -#[inline] -pub fn decode_allocation(decoder: &mut D) -> Result<&'tcx Allocation, D::Error> -where - D: TyDecoder<'tcx>, -{ - Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?)) -} - -#[macro_export] -macro_rules! __impl_decoder_methods { - ($($name:ident -> $ty:ty;)*) => { - $( - #[inline] - fn $name(&mut self) -> Result<$ty, Self::Error> { - self.opaque.$name() - } - )* - } -} - -#[macro_export] -macro_rules! impl_arena_allocatable_decoder { - ([]$args:tt) => {}; - ([decode $(, $attrs:ident)*] - [[$DecoderName:ident [$($typaram:tt),*]], [$name:ident: $ty:ty], $tcx:lifetime]) => { - impl<$($typaram),*> SpecializedDecoder<&$tcx $ty> for $DecoderName<$($typaram),*> { - #[inline] - fn specialized_decode(&mut self) -> Result<&$tcx $ty, Self::Error> { - decode_arena_allocable(self) - } - } - - impl<$($typaram),*> SpecializedDecoder<&$tcx [$ty]> for $DecoderName<$($typaram),*> { - #[inline] - fn specialized_decode(&mut self) -> Result<&$tcx [$ty], Self::Error> { - decode_arena_allocable_slice(self) - } - } - }; - ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { - impl_arena_allocatable_decoder!([$($attrs),*]$args); - }; -} - -#[macro_export] -macro_rules! impl_arena_allocatable_decoders { - ($args:tt, [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => { - $( - impl_arena_allocatable_decoder!($a [$args, [$name: $ty], $tcx]); - )* - } -} - -#[macro_export] -macro_rules! implement_ty_decoder { - ($DecoderName:ident <$($typaram:tt),*>) => { - mod __ty_decoder_impl { - use std::borrow::Cow; - - use rustc_serialize::{Decoder, SpecializedDecoder}; - - use $crate::infer::canonical::CanonicalVarInfos; - use $crate::ty; - use $crate::ty::codec::*; - use $crate::ty::subst::SubstsRef; - use rustc_hir::def_id::{CrateNum}; - - use rustc_span::Span; - - use super::$DecoderName; - - impl<$($typaram ),*> Decoder for $DecoderName<$($typaram),*> { - type Error = String; - - __impl_decoder_methods! { - read_nil -> (); - - read_u128 -> u128; - read_u64 -> u64; - read_u32 -> u32; - read_u16 -> u16; - read_u8 -> u8; - read_usize -> usize; - - read_i128 -> i128; - read_i64 -> i64; - read_i32 -> i32; - read_i16 -> i16; - read_i8 -> i8; - read_isize -> isize; - - read_bool -> bool; - read_f64 -> f64; - read_f32 -> f32; - read_char -> char; - read_str -> Cow<'_, str>; - } - - fn error(&mut self, err: &str) -> Self::Error { - self.opaque.error(err) - } - } - - // FIXME(#36588): These impls are horribly unsound as they allow - // the caller to pick any lifetime for `'tcx`, including `'static`, - // by using the unspecialized proxies to them. - - arena_types!(impl_arena_allocatable_decoders, [$DecoderName [$($typaram),*]], 'tcx); - - impl<$($typaram),*> SpecializedDecoder - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result { - decode_cnum(self) - } - } - - impl<$($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - decode_ty(self) - } - } - - impl<$($typaram),*> SpecializedDecoder<&'tcx [(ty::Predicate<'tcx>, Span)]> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) - -> Result<&'tcx [(ty::Predicate<'tcx>, Span)], Self::Error> { - decode_spanned_predicates(self) - } - } - - impl<$($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - decode_substs(self) - } - } - - impl<$($typaram),*> SpecializedDecoder<$crate::mir::Place<'tcx>> - for $DecoderName<$($typaram),*> { - fn specialized_decode( - &mut self - ) -> Result<$crate::mir::Place<'tcx>, Self::Error> { - decode_place(self) - } - } - - impl<$($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result, Self::Error> { - decode_region(self) - } - } - - impl<$($typaram),*> SpecializedDecoder<&'tcx ty::List>> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) - -> Result<&'tcx ty::List>, Self::Error> { - decode_ty_slice(self) - } - } - - impl<$($typaram),*> SpecializedDecoder<&'tcx ty::AdtDef> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result<&'tcx ty::AdtDef, Self::Error> { - decode_adt_def(self) - } - } - - impl<$($typaram),*> SpecializedDecoder<&'tcx ty::List>> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) - -> Result<&'tcx ty::List>, Self::Error> { - decode_existential_predicate_slice(self) - } - } - - impl<$($typaram),*> SpecializedDecoder> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) - -> Result, Self::Error> { - decode_canonical_var_infos(self) - } - } - - impl<$($typaram),*> SpecializedDecoder<&'tcx $crate::ty::Const<'tcx>> - for $DecoderName<$($typaram),*> { - fn specialized_decode(&mut self) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { - decode_const(self) - } - } - - impl<$($typaram),*> SpecializedDecoder<&'tcx $crate::mir::interpret::Allocation> - for $DecoderName<$($typaram),*> { - fn specialized_decode( - &mut self - ) -> Result<&'tcx $crate::mir::interpret::Allocation, Self::Error> { - decode_allocation(self) - } - } - } - } -} 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/erase_regions.rs b/src/librustc/ty/erase_regions.rs deleted file mode 100644 index 4bf08096edeb8..0000000000000 --- a/src/librustc/ty/erase_regions.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::ty::fold::{TypeFoldable, TypeFolder}; -use crate::ty::{self, Ty, TyCtxt, TypeFlags}; - -pub(super) fn provide(providers: &mut ty::query::Providers<'_>) { - *providers = ty::query::Providers { erase_regions_ty, ..*providers }; -} - -fn erase_regions_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - // N.B., use `super_fold_with` here. If we used `fold_with`, it - // could invoke the `erase_regions_ty` query recursively. - ty.super_fold_with(&mut RegionEraserVisitor { tcx }) -} - -impl<'tcx> TyCtxt<'tcx> { - /// Returns an equivalent value with all free regions removed (note - /// that late-bound regions remain, because they are important for - /// subtyping, but they are anonymized and normalized as well).. - pub fn erase_regions(self, value: &T) -> T - where - T: TypeFoldable<'tcx>, - { - // If there's nothing to erase avoid performing the query at all - if !value.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND | TypeFlags::HAS_FREE_REGIONS) { - return value.clone(); - } - - let value1 = value.fold_with(&mut RegionEraserVisitor { tcx: self }); - debug!("erase_regions({:?}) = {:?}", value, value1); - value1 - } -} - -struct RegionEraserVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl TypeFolder<'tcx> for RegionEraserVisitor<'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.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) } - } - - fn fold_binder(&mut self, t: &ty::Binder) -> ty::Binder - where - T: TypeFoldable<'tcx>, - { - let u = self.tcx.anonymize_late_bound_regions(t); - u.super_fold_with(self) - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - // because late-bound regions affect subtyping, we can't - // erase the bound/free distinction, but we can replace - // all free regions with 'erased. - // - // Note that we *CAN* replace early-bound regions -- the - // type system never "sees" those, they get substituted - // away. In codegen, they will always be erased to 'erased - // whenever a substitution occurs. - match *r { - ty::ReLateBound(..) => r, - _ => self.tcx.lifetimes.re_erased, - } - } -} 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/flags.rs b/src/librustc/ty/flags.rs deleted file mode 100644 index 5243e1fbf579b..0000000000000 --- a/src/librustc/ty/flags.rs +++ /dev/null @@ -1,255 +0,0 @@ -use crate::ty::subst::{GenericArgKind, SubstsRef}; -use crate::ty::{self, InferConst, Ty, TypeFlags}; - -#[derive(Debug)] -pub struct FlagComputation { - pub flags: TypeFlags, - - // see `TyS::outer_exclusive_binder` for details - pub outer_exclusive_binder: ty::DebruijnIndex, -} - -impl FlagComputation { - fn new() -> FlagComputation { - FlagComputation { flags: TypeFlags::empty(), outer_exclusive_binder: ty::INNERMOST } - } - - #[allow(rustc::usage_of_ty_tykind)] - pub fn for_kind(kind: &ty::TyKind<'_>) -> FlagComputation { - let mut result = FlagComputation::new(); - result.add_kind(kind); - result - } - - pub fn for_const(c: &ty::Const<'_>) -> TypeFlags { - let mut result = FlagComputation::new(); - result.add_const(c); - result.flags - } - - fn add_flags(&mut self, flags: TypeFlags) { - self.flags = self.flags | (flags & TypeFlags::NOMINAL_FLAGS); - } - - /// indicates that `self` refers to something at binding level `binder` - fn add_binder(&mut self, binder: ty::DebruijnIndex) { - let exclusive_binder = binder.shifted_in(1); - self.add_exclusive_binder(exclusive_binder); - } - - /// indicates that `self` refers to something *inside* binding - /// level `binder` -- not bound by `binder`, but bound by the next - /// binder internal to it - fn add_exclusive_binder(&mut self, exclusive_binder: ty::DebruijnIndex) { - self.outer_exclusive_binder = self.outer_exclusive_binder.max(exclusive_binder); - } - - /// Adds the flags/depth from a set of types that appear within the current type, but within a - /// region binder. - fn add_bound_computation(&mut self, computation: &FlagComputation) { - self.add_flags(computation.flags); - - // The types that contributed to `computation` occurred within - // a region binder, so subtract one from the region depth - // within when adding the depth to `self`. - let outer_exclusive_binder = computation.outer_exclusive_binder; - if outer_exclusive_binder > ty::INNERMOST { - self.add_exclusive_binder(outer_exclusive_binder.shifted_out(1)); - } // otherwise, this binder captures nothing - } - - #[allow(rustc::usage_of_ty_tykind)] - fn add_kind(&mut self, kind: &ty::TyKind<'_>) { - match kind { - &ty::Bool - | &ty::Char - | &ty::Int(_) - | &ty::Float(_) - | &ty::Uint(_) - | &ty::Never - | &ty::Str - | &ty::Foreign(..) => {} - - // 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::Param(_) => { - self.add_flags(TypeFlags::HAS_TY_PARAM); - } - - &ty::Generator(_, ref substs, _) => { - self.add_substs(substs); - } - - &ty::GeneratorWitness(ref ts) => { - let mut computation = FlagComputation::new(); - computation.add_tys(&ts.skip_binder()[..]); - self.add_bound_computation(&computation); - } - - &ty::Closure(_, ref substs) => { - self.add_substs(substs); - } - - &ty::Bound(debruijn, _) => { - self.add_binder(debruijn); - } - - &ty::Placeholder(..) => { - self.add_flags(TypeFlags::HAS_TY_PLACEHOLDER); - } - - &ty::Infer(infer) => { - self.add_flags(TypeFlags::HAS_TY_INFER); - match infer { - ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {} - - ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_) => { - self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX) - } - } - } - - &ty::Adt(_, substs) => { - self.add_substs(substs); - } - - &ty::Projection(ref data) => { - self.add_flags(TypeFlags::HAS_TY_PROJECTION); - 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); - } - - &ty::Dynamic(ref obj, r) => { - let mut computation = FlagComputation::new(); - for predicate in obj.skip_binder().iter() { - match *predicate { - ty::ExistentialPredicate::Trait(tr) => computation.add_substs(tr.substs), - ty::ExistentialPredicate::Projection(p) => { - let mut proj_computation = FlagComputation::new(); - proj_computation.add_existential_projection(&p); - self.add_bound_computation(&proj_computation); - } - ty::ExistentialPredicate::AutoTrait(_) => {} - } - } - self.add_bound_computation(&computation); - self.add_region(r); - } - - &ty::Array(tt, len) => { - self.add_ty(tt); - self.add_const(len); - } - - &ty::Slice(tt) => self.add_ty(tt), - - &ty::RawPtr(ref m) => { - self.add_ty(m.ty); - } - - &ty::Ref(r, ty, _) => { - self.add_region(r); - self.add_ty(ty); - } - - &ty::Tuple(ref substs) => { - self.add_substs(substs); - } - - &ty::FnDef(_, substs) => { - self.add_substs(substs); - } - - &ty::FnPtr(f) => { - self.add_fn_sig(f); - } - } - } - - fn add_ty(&mut self, ty: Ty<'_>) { - self.add_flags(ty.flags); - self.add_exclusive_binder(ty.outer_exclusive_binder); - } - - fn add_tys(&mut self, tys: &[Ty<'_>]) { - for &ty in tys { - self.add_ty(ty); - } - } - - fn add_fn_sig(&mut self, fn_sig: ty::PolyFnSig<'_>) { - let mut computation = FlagComputation::new(); - - computation.add_tys(fn_sig.skip_binder().inputs()); - computation.add_ty(fn_sig.skip_binder().output()); - - self.add_bound_computation(&computation); - } - - fn add_region(&mut self, r: ty::Region<'_>) { - self.add_flags(r.type_flags()); - if let ty::ReLateBound(debruijn, _) = *r { - self.add_binder(debruijn); - } - } - - fn add_const(&mut self, c: &ty::Const<'_>) { - self.add_ty(c.ty); - match c.val { - ty::ConstKind::Unevaluated(_, substs, _) => { - self.add_substs(substs); - self.add_flags(TypeFlags::HAS_CT_PROJECTION); - } - ty::ConstKind::Infer(infer) => { - self.add_flags(TypeFlags::HAS_CT_INFER); - match infer { - InferConst::Fresh(_) => {} - InferConst::Var(_) => self.add_flags(TypeFlags::KEEP_IN_LOCAL_TCX), - } - } - ty::ConstKind::Bound(debruijn, _) => self.add_binder(debruijn), - ty::ConstKind::Param(_) => { - self.add_flags(TypeFlags::HAS_CT_PARAM); - } - ty::ConstKind::Placeholder(_) => { - self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER); - } - ty::ConstKind::Value(_) => {} - } - } - - fn add_existential_projection(&mut self, projection: &ty::ExistentialProjection<'_>) { - self.add_substs(projection.substs); - self.add_ty(projection.ty); - } - - fn add_projection_ty(&mut self, projection_ty: &ty::ProjectionTy<'_>) { - self.add_substs(projection_ty.substs); - } - - fn add_substs(&mut self, substs: SubstsRef<'_>) { - for kind in substs { - match kind.unpack() { - GenericArgKind::Type(ty) => self.add_ty(ty), - GenericArgKind::Lifetime(lt) => self.add_region(lt), - GenericArgKind::Const(ct) => self.add_const(ct), - } - } - } -} 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 144e3bc9c8bc6..0000000000000 --- a/src/librustc/ty/inhabitedness/mod.rs +++ /dev/null @@ -1,206 +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>) -> 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).contains(self, module) - } - - pub fn is_ty_uninhabited_from_any_module(self, ty: Ty<'tcx>) -> bool { - !ty.uninhabited_from(self).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>) -> 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())), - ) - } - } -} - -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, - ) -> 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)), - ) - } - } -} - -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, - ) -> DefIdForest { - let data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(tcx); - // 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>) -> DefIdForest { - match self.kind { - Adt(def, substs) => def.uninhabited_from(tcx, substs), - - Never => DefIdForest::full(tcx), - - Tuple(ref tys) => { - DefIdForest::union(tcx, tys.iter().map(|ty| ty.expect_ty().uninhabited_from(tcx))) - } - - Array(ty, len) => match len.try_eval_usize(tcx, ty::ParamEnv::empty()) { - // 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), - _ => 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/ty/instance.rs deleted file mode 100644 index 445df76cd32be..0000000000000 --- a/src/librustc/ty/instance.rs +++ /dev/null @@ -1,426 +0,0 @@ -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_hir::def::Namespace; -use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_macros::HashStable; - -use std::fmt; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] -#[derive(HashStable, Lift)] -pub struct Instance<'tcx> { - pub def: InstanceDef<'tcx>, - pub substs: SubstsRef<'tcx>, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub enum InstanceDef<'tcx> { - Item(DefId), - Intrinsic(DefId), - - /// `::method` where `method` receives unsizeable `self: Self`. - VtableShim(DefId), - - /// `fn()` pointer where the function itself cannot be turned into a pointer. - /// - /// One example is `::fn`, where the shim contains - /// a virtual call, which codegen supports only via a direct call to the - /// `::fn` instance (an `InstanceDef::Virtual`). - /// - /// Another example is functions annotated with `#[track_caller]`, which - /// must have their implicit caller location argument populated for a call. - /// Because this is a required part of the function's ABI but can't be tracked - /// as a property of the function pointer, we use a single "caller location" - /// (the definition of the function itself). - ReifyShim(DefId), - - /// `::call_*` - /// `DefId` is `FnTrait::call_*`. - FnPtrShim(DefId, Ty<'tcx>), - - /// `::fn`, "direct calls" of which are implicitly - /// codegen'd as virtual calls. - /// - /// NB: if this is reified to a `fn` pointer, a `ReifyShim` is used - /// (see `ReifyShim` above for more details on that). - Virtual(DefId, usize), - - /// `<[mut closure] as FnOnce>::call_once` - ClosureOnceShim { - call_once: DefId, - }, - - /// `core::ptr::drop_in_place::`. - /// The `DefId` is for `core::ptr::drop_in_place`. - /// The `Option>` is either `Some(T)`, or `None` for empty drop - /// glue. - DropGlue(DefId, Option>), - - ///`::clone` shim. - CloneShim(DefId, Ty<'tcx>), -} - -impl<'tcx> Instance<'tcx> { - /// Returns the `Ty` corresponding to this `Instance`, - /// with generic substitutions applied and lifetimes erased. - /// - /// This method can only be called when the 'substs' for this Instance - /// are fully monomorphic (no `ty::Param`'s are present). - /// This is usually the case (e.g. during codegen). - /// However, during constant evaluation, we may want - /// to try to resolve a `Instance` using generic parameters - /// (e.g. when we are attempting to to do const-propagation). - /// In this case, `Instance.ty_env` should be used to provide - /// the `ParamEnv` for our generic context. - pub fn monomorphic_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - let ty = tcx.type_of(self.def.def_id()); - // 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() { - 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) - } - - /// Like `Instance.ty`, but allows a `ParamEnv` to be specified for use during - /// normalization. This method is only really useful during constant evaluation, - /// where we are dealing with potentially generic types. - pub fn ty_env(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { - let ty = tcx.type_of(self.def.def_id()); - tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty) - } - - /// Finds a crate that contains a monomorphization of this instance that - /// can be linked to from the local crate. A return value of `None` means - /// no upstream crate provides such an exported monomorphization. - /// - /// This method already takes into account the global `-Zshare-generics` - /// setting, always returning `None` if `share-generics` is off. - pub fn upstream_monomorphization(&self, tcx: TyCtxt<'tcx>) -> Option { - // If we are not in share generics mode, we don't link to upstream - // monomorphizations but always instantiate our own internal versions - // instead. - if !tcx.sess.opts.share_generics() { - return None; - } - - // If this is an item that is defined in the local crate, no upstream - // crate can know about it/provide a monomorphization. - if self.def_id().is_local() { - return None; - } - - // If this a non-generic instance, it cannot be a shared monomorphization. - self.substs.non_erasable_generics().next()?; - - match self.def { - InstanceDef::Item(def_id) => tcx - .upstream_monomorphizations_for(def_id) - .and_then(|monos| monos.get(&self.substs).cloned()), - InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.substs), - _ => None, - } - } -} - -impl<'tcx> InstanceDef<'tcx> { - #[inline] - pub fn def_id(&self) -> DefId { - match *self { - InstanceDef::Item(def_id) - | InstanceDef::VtableShim(def_id) - | InstanceDef::ReifyShim(def_id) - | InstanceDef::FnPtrShim(def_id, _) - | InstanceDef::Virtual(def_id, _) - | InstanceDef::Intrinsic(def_id) - | InstanceDef::ClosureOnceShim { call_once: def_id } - | InstanceDef::DropGlue(def_id, _) - | InstanceDef::CloneShim(def_id, _) => def_id, - } - } - - #[inline] - pub fn attrs(&self, tcx: TyCtxt<'tcx>) -> ty::Attributes<'tcx> { - tcx.get_attrs(self.def_id()) - } - - /// Returns `true` if the LLVM version of this instance is unconditionally - /// marked with `inline`. This implies that a copy of this instance is - /// generated in every codegen unit. - /// Note that this is only a hint. See the documentation for - /// `generates_cgu_internal_copy` for more information. - pub fn requires_inline(&self, tcx: TyCtxt<'tcx>) -> bool { - use crate::hir::map::DefPathData; - let def_id = match *self { - ty::InstanceDef::Item(def_id) => def_id, - ty::InstanceDef::DropGlue(_, Some(_)) => return false, - _ => return true, - }; - match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::Ctor | DefPathData::ClosureExpr => true, - _ => false, - } - } - - /// Returns `true` if the machine code for this instance is instantiated in - /// each codegen unit that references it. - /// Note that this is only a hint! The compiler can globally decide to *not* - /// do this in order to speed up compilation. CGU-internal copies are - /// only exist to enable inlining. If inlining is not performed (e.g. at - /// `-Copt-level=0`) then the time for generating them is wasted and it's - /// better to create a single copy with external linkage. - pub fn generates_cgu_internal_copy(&self, tcx: TyCtxt<'tcx>) -> bool { - if self.requires_inline(tcx) { - return true; - } - if let ty::InstanceDef::DropGlue(.., Some(ty)) = *self { - // Drop glue generally wants to be instantiated at every codegen - // unit, but without an #[inline] hint. We should make this - // available to normal end-users. - if tcx.sess.opts.incremental.is_none() { - return true; - } - // When compiling with incremental, we can generate a *lot* of - // codegen units. Including drop glue into all of them has a - // considerable compile time cost. - // - // We include enums without destructors to allow, say, optimizing - // drops of `Option::None` before LTO. We also respect the intent of - // `#[inline]` on `Drop::drop` implementations. - return ty.ty_adt_def().map_or(true, |adt_def| { - adt_def.destructor(tcx).map_or(adt_def.is_enum(), |dtor| { - tcx.codegen_fn_attrs(dtor.did).requests_inline() - }) - }); - } - tcx.codegen_fn_attrs(self.def_id()).requests_inline() - } - - pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { - match *self { - InstanceDef::Item(def_id) => { - tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::TRACK_CALLER) - } - _ => false, - } - } -} - -impl<'tcx> fmt::Display for Instance<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - let substs = tcx.lift(&self.substs).expect("could not lift for printing"); - FmtPrinter::new(tcx, &mut *f, Namespace::ValueNS) - .print_def_path(self.def_id(), substs)?; - Ok(()) - })?; - - match self.def { - InstanceDef::Item(_) => Ok(()), - InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), - InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), - InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), - InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), - InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty), - InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), - InstanceDef::DropGlue(_, ty) => write!(f, " - shim({:?})", ty), - InstanceDef::CloneShim(_, ty) => write!(f, " - shim({:?})", ty), - } - } -} - -impl<'tcx> Instance<'tcx> { - pub fn new(def_id: DefId, substs: SubstsRef<'tcx>) -> Instance<'tcx> { - assert!( - !substs.has_escaping_bound_vars(), - "substs of instance {:?} not normalized for codegen: {:?}", - def_id, - substs - ); - Instance { def: InstanceDef::Item(def_id), substs } - } - - pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> { - Instance::new(def_id, tcx.empty_substs_for_def_id(def_id)) - } - - #[inline] - pub fn def_id(&self) -> DefId { - self.def.def_id() - } - - /// Resolves a `(def_id, substs)` pair to an (optional) instance -- most commonly, - /// this is used to find the precise code that will run for a trait method invocation, - /// if known. - /// - /// Returns `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 - /// 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`. - 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) - } - - pub fn resolve_for_fn_ptr( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> Option> { - debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); - Instance::resolve(tcx, param_env, def_id, substs).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]"); - resolved.def = InstanceDef::ReifyShim(def_id); - } - InstanceDef::Virtual(def_id, _) => { - debug!(" => fn pointer created for virtual call"); - resolved.def = InstanceDef::ReifyShim(def_id); - } - _ => {} - } - - resolved - }) - } - - pub fn resolve_for_vtable( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> Option> { - debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); - let fn_sig = tcx.fn_sig(def_id); - let is_vtable_shim = !fn_sig.inputs().skip_binder().is_empty() - && fn_sig.input(0).skip_binder().is_param(0) - && tcx.generics_of(def_id).has_self; - if is_vtable_shim { - debug!(" => associated item with unsizeable self: Self"); - Some(Instance { def: InstanceDef::VtableShim(def_id), substs }) - } else { - Instance::resolve(tcx, param_env, def_id, substs) - } - } - - pub fn resolve_closure( - tcx: TyCtxt<'tcx>, - def_id: DefId, - substs: ty::SubstsRef<'tcx>, - requested_kind: ty::ClosureKind, - ) -> Instance<'tcx> { - let actual_kind = substs.as_closure().kind(def_id, tcx); - - match needs_fn_once_adapter_shim(actual_kind, requested_kind) { - Ok(true) => Instance::fn_once_adapter_instance(tcx, def_id, substs), - _ => Instance::new(def_id, substs), - } - } - - pub fn resolve_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> { - let def_id = tcx.require_lang_item(DropInPlaceFnLangItem, None); - let substs = tcx.intern_substs(&[ty.into()]); - Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap() - } - - pub fn fn_once_adapter_instance( - tcx: TyCtxt<'tcx>, - closure_did: DefId, - substs: ty::SubstsRef<'tcx>, - ) -> Instance<'tcx> { - debug!("fn_once_adapter_shim({:?}, {:?})", closure_did, substs); - let fn_once = tcx.lang_items().fn_once_trait().unwrap(); - let call_once = tcx - .associated_items(fn_once) - .in_definition_order() - .find(|it| it.kind == ty::AssocKind::Method) - .unwrap() - .def_id; - let def = ty::InstanceDef::ClosureOnceShim { call_once }; - - let self_ty = tcx.mk_closure(closure_did, substs); - - let sig = substs.as_closure().sig(closure_did, tcx); - let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - assert_eq!(sig.inputs().len(), 1); - let substs = tcx.mk_substs_trait(self_ty, &[sig.inputs()[0].into()]); - - debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); - Instance { def, substs } - } - - pub fn is_vtable_shim(&self) -> bool { - if let InstanceDef::VtableShim(..) = self.def { true } else { false } - } -} - -fn needs_fn_once_adapter_shim( - actual_closure_kind: ty::ClosureKind, - trait_closure_kind: ty::ClosureKind, -) -> Result { - match (actual_closure_kind, trait_closure_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) - | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) - | (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { - // No adapter needed. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { - // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at codegen time, these are - // basically the same thing, so we can just return llfn. - Ok(false) - } - (ty::ClosureKind::Fn, ty::ClosureKind::FnOnce) - | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut - // self, ...)`. We want a `fn(self, ...)`. We can produce - // this by doing something like: - // - // fn call_once(self, ...) { call_mut(&self, ...) } - // fn call_once(mut self, ...) { call_mut(&mut self, ...) } - // - // These are both the same at codegen time. - Ok(true) - } - (ty::ClosureKind::FnMut, _) | (ty::ClosureKind::FnOnce, _) => Err(()), - } -} - -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/ty/layout.rs b/src/librustc/ty/layout.rs deleted file mode 100644 index dedb3035cedb3..0000000000000 --- a/src/librustc/ty/layout.rs +++ /dev/null @@ -1,2719 +0,0 @@ -use crate::session::{self, DataTypeKind}; -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_span::DUMMY_SP; - -use std::cmp; -use std::fmt; -use std::iter; -use std::mem; -use std::ops::Bound; - -use crate::ich::StableHashingContext; -use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; -use crate::ty::subst::Subst; -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_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}; - -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 { - memory_index = invert_mapping(&inverse_memory_index); - } else { - memory_index = 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(def_id, ref substs) => { - let tys = substs.as_closure().upvar_tys(def_id, tcx); - 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 mut abi = 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 }, - }; - - if st.iter().all(|v| v.abi.is_uninhabited()) { - abi = Abi::Uninhabited; - } - - 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(def_id, tcx).count(); - // FIXME(eddyb) set the correct vaidity range for the discriminant. - let discr_layout = self.layout_of(substs.as_generator().discr_ty(tcx))?; - let discr = match &discr_layout.abi { - Abi::Scalar(s) => s.clone(), - _ => bug!(), - }; - 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(def_id, tcx) - .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; - } - session::FieldInfo { - name: name.to_string(), - offset: offset.bytes(), - size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), - } - } - }) - .collect(); - - session::VariantInfo { - name: n.map(|n| n.to_string()), - kind: if layout.is_unsized() { - session::SizeKind::Min - } else { - session::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 index == variant_index => 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(def_id, ref substs) => { - substs.as_closure().upvar_tys(def_id, tcx).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(def_id, tcx).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(def_id, tcx); - - 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(def_id, substs, _) => { - let sig = substs.as_generator().poly_sig(def_id, tcx); - - 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 d3c4ddf1ed30a..0000000000000 --- a/src/librustc/ty/mod.rs +++ /dev/null @@ -1,3198 +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::session::DataTypeKind; -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, LOCAL_CRATE}; -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_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)), - } - } - - /// 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 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_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; - - /// Does this have [Projection] or [UnnormalizedProjection]? - const HAS_TY_PROJECTION = 1 << 9; - /// Does this have [Opaque]? - const HAS_TY_OPAQUE = 1 << 10; - /// Does this have [ConstKind::Unevaluated]? - const HAS_CT_PROJECTION = 1 << 11; - - /// 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 << 12; - - /// Is an error type reachable? - const HAS_TY_ERR = 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; - - /// 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_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_const(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, - 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).expect("no def for `DefId`") { - 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 { - let item = if def_id.krate != LOCAL_CRATE { - if let Some(DefKind::AssocFn) = self.def_kind(def_id) { - Some(self.associated_item(def_id)) - } else { - None - } - } else { - self.opt_associated_item(def_id) - }; - - item.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 - // `modern()` 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.krate { - LOCAL_CRATE => self.hir().definitions().expansion_that_defined(scope.index), - _ => ExpnId::root(), - } - } - - pub fn adjust_ident(self, mut ident: Ident, scope: DefId) -> Ident { - ident.span.modernize_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.modernize_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), - }; - (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); - *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 b397a2c80d59b..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(def_id, ref substs) => { - for upvar_ty in substs.as_closure().upvar_tys(def_id, tcx) { - compute_components(tcx, upvar_ty, out); - } - } - - ty::Generator(def_id, ref substs, _) => { - // Same as the closure case - for upvar_ty in substs.as_generator().upvar_tys(def_id, tcx) { - 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/print/pretty.rs b/src/librustc/ty/print/pretty.rs deleted file mode 100644 index 05dcc9e85ac52..0000000000000 --- a/src/librustc/ty/print/pretty.rs +++ /dev/null @@ -1,1853 +0,0 @@ -use crate::hir::map::{DefPathData, DisambiguatedDefPathData}; -use crate::middle::cstore::{ExternCrate, ExternCrateSource}; -use crate::middle::region; -use crate::mir::interpret::{sign_extend, truncate, ConstValue, Scalar}; -use crate::ty::layout::{Integer, IntegerExt, Size}; -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_target::spec::abi::Abi; - -use std::cell::Cell; -use std::collections::BTreeMap; -use std::fmt::{self, Write as _}; -use std::ops::{Deref, DerefMut}; - -// `pretty` is a separate module only for organization. -use super::*; - -macro_rules! p { - (@write($($data:expr),+)) => { - write!(scoped_cx!(), $($data),+)? - }; - (@print($x:expr)) => { - scoped_cx!() = $x.print(scoped_cx!())? - }; - (@$method:ident($($arg:expr),*)) => { - scoped_cx!() = scoped_cx!().$method($($arg),*)? - }; - ($($kind:ident $data:tt),+) => {{ - $(p!(@$kind $data);)+ - }}; -} -macro_rules! define_scoped_cx { - ($cx:ident) => { - #[allow(unused_macros)] - macro_rules! scoped_cx { - () => { - $cx - }; - } - }; -} - -thread_local! { - static FORCE_IMPL_FILENAME_LINE: Cell = Cell::new(false); - static SHOULD_PREFIX_WITH_CRATE: Cell = Cell::new(false); - static NO_QUERIES: Cell = Cell::new(false); -} - -/// Avoids running any queries during any prints that occur -/// during the closure. This may alter the appearance of some -/// types (e.g. forcing verbose printing for opaque types). -/// This method is used during some queries (e.g. `predicates_of` -/// for opaque types), to ensure that any debug printing that -/// occurs during the query computation does not end up recursively -/// calling the same query. -pub fn with_no_queries R, R>(f: F) -> R { - NO_QUERIES.with(|no_queries| { - let old = no_queries.replace(true); - let result = f(); - no_queries.set(old); - result - }) -} - -/// Force us to name impls with just the filename/line number. We -/// normally try to use types. But at some points, notably while printing -/// cycle errors, this can result in extra or suboptimal error output, -/// so this variable disables that check. -pub fn with_forced_impl_filename_line R, R>(f: F) -> R { - FORCE_IMPL_FILENAME_LINE.with(|force| { - let old = force.replace(true); - let result = f(); - force.set(old); - result - }) -} - -/// Adds the `crate::` prefix to paths where appropriate. -pub fn with_crate_prefix R, R>(f: F) -> R { - SHOULD_PREFIX_WITH_CRATE.with(|flag| { - let old = flag.replace(true); - let result = f(); - flag.set(old); - result - }) -} - -/// The "region highlights" are used to control region printing during -/// specific error messages. When a "region highlight" is enabled, it -/// gives an alternate way to print specific regions. For now, we -/// always print those regions using a number, so something like "`'0`". -/// -/// Regions not selected by the region highlight mode are presently -/// unaffected. -#[derive(Copy, Clone, Default)] -pub struct RegionHighlightMode { - /// If enabled, when we see the selected region, use "`'N`" - /// instead of the ordinary behavior. - highlight_regions: [Option<(ty::RegionKind, usize)>; 3], - - /// If enabled, when printing a "free region" that originated from - /// the given `ty::BoundRegion`, print it as "`'1`". Free regions that would ordinarily - /// have names print as normal. - /// - /// This is used when you have a signature like `fn foo(x: &u32, - /// y: &'a u32)` and we want to give a name to the region of the - /// reference `x`. - highlight_bound_region: Option<(ty::BoundRegion, usize)>, -} - -impl RegionHighlightMode { - /// If `region` and `number` are both `Some`, invokes - /// `highlighting_region`. - pub fn maybe_highlighting_region( - &mut self, - region: Option>, - number: Option, - ) { - if let Some(k) = region { - if let Some(n) = number { - self.highlighting_region(k, n); - } - } - } - - /// Highlights the region inference variable `vid` as `'N`. - pub fn highlighting_region(&mut self, region: ty::Region<'_>, number: usize) { - let num_slots = self.highlight_regions.len(); - let first_avail_slot = - self.highlight_regions.iter_mut().find(|s| s.is_none()).unwrap_or_else(|| { - bug!("can only highlight {} placeholders at a time", num_slots,) - }); - *first_avail_slot = Some((*region, number)); - } - - /// Convenience wrapper for `highlighting_region`. - pub fn highlighting_region_vid(&mut self, vid: ty::RegionVid, number: usize) { - self.highlighting_region(&ty::ReVar(vid), number) - } - - /// Returns `Some(n)` with the number to use for the given region, if any. - fn region_highlighted(&self, region: ty::Region<'_>) -> Option { - self.highlight_regions - .iter() - .filter_map(|h| match h { - Some((r, n)) if r == region => Some(*n), - _ => None, - }) - .next() - } - - /// Highlight the given bound region. - /// We can only highlight one bound region at a time. See - /// the field `highlight_bound_region` for more detailed notes. - pub fn highlighting_bound_region(&mut self, br: ty::BoundRegion, number: usize) { - assert!(self.highlight_bound_region.is_none()); - self.highlight_bound_region = Some((br, number)); - } -} - -/// Trait for printers that pretty-print using `fmt::Write` to the printer. -pub trait PrettyPrinter<'tcx>: - Printer< - 'tcx, - Error = fmt::Error, - Path = Self, - Region = Self, - Type = Self, - DynExistential = Self, - Const = Self, - > + fmt::Write -{ - /// Like `print_def_path` but for value paths. - fn print_value_path( - self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - self.print_def_path(def_id, substs) - } - - fn in_binder(self, value: &ty::Binder) -> Result - where - T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, - { - value.skip_binder().print(self) - } - - /// Prints comma-separated elements. - fn comma_sep(mut self, mut elems: impl Iterator) -> Result - where - T: Print<'tcx, Self, Output = Self, Error = Self::Error>, - { - if let Some(first) = elems.next() { - self = first.print(self)?; - for elem in elems { - self.write_str(", ")?; - self = elem.print(self)?; - } - } - Ok(self) - } - - /// Prints `<...>` around what `f` prints. - fn generic_delimiters( - self, - f: impl FnOnce(Self) -> Result, - ) -> Result; - - /// Returns `true` if the region should be printed in - /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`. - /// This is typically the case for all non-`'_` regions. - fn region_should_not_be_omitted(&self, region: ty::Region<'_>) -> bool; - - // Defaults (should not be overridden): - - /// If possible, this returns a global path resolving to `def_id` that is visible - /// from at least one local module, and returns `true`. If the crate defining `def_id` is - /// declared with an `extern crate`, the path is guaranteed to use the `extern crate`. - fn try_print_visible_def_path(self, def_id: DefId) -> Result<(Self, bool), Self::Error> { - let mut callers = Vec::new(); - self.try_print_visible_def_path_recur(def_id, &mut callers) - } - - /// Does the work of `try_print_visible_def_path`, building the - /// full definition path recursively before attempting to - /// post-process it into the valid and visible version that - /// accounts for re-exports. - /// - /// This method should only be called by itself or - /// `try_print_visible_def_path`. - /// - /// `callers` is a chain of visible_parent's leading to `def_id`, - /// to support cycle detection during recursion. - fn try_print_visible_def_path_recur( - mut self, - def_id: DefId, - callers: &mut Vec, - ) -> Result<(Self, bool), Self::Error> { - define_scoped_cx!(self); - - debug!("try_print_visible_def_path: def_id={:?}", def_id); - - // If `def_id` is a direct or injected extern crate, return the - // path to the crate followed by the path to the item within the crate. - if def_id.index == CRATE_DEF_INDEX { - let cnum = def_id.krate; - - if cnum == LOCAL_CRATE { - return Ok((self.path_crate(cnum)?, true)); - } - - // In local mode, when we encounter a crate other than - // LOCAL_CRATE, execution proceeds in one of two ways: - // - // 1. For a direct dependency, where user added an - // `extern crate` manually, we put the `extern - // crate` as the parent. So you wind up with - // something relative to the current crate. - // 2. For an extern inferred from a path or an indirect crate, - // where there is no explicit `extern crate`, we just prepend - // the crate name. - match self.tcx().extern_crate(def_id) { - Some(&ExternCrate { - src: ExternCrateSource::Extern(def_id), - dependency_of: LOCAL_CRATE, - span, - .. - }) => { - debug!("try_print_visible_def_path: def_id={:?}", def_id); - return Ok(( - if !span.is_dummy() { - self.print_def_path(def_id, &[])? - } else { - self.path_crate(cnum)? - }, - true, - )); - } - None => { - return Ok((self.path_crate(cnum)?, true)); - } - _ => {} - } - } - - if def_id.is_local() { - return Ok((self, false)); - } - - let visible_parent_map = self.tcx().visible_parent_map(LOCAL_CRATE); - - let mut cur_def_key = self.tcx().def_key(def_id); - debug!("try_print_visible_def_path: cur_def_key={:?}", cur_def_key); - - // For a constructor, we want the name of its parent rather than . - 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"), - }; - - cur_def_key = self.tcx().def_key(parent); - } - _ => {} - } - - let visible_parent = match visible_parent_map.get(&def_id).cloned() { - Some(parent) => parent, - None => return Ok((self, false)), - }; - if callers.contains(&visible_parent) { - return Ok((self, false)); - } - callers.push(visible_parent); - // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid - // knowing ahead of time whether the entire path will succeed or not. - // To support printers that do not implement `PrettyPrinter`, a `Vec` or - // linked list on the stack would need to be built, before any printing. - match self.try_print_visible_def_path_recur(visible_parent, callers)? { - (cx, false) => return Ok((cx, false)), - (cx, true) => self = cx, - } - callers.pop(); - let actual_parent = self.tcx().parent(def_id); - debug!( - "try_print_visible_def_path: visible_parent={:?} actual_parent={:?}", - visible_parent, actual_parent, - ); - - let mut data = cur_def_key.disambiguated_data.data; - debug!( - "try_print_visible_def_path: data={:?} visible_parent={:?} actual_parent={:?}", - data, visible_parent, actual_parent, - ); - - match data { - // In order to output a path that could actually be imported (valid and visible), - // we need to handle re-exports correctly. - // - // For example, take `std::os::unix::process::CommandExt`, this trait is actually - // defined at `std::sys::unix::ext::process::CommandExt` (at time of writing). - // - // `std::os::unix` rexports the contents of `std::sys::unix::ext`. `std::sys` is - // private so the "true" path to `CommandExt` isn't accessible. - // - // In this case, the `visible_parent_map` will look something like this: - // - // (child) -> (parent) - // `std::sys::unix::ext::process::CommandExt` -> `std::sys::unix::ext::process` - // `std::sys::unix::ext::process` -> `std::sys::unix::ext` - // `std::sys::unix::ext` -> `std::os` - // - // This is correct, as the visible parent of `std::sys::unix::ext` is in fact - // `std::os`. - // - // When printing the path to `CommandExt` and looking at the `cur_def_key` that - // corresponds to `std::sys::unix::ext`, we would normally print `ext` and then go - // to the parent - resulting in a mangled path like - // `std::os::ext::process::CommandExt`. - // - // Instead, we must detect that there was a re-export and instead print `unix` - // (which is the name `std::sys::unix::ext` was re-exported as in `std::os`). To - // do this, we compare the parent of `std::sys::unix::ext` (`std::sys::unix`) with - // the visible parent (`std::os`). If these do not match, then we iterate over - // the children of the visible parent (as was done when computing - // `visible_parent_map`), looking for the specific child we currently have and then - // have access to the re-exported name. - DefPathData::TypeNs(ref mut name) if Some(visible_parent) != actual_parent => { - let reexport = self - .tcx() - .item_children(visible_parent) - .iter() - .find(|child| child.res.def_id() == def_id) - .map(|child| child.ident.name); - if let Some(reexport) = reexport { - *name = reexport; - } - } - // Re-exported `extern crate` (#43189). - DefPathData::CrateRoot => { - data = DefPathData::TypeNs(self.tcx().original_crate_name(def_id.krate)); - } - _ => {} - } - debug!("try_print_visible_def_path: data={:?}", data); - - Ok((self.path_append(Ok, &DisambiguatedDefPathData { data, disambiguator: 0 })?, true)) - } - - fn pretty_path_qualified( - self, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - if trait_ref.is_none() { - // Inherent impls. Try to print `Foo::bar` for an inherent - // impl on `Foo`, but fallback to `::bar` if self-type is - // anything other than a simple path. - match self_ty.kind { - ty::Adt(..) - | ty::Foreign(_) - | ty::Bool - | ty::Char - | ty::Str - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) => { - return self_ty.print(self); - } - - _ => {} - } - } - - self.generic_delimiters(|mut cx| { - define_scoped_cx!(cx); - - p!(print(self_ty)); - if let Some(trait_ref) = trait_ref { - p!(write(" as "), print(trait_ref.print_only_trait_path())); - } - Ok(cx) - }) - } - - fn pretty_path_append_impl( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - self = print_prefix(self)?; - - self.generic_delimiters(|mut cx| { - define_scoped_cx!(cx); - - p!(write("impl ")); - if let Some(trait_ref) = trait_ref { - p!(print(trait_ref.print_only_trait_path()), write(" for ")); - } - p!(print(self_ty)); - - Ok(cx) - }) - } - - fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result { - define_scoped_cx!(self); - - match ty.kind { - ty::Bool => p!(write("bool")), - ty::Char => p!(write("char")), - ty::Int(t) => p!(write("{}", t.name_str())), - ty::Uint(t) => p!(write("{}", t.name_str())), - ty::Float(t) => p!(write("{}", t.name_str())), - ty::RawPtr(ref tm) => { - p!(write( - "*{} ", - match tm.mutbl { - hir::Mutability::Mut => "mut", - hir::Mutability::Not => "const", - } - )); - p!(print(tm.ty)) - } - ty::Ref(r, ty, mutbl) => { - p!(write("&")); - if self.region_should_not_be_omitted(r) { - p!(print(r), write(" ")); - } - p!(print(ty::TypeAndMut { ty, mutbl })) - } - ty::Never => p!(write("!")), - ty::Tuple(ref tys) => { - p!(write("(")); - 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(")")) - } - ty::FnDef(def_id, substs) => { - let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs); - p!(print(sig), write(" {{"), print_value_path(def_id, substs), write("}}")); - } - ty::FnPtr(ref bare_fn) => p!(print(bare_fn)), - ty::Infer(infer_ty) => { - if let ty::TyVar(ty_vid) = infer_ty { - if let Some(name) = self.infer_ty_name(ty_vid) { - p!(write("{}", name)) - } else { - p!(write("{}", infer_ty)) - } - } else { - p!(write("{}", infer_ty)) - } - } - ty::Error => p!(write("[type error]")), - ty::Param(ref param_ty) => p!(write("{}", param_ty)), - ty::Bound(debruijn, bound_ty) => match bound_ty.kind { - ty::BoundTyKind::Anon => { - if debruijn == ty::INNERMOST { - p!(write("^{}", bound_ty.var.index())) - } else { - p!(write("^{}_{}", debruijn.index(), bound_ty.var.index())) - } - } - - ty::BoundTyKind::Param(p) => p!(write("{}", p)), - }, - ty::Adt(def, substs) => { - p!(print_def_path(def.did, substs)); - } - ty::Dynamic(data, r) => { - let print_r = self.region_should_not_be_omitted(r); - if print_r { - p!(write("(")); - } - p!(write("dyn "), print(data)); - if print_r { - p!(write(" + "), print(r), write(")")); - } - } - ty::Foreign(def_id) => { - p!(print_def_path(def_id, &[])); - } - ty::Projection(ref data) => p!(print(data)), - ty::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`), - // and should have no effect on any compiler output. - if self.tcx().sess.verbose() || NO_QUERIES.with(|q| q.get()) { - p!(write("Opaque({:?}, {:?})", def_id, substs)); - return Ok(self); - } - - return Ok(with_no_queries(|| { - let def_key = self.tcx().def_key(def_id); - if let Some(name) = def_key.disambiguated_data.data.get_opt_name() { - p!(write("{}", name)); - 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(">")); - } - return Ok(self); - } - // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, - // by looking up the projections associated with the def_id. - let bounds = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs); - - let mut first = true; - let mut is_sized = false; - p!(write("impl")); - for predicate in bounds.predicates { - if let Some(trait_ref) = predicate.to_opt_poly_trait_ref() { - // Don't print +Sized, but rather +?Sized if absent. - if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() { - is_sized = true; - continue; - } - - p!( - write("{}", if first { " " } else { "+" }), - print(trait_ref.print_only_trait_path()) - ); - first = false; - } - } - if !is_sized { - p!(write("{}?Sized", if first { " " } else { "+" })); - } else if first { - p!(write(" Sized")); - } - Ok(self) - })?); - } - ty::Str => p!(write("str")), - ty::Generator(did, substs, movability) => { - let upvar_tys = substs.as_generator().upvar_tys(did, self.tcx()); - let witness = substs.as_generator().witness(did, self.tcx()); - match movability { - hir::Movability::Movable => p!(write("[generator")), - hir::Movability::Static => p!(write("[static generator")), - } - - // FIXME(eddyb) should use `def_span`. - if let Some(hir_id) = self.tcx().hir().as_local_hir_id(did) { - p!(write("@{:?}", self.tcx().hir().span(hir_id))); - let mut sep = " "; - for (&var_id, upvar_ty) in - self.tcx().upvars(did).as_ref().iter().flat_map(|v| v.keys()).zip(upvar_tys) - { - p!(write("{}{}:", sep, self.tcx().hir().name(var_id)), print(upvar_ty)); - sep = ", "; - } - } else { - // Cross-crate closure types should only be - // visible in codegen bug reports, I imagine. - p!(write("@{:?}", did)); - let mut sep = " "; - for (index, upvar_ty) in upvar_tys.enumerate() { - p!(write("{}{}:", sep, index), print(upvar_ty)); - sep = ", "; - } - } - - p!(write(" "), print(witness), write("]")) - } - ty::GeneratorWitness(types) => { - p!(in_binder(&types)); - } - ty::Closure(did, substs) => { - let upvar_tys = substs.as_closure().upvar_tys(did, self.tcx()); - p!(write("[closure")); - - // FIXME(eddyb) should use `def_span`. - if let Some(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)); - } else { - p!(write("@{:?}", self.tcx().hir().span(hir_id))); - } - let mut sep = " "; - for (&var_id, upvar_ty) in - self.tcx().upvars(did).as_ref().iter().flat_map(|v| v.keys()).zip(upvar_tys) - { - p!(write("{}{}:", sep, self.tcx().hir().name(var_id)), print(upvar_ty)); - sep = ", "; - } - } else { - // Cross-crate closure types should only be - // visible in codegen bug reports, I imagine. - p!(write("@{:?}", did)); - let mut sep = " "; - for (index, upvar_ty) in upvar_tys.enumerate() { - p!(write("{}{}:", sep, index), print(upvar_ty)); - sep = ", "; - } - } - - if self.tcx().sess.verbose() { - p!(write( - " closure_kind_ty={:?} closure_sig_ty={:?}", - substs.as_closure().kind_ty(did, self.tcx()), - substs.as_closure().sig_ty(did, self.tcx()) - )); - } - - p!(write("]")) - } - ty::Array(ty, sz) => { - p!(write("["), print(ty), write("; ")); - if self.tcx().sess.verbose() { - p!(write("{:?}", sz)); - } else if let ty::ConstKind::Unevaluated(..) = sz.val { - // do not try to evaluate unevaluated constants. If we are const evaluating an - // array length anon const, rustc will (with debug assertions) print the - // constant's path. Which will end up here again. - p!(write("_")); - } else if let Some(n) = sz.try_eval_usize(self.tcx(), ty::ParamEnv::empty()) { - p!(write("{}", n)); - } else { - p!(write("_")); - } - p!(write("]")) - } - ty::Slice(ty) => p!(write("["), print(ty), write("]")), - } - - Ok(self) - } - - fn infer_ty_name(&self, _: ty::TyVid) -> Option { - None - } - - fn pretty_print_dyn_existential( - mut self, - predicates: &'tcx ty::List>, - ) -> Result { - define_scoped_cx!(self); - - // Generate the main trait ref, including associated types. - let mut first = true; - - if let Some(principal) = predicates.principal() { - p!(print_def_path(principal.def_id, &[])); - - let mut resugared = false; - - // Special-case `Fn(...) -> ...` and resugar it. - let fn_trait_kind = self.tcx().fn_trait_kind_from_lang_item(principal.def_id); - if !self.tcx().sess.verbose() && fn_trait_kind.is_some() { - if let ty::Tuple(ref args) = principal.substs.type_at(0).kind { - let mut projections = predicates.projection_bounds(); - if let (Some(proj), None) = (projections.next(), projections.next()) { - let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect(); - p!(pretty_fn_sig(&tys, false, proj.ty)); - resugared = true; - } - } - } - - // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, - // in order to place the projections inside the `<...>`. - if !resugared { - // Use a type that can't appear in defaults of type parameters. - let dummy_self = self.tcx().mk_ty_infer(ty::FreshTy(0)); - let principal = principal.with_self_ty(self.tcx(), dummy_self); - - let args = self.generic_args_to_print( - self.tcx().generics_of(principal.def_id), - principal.substs, - ); - - // Don't print `'_` if there's no unerased regions. - let print_regions = args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => *r != ty::ReErased, - _ => false, - }); - let mut args = args.iter().cloned().filter(|arg| match arg.unpack() { - GenericArgKind::Lifetime(_) => print_regions, - _ => true, - }); - let mut projections = predicates.projection_bounds(); - - let arg0 = args.next(); - let projection0 = projections.next(); - if arg0.is_some() || projection0.is_some() { - let args = arg0.into_iter().chain(args); - let projections = projection0.into_iter().chain(projections); - - p!(generic_delimiters(|mut cx| { - cx = cx.comma_sep(args)?; - if arg0.is_some() && projection0.is_some() { - write!(cx, ", ")?; - } - cx.comma_sep(projections) - })); - } - } - first = false; - } - - // Builtin bounds. - // FIXME(eddyb) avoid printing twice (needed to ensure - // that the auto traits are sorted *and* printed via cx). - let mut auto_traits: Vec<_> = - predicates.auto_traits().map(|did| (self.tcx().def_path_str(did), did)).collect(); - - // The auto traits come ordered by `DefPathHash`. While - // `DefPathHash` is *stable* in the sense that it depends on - // neither the host nor the phase of the moon, it depends - // "pseudorandomly" on the compiler version and the target. - // - // To avoid that causing instabilities in compiletest - // output, sort the auto-traits alphabetically. - auto_traits.sort(); - - for (_, def_id) in auto_traits { - if !first { - p!(write(" + ")); - } - first = false; - - p!(print_def_path(def_id, &[])); - } - - Ok(self) - } - - fn pretty_fn_sig( - mut self, - inputs: &[Ty<'tcx>], - c_variadic: bool, - output: Ty<'tcx>, - ) -> Result { - define_scoped_cx!(self); - - p!(write("(")); - 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(")")); - if !output.is_unit() { - p!(write(" -> "), print(output)); - } - - Ok(self) - } - - fn pretty_print_const( - mut self, - ct: &'tcx ty::Const<'tcx>, - print_ty: bool, - ) -> Result { - define_scoped_cx!(self); - - if self.tcx().sess.verbose() { - p!(write("Const({:?}: {:?})", ct.val, ct.ty)); - return Ok(self); - } - - macro_rules! print_underscore { - () => {{ - p!(write("_")); - if print_ty { - p!(write(": "), print(ct.ty)); - } - }}; - } - - match (ct.val, &ct.ty.kind) { - (_, ty::FnDef(did, substs)) => p!(print_value_path(*did, substs)), - (ty::ConstKind::Unevaluated(did, substs, promoted), _) => { - if let Some(promoted) = promoted { - p!(print_value_path(did, substs)); - 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)), - _ => { - if did.is_local() { - let span = self.tcx().def_span(did); - if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) - { - p!(write("{}", snip)) - } else { - print_underscore!() - } - } else { - print_underscore!() - } - } - } - } - } - (ty::ConstKind::Infer(..), _) => print_underscore!(), - (ty::ConstKind::Param(ParamConst { name, .. }), _) => p!(write("{}", name)), - (ty::ConstKind::Value(value), _) => { - return self.pretty_print_const_value(value, ct.ty, print_ty); - } - - _ => { - // fallback - p!(write("{:?}", ct.val)); - if print_ty { - p!(write(": "), print(ct.ty)); - } - } - }; - Ok(self) - } - - fn pretty_print_const_value( - mut self, - ct: ConstValue<'tcx>, - ty: Ty<'tcx>, - print_ty: bool, - ) -> Result { - define_scoped_cx!(self); - - if self.tcx().sess.verbose() { - p!(write("ConstValue({:?}: {:?})", ct, ty)); - return Ok(self); - } - - let u8 = self.tcx().types.u8; - - match (ct, &ty.kind) { - (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Bool) => { - p!(write("{}", if data == 0 { "false" } else { "true" })) - } - (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Float(ast::FloatTy::F32)) => { - p!(write("{}f32", Single::from_bits(data))) - } - (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Float(ast::FloatTy::F64)) => { - p!(write("{}f64", Double::from_bits(data))) - } - (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Uint(ui)) => { - let bit_size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size(); - let max = truncate(u128::MAX, bit_size); - - let ui_str = ui.name_str(); - if data == max { - p!(write("std::{}::MAX", ui_str)) - } else { - p!(write("{}{}", data, ui_str)) - }; - } - (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Int(i)) => { - let bit_size = Integer::from_attr(&self.tcx(), SignedInt(*i)).size().bits() as u128; - let min = 1u128 << (bit_size - 1); - let max = min - 1; - - let ty = self.tcx().lift(&ty).unwrap(); - let size = self.tcx().layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; - let i_str = i.name_str(); - match data { - d if d == min => p!(write("std::{}::MIN", i_str)), - d if d == max => p!(write("std::{}::MAX", i_str)), - _ => p!(write("{}{}", sign_extend(data, size) as i128, i_str)), - } - } - (ConstValue::Scalar(Scalar::Raw { data, .. }), ty::Char) => { - p!(write("{:?}", ::std::char::from_u32(data as u32).unwrap())) - } - (ConstValue::Scalar(_), ty::RawPtr(_)) => p!(write("{{pointer}}")), - (ConstValue::Scalar(Scalar::Ptr(ptr)), ty::FnPtr(_)) => { - let instance = { - let alloc_map = self.tcx().alloc_map.lock(); - alloc_map.unwrap_fn(ptr.alloc_id) - }; - p!(print_value_path(instance.def_id(), instance.substs)); - } - _ => { - let printed = if let ty::Ref(_, ref_ty, _) = ty.kind { - let byte_str = match (ct, &ref_ty.kind) { - (ConstValue::Scalar(Scalar::Ptr(ptr)), ty::Array(t, n)) if *t == u8 => { - let n = n.eval_usize(self.tcx(), ty::ParamEnv::empty()); - Some( - self.tcx() - .alloc_map - .lock() - .unwrap_memory(ptr.alloc_id) - .get_bytes(&self.tcx(), ptr, Size::from_bytes(n)) - .unwrap(), - ) - } - (ConstValue::Slice { data, start, end }, ty::Slice(t)) if *t == u8 => { - // The `inspect` here is okay since we checked the bounds, and there are - // no relocations (we have an active slice reference here). We don't use - // this result to affect interpreter execution. - Some(data.inspect_with_undef_and_ptr_outside_interpreter(start..end)) - } - _ => None, - }; - - if let Some(byte_str) = byte_str { - p!(write("b\"")); - for &c in byte_str { - for e in std::ascii::escape_default(c) { - self.write_char(e as char)?; - } - } - p!(write("\"")); - true - } else if let (ConstValue::Slice { data, start, end }, ty::Str) = - (ct, &ref_ty.kind) - { - // The `inspect` here is okay since we checked the bounds, and there are no - // relocations (we have an active `str` reference here). We don't use this - // result to affect interpreter execution. - let slice = data.inspect_with_undef_and_ptr_outside_interpreter(start..end); - let s = ::std::str::from_utf8(slice).expect("non utf8 str from miri"); - p!(write("{:?}", s)); - true - } else { - false - } - } else { - false - }; - if !printed { - // fallback - p!(write("{:?}", ct)); - if print_ty { - p!(write(": "), print(ty)); - } - } - } - }; - Ok(self) - } -} - -// HACK(eddyb) boxed to avoid moving around a large struct by-value. -pub struct FmtPrinter<'a, 'tcx, F>(Box>); - -pub struct FmtPrinterData<'a, 'tcx, F> { - tcx: TyCtxt<'tcx>, - fmt: F, - - empty_path: bool, - in_value: bool, - - used_region_names: FxHashSet, - region_index: usize, - binder_depth: usize, - - pub region_highlight_mode: RegionHighlightMode, - - pub name_resolver: Option Option>>, -} - -impl Deref for FmtPrinter<'a, 'tcx, F> { - type Target = FmtPrinterData<'a, 'tcx, F>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for FmtPrinter<'_, '_, F> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl FmtPrinter<'a, 'tcx, F> { - pub fn new(tcx: TyCtxt<'tcx>, fmt: F, ns: Namespace) -> Self { - FmtPrinter(Box::new(FmtPrinterData { - tcx, - fmt, - empty_path: false, - in_value: ns == Namespace::ValueNS, - used_region_names: Default::default(), - region_index: 0, - binder_depth: 0, - region_highlight_mode: RegionHighlightMode::default(), - name_resolver: None, - })) - } -} - -// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always -// (but also some things just print a `DefId` generally so maybe we need this?) -fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace { - match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::ImplTrait => { - Namespace::TypeNS - } - - DefPathData::ValueNs(..) - | DefPathData::AnonConst - | DefPathData::ClosureExpr - | DefPathData::Ctor => Namespace::ValueNS, - - DefPathData::MacroNs(..) => Namespace::MacroNS, - - _ => Namespace::TypeNS, - } -} - -impl TyCtxt<'t> { - /// Returns a string identifying this `DefId`. This string is - /// suitable for user output. - pub fn def_path_str(self, def_id: DefId) -> String { - self.def_path_str_with_substs(def_id, &[]) - } - - pub fn def_path_str_with_substs(self, def_id: DefId, substs: &'t [GenericArg<'t>]) -> String { - let ns = guess_def_namespace(self, def_id); - debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns); - let mut s = String::new(); - let _ = FmtPrinter::new(self, &mut s, ns).print_def_path(def_id, substs); - s - } -} - -impl fmt::Write for FmtPrinter<'_, '_, F> { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.fmt.write_str(s) - } -} - -impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { - type Error = fmt::Error; - - type Path = Self; - type Region = Self; - type Type = Self; - type DynExistential = Self; - type Const = Self; - - fn tcx(&'a self) -> TyCtxt<'tcx> { - self.tcx - } - - fn print_def_path( - mut self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - define_scoped_cx!(self); - - if substs.is_empty() { - match self.try_print_visible_def_path(def_id)? { - (cx, true) => return Ok(cx), - (cx, false) => self = cx, - } - } - - let key = self.tcx.def_key(def_id); - if let DefPathData::Impl = key.disambiguated_data.data { - // Always use types for non-local impls, where types are always - // available, and filename/line-number is mostly uninteresting. - let use_types = !def_id.is_local() || { - // Otherwise, use filename/line-number if forced. - let force_no_types = FORCE_IMPL_FILENAME_LINE.with(|f| f.get()); - !force_no_types - }; - - if !use_types { - // If no type info is available, fall back to - // pretty printing some span information. This should - // only occur very early in the compiler pipeline. - let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; - let span = self.tcx.def_span(def_id); - - self = self.print_def_path(parent_def_id, &[])?; - - // HACK(eddyb) copy of `path_append` to avoid - // constructing a `DisambiguatedDefPathData`. - if !self.empty_path { - write!(self, "::")?; - } - write!(self, "", span)?; - self.empty_path = false; - - return Ok(self); - } - } - - self.default_print_def_path(def_id, substs) - } - - fn print_region(self, region: ty::Region<'_>) -> Result { - self.pretty_print_region(region) - } - - fn print_type(self, ty: Ty<'tcx>) -> Result { - self.pretty_print_type(ty) - } - - fn print_dyn_existential( - self, - predicates: &'tcx ty::List>, - ) -> Result { - self.pretty_print_dyn_existential(predicates) - } - - fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result { - self.pretty_print_const(ct, true) - } - - fn path_crate(mut self, cnum: CrateNum) -> Result { - self.empty_path = true; - if cnum == LOCAL_CRATE { - if self.tcx.sess.rust_2018() { - // We add the `crate::` keyword on Rust 2018, only when desired. - if SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) { - write!(self, "{}", kw::Crate)?; - self.empty_path = false; - } - } - } else { - write!(self, "{}", self.tcx.crate_name(cnum))?; - self.empty_path = false; - } - Ok(self) - } - - fn path_qualified( - mut self, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - self = self.pretty_path_qualified(self_ty, trait_ref)?; - self.empty_path = false; - Ok(self) - } - - fn path_append_impl( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - _disambiguated_data: &DisambiguatedDefPathData, - self_ty: Ty<'tcx>, - trait_ref: Option>, - ) -> Result { - self = self.pretty_path_append_impl( - |mut cx| { - cx = print_prefix(cx)?; - if !cx.empty_path { - write!(cx, "::")?; - } - - Ok(cx) - }, - self_ty, - trait_ref, - )?; - self.empty_path = false; - Ok(self) - } - - fn path_append( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - disambiguated_data: &DisambiguatedDefPathData, - ) -> Result { - self = print_prefix(self)?; - - // Skip `::{{constructor}}` on tuple/unit structs. - match disambiguated_data.data { - DefPathData::Ctor => return Ok(self), - _ => {} - } - - // FIXME(eddyb) `name` should never be empty, but it - // currently is for `extern { ... }` "foreign modules". - let name = disambiguated_data.data.as_symbol().as_str(); - if !name.is_empty() { - if !self.empty_path { - write!(self, "::")?; - } - if ast::Ident::from_str(&name).is_raw_guess() { - write!(self, "r#")?; - } - write!(self, "{}", name)?; - - // FIXME(eddyb) this will print e.g. `{{closure}}#3`, but it - // might be nicer to use something else, e.g. `{closure#3}`. - let dis = disambiguated_data.disambiguator; - let print_dis = disambiguated_data.data.get_opt_name().is_none() - || dis != 0 && self.tcx.sess.verbose(); - if print_dis { - write!(self, "#{}", dis)?; - } - - self.empty_path = false; - } - - Ok(self) - } - - fn path_generic_args( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - args: &[GenericArg<'tcx>], - ) -> Result { - self = print_prefix(self)?; - - // Don't print `'_` if there's no unerased regions. - let print_regions = args.iter().any(|arg| match arg.unpack() { - GenericArgKind::Lifetime(r) => *r != ty::ReErased, - _ => false, - }); - let args = args.iter().cloned().filter(|arg| match arg.unpack() { - GenericArgKind::Lifetime(_) => print_regions, - _ => true, - }); - - if args.clone().next().is_some() { - if self.in_value { - write!(self, "::")?; - } - self.generic_delimiters(|cx| cx.comma_sep(args)) - } else { - Ok(self) - } - } -} - -impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { - fn infer_ty_name(&self, id: ty::TyVid) -> Option { - self.0.name_resolver.as_ref().and_then(|func| func(id)) - } - - fn print_value_path( - mut self, - def_id: DefId, - substs: &'tcx [GenericArg<'tcx>], - ) -> Result { - let was_in_value = std::mem::replace(&mut self.in_value, true); - self = self.print_def_path(def_id, substs)?; - self.in_value = was_in_value; - - Ok(self) - } - - fn in_binder(self, value: &ty::Binder) -> Result - where - T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, - { - self.pretty_in_binder(value) - } - - fn generic_delimiters( - mut self, - f: impl FnOnce(Self) -> Result, - ) -> Result { - write!(self, "<")?; - - let was_in_value = std::mem::replace(&mut self.in_value, false); - let mut inner = f(self)?; - inner.in_value = was_in_value; - - write!(inner, ">")?; - Ok(inner) - } - - fn region_should_not_be_omitted(&self, region: ty::Region<'_>) -> bool { - let highlight = self.region_highlight_mode; - if highlight.region_highlighted(region).is_some() { - return true; - } - - if self.tcx.sess.verbose() { - return true; - } - - let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; - - match *region { - ty::ReEarlyBound(ref data) => { - data.name != kw::Invalid && data.name != kw::UnderscoreLifetime - } - - ty::ReLateBound(_, br) - | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) - | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { - if let ty::BrNamed(_, name) = br { - if name != kw::Invalid && name != kw::UnderscoreLifetime { - return true; - } - } - - if let Some((region, _)) = highlight.highlight_bound_region { - if br == region { - return true; - } - } - - false - } - - ty::ReScope(_) | ty::ReVar(_) if identify_regions => true, - - ty::ReVar(_) | ty::ReScope(_) | ty::ReErased => false, - - ty::ReStatic | ty::ReEmpty(_) | ty::ReClosureBound(_) => true, - } - } -} - -// HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`. -impl FmtPrinter<'_, '_, F> { - pub fn pretty_print_region(mut self, region: ty::Region<'_>) -> Result { - define_scoped_cx!(self); - - // Watch out for region highlights. - let highlight = self.region_highlight_mode; - if let Some(n) = highlight.region_highlighted(region) { - p!(write("'{}", n)); - return Ok(self); - } - - if self.tcx.sess.verbose() { - p!(write("{:?}", region)); - return Ok(self); - } - - let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; - - // These printouts are concise. They do not contain all the information - // the user might want to diagnose an error, but there is basically no way - // to fit that into a short string. Hence the recommendation to use - // `explain_region()` or `note_and_explain_region()`. - match *region { - ty::ReEarlyBound(ref data) => { - if data.name != kw::Invalid { - p!(write("{}", data.name)); - return Ok(self); - } - } - ty::ReLateBound(_, br) - | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) - | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { - if let ty::BrNamed(_, name) = br { - if name != kw::Invalid && name != kw::UnderscoreLifetime { - p!(write("{}", name)); - return Ok(self); - } - } - - if let Some((region, counter)) = highlight.highlight_bound_region { - if br == region { - p!(write("'{}", counter)); - return Ok(self); - } - } - } - ty::ReScope(scope) if identify_regions => { - match scope.data { - region::ScopeData::Node => p!(write("'{}s", scope.item_local_id().as_usize())), - region::ScopeData::CallSite => { - p!(write("'{}cs", scope.item_local_id().as_usize())) - } - region::ScopeData::Arguments => { - p!(write("'{}as", scope.item_local_id().as_usize())) - } - region::ScopeData::Destruction => { - p!(write("'{}ds", scope.item_local_id().as_usize())) - } - region::ScopeData::Remainder(first_statement_index) => p!(write( - "'{}_{}rs", - scope.item_local_id().as_usize(), - first_statement_index.index() - )), - } - return Ok(self); - } - ty::ReVar(region_vid) if identify_regions => { - p!(write("{:?}", region_vid)); - return Ok(self); - } - ty::ReVar(_) => {} - ty::ReScope(_) | ty::ReErased => {} - ty::ReStatic => { - p!(write("'static")); - return Ok(self); - } - ty::ReEmpty(ty::UniverseIndex::ROOT) => { - p!(write("'")); - return Ok(self); - } - ty::ReEmpty(ui) => { - p!(write("'", ui)); - return Ok(self); - } - - // The user should never encounter these in unsubstituted form. - ty::ReClosureBound(vid) => { - p!(write("{:?}", vid)); - return Ok(self); - } - } - - p!(write("'_")); - - Ok(self) - } -} - -// HACK(eddyb) limited to `FmtPrinter` because of `binder_depth`, -// `region_index` and `used_region_names`. -impl FmtPrinter<'_, 'tcx, F> { - pub fn name_all_regions( - mut self, - value: &ty::Binder, - ) -> Result<(Self, (T, BTreeMap>)), fmt::Error> - where - T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, - { - fn name_by_region_index(index: usize) -> Symbol { - match index { - 0 => Symbol::intern("'r"), - 1 => Symbol::intern("'s"), - i => Symbol::intern(&format!("'t{}", i - 2)), - } - } - - // Replace any anonymous late-bound regions with named - // variants, using new unique identifiers, so that we can - // clearly differentiate between named and unnamed regions in - // the output. We'll probably want to tweak this over time to - // decide just how much information to give. - if self.binder_depth == 0 { - self.prepare_late_bound_region_info(value); - } - - let mut empty = true; - let mut start_or_continue = |cx: &mut Self, start: &str, cont: &str| { - write!( - cx, - "{}", - if empty { - empty = false; - start - } else { - cont - } - ) - }; - - define_scoped_cx!(self); - - let mut region_index = self.region_index; - let new_value = self.tcx.replace_late_bound_regions(value, |br| { - let _ = start_or_continue(&mut self, "for<", ", "); - let br = match br { - ty::BrNamed(_, name) => { - let _ = write!(self, "{}", name); - br - } - ty::BrAnon(_) | ty::BrEnv => { - let name = loop { - let name = name_by_region_index(region_index); - region_index += 1; - if !self.used_region_names.contains(&name) { - break name; - } - }; - let _ = write!(self, "{}", name); - ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name) - } - }; - self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)) - }); - start_or_continue(&mut self, "", "> ")?; - - self.binder_depth += 1; - self.region_index = region_index; - Ok((self, new_value)) - } - - pub fn pretty_in_binder(self, value: &ty::Binder) -> Result - where - T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, - { - let old_region_index = self.region_index; - let (new, new_value) = self.name_all_regions(value)?; - let mut inner = new_value.0.print(new)?; - inner.region_index = old_region_index; - inner.binder_depth -= 1; - Ok(inner) - } - - fn prepare_late_bound_region_info(&mut self, value: &ty::Binder) - where - T: TypeFoldable<'tcx>, - { - struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet); - impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> { - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { - match *r { - ty::ReLateBound(_, ty::BrNamed(_, name)) => { - self.0.insert(name); - } - _ => {} - } - r.super_visit_with(self) - } - } - - self.used_region_names.clear(); - let mut collector = LateBoundRegionNameCollector(&mut self.used_region_names); - value.visit_with(&mut collector); - self.region_index = 0; - } -} - -impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::Binder -where - T: Print<'tcx, P, Output = P, Error = P::Error> + TypeFoldable<'tcx>, -{ - type Output = P; - type Error = P::Error; - fn print(&self, cx: P) -> Result { - cx.in_binder(self) - } -} - -impl<'tcx, T, U, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate -where - T: Print<'tcx, P, Output = P, Error = P::Error>, - U: Print<'tcx, P, Output = P, Error = P::Error>, -{ - type Output = P; - type Error = P::Error; - fn print(&self, mut cx: P) -> Result { - define_scoped_cx!(cx); - p!(print(self.0), write(": "), print(self.1)); - Ok(cx) - } -} - -macro_rules! forward_display_to_print { - ($($ty:ty),+) => { - $(impl fmt::Display for $ty { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - tcx.lift(self) - .expect("could not lift for printing") - .print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; - Ok(()) - }) - } - })+ - }; -} - -macro_rules! define_print_and_forward_display { - (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { - $(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty { - type Output = P; - type Error = fmt::Error; - fn print(&$self, $cx: P) -> Result { - #[allow(unused_mut)] - let mut $cx = $cx; - define_scoped_cx!($cx); - let _: () = $print; - #[allow(unreachable_code)] - Ok($cx) - } - })+ - - forward_display_to_print!($($ty),+); - }; -} - -// HACK(eddyb) this is separate because `ty::RegionKind` doesn't need lifting. -impl fmt::Display for ty::RegionKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ty::tls::with(|tcx| { - self.print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; - Ok(()) - }) - } -} - -/// Wrapper type for `ty::TraitRef` which opts-in to pretty printing only -/// the trait path. That is, it will print `Trait` instead of -/// `>`. -#[derive(Copy, Clone, TypeFoldable, Lift)] -pub struct TraitRefPrintOnlyTraitPath<'tcx>(ty::TraitRef<'tcx>); - -impl fmt::Debug for TraitRefPrintOnlyTraitPath<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl ty::TraitRef<'tcx> { - pub fn print_only_trait_path(self) -> TraitRefPrintOnlyTraitPath<'tcx> { - TraitRefPrintOnlyTraitPath(self) - } -} - -impl ty::Binder> { - pub fn print_only_trait_path(self) -> ty::Binder> { - self.map_bound(|tr| tr.print_only_trait_path()) - } -} - -forward_display_to_print! { - Ty<'tcx>, - &'tcx ty::List>, - &'tcx ty::Const<'tcx>, - - // HACK(eddyb) these are exhaustive instead of generic, - // because `for<'tcx>` isn't possible yet. - ty::Binder<&'tcx ty::List>>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder>, - ty::Binder, ty::Region<'tcx>>>, - ty::Binder, ty::Region<'tcx>>>, - - ty::OutlivesPredicate, ty::Region<'tcx>>, - ty::OutlivesPredicate, ty::Region<'tcx>> -} - -define_print_and_forward_display! { - (self, cx): - - &'tcx ty::List> { - p!(write("{{")); - let mut tys = self.iter(); - if let Some(&ty) = tys.next() { - p!(print(ty)); - for &ty in tys { - p!(write(", "), print(ty)); - } - } - p!(write("}}")) - } - - ty::TypeAndMut<'tcx> { - p!(write("{}", self.mutbl.prefix_str()), print(self.ty)) - } - - ty::ExistentialTraitRef<'tcx> { - // Use a type that can't appear in defaults of type parameters. - let dummy_self = cx.tcx().mk_ty_infer(ty::FreshTy(0)); - let trait_ref = self.with_self_ty(cx.tcx(), dummy_self); - p!(print(trait_ref.print_only_trait_path())) - } - - ty::ExistentialProjection<'tcx> { - let name = cx.tcx().associated_item(self.item_def_id).ident; - p!(write("{} = ", name), print(self.ty)) - } - - ty::ExistentialPredicate<'tcx> { - match *self { - ty::ExistentialPredicate::Trait(x) => p!(print(x)), - ty::ExistentialPredicate::Projection(x) => p!(print(x)), - ty::ExistentialPredicate::AutoTrait(def_id) => { - p!(print_def_path(def_id, &[])); - } - } - } - - ty::FnSig<'tcx> { - p!(write("{}", self.unsafety.prefix_str())); - - if self.abi != Abi::Rust { - p!(write("extern {} ", self.abi)); - } - - p!(write("fn"), pretty_fn_sig(self.inputs(), self.c_variadic, self.output())); - } - - ty::InferTy { - if cx.tcx().sess.verbose() { - p!(write("{:?}", self)); - return Ok(cx); - } - match *self { - ty::TyVar(_) => p!(write("_")), - ty::IntVar(_) => p!(write("{}", "{integer}")), - ty::FloatVar(_) => p!(write("{}", "{float}")), - ty::FreshTy(v) => p!(write("FreshTy({})", v)), - ty::FreshIntTy(v) => p!(write("FreshIntTy({})", v)), - ty::FreshFloatTy(v) => p!(write("FreshFloatTy({})", v)) - } - } - - ty::TraitRef<'tcx> { - p!(write("<{} as {}>", self.self_ty(), self.print_only_trait_path())) - } - - TraitRefPrintOnlyTraitPath<'tcx> { - p!(print_def_path(self.0.def_id, self.0.substs)); - } - - ty::ParamTy { - p!(write("{}", self.name)) - } - - ty::ParamConst { - p!(write("{}", self.name)) - } - - ty::SubtypePredicate<'tcx> { - p!(print(self.a), write(" <: "), print(self.b)) - } - - ty::TraitPredicate<'tcx> { - p!(print(self.trait_ref.self_ty()), write(": "), - print(self.trait_ref.print_only_trait_path())) - } - - ty::ProjectionPredicate<'tcx> { - p!(print(self.projection_ty), write(" == "), print(self.ty)) - } - - ty::ProjectionTy<'tcx> { - p!(print_def_path(self.item_def_id, self.substs)); - } - - ty::ClosureKind { - match *self { - ty::ClosureKind::Fn => p!(write("Fn")), - ty::ClosureKind::FnMut => p!(write("FnMut")), - ty::ClosureKind::FnOnce => p!(write("FnOnce")), - } - } - - ty::Predicate<'tcx> { - match *self { - ty::Predicate::Trait(ref data, constness) => { - if let hir::Constness::Const = constness { - p!(write("const ")); - } - p!(print(data)) - } - ty::Predicate::Subtype(ref predicate) => p!(print(predicate)), - ty::Predicate::RegionOutlives(ref predicate) => p!(print(predicate)), - ty::Predicate::TypeOutlives(ref predicate) => p!(print(predicate)), - ty::Predicate::Projection(ref predicate) => p!(print(predicate)), - ty::Predicate::WellFormed(ty) => p!(print(ty), write(" well-formed")), - ty::Predicate::ObjectSafe(trait_def_id) => { - p!(write("the trait `"), - print_def_path(trait_def_id, &[]), - write("` is object-safe")) - } - ty::Predicate::ClosureKind(closure_def_id, _closure_substs, kind) => { - p!(write("the closure `"), - print_value_path(closure_def_id, &[]), - write("` implements the trait `{}`", kind)) - } - ty::Predicate::ConstEvaluatable(def_id, substs) => { - p!(write("the constant `"), - print_value_path(def_id, substs), - write("` can be evaluated")) - } - } - } - - GenericArg<'tcx> { - match self.unpack() { - GenericArgKind::Lifetime(lt) => p!(print(lt)), - GenericArgKind::Type(ty) => p!(print(ty)), - GenericArgKind::Const(ct) => p!(print(ct)), - } - } -} diff --git a/src/librustc/ty/query/caches.rs b/src/librustc/ty/query/caches.rs deleted file mode 100644 index efc2804bd4d59..0000000000000 --- a/src/librustc/ty/query/caches.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::dep_graph::DepNodeIndex; -use crate::ty::query::config::QueryAccessors; -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; - -pub(crate) trait CacheSelector { - type Cache: QueryCache; -} - -pub(crate) trait QueryCache: Default { - 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, Q>( - &self, - state: &'tcx QueryState<'tcx, Q>, - get_cache: GetCache, - key: K, - // `on_hit` can be called while holding a lock to the query state shard. - on_hit: OnHit, - on_miss: OnMiss, - ) -> R - where - Q: QueryAccessors<'tcx>, - GetCache: for<'a> Fn(&'a mut QueryStateShard<'tcx, Q>) -> &'a mut Self::Sharded, - OnHit: FnOnce(&V, DepNodeIndex) -> R, - OnMiss: FnOnce(K, QueryLookup<'tcx, Q>) -> R; - - fn complete( - &self, - tcx: TyCtxt<'tcx>, - lock_sharded_storage: &mut Self::Sharded, - key: K, - value: V, - 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; -} - -#[derive(Default)] -pub struct DefaultCache; - -impl QueryCache for DefaultCache { - type Sharded = FxHashMap; - - #[inline(always)] - fn lookup<'tcx, R, GetCache, OnHit, OnMiss, Q>( - &self, - state: &'tcx QueryState<'tcx, Q>, - get_cache: GetCache, - key: K, - on_hit: OnHit, - on_miss: OnMiss, - ) -> R - where - Q: QueryAccessors<'tcx>, - GetCache: for<'a> Fn(&'a mut QueryStateShard<'tcx, Q>) -> &'a mut Self::Sharded, - OnHit: FnOnce(&V, DepNodeIndex) -> R, - OnMiss: FnOnce(K, QueryLookup<'tcx, Q>) -> 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 178c2362def6e..0000000000000 --- a/src/librustc/ty/query/config.rs +++ /dev/null @@ -1,88 +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::{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; - - type Cache: QueryCache; - - fn query(key: Self::Key) -> Query<'tcx>; - - // 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>; - - fn to_dep_node(tcx: TyCtxt<'tcx>, key: &Self::Key) -> DepNode; - - fn dep_kind() -> DepKind; - - // 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 -where - >::Cache: QueryCache>::Value>, -{ - 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 3d17883fec3bd..0000000000000 --- a/src/librustc/ty/query/mod.rs +++ /dev/null @@ -1,208 +0,0 @@ -use crate::dep_graph::{self, DepConstructor, DepNode, DepNodeParams}; -use crate::hir::exports::Export; -use crate::hir::map; -use crate::hir::{HirOwner, HirOwnerItems}; -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::session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; -use crate::session::CrateDisambiguator; -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, DefIndex}; -use rustc_hir::{Crate, HirIdSet, ItemLocalId, TraitCandidate}; -use rustc_index::vec::IndexVec; -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::convert::TryFrom; -use std::ops::Deref; -use std::sync::Arc; - -#[macro_use] -mod plumbing; -pub 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 `DefIndex` 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 { - use crate::dep_graph::DepKind; - - // We must avoid ever having to call `force_from_dep_node()` for a - // `DepNode::codegen_unit`: - // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we - // would always end up having to evaluate the first caller of the - // `codegen_unit` query that *is* reconstructible. This might very well be - // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just - // to re-trigger calling the `codegen_unit` query with the right key. At - // that point we would already have re-done all the work we are trying to - // avoid doing in the first place. - // The solution is simple: Just explicitly call the `codegen_unit` query for - // each CGU, right after partitioning. This way `try_mark_green` will always - // hit the cache instead of having to go through `force_from_dep_node`. - // This assertion makes sure, we actually keep applying the solution above. - debug_assert!( - dep_node.kind != DepKind::codegen_unit, - "calling force_from_dep_node() on DepKind::codegen_unit" - ); - - if !dep_node.kind.can_reconstruct_query_key() { - return false; - } - - rustc_dep_node_force!([dep_node, tcx] - // These are inputs that are expected to be pre-allocated and that - // should therefore always be red or green already. - DepKind::CrateMetadata | - - // These are anonymous nodes. - DepKind::TraitSelect | - - // We don't have enough information to reconstruct the query key of - // these. - DepKind::CompileCodegenUnit => { - bug!("force_from_dep_node: encountered {:?}", dep_node) - } - ); - - false -} - -impl DepNode { - /// Check whether the query invocation corresponding to the given - /// DepNode is eligible for on-disk-caching. If so, this is method - /// will execute the query corresponding to the given DepNode. - /// Also, as a sanity check, it expects that the corresponding query - /// invocation has been marked as green already. - pub fn try_load_from_on_disk_cache<'tcx>(&self, tcx: TyCtxt<'tcx>) { - use crate::dep_graph::DepKind; - - rustc_dep_node_try_load_from_on_disk_cache!(self, tcx) - } -} diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs deleted file mode 100644 index acf67f52dceaa..0000000000000 --- a/src/librustc/ty/query/plumbing.rs +++ /dev/null @@ -1,1164 +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::{DepNode, DepNodeIndex, SerializedDepNodeIndex}; -use crate::ty::query::caches::QueryCache; -use crate::ty::query::config::{QueryAccessors, QueryDescription}; -use crate::ty::query::job::{QueryInfo, QueryJob, QueryJobId, 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::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, D: QueryAccessors<'tcx> + ?Sized> { - pub(super) cache: <>::Cache as QueryCache>::Sharded, - pub(super) active: FxHashMap>, - - /// Used to generate unique ids for active jobs. - pub(super) jobs: u32, -} - -impl<'tcx, Q: QueryAccessors<'tcx>> QueryStateShard<'tcx, Q> { - fn get_cache( - &mut self, - ) -> &mut <>::Cache as QueryCache>::Sharded { - &mut self.cache - } -} - -impl<'tcx, Q: QueryAccessors<'tcx>> Default for QueryStateShard<'tcx, Q> { - fn default() -> QueryStateShard<'tcx, Q> { - QueryStateShard { cache: Default::default(), active: Default::default(), jobs: 0 } - } -} - -pub(crate) struct QueryState<'tcx, D: QueryAccessors<'tcx> + ?Sized> { - pub(super) cache: D::Cache, - pub(super) shards: Sharded>, - #[cfg(debug_assertions)] - pub(super) cache_hits: AtomicUsize, -} - -impl<'tcx, Q: QueryAccessors<'tcx>> QueryState<'tcx, Q> { - pub(super) fn get_lookup(&'tcx self, key: &K) -> QueryLookup<'tcx, Q> { - // 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. -pub(super) 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, M: QueryAccessors<'tcx>> QueryState<'tcx, M> { - pub fn iter_results( - &self, - f: impl for<'a> FnOnce( - Box + 'a>, - ) -> R, - ) -> R { - self.cache.iter(&self.shards, |shard| &mut shard.cache, f) - } - pub fn all_inactive(&self) -> bool { - let shards = self.shards.lock_shards(); - shards.iter().all(|shard| shard.active.is_empty()) - } -} - -impl<'tcx, M: QueryAccessors<'tcx>> Default for QueryState<'tcx, M> { - fn default() -> QueryState<'tcx, M> { - QueryState { - cache: M::Cache::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, Q: QueryAccessors<'tcx>> { - pub(super) key_hash: u64, - pub(super) shard: usize, - pub(super) lock: LockGuard<'tcx, QueryStateShard<'tcx, Q>>, -} - -/// A type representing the responsibility to execute the job in the `job` field. -/// This will poison the relevant query if dropped. -pub(super) struct JobOwner<'tcx, Q: QueryDescription<'tcx>> { - tcx: TyCtxt<'tcx>, - key: Q::Key, - id: QueryJobId, -} - -impl<'tcx, Q: QueryDescription<'tcx>> JobOwner<'tcx, Q> { - /// 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)] - pub(super) fn try_start( - tcx: TyCtxt<'tcx>, - span: Span, - key: &Q::Key, - mut lookup: QueryLookup<'tcx, Q>, - ) -> TryGetJob<'tcx, Q> { - 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 { 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::( - (*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)] - pub(super) fn complete(self, result: &Q::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 tcx = self.tcx; - - // Forget ourself so our destructor won't poison the query - mem::forget(self); - - let job = { - let state = Q::query_state(tcx); - 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, Q: QueryDescription<'tcx>> Drop for JobOwner<'tcx, Q> { - #[inline(never)] - #[cold] - fn drop(&mut self) { - // Poison the query so jobs waiting on it panic. - let state = Q::query_state(self.tcx); - 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<'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`. -pub(super) enum TryGetJob<'tcx, D: QueryDescription<'tcx>> { - /// The query is not yet started. Contains a guard to the cache eventually used to start it. - NotYetStarted(JobOwner<'tcx, D>), - - /// 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((D::Value, DepNodeIndex)), - - /// Trying to execute the query resulted in a cycle. - Cycle(D::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)] - pub(super) 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, - key: Q::Key, - // `on_hit` can be called while holding a lock to the query cache - on_hit: OnHit, - on_miss: OnMiss, - ) -> R - where - Q: QueryDescription<'tcx> + 'tcx, - OnHit: FnOnce(&Q::Value, DepNodeIndex) -> R, - OnMiss: FnOnce(Q::Key, QueryLookup<'tcx, Q>) -> R, - { - let state = Q::query_state(self); - - 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::( - key, - |value, index| { - self.dep_graph.read_index(index); - value.clone() - }, - |key, lookup| self.try_execute_query::(span, key, lookup), - ) - } - - #[inline(always)] - pub(super) fn try_execute_query>( - self, - span: Span, - key: Q::Key, - lookup: QueryLookup<'tcx, Q>, - ) -> 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(&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(&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>( - self, - key: Q::Key, - job: JobOwner<'tcx, Q>, - 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(&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::( - 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]))*) - } - - impl<$tcx> Queries<$tcx> { - pub 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 fn try_collect_active_jobs( - &self - ) -> Option>> { - let mut jobs = FxHashMap::default(); - - $( - // We use try_lock_shards here since we are called from the - // deadlock handler, and this shouldn't be locked. - let shards = self.$name.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: - as QueryAccessors<'tcx>>::dep_kind(), - }; - let info = QueryInfo { - span: job.span, - query: queries::$name::query(k.clone()) - }; - Some((id, QueryJobInfo { info, job: job.clone() })) - } else { - None - } - }) - })); - )* - - Some(jobs) - } - } - - #[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 ()> - })* - } - - // This module and the functions in it exist only to provide a - // predictable symbol name prefix for query providers. This is helpful - // for analyzing queries in profilers. - pub(super) mod __query_compute { - $(#[inline(never)] - pub fn $name R, R>(f: F) -> R { - f() - })* - } - - $(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)*]); - - type Cache = query_storage!([$($modifiers)*][$K, $V]); - - #[inline(always)] - fn query(key: Self::Key) -> Query<'tcx> { - Query::$name(key) - } - - #[inline(always)] - fn query_state<'a>(tcx: TyCtxt<$tcx>) -> &'a QueryState<$tcx, Self> { - &tcx.queries.$name - } - - #[allow(unused)] - #[inline(always)] - fn to_dep_node(tcx: TyCtxt<$tcx>, key: &Self::Key) -> DepNode { - DepConstructor::$node(tcx, *key) - } - - #[inline(always)] - fn dep_kind() -> dep_graph::DepKind { - dep_graph::DepKind::$node - } - - #[inline] - fn compute(tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value { - __query_compute::$name(move || { - 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, queries::$name<$tcx>>,)* - } - }; -} - -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/values.rs b/src/librustc/ty/query/values.rs deleted file mode 100644 index b01d15c29b2db..0000000000000 --- a/src/librustc/ty/query/values.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::ty::{self, AdtSizedConstraint, Ty, TyCtxt}; - -use rustc_span::symbol::Symbol; - -pub(super) trait Value<'tcx>: Sized { - fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self; -} - -impl<'tcx, T> Value<'tcx> for T { - default fn from_cycle_error(tcx: TyCtxt<'tcx>) -> T { - tcx.sess.abort_if_errors(); - bug!("Value::from_cycle_error called without errors"); - } -} - -impl<'tcx> Value<'tcx> for Ty<'tcx> { - fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - tcx.types.err - } -} - -impl<'tcx> Value<'tcx> for ty::SymbolName { - fn from_cycle_error(_: TyCtxt<'tcx>) -> Self { - ty::SymbolName { name: Symbol::intern("") } - } -} - -impl<'tcx> Value<'tcx> for AdtSizedConstraint<'tcx> { - fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self { - AdtSizedConstraint(tcx.intern_type_list(&[tcx.types.err])) - } -} diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs deleted file mode 100644 index fb4184a9fb347..0000000000000 --- a/src/librustc/ty/relate.rs +++ /dev/null @@ -1,990 +0,0 @@ -//! Generalized type relating mechanism. -//! -//! A type relation `R` relates a pair of values `(A, B)`. `A and B` are usually -//! types or regions but can be other things. Examples of type relations are -//! 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_target::spec::abi; -use std::iter; -use std::rc::Rc; - -pub type RelateResult<'tcx, T> = Result>; - -#[derive(Clone, Debug)] -pub enum Cause { - ExistentialRegionBound, // relating an existential region bound -} - -pub trait TypeRelation<'tcx>: Sized { - fn tcx(&self) -> TyCtxt<'tcx>; - - fn param_env(&self) -> ty::ParamEnv<'tcx>; - - /// Returns a static string we can use for printouts. - fn tag(&self) -> &'static str; - - /// Returns `true` if the value `a` is the "expected" type in the - /// relation. Just affects error messages. - fn a_is_expected(&self) -> bool; - - fn with_cause(&mut self, _cause: Cause, f: F) -> R - where - F: FnOnce(&mut Self) -> R, - { - f(self) - } - - /// Generic relation routine suitable for most anything. - fn relate>(&mut self, a: &T, b: &T) -> RelateResult<'tcx, T> { - Relate::relate(self, a, b) - } - - /// Relate the two substitutions for the given item. The default - /// is to look up the variance for the item and proceed - /// accordingly. - fn relate_item_substs( - &mut self, - item_def_id: DefId, - a_subst: SubstsRef<'tcx>, - b_subst: SubstsRef<'tcx>, - ) -> RelateResult<'tcx, SubstsRef<'tcx>> { - debug!( - "relate_item_substs(item_def_id={:?}, a_subst={:?}, b_subst={:?})", - item_def_id, a_subst, b_subst - ); - - let opt_variances = self.tcx().variances_of(item_def_id); - relate_substs(self, Some(opt_variances), a_subst, b_subst) - } - - /// Switch variance for the purpose of relating `a` and `b`. - fn relate_with_variance>( - &mut self, - variance: ty::Variance, - a: &T, - b: &T, - ) -> RelateResult<'tcx, T>; - - // Overridable relations. You shouldn't typically call these - // directly, instead call `relate()`, which in turn calls - // these. This is both more uniform but also allows us to add - // additional hooks for other types in the future if needed - // without making older code, which called `relate`, obsolete. - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>; - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>>; - - fn consts( - &mut self, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>; - - fn binders( - &mut self, - a: &ty::Binder, - b: &ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> - where - T: Relate<'tcx>; -} - -pub trait Relate<'tcx>: TypeFoldable<'tcx> { - fn relate>( - relation: &mut R, - a: &Self, - b: &Self, - ) -> RelateResult<'tcx, Self>; -} - -/////////////////////////////////////////////////////////////////////////// -// Relate impls - -impl<'tcx> Relate<'tcx> for ty::TypeAndMut<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::TypeAndMut<'tcx>, - b: &ty::TypeAndMut<'tcx>, - ) -> RelateResult<'tcx, ty::TypeAndMut<'tcx>> { - debug!("{}.mts({:?}, {:?})", relation.tag(), a, b); - if a.mutbl != b.mutbl { - Err(TypeError::Mutability) - } else { - let mutbl = a.mutbl; - let variance = match mutbl { - ast::Mutability::Not => ty::Covariant, - ast::Mutability::Mut => ty::Invariant, - }; - let ty = relation.relate_with_variance(variance, &a.ty, &b.ty)?; - Ok(ty::TypeAndMut { ty, mutbl }) - } - } -} - -pub fn relate_substs>( - relation: &mut R, - variances: Option<&[ty::Variance]>, - a_subst: SubstsRef<'tcx>, - b_subst: SubstsRef<'tcx>, -) -> RelateResult<'tcx, SubstsRef<'tcx>> { - let tcx = relation.tcx(); - - let params = a_subst.iter().zip(b_subst).enumerate().map(|(i, (a, b))| { - let variance = variances.map_or(ty::Invariant, |v| v[i]); - relation.relate_with_variance(variance, a, b) - }); - - Ok(tcx.mk_substs(params)?) -} - -impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::FnSig<'tcx>, - b: &ty::FnSig<'tcx>, - ) -> RelateResult<'tcx, ty::FnSig<'tcx>> { - let tcx = relation.tcx(); - - if a.c_variadic != b.c_variadic { - return Err(TypeError::VariadicMismatch(expected_found( - relation, - &a.c_variadic, - &b.c_variadic, - ))); - } - let unsafety = relation.relate(&a.unsafety, &b.unsafety)?; - let abi = relation.relate(&a.abi, &b.abi)?; - - if a.inputs().len() != b.inputs().len() { - return Err(TypeError::ArgCount); - } - - let inputs_and_output = a - .inputs() - .iter() - .cloned() - .zip(b.inputs().iter().cloned()) - .map(|x| (x, false)) - .chain(iter::once(((a.output(), b.output()), true))) - .map(|((a, b), is_output)| { - if is_output { - relation.relate(&a, &b) - } else { - relation.relate_with_variance(ty::Contravariant, &a, &b) - } - }); - Ok(ty::FnSig { - inputs_and_output: tcx.mk_type_list(inputs_and_output)?, - c_variadic: a.c_variadic, - unsafety, - abi, - }) - } -} - -impl<'tcx> Relate<'tcx> for ast::Unsafety { - fn relate>( - relation: &mut R, - a: &ast::Unsafety, - b: &ast::Unsafety, - ) -> RelateResult<'tcx, ast::Unsafety> { - if a != b { - Err(TypeError::UnsafetyMismatch(expected_found(relation, a, b))) - } else { - Ok(*a) - } - } -} - -impl<'tcx> Relate<'tcx> for abi::Abi { - fn relate>( - relation: &mut R, - a: &abi::Abi, - b: &abi::Abi, - ) -> RelateResult<'tcx, abi::Abi> { - if a == b { Ok(*a) } else { Err(TypeError::AbiMismatch(expected_found(relation, a, b))) } - } -} - -impl<'tcx> Relate<'tcx> for ty::ProjectionTy<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::ProjectionTy<'tcx>, - b: &ty::ProjectionTy<'tcx>, - ) -> RelateResult<'tcx, ty::ProjectionTy<'tcx>> { - if a.item_def_id != b.item_def_id { - Err(TypeError::ProjectionMismatched(expected_found( - relation, - &a.item_def_id, - &b.item_def_id, - ))) - } else { - let substs = relation.relate(&a.substs, &b.substs)?; - Ok(ty::ProjectionTy { item_def_id: a.item_def_id, substs: &substs }) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::ExistentialProjection<'tcx>, - b: &ty::ExistentialProjection<'tcx>, - ) -> RelateResult<'tcx, ty::ExistentialProjection<'tcx>> { - if a.item_def_id != b.item_def_id { - Err(TypeError::ProjectionMismatched(expected_found( - relation, - &a.item_def_id, - &b.item_def_id, - ))) - } else { - let ty = relation.relate(&a.ty, &b.ty)?; - let substs = relation.relate(&a.substs, &b.substs)?; - Ok(ty::ExistentialProjection { item_def_id: a.item_def_id, substs, ty }) - } - } -} - -impl<'tcx> Relate<'tcx> for Vec> { - fn relate>( - relation: &mut R, - a: &Vec>, - b: &Vec>, - ) -> RelateResult<'tcx, Vec>> { - // To be compatible, `a` and `b` must be for precisely the - // same set of traits and item names. We always require that - // projection bounds lists are sorted by trait-def-id and item-name, - // so we can just iterate through the lists pairwise, so long as they are the - // same length. - if a.len() != b.len() { - Err(TypeError::ProjectionBoundsLength(expected_found(relation, &a.len(), &b.len()))) - } else { - a.iter().zip(b).map(|(a, b)| relation.relate(a, b)).collect() - } - } -} - -impl<'tcx> Relate<'tcx> for ty::TraitRef<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::TraitRef<'tcx>, - b: &ty::TraitRef<'tcx>, - ) -> RelateResult<'tcx, ty::TraitRef<'tcx>> { - // Different traits cannot be related. - if a.def_id != b.def_id { - Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id))) - } else { - let substs = relate_substs(relation, None, a.substs, b.substs)?; - Ok(ty::TraitRef { def_id: a.def_id, substs }) - } - } -} - -impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::ExistentialTraitRef<'tcx>, - b: &ty::ExistentialTraitRef<'tcx>, - ) -> RelateResult<'tcx, ty::ExistentialTraitRef<'tcx>> { - // Different traits cannot be related. - if a.def_id != b.def_id { - Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id))) - } else { - let substs = relate_substs(relation, None, a.substs, b.substs)?; - Ok(ty::ExistentialTraitRef { def_id: a.def_id, substs }) - } - } -} - -#[derive(Debug, Clone, TypeFoldable)] -struct GeneratorWitness<'tcx>(&'tcx ty::List>); - -impl<'tcx> Relate<'tcx> for GeneratorWitness<'tcx> { - fn relate>( - relation: &mut R, - a: &GeneratorWitness<'tcx>, - b: &GeneratorWitness<'tcx>, - ) -> RelateResult<'tcx, GeneratorWitness<'tcx>> { - assert_eq!(a.0.len(), b.0.len()); - let tcx = relation.tcx(); - let types = tcx.mk_type_list(a.0.iter().zip(b.0).map(|(a, b)| relation.relate(a, b)))?; - Ok(GeneratorWitness(types)) - } -} - -impl<'tcx> Relate<'tcx> for Ty<'tcx> { - fn relate>( - relation: &mut R, - a: &Ty<'tcx>, - b: &Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> { - relation.tys(a, b) - } -} - -/// The main "type relation" routine. Note that this does not handle -/// inference artifacts, so you should filter those out before calling -/// it. -pub fn super_relate_tys>( - relation: &mut R, - a: Ty<'tcx>, - b: Ty<'tcx>, -) -> RelateResult<'tcx, Ty<'tcx>> { - let tcx = relation.tcx(); - debug!("super_relate_tys: a={:?} b={:?}", a, b); - match (&a.kind, &b.kind) { - (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { - // The caller should handle these cases! - bug!("var types encountered in super_relate_tys") - } - - (ty::Bound(..), _) | (_, ty::Bound(..)) => { - bug!("bound types encountered in super_relate_tys") - } - - (&ty::Error, _) | (_, &ty::Error) => Ok(tcx.types.err), - - (&ty::Never, _) - | (&ty::Char, _) - | (&ty::Bool, _) - | (&ty::Int(_), _) - | (&ty::Uint(_), _) - | (&ty::Float(_), _) - | (&ty::Str, _) - if a == b => - { - Ok(a) - } - - (&ty::Param(ref a_p), &ty::Param(ref b_p)) if a_p.index == b_p.index => Ok(a), - - (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a), - - (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def => { - let substs = relation.relate_item_substs(a_def.did, a_substs, b_substs)?; - Ok(tcx.mk_adt(a_def, substs)) - } - - (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(tcx.mk_foreign(a_id)), - - (&ty::Dynamic(ref a_obj, ref a_region), &ty::Dynamic(ref b_obj, ref b_region)) => { - let region_bound = relation.with_cause(Cause::ExistentialRegionBound, |relation| { - relation.relate_with_variance(ty::Contravariant, a_region, b_region) - })?; - Ok(tcx.mk_dynamic(relation.relate(a_obj, b_obj)?, region_bound)) - } - - (&ty::Generator(a_id, a_substs, movability), &ty::Generator(b_id, b_substs, _)) - if a_id == b_id => - { - // All Generator types with the same id represent - // the (anonymous) type of the same generator expression. So - // all of their regions should be equated. - let substs = relation.relate(&a_substs, &b_substs)?; - Ok(tcx.mk_generator(a_id, substs, movability)) - } - - (&ty::GeneratorWitness(a_types), &ty::GeneratorWitness(b_types)) => { - // Wrap our types with a temporary GeneratorWitness struct - // inside the binder so we can related them - let a_types = a_types.map_bound(GeneratorWitness); - let b_types = b_types.map_bound(GeneratorWitness); - // Then remove the GeneratorWitness for the result - let types = relation.relate(&a_types, &b_types)?.map_bound(|witness| witness.0); - Ok(tcx.mk_generator_witness(types)) - } - - (&ty::Closure(a_id, a_substs), &ty::Closure(b_id, b_substs)) if a_id == b_id => { - // All Closure types with the same id represent - // the (anonymous) type of the same closure expression. So - // all of their regions should be equated. - let substs = relation.relate(&a_substs, &b_substs)?; - Ok(tcx.mk_closure(a_id, &substs)) - } - - (&ty::RawPtr(ref a_mt), &ty::RawPtr(ref b_mt)) => { - let mt = relation.relate(a_mt, b_mt)?; - Ok(tcx.mk_ptr(mt)) - } - - (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => { - let r = relation.relate_with_variance(ty::Contravariant, &a_r, &b_r)?; - let a_mt = ty::TypeAndMut { ty: a_ty, mutbl: a_mutbl }; - let b_mt = ty::TypeAndMut { ty: b_ty, mutbl: b_mutbl }; - let mt = relation.relate(&a_mt, &b_mt)?; - Ok(tcx.mk_ref(r, mt)) - } - - (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => { - 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))), - Err(err) => { - // Check whether the lengths are both concrete/known values, - // but are unequal, for better diagnostics. - let sz_a = sz_a.try_eval_usize(tcx, relation.param_env()); - let sz_b = sz_b.try_eval_usize(tcx, relation.param_env()); - match (sz_a, sz_b) { - (Some(sz_a_val), Some(sz_b_val)) => Err(TypeError::FixedArraySize( - expected_found(relation, &sz_a_val, &sz_b_val), - )), - _ => return Err(err), - } - } - } - } - - (&ty::Slice(a_t), &ty::Slice(b_t)) => { - let t = relation.relate(&a_t, &b_t)?; - Ok(tcx.mk_slice(t)) - } - - (&ty::Tuple(as_), &ty::Tuple(bs)) => { - if as_.len() == bs.len() { - Ok(tcx.mk_tup( - as_.iter() - .zip(bs) - .map(|(a, b)| relation.relate(&a.expect_ty(), &b.expect_ty())), - )?) - } else if !(as_.is_empty() || bs.is_empty()) { - Err(TypeError::TupleSize(expected_found(relation, &as_.len(), &bs.len()))) - } else { - Err(TypeError::Sorts(expected_found(relation, &a, &b))) - } - } - - (&ty::FnDef(a_def_id, a_substs), &ty::FnDef(b_def_id, b_substs)) - if a_def_id == b_def_id => - { - let substs = relation.relate_item_substs(a_def_id, a_substs, b_substs)?; - Ok(tcx.mk_fn_def(a_def_id, substs)) - } - - (&ty::FnPtr(a_fty), &ty::FnPtr(b_fty)) => { - let fty = relation.relate(&a_fty, &b_fty)?; - 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)?; - Ok(tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs)) - } - - (&ty::Opaque(a_def_id, a_substs), &ty::Opaque(b_def_id, b_substs)) - if a_def_id == b_def_id => - { - let substs = relate_substs(relation, None, a_substs, b_substs)?; - Ok(tcx.mk_opaque(a_def_id, substs)) - } - - _ => Err(TypeError::Sorts(expected_found(relation, &a, &b))), - } -} - -/// The main "const relation" routine. Note that this does not handle -/// inference artifacts, so you should filter those out before calling -/// it. -pub fn super_relate_consts>( - relation: &mut R, - a: &'tcx ty::Const<'tcx>, - b: &'tcx ty::Const<'tcx>, -) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - let tcx = relation.tcx(); - - let eagerly_eval = |x: &'tcx ty::Const<'tcx>| { - if !x.val.has_local_value() { - return x.eval(tcx, relation.param_env()).val; - } - x.val - }; - - // Currently, the values that can be unified are primitive types, - // and those that derive both `PartialEq` and `Eq`, corresponding - // 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::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => { - return Ok(a); - } - (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) if p1 == p2 => { - return Ok(a); - } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => { - let new_val = match (a_val, b_val) { - (ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => { - if a_val == b_val { - Ok(ConstValue::Scalar(a_val)) - } else if let ty::FnPtr(_) = a.ty.kind { - 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); - if a_instance == b_instance { - Ok(ConstValue::Scalar(a_val)) - } else { - Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) - } - } else { - Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) - } - } - - (a_val @ ConstValue::Slice { .. }, b_val @ 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 { - Ok(a_val) - } else { - Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) - } - } - - // FIXME(const_generics): handle `ConstValue::ByRef`. - _ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))), - }; - - new_val.map(ty::ConstKind::Value) - } - - // FIXME(const_generics): this is wrong, as it is a projection - ( - ty::ConstKind::Unevaluated(a_def_id, a_substs, a_promoted), - ty::ConstKind::Unevaluated(b_def_id, b_substs, b_promoted), - ) if a_def_id == b_def_id && a_promoted == b_promoted => { - let substs = - relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?; - Ok(ty::ConstKind::Unevaluated(a_def_id, &substs, a_promoted)) - } - _ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))), - }; - new_const_val.map(|val| tcx.mk_const(ty::Const { val, ty: a.ty })) -} - -impl<'tcx> Relate<'tcx> for &'tcx ty::List> { - fn relate>( - relation: &mut R, - a: &Self, - b: &Self, - ) -> RelateResult<'tcx, Self> { - if a.len() != b.len() { - return Err(TypeError::ExistentialMismatch(expected_found(relation, a, b))); - } - - let tcx = relation.tcx(); - let v = a.iter().zip(b.iter()).map(|(ep_a, ep_b)| { - use crate::ty::ExistentialPredicate::*; - match (*ep_a, *ep_b) { - (Trait(ref a), Trait(ref b)) => Ok(Trait(relation.relate(a, b)?)), - (Projection(ref a), Projection(ref b)) => Ok(Projection(relation.relate(a, b)?)), - (AutoTrait(ref a), AutoTrait(ref b)) if a == b => Ok(AutoTrait(*a)), - _ => Err(TypeError::ExistentialMismatch(expected_found(relation, a, b))), - } - }); - Ok(tcx.mk_existential_predicates(v)?) - } -} - -impl<'tcx> Relate<'tcx> for ty::ClosureSubsts<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::ClosureSubsts<'tcx>, - b: &ty::ClosureSubsts<'tcx>, - ) -> RelateResult<'tcx, ty::ClosureSubsts<'tcx>> { - let substs = relate_substs(relation, None, a.substs, b.substs)?; - Ok(ty::ClosureSubsts { substs }) - } -} - -impl<'tcx> Relate<'tcx> for ty::GeneratorSubsts<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::GeneratorSubsts<'tcx>, - b: &ty::GeneratorSubsts<'tcx>, - ) -> RelateResult<'tcx, ty::GeneratorSubsts<'tcx>> { - let substs = relate_substs(relation, None, a.substs, b.substs)?; - Ok(ty::GeneratorSubsts { substs }) - } -} - -impl<'tcx> Relate<'tcx> for SubstsRef<'tcx> { - fn relate>( - relation: &mut R, - a: &SubstsRef<'tcx>, - b: &SubstsRef<'tcx>, - ) -> RelateResult<'tcx, SubstsRef<'tcx>> { - relate_substs(relation, None, a, b) - } -} - -impl<'tcx> Relate<'tcx> for ty::Region<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::Region<'tcx>, - b: &ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - relation.regions(*a, *b) - } -} - -impl<'tcx> Relate<'tcx> for &'tcx ty::Const<'tcx> { - fn relate>( - relation: &mut R, - a: &&'tcx ty::Const<'tcx>, - b: &&'tcx ty::Const<'tcx>, - ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { - relation.consts(*a, *b) - } -} - -impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for ty::Binder { - fn relate>( - relation: &mut R, - a: &ty::Binder, - b: &ty::Binder, - ) -> RelateResult<'tcx, ty::Binder> { - relation.binders(a, b) - } -} - -impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for Rc { - fn relate>( - relation: &mut R, - a: &Rc, - b: &Rc, - ) -> RelateResult<'tcx, Rc> { - let a: &T = a; - let b: &T = b; - Ok(Rc::new(relation.relate(a, b)?)) - } -} - -impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for Box { - fn relate>( - relation: &mut R, - a: &Box, - b: &Box, - ) -> RelateResult<'tcx, Box> { - let a: &T = a; - let b: &T = b; - Ok(Box::new(relation.relate(a, b)?)) - } -} - -impl<'tcx> Relate<'tcx> for GenericArg<'tcx> { - fn relate>( - relation: &mut R, - a: &GenericArg<'tcx>, - b: &GenericArg<'tcx>, - ) -> RelateResult<'tcx, GenericArg<'tcx>> { - match (a.unpack(), b.unpack()) { - (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => { - Ok(relation.relate(&a_lt, &b_lt)?.into()) - } - (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => { - Ok(relation.relate(&a_ty, &b_ty)?.into()) - } - (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => { - Ok(relation.relate(&a_ct, &b_ct)?.into()) - } - (GenericArgKind::Lifetime(unpacked), x) => { - bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) - } - (GenericArgKind::Type(unpacked), x) => { - bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) - } - (GenericArgKind::Const(unpacked), x) => { - bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) - } - } - } -} - -impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::TraitPredicate<'tcx>, - b: &ty::TraitPredicate<'tcx>, - ) -> RelateResult<'tcx, ty::TraitPredicate<'tcx>> { - Ok(ty::TraitPredicate { trait_ref: relation.relate(&a.trait_ref, &b.trait_ref)? }) - } -} - -impl<'tcx> Relate<'tcx> for ty::ProjectionPredicate<'tcx> { - fn relate>( - relation: &mut R, - a: &ty::ProjectionPredicate<'tcx>, - b: &ty::ProjectionPredicate<'tcx>, - ) -> RelateResult<'tcx, ty::ProjectionPredicate<'tcx>> { - Ok(ty::ProjectionPredicate { - projection_ty: relation.relate(&a.projection_ty, &b.projection_ty)?, - ty: relation.relate(&a.ty, &b.ty)?, - }) - } -} - -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 - -pub fn expected_found(relation: &mut R, a: &T, b: &T) -> ExpectedFound -where - R: TypeRelation<'tcx>, - T: Clone, -{ - expected_found_bool(relation.a_is_expected(), a, b) -} - -pub fn expected_found_bool(a_is_expected: bool, a: &T, b: &T) -> ExpectedFound -where - T: Clone, -{ - let a = a.clone(); - let b = b.clone(); - if a_is_expected { - ExpectedFound { expected: a, found: b } - } else { - ExpectedFound { expected: b, found: a } - } -} diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs deleted file mode 100644 index e2fa03139110c..0000000000000 --- a/src/librustc/ty/structural_impls.rs +++ /dev/null @@ -1,1084 +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::ReClosureBound(ref vid) => write!(f, "ReClosureBound({:?})", vid), - - 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/util/bug.rs b/src/librustc/util/bug.rs deleted file mode 100644 index c12b2859f728e..0000000000000 --- a/src/librustc/util/bug.rs +++ /dev/null @@ -1,41 +0,0 @@ -// These functions are used by macro expansion for bug! and span_bug! - -use crate::ty::tls; -use rustc_span::{MultiSpan, Span}; -use std::fmt; - -#[cold] -#[inline(never)] -pub fn bug_fmt(file: &'static str, line: u32, args: fmt::Arguments<'_>) -> ! { - // this wrapper mostly exists so I don't have to write a fully - // qualified path of None:: inside the bug!() macro definition - opt_span_bug_fmt(file, line, None::, args); -} - -#[cold] -#[inline(never)] -pub fn span_bug_fmt>( - file: &'static str, - line: u32, - span: S, - args: fmt::Arguments<'_>, -) -> ! { - opt_span_bug_fmt(file, line, Some(span), args); -} - -fn opt_span_bug_fmt>( - file: &'static str, - line: u32, - span: Option, - args: fmt::Arguments<'_>, -) -> ! { - tls::with_opt(move |tcx| { - let msg = format!("{}:{}: {}", file, line, args); - match (tcx, span) { - (Some(tcx), Some(span)) => tcx.sess.diagnostic().span_bug(span, &msg), - (Some(tcx), None) => tcx.sess.diagnostic().bug(&msg), - (None, _) => panic!(msg), - } - }); - unreachable!(); -} 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..ba3adc4a135cb 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; @@ -132,9 +133,9 @@ impl Neg for Round { pub type ExpInt = i16; // \c ilogb error results. -pub const IEK_INF: ExpInt = ExpInt::max_value(); -pub const IEK_NAN: ExpInt = ExpInt::min_value(); -pub const IEK_ZERO: ExpInt = ExpInt::min_value() + 1; +pub const IEK_INF: ExpInt = ExpInt::MAX; +pub const IEK_NAN: ExpInt = ExpInt::MIN; +pub const IEK_ZERO: ExpInt = ExpInt::MIN + 1; #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct ParseError(pub &'static str); 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_apfloat/tests/ieee.rs b/src/librustc_apfloat/tests/ieee.rs index e5b06cf225d16..2d8bb7d1e8e03 100644 --- a/src/librustc_apfloat/tests/ieee.rs +++ b/src/librustc_apfloat/tests/ieee.rs @@ -2997,8 +2997,8 @@ fn scalbn() { assert!(smallest_f64.scalbn(2099).is_infinite()); // Test for integer overflows when adding to exponent. - assert!(smallest_f64.scalbn(-ExpInt::max_value()).is_pos_zero()); - assert!(largest_f64.scalbn(ExpInt::max_value()).is_infinite()); + assert!(smallest_f64.scalbn(-ExpInt::MAX).is_pos_zero()); + assert!(largest_f64.scalbn(ExpInt::MAX).is_infinite()); assert!(largest_denormal_f64.bitwise_eq(largest_denormal_f64.scalbn(0),)); assert!(neg_largest_denormal_f64.bitwise_eq(neg_largest_denormal_f64.scalbn(0),)); diff --git a/src/librustc_arena/Cargo.toml b/src/librustc_arena/Cargo.toml new file mode 100644 index 0000000000000..dfae956e2b6d5 --- /dev/null +++ b/src/librustc_arena/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_arena" +version = "0.0.0" +edition = "2018" + +[lib] +name = "rustc_arena" +path = "lib.rs" + +[dependencies] +rustc_data_structures = { path = "../librustc_data_structures" } +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_arena/lib.rs b/src/librustc_arena/lib.rs new file mode 100644 index 0000000000000..5cf4f97fb8863 --- /dev/null +++ b/src/librustc_arena/lib.rs @@ -0,0 +1,706 @@ +//! The arena, a fast but limited type of allocator. +//! +//! Arenas are a type of allocator that destroy the objects within, all at +//! once, once the arena itself is destroyed. They do not support deallocation +//! of individual objects while the arena itself is still alive. The benefit +//! of an arena is very fast allocation; just a pointer bump. +//! +//! This crate implements several kinds of arena. + +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + test(no_crate_inject, attr(deny(warnings))) +)] +#![feature(core_intrinsics)] +#![feature(dropck_eyepatch)] +#![feature(raw_vec_internals)] +#![cfg_attr(test, feature(test))] +#![allow(deprecated)] + +extern crate alloc; + +use rustc_data_structures::cold_path; +use smallvec::SmallVec; + +use std::alloc::Layout; +use std::cell::{Cell, RefCell}; +use std::cmp; +use std::intrinsics; +use std::marker::{PhantomData, Send}; +use std::mem; +use std::ptr; +use std::slice; + +use alloc::raw_vec::RawVec; + +/// An arena that can hold objects of only one type. +pub struct TypedArena { + /// A pointer to the next object to be allocated. + ptr: Cell<*mut T>, + + /// A pointer to the end of the allocated area. When this pointer is + /// reached, a new chunk is allocated. + end: Cell<*mut T>, + + /// A vector of arena chunks. + chunks: RefCell>>, + + /// Marker indicating that dropping the arena causes its owned + /// instances of `T` to be dropped. + _own: PhantomData, +} + +struct TypedArenaChunk { + /// The raw storage for the arena chunk. + storage: RawVec, + /// The number of valid entries in the chunk. + entries: usize, +} + +impl TypedArenaChunk { + #[inline] + unsafe fn new(capacity: usize) -> TypedArenaChunk { + TypedArenaChunk { storage: RawVec::with_capacity(capacity), entries: 0 } + } + + /// Destroys this arena chunk. + #[inline] + unsafe fn destroy(&mut self, len: usize) { + // The branch on needs_drop() is an -O1 performance optimization. + // Without the branch, dropping TypedArena takes linear time. + if mem::needs_drop::() { + let mut start = self.start(); + // Destroy all allocated objects. + for _ in 0..len { + ptr::drop_in_place(start); + start = start.offset(1); + } + } + } + + // Returns a pointer to the first allocated object. + #[inline] + fn start(&self) -> *mut T { + self.storage.ptr() + } + + // Returns a pointer to the end of the allocated space. + #[inline] + fn end(&self) -> *mut T { + unsafe { + if mem::size_of::() == 0 { + // A pointer as large as possible for zero-sized elements. + !0 as *mut T + } else { + self.start().add(self.storage.capacity()) + } + } + } +} + +// The arenas start with PAGE-sized chunks, and then each new chunk is twice as +// big as its predecessor, up until we reach HUGE_PAGE-sized chunks, whereupon +// we stop growing. This scales well, from arenas that are barely used up to +// arenas that are used for 100s of MiBs. Note also that the chosen sizes match +// the usual sizes of pages and huge pages on Linux. +const PAGE: usize = 4096; +const HUGE_PAGE: usize = 2 * 1024 * 1024; + +impl Default for TypedArena { + /// Creates a new `TypedArena`. + fn default() -> TypedArena { + TypedArena { + // We set both `ptr` and `end` to 0 so that the first call to + // alloc() will trigger a grow(). + ptr: Cell::new(ptr::null_mut()), + end: Cell::new(ptr::null_mut()), + chunks: RefCell::new(vec![]), + _own: PhantomData, + } + } +} + +impl TypedArena { + /// Allocates an object in the `TypedArena`, returning a reference to it. + #[inline] + pub fn alloc(&self, object: T) -> &mut T { + if self.ptr == self.end { + self.grow(1) + } + + unsafe { + if mem::size_of::() == 0 { + self.ptr.set(intrinsics::arith_offset(self.ptr.get() as *mut u8, 1) as *mut T); + let ptr = mem::align_of::() as *mut T; + // Don't drop the object. This `write` is equivalent to `forget`. + ptr::write(ptr, object); + &mut *ptr + } else { + let ptr = self.ptr.get(); + // Advance the pointer. + self.ptr.set(self.ptr.get().offset(1)); + // Write into uninitialized memory. + ptr::write(ptr, object); + &mut *ptr + } + } + } + + #[inline] + fn can_allocate(&self, additional: usize) -> bool { + let available_bytes = self.end.get() as usize - self.ptr.get() as usize; + let additional_bytes = additional.checked_mul(mem::size_of::()).unwrap(); + available_bytes >= additional_bytes + } + + /// Ensures there's enough space in the current chunk to fit `len` objects. + #[inline] + fn ensure_capacity(&self, additional: usize) { + if !self.can_allocate(additional) { + self.grow(additional); + debug_assert!(self.can_allocate(additional)); + } + } + + #[inline] + unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T { + assert!(mem::size_of::() != 0); + assert!(len != 0); + + self.ensure_capacity(len); + + let start_ptr = self.ptr.get(); + self.ptr.set(start_ptr.add(len)); + start_ptr + } + + /// Allocates a slice of objects that are copied into the `TypedArena`, returning a mutable + /// reference to it. Will panic if passed a zero-sized types. + /// + /// Panics: + /// + /// - Zero-sized types + /// - Zero-length slices + #[inline] + pub fn alloc_slice(&self, slice: &[T]) -> &mut [T] + where + T: Copy, + { + unsafe { + let len = slice.len(); + let start_ptr = self.alloc_raw_slice(len); + slice.as_ptr().copy_to_nonoverlapping(start_ptr, len); + slice::from_raw_parts_mut(start_ptr, len) + } + } + + #[inline] + pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { + assert!(mem::size_of::() != 0); + let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); + if vec.is_empty() { + return &mut []; + } + // Move the content to the arena by copying it and then forgetting + // the content of the SmallVec + unsafe { + let len = vec.len(); + let start_ptr = self.alloc_raw_slice(len); + vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); + vec.set_len(0); + slice::from_raw_parts_mut(start_ptr, len) + } + } + + /// Grows the arena. + #[inline(never)] + #[cold] + fn grow(&self, additional: usize) { + unsafe { + // We need the element size to convert chunk sizes (ranging from + // PAGE to HUGE_PAGE bytes) to element counts. + let elem_size = cmp::max(1, mem::size_of::()); + let mut chunks = self.chunks.borrow_mut(); + let mut new_cap; + if let Some(last_chunk) = chunks.last_mut() { + let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize; + last_chunk.entries = used_bytes / mem::size_of::(); + + // If the previous chunk's capacity is less than HUGE_PAGE + // bytes, then this chunk will be least double the previous + // chunk's size. + new_cap = last_chunk.storage.capacity(); + if new_cap < HUGE_PAGE / elem_size { + new_cap = new_cap.checked_mul(2).unwrap(); + } + } else { + new_cap = PAGE / elem_size; + } + // Also ensure that this chunk can fit `additional`. + new_cap = cmp::max(additional, new_cap); + + let chunk = TypedArenaChunk::::new(new_cap); + self.ptr.set(chunk.start()); + self.end.set(chunk.end()); + chunks.push(chunk); + } + } + + /// Clears the arena. Deallocates all but the longest chunk which may be reused. + pub fn clear(&mut self) { + unsafe { + // Clear the last chunk, which is partially filled. + let mut chunks_borrow = self.chunks.borrow_mut(); + if let Some(mut last_chunk) = chunks_borrow.last_mut() { + self.clear_last_chunk(&mut last_chunk); + let len = chunks_borrow.len(); + // If `T` is ZST, code below has no effect. + for mut chunk in chunks_borrow.drain(..len - 1) { + chunk.destroy(chunk.entries); + } + } + } + } + + // Drops the contents of the last chunk. The last chunk is partially empty, unlike all other + // chunks. + fn clear_last_chunk(&self, last_chunk: &mut TypedArenaChunk) { + // Determine how much was filled. + let start = last_chunk.start() as usize; + // We obtain the value of the pointer to the first uninitialized element. + let end = self.ptr.get() as usize; + // We then calculate the number of elements to be dropped in the last chunk, + // which is the filled area's length. + let diff = if mem::size_of::() == 0 { + // `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get + // the number of zero-sized values in the last and only chunk, just out of caution. + // Recall that `end` was incremented for each allocated value. + end - start + } else { + (end - start) / mem::size_of::() + }; + // Pass that to the `destroy` method. + unsafe { + last_chunk.destroy(diff); + } + // Reset the chunk. + self.ptr.set(last_chunk.start()); + } +} + +unsafe impl<#[may_dangle] T> Drop for TypedArena { + fn drop(&mut self) { + unsafe { + // Determine how much was filled. + let mut chunks_borrow = self.chunks.borrow_mut(); + if let Some(mut last_chunk) = chunks_borrow.pop() { + // Drop the contents of the last chunk. + self.clear_last_chunk(&mut last_chunk); + // The last chunk will be dropped. Destroy all other chunks. + for chunk in chunks_borrow.iter_mut() { + chunk.destroy(chunk.entries); + } + } + // RawVec handles deallocation of `last_chunk` and `self.chunks`. + } + } +} + +unsafe impl Send for TypedArena {} + +pub struct DroplessArena { + /// A pointer to the next object to be allocated. + ptr: Cell<*mut u8>, + + /// A pointer to the end of the allocated area. When this pointer is + /// reached, a new chunk is allocated. + end: Cell<*mut u8>, + + /// A vector of arena chunks. + chunks: RefCell>>, +} + +unsafe impl Send for DroplessArena {} + +impl Default for DroplessArena { + #[inline] + fn default() -> DroplessArena { + DroplessArena { + ptr: Cell::new(ptr::null_mut()), + end: Cell::new(ptr::null_mut()), + chunks: Default::default(), + } + } +} + +impl DroplessArena { + #[inline(never)] + #[cold] + fn grow(&self, additional: usize) { + unsafe { + let mut chunks = self.chunks.borrow_mut(); + let mut new_cap; + if let Some(last_chunk) = chunks.last_mut() { + // There is no need to update `last_chunk.entries` because that + // field isn't used by `DroplessArena`. + + // If the previous chunk's capacity is less than HUGE_PAGE + // bytes, then this chunk will be least double the previous + // chunk's size. + new_cap = last_chunk.storage.capacity(); + if new_cap < HUGE_PAGE { + new_cap = new_cap.checked_mul(2).unwrap(); + } + } else { + new_cap = PAGE; + } + // Also ensure that this chunk can fit `additional`. + new_cap = cmp::max(additional, new_cap); + + let chunk = TypedArenaChunk::::new(new_cap); + self.ptr.set(chunk.start()); + self.end.set(chunk.end()); + chunks.push(chunk); + } + } + + /// Allocates a byte slice with specified layout from the current memory + /// chunk. Returns `None` if there is no free space left to satisfy the + /// request. + #[inline] + fn alloc_raw_without_grow(&self, layout: Layout) -> Option<*mut u8> { + let ptr = self.ptr.get() as usize; + let end = self.end.get() as usize; + let align = layout.align(); + let bytes = layout.size(); + // The allocation request fits into the current chunk iff: + // + // let aligned = align_to(ptr, align); + // ptr <= aligned && aligned + bytes <= end + // + // Except that we work with fixed width integers and need to be careful + // about potential overflow in the calcuation. If the overflow does + // happen, then we definitely don't have enough free and need to grow + // the arena. + let aligned = ptr.checked_add(align - 1)? & !(align - 1); + let new_ptr = aligned.checked_add(bytes)?; + if new_ptr <= end { + self.ptr.set(new_ptr as *mut u8); + Some(aligned as *mut u8) + } else { + None + } + } + + #[inline] + pub fn alloc_raw(&self, layout: Layout) -> *mut u8 { + assert!(layout.size() != 0); + loop { + if let Some(a) = self.alloc_raw_without_grow(layout) { + break a; + } + // No free space left. Allocate a new chunk to satisfy the request. + // On failure the grow will panic or abort. + self.grow(layout.size()); + } + } + + #[inline] + pub fn alloc(&self, object: T) -> &mut T { + assert!(!mem::needs_drop::()); + + let mem = self.alloc_raw(Layout::for_value::(&object)) as *mut T; + + unsafe { + // Write into uninitialized memory. + ptr::write(mem, object); + &mut *mem + } + } + + /// Allocates a slice of objects that are copied into the `DroplessArena`, returning a mutable + /// reference to it. Will panic if passed a zero-sized type. + /// + /// Panics: + /// + /// - Zero-sized types + /// - Zero-length slices + #[inline] + pub fn alloc_slice(&self, slice: &[T]) -> &mut [T] + where + T: Copy, + { + assert!(!mem::needs_drop::()); + assert!(mem::size_of::() != 0); + assert!(!slice.is_empty()); + + let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T; + + unsafe { + mem.copy_from_nonoverlapping(slice.as_ptr(), slice.len()); + slice::from_raw_parts_mut(mem, slice.len()) + } + } + + #[inline] + unsafe fn write_from_iter>( + &self, + mut iter: I, + len: usize, + mem: *mut T, + ) -> &mut [T] { + let mut i = 0; + // Use a manual loop since LLVM manages to optimize it better for + // slice iterators + loop { + let value = iter.next(); + if i >= len || value.is_none() { + // We only return as many items as the iterator gave us, even + // though it was supposed to give us `len` + return slice::from_raw_parts_mut(mem, i); + } + ptr::write(mem.add(i), value.unwrap()); + i += 1; + } + } + + #[inline] + pub fn alloc_from_iter>(&self, iter: I) -> &mut [T] { + let iter = iter.into_iter(); + assert!(mem::size_of::() != 0); + assert!(!mem::needs_drop::()); + + let size_hint = iter.size_hint(); + + match size_hint { + (min, Some(max)) if min == max => { + // We know the exact number of elements the iterator will produce here + let len = min; + + if len == 0 { + return &mut []; + } + + let mem = self.alloc_raw(Layout::array::(len).unwrap()) as *mut T; + unsafe { self.write_from_iter(iter, len, mem) } + } + (_, _) => { + cold_path(move || -> &mut [T] { + let mut vec: SmallVec<[_; 8]> = iter.collect(); + if vec.is_empty() { + return &mut []; + } + // Move the content to the arena by copying it and then forgetting + // the content of the SmallVec + unsafe { + let len = vec.len(); + let start_ptr = + self.alloc_raw(Layout::for_value::<[T]>(vec.as_slice())) as *mut T; + vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); + vec.set_len(0); + slice::from_raw_parts_mut(start_ptr, len) + } + }) + } + } + } +} + +/// Calls the destructor for an object when dropped. +struct DropType { + drop_fn: unsafe fn(*mut u8), + obj: *mut u8, +} + +unsafe fn drop_for_type(to_drop: *mut u8) { + std::ptr::drop_in_place(to_drop as *mut T) +} + +impl Drop for DropType { + fn drop(&mut self) { + unsafe { (self.drop_fn)(self.obj) } + } +} + +/// An arena which can be used to allocate any type. +/// Allocating in this arena is unsafe since the type system +/// doesn't know which types it contains. In order to +/// allocate safely, you must store a PhantomData +/// alongside this arena for each type T you allocate. +#[derive(Default)] +pub struct DropArena { + /// A list of destructors to run when the arena drops. + /// Ordered so `destructors` gets dropped before the arena + /// since its destructor can reference memory in the arena. + destructors: RefCell>, + arena: DroplessArena, +} + +impl DropArena { + #[inline] + pub unsafe fn alloc(&self, object: T) -> &mut T { + let mem = self.arena.alloc_raw(Layout::new::()) as *mut T; + // Write into uninitialized memory. + ptr::write(mem, object); + let result = &mut *mem; + // Record the destructor after doing the allocation as that may panic + // and would cause `object`'s destuctor to run twice if it was recorded before + self.destructors + .borrow_mut() + .push(DropType { drop_fn: drop_for_type::, obj: result as *mut T as *mut u8 }); + result + } + + #[inline] + pub unsafe fn alloc_from_iter>(&self, iter: I) -> &mut [T] { + let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); + if vec.is_empty() { + return &mut []; + } + let len = vec.len(); + + let start_ptr = self.arena.alloc_raw(Layout::array::(len).unwrap()) as *mut T; + + let mut destructors = self.destructors.borrow_mut(); + // Reserve space for the destructors so we can't panic while adding them + destructors.reserve(len); + + // Move the content to the arena by copying it and then forgetting + // the content of the SmallVec + vec.as_ptr().copy_to_nonoverlapping(start_ptr, len); + mem::forget(vec.drain(..)); + + // Record the destructors after doing the allocation as that may panic + // and would cause `object`'s destuctor to run twice if it was recorded before + for i in 0..len { + destructors.push(DropType { + drop_fn: drop_for_type::, + obj: start_ptr.offset(i as isize) as *mut u8, + }); + } + + slice::from_raw_parts_mut(start_ptr, len) + } +} + +#[macro_export] +macro_rules! arena_for_type { + ([][$ty:ty]) => { + $crate::TypedArena<$ty> + }; + ([few $(, $attrs:ident)*][$ty:ty]) => { + ::std::marker::PhantomData<$ty> + }; + ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { + $crate::arena_for_type!([$($attrs),*]$args) + }; +} + +#[macro_export] +macro_rules! which_arena_for_type { + ([][$arena:expr]) => { + ::std::option::Option::Some($arena) + }; + ([few$(, $attrs:ident)*][$arena:expr]) => { + ::std::option::Option::None + }; + ([$ignore:ident$(, $attrs:ident)*]$args:tt) => { + $crate::which_arena_for_type!([$($attrs),*]$args) + }; +} + +#[macro_export] +macro_rules! declare_arena { + // This macro has to take the same input as + // `impl_arena_allocatable_decoders` which requires a second version of + // each type. We ignore that type until we can fix + // `impl_arena_allocatable_decoders`. + ([], [$($a:tt $name:ident: $ty:ty, $_gen_ty:ty;)*], $tcx:lifetime) => { + #[derive(Default)] + pub struct Arena<$tcx> { + pub dropless: $crate::DroplessArena, + drop: $crate::DropArena, + $($name: $crate::arena_for_type!($a[$ty]),)* + } + + pub trait ArenaAllocatable<'tcx, T = Self>: Sized { + fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self; + fn allocate_from_iter<'a>( + arena: &'a Arena<'tcx>, + iter: impl ::std::iter::IntoIterator, + ) -> &'a mut [Self]; + } + + impl<'tcx, T: Copy> ArenaAllocatable<'tcx, ()> for T { + #[inline] + fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self { + arena.dropless.alloc(self) + } + #[inline] + fn allocate_from_iter<'a>( + arena: &'a Arena<'tcx>, + iter: impl ::std::iter::IntoIterator, + ) -> &'a mut [Self] { + arena.dropless.alloc_from_iter(iter) + } + + } + $( + impl<$tcx> ArenaAllocatable<$tcx, $ty> for $ty { + #[inline] + fn allocate_on<'a>(self, arena: &'a Arena<$tcx>) -> &'a mut Self { + if !::std::mem::needs_drop::() { + return arena.dropless.alloc(self); + } + match $crate::which_arena_for_type!($a[&arena.$name]) { + ::std::option::Option::<&$crate::TypedArena>::Some(ty_arena) => { + ty_arena.alloc(self) + } + ::std::option::Option::None => unsafe { arena.drop.alloc(self) }, + } + } + + #[inline] + fn allocate_from_iter<'a>( + arena: &'a Arena<$tcx>, + iter: impl ::std::iter::IntoIterator, + ) -> &'a mut [Self] { + if !::std::mem::needs_drop::() { + return arena.dropless.alloc_from_iter(iter); + } + match $crate::which_arena_for_type!($a[&arena.$name]) { + ::std::option::Option::<&$crate::TypedArena>::Some(ty_arena) => { + ty_arena.alloc_from_iter(iter) + } + ::std::option::Option::None => unsafe { arena.drop.alloc_from_iter(iter) }, + } + } + } + )* + + impl<'tcx> Arena<'tcx> { + #[inline] + pub fn alloc, U>(&self, value: T) -> &mut T { + value.allocate_on(self) + } + + #[inline] + pub fn alloc_slice(&self, value: &[T]) -> &mut [T] { + if value.is_empty() { + return &mut []; + } + self.dropless.alloc_slice(value) + } + + pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, U>, U>( + &'a self, + iter: impl ::std::iter::IntoIterator, + ) -> &'a mut [T] { + T::allocate_from_iter(self, iter) + } + } + } +} + +#[cfg(test)] +mod tests; diff --git a/src/libarena/tests.rs b/src/librustc_arena/tests.rs similarity index 100% rename from src/libarena/tests.rs rename to src/librustc_arena/tests.rs diff --git a/src/librustc_ast/Cargo.toml b/src/librustc_ast/Cargo.toml index 867bcf7923264..6bd65fd5f96c7 100644 --- a/src/librustc_ast/Cargo.toml +++ b/src/librustc_ast/Cargo.toml @@ -10,7 +10,7 @@ path = "lib.rs" doctest = false [dependencies] -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } log = "0.4" scoped-tls = "1.0" rustc_span = { path = "../librustc_span" } @@ -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 08ce0cc2c56be..e98d709539d79 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -5,7 +5,7 @@ //! additional metadata), and [`ItemKind`] (which represents a concrete type and contains //! information specific to the type of the item). //! -//! Other module items that worth mentioning: +//! Other module items worth mentioning: //! - [`Ty`] and [`TyKind`]: A parsed Rust type. //! - [`Expr`] and [`ExprKind`]: A parsed Rust expression. //! - [`Pat`] and [`PatKind`]: A parsed Rust pattern. Patterns are often dual to expressions. @@ -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}; @@ -31,11 +29,10 @@ use crate::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::thin_vec::ThinVec; -use rustc_index::vec::Idx; 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; @@ -215,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 { @@ -249,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(|input| GenericArg::Type(input)).collect(), - constraints: vec![], - } + let args = self + .inputs + .iter() + .cloned() + .map(|input| AngleBracketedArg::Arg(GenericArg::Type(input))) + .collect(); + AngleBracketedArgs { span: self.span, args } } } @@ -292,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, } } } @@ -356,7 +362,11 @@ impl Default for Generics { fn default() -> Generics { Generics { params: Vec::new(), - where_clause: WhereClause { predicates: Vec::new(), span: DUMMY_SP }, + where_clause: WhereClause { + has_where_token: false, + predicates: Vec::new(), + span: DUMMY_SP, + }, span: DUMMY_SP, } } @@ -365,6 +375,11 @@ impl Default for Generics { /// A where-clause in a definition. #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct WhereClause { + /// `true` if we ate a `where` token: this can happen + /// if we parsed no predicates (e.g. `struct Foo where {} + /// This allows us to accurately pretty-print + /// in `nt_to_tokenstream` + pub has_where_token: bool, pub predicates: Vec, pub span: Span, } @@ -685,19 +700,8 @@ pub enum PatKind { MacCall(MacCall), } -#[derive( - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - Debug, - Copy, - HashStable_Generic -)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(HashStable_Generic)] pub enum Mutability { Mut, Not, @@ -1011,11 +1015,12 @@ pub struct Expr { pub kind: ExprKind, pub span: Span, pub attrs: AttrVec, + pub tokens: Option, } // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr, 96); +rustc_data_structures::static_assert_size!(Expr, 104); impl Expr { /// Returns `true` if this expression would be valid somewhere that expects a value; @@ -1126,7 +1131,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, @@ -1169,7 +1174,9 @@ pub enum ExprKind { /// and the remaining elements are the rest of the arguments. /// Thus, `x.foo::(a, b, c, d)` is represented as /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`. - MethodCall(PathSegment, Vec>), + /// This `Span` is the span of the function, without the dot and receiver + /// (e.g. `foo(a, b)` in `x.foo(a, b)` + MethodCall(PathSegment, Vec>, Span), /// A tuple (e.g., `(a, b, c, d)`). Tup(Vec>), /// A binary operation (e.g., `a + b`, `a * b`). @@ -1257,6 +1264,8 @@ pub enum ExprKind { /// Output of the `asm!()` macro. InlineAsm(P), + /// Output of the `llvm_asm!()` macro. + LlvmInlineAsm(P), /// A macro invocation; pre-expansion. MacCall(MacCall), @@ -1322,19 +1331,8 @@ pub enum CaptureBy { /// The movability of a generator / closure literal: /// whether a generator contains self-references, causing it to be `!Unpin`. -#[derive( - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - Debug, - Copy, - HashStable_Generic -)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug, Copy)] +#[derive(HashStable_Generic)] pub enum Movability { /// May contain self-references, `!Unpin`. Static, @@ -1450,7 +1448,7 @@ impl MacDelimiter { pub struct MacroDef { pub body: P, /// `true` if macro was defined with `macro_rules`. - pub legacy: bool, + pub macro_rules: bool, } #[derive(Clone, RustcEncodable, RustcDecodable, Debug, Copy, Hash, Eq, PartialEq)] @@ -1583,8 +1581,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(..) @@ -1615,19 +1612,8 @@ pub struct FnSig { pub decl: P, } -#[derive( - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - HashStable_Generic, - RustcEncodable, - RustcDecodable, - Debug -)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(HashStable_Generic)] pub enum FloatTy { F32, F64, @@ -1648,7 +1634,7 @@ impl FloatTy { } } - pub fn bit_width(self) -> usize { + pub fn bit_width(self) -> u64 { match self { FloatTy::F32 => 32, FloatTy::F64 => 64, @@ -1656,19 +1642,8 @@ impl FloatTy { } } -#[derive( - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - HashStable_Generic, - RustcEncodable, - RustcDecodable, - Debug -)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(HashStable_Generic)] pub enum IntTy { Isize, I8, @@ -1708,7 +1683,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, @@ -1732,19 +1707,8 @@ impl IntTy { } } -#[derive( - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - HashStable_Generic, - RustcEncodable, - RustcDecodable, - Copy, - Debug -)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Copy, Debug)] +#[derive(HashStable_Generic)] pub enum UintTy { Usize, U8, @@ -1781,7 +1745,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, @@ -1896,15 +1860,6 @@ impl TyKind { pub fn is_unit(&self) -> bool { if let TyKind::Tup(ref tys) = *self { tys.is_empty() } else { false } } - - /// HACK(type_alias_impl_trait, Centril): A temporary crutch used - /// in lowering to avoid making larger changes there and beyond. - pub fn opaque_top_hack(&self) -> Option<&GenericBounds> { - match self { - Self::ImplTrait(_, bounds) => Some(bounds), - _ => None, - } - } } /// Syntax used to declare a trait object. @@ -1914,39 +1869,146 @@ 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("}}")?, + _ => c.fmt(f)?, + } + } + 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, + pub line_spans: Vec, +} + /// 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. @@ -2153,7 +2215,7 @@ impl FnRetTy { /// Module declaration. /// /// E.g., `mod foo;` or `mod foo { .. }`. -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default)] pub struct Mod { /// A span from the first token past `{` to the last token until `}`. /// For `mod foo;`, the inner span ranges from the first token @@ -2251,15 +2313,10 @@ pub enum AttrStyle { Inner, } -#[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, Copy)] -pub struct AttrId(pub usize); - -impl Idx for AttrId { - fn new(idx: usize) -> Self { - AttrId(idx) - } - fn index(self) -> usize { - self.0 +rustc_index::newtype_index! { + pub struct AttrId { + ENCODABLE = custom + DEBUG_FORMAT = "AttrId({})" } } @@ -2504,7 +2561,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 249311851fb1b..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 { @@ -366,14 +366,14 @@ pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem { } crate fn mk_attr_id() -> AttrId { - use std::sync::atomic::AtomicUsize; + use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; - static NEXT_ATTR_ID: AtomicUsize = AtomicUsize::new(0); + static NEXT_ATTR_ID: AtomicU32 = AtomicU32::new(0); let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst); - assert!(id != ::std::usize::MAX); - AttrId(id) + assert!(id != u32::MAX); + AttrId::from_u32(id) } pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute { @@ -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/crate_disambiguator.rs b/src/librustc_ast/crate_disambiguator.rs new file mode 100644 index 0000000000000..95d4c09dac311 --- /dev/null +++ b/src/librustc_ast/crate_disambiguator.rs @@ -0,0 +1,35 @@ +// This is here because `rustc_session` wants to refer to it, +// and so does `rustc_hir`, but `rustc_hir` shouldn't refer to `rustc_session`. + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::{base_n, impl_stable_hash_via_hash}; + +use std::fmt; + +/// Hash value constructed out of all the `-C metadata` arguments passed to the +/// compiler. Together with the crate-name forms a unique global identifier for +/// the crate. +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, RustcEncodable, RustcDecodable)] +pub struct CrateDisambiguator(Fingerprint); + +impl CrateDisambiguator { + pub fn to_fingerprint(self) -> Fingerprint { + self.0 + } +} + +impl fmt::Display for CrateDisambiguator { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let (a, b) = self.0.as_value(); + let as_u128 = a as u128 | ((b as u128) << 64); + f.write_str(&base_n::encode(as_u128, base_n::CASE_INSENSITIVE)) + } +} + +impl From for CrateDisambiguator { + fn from(fingerprint: Fingerprint) -> CrateDisambiguator { + CrateDisambiguator(fingerprint) + } +} + +impl_stable_hash_via_hash!(CrateDisambiguator); 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 adb96356aae7d..cb3118cba23dd 100644 --- a/src/librustc_ast/lib.rs +++ b/src/librustc_ast/lib.rs @@ -7,15 +7,22 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] #![feature(bool_to_option)] #![feature(box_syntax)] +#![feature(const_if_match)] #![feature(const_fn)] // For the `transmute` in `P::new` +#![feature(const_panic)] #![feature(const_transmute)] #![feature(crate_visibility_modifier)] #![feature(label_break_value)] #![feature(nll)] +#![feature(or_patterns)] #![feature(try_trait)] #![feature(unicode_internals)] #![recursion_limit = "256"] +// FIXME(#56935): Work around ICEs during cross-compilation. +#[allow(unused)] +extern crate rustc_macros; + #[macro_export] macro_rules! unwrap_or { ($opt:expr, $default:expr) => { @@ -31,13 +38,13 @@ pub mod util { pub mod comments; pub mod lev_distance; pub mod literal; - pub mod map_in_place; pub mod parser; } pub mod ast; pub mod attr; pub use attr::{with_default_globals, with_globals, GLOBALS}; +pub mod crate_disambiguator; pub mod entry; pub mod expand; pub mod mut_visit; @@ -51,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 a1a5b9debc50d..2ffef9d48c181 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); } @@ -591,7 +594,7 @@ pub fn noop_visit_mac(mac: &mut MacCall, vis: &mut T) { } pub fn noop_visit_macro_def(macro_def: &mut MacroDef, vis: &mut T) { - let MacroDef { body, legacy: _ } = macro_def; + let MacroDef { body, macro_rules: _ } = macro_def; visit_mac_args(body, vis); } @@ -783,7 +786,7 @@ pub fn noop_visit_generics(generics: &mut Generics, vis: &mut T) } pub fn noop_visit_where_clause(wc: &mut WhereClause, vis: &mut T) { - let WhereClause { predicates, span } = wc; + let WhereClause { has_where_token: _, predicates, span } = wc; visit_vec(predicates, |predicate| vis.visit_where_predicate(predicate)); vis.visit_span(span); } @@ -1092,7 +1095,10 @@ pub fn noop_visit_anon_const(AnonConst { id, value }: &mut AnonCo vis.visit_expr(value); } -pub fn noop_visit_expr(Expr { kind, id, span, attrs }: &mut Expr, vis: &mut T) { +pub fn noop_visit_expr( + Expr { kind, id, span, attrs, tokens: _ }: &mut Expr, + vis: &mut T, +) { match kind { ExprKind::Box(expr) => vis.visit_expr(expr), ExprKind::Array(exprs) => visit_exprs(exprs, vis), @@ -1105,11 +1111,12 @@ pub fn noop_visit_expr(Expr { kind, id, span, attrs }: &mut Expr, vis.visit_expr(f); visit_exprs(args, vis); } - ExprKind::MethodCall(PathSegment { ident, id, args }, exprs) => { + ExprKind::MethodCall(PathSegment { ident, id, args }, exprs, span) => { vis.visit_ident(ident); vis.visit_id(id); visit_opt(args, |args| vis.visit_generic_args(args)); visit_exprs(exprs, vis); + vis.visit_span(span); } ExprKind::Binary(_binop, lhs, rhs) => { vis.visit_expr(lhs); @@ -1203,7 +1210,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 +1242,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/node_id.rs b/src/librustc_ast/node_id.rs index 58d2334a7b148..cd562c48e9115 100644 --- a/src/librustc_ast/node_id.rs +++ b/src/librustc_ast/node_id.rs @@ -12,7 +12,7 @@ rustc_index::newtype_index! { rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId); /// `NodeId` used to represent the root of the crate. -pub const CRATE_NODE_ID: NodeId = NodeId::from_u32_const(0); +pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0); /// When parsing and doing expansions, we initially give all AST nodes this AST /// node value. Then later, in the renumber pass, we renumber them to have diff --git a/src/librustc_ast/token.rs b/src/librustc_ast/token.rs index 3fc6444168e24..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) } @@ -424,7 +424,7 @@ impl Token { NtExpr(..) | NtBlock(..) | NtLiteral(..) => true, _ => false, }, - _ => self.can_begin_literal_or_bool(), + _ => self.can_begin_literal_maybe_minus(), } } @@ -448,13 +448,22 @@ impl Token { /// Returns `true` if the token is any literal, a minus (which can prefix a literal, /// for example a '-42', or one of the boolean idents). /// - /// Keep this in sync with `Lit::from_token`. - pub fn can_begin_literal_or_bool(&self) -> bool { + /// In other words, would this token be a valid start of `parse_literal_maybe_minus`? + /// + /// Keep this in sync with and `Lit::from_token`, excluding unary negation. + pub fn can_begin_literal_maybe_minus(&self) -> bool { match self.uninterpolate().kind { Literal(..) | BinOp(Minus) => true, Ident(name, false) if name.is_bool_lit() => true, Interpolated(ref nt) => match &**nt { - NtExpr(e) | NtLiteral(e) => matches!(e.kind, ast::ExprKind::Lit(_)), + NtLiteral(_) => true, + NtExpr(e) => match &e.kind { + ast::ExprKind::Lit(_) => true, + ast::ExprKind::Unary(ast::UnOp::Neg, e) => { + matches!(&e.kind, ast::ExprKind::Lit(_)) + } + _ => false, + }, _ => false, }, _ => false, @@ -479,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, } } @@ -568,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`. @@ -598,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, @@ -737,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/tokenstream.rs b/src/librustc_ast/tokenstream.rs index 916a5ff6f46f4..15ae12ebf10e3 100644 --- a/src/librustc_ast/tokenstream.rs +++ b/src/librustc_ast/tokenstream.rs @@ -21,6 +21,8 @@ use rustc_macros::HashStable_Generic; use rustc_span::{Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; +use log::debug; + use std::{iter, mem}; /// When the main rust parser encounters a syntax-extension invocation, it @@ -338,8 +340,71 @@ impl TokenStream { true } - let mut t1 = self.trees().filter(semantic_tree); - let mut t2 = other.trees().filter(semantic_tree); + // When comparing two `TokenStream`s, we ignore the `IsJoint` information. + // + // However, `rustc_parse::lexer::tokentrees::TokenStreamBuilder` will + // use `Token.glue` on adjacent tokens with the proper `IsJoint`. + // Since we are ignoreing `IsJoint`, a 'glued' token (e.g. `BinOp(Shr)`) + // and its 'split'/'unglued' compoenents (e.g. `Gt, Gt`) are equivalent + // when determining if two `TokenStream`s are 'probably equal'. + // + // Therefore, we use `break_two_token_op` to convert all tokens + // to the 'unglued' form (if it exists). This ensures that two + // `TokenStream`s which differ only in how their tokens are glued + // will be considered 'probably equal', which allows us to keep spans. + // + // This is important when the original `TokenStream` contained + // extra spaces (e.g. `f :: < Vec < _ > > ( ) ;'). These extra spaces + // will be omitted when we pretty-print, which can cause the original + // and reparsed `TokenStream`s to differ in the assignment of `IsJoint`, + // leading to some tokens being 'glued' together in one stream but not + // the other. See #68489 for more details. + fn break_tokens(tree: TokenTree) -> impl Iterator { + // In almost all cases, we should have either zero or one levels + // of 'unglueing'. However, in some unusual cases, we may need + // to iterate breaking tokens mutliple times. For example: + // '[BinOpEq(Shr)] => [Gt, Ge] -> [Gt, Gt, Eq]' + let mut token_trees: SmallVec<[_; 2]>; + if let TokenTree::Token(token) = &tree { + let mut out = SmallVec::<[_; 2]>::new(); + out.push(token.clone()); + // Iterate to fixpoint: + // * We start off with 'out' containing our initial token, and `temp` empty + // * If we are able to break any tokens in `out`, then `out` will have + // at least one more element than 'temp', so we will try to break tokens + // again. + // * If we cannot break any tokens in 'out', we are done + loop { + let mut temp = SmallVec::<[_; 2]>::new(); + let mut changed = false; + + for token in out.into_iter() { + if let Some((first, second)) = token.kind.break_two_token_op() { + temp.push(Token::new(first, DUMMY_SP)); + temp.push(Token::new(second, DUMMY_SP)); + changed = true; + } else { + temp.push(token); + } + } + out = temp; + if !changed { + break; + } + } + token_trees = out.into_iter().map(TokenTree::Token).collect(); + if token_trees.len() != 1 { + debug!("break_tokens: broke {:?} to {:?}", tree, token_trees); + } + } else { + token_trees = SmallVec::new(); + token_trees.push(tree); + } + token_trees.into_iter() + } + + let mut t1 = self.trees().filter(semantic_tree).flat_map(break_tokens); + let mut t2 = other.trees().filter(semantic_tree).flat_map(break_tokens); for (t1, t2) in t1.by_ref().zip(t2.by_ref()) { if !t1.probably_equal_for_proc_macro(&t2) { return false; 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 d1757394f3a1d..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(); @@ -189,7 +198,7 @@ impl Lit { /// Converts arbitrary token into an AST literal. /// - /// Keep this in sync with `Token::can_begin_literal_or_bool`. + /// Keep this in sync with `Token::can_begin_literal_or_bool` excluding unary negation. pub fn from_token(token: &Token) -> Result { let lit = match token.uninterpolate().kind { token::Ident(name, false) if name.is_bool_lit() => { diff --git a/src/librustc_ast/util/parser.rs b/src/librustc_ast/util/parser.rs index b98cc96b3c647..d8b44a22f2c92 100644 --- a/src/librustc_ast/util/parser.rs +++ b/src/librustc_ast/util/parser.rs @@ -394,7 +394,7 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { contains_exterior_struct_lit(&x) } - ast::ExprKind::MethodCall(.., ref exprs) => { + ast::ExprKind::MethodCall(.., ref exprs, _) => { // X { y: 1 }.bar(...) contains_exterior_struct_lit(&exprs[0]) } diff --git a/src/librustc_ast/visit.rs b/src/librustc_ast/visit.rs index 39028b7583c63..ccab46703dffe 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); @@ -721,7 +726,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(callee_expression); walk_list!(visitor, visit_expr, arguments); } - ExprKind::MethodCall(ref segment, ref arguments) => { + ExprKind::MethodCall(ref segment, ref arguments, _span) => { visitor.visit_path_segment(expression.span, segment); walk_list!(visitor, visit_expr, arguments); } @@ -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/Cargo.toml b/src/librustc_ast_lowering/Cargo.toml index 23dc80facae1d..d71eb4fcd5c27 100644 --- a/src/librustc_ast_lowering/Cargo.toml +++ b/src/librustc_ast_lowering/Cargo.toml @@ -10,8 +10,8 @@ path = "lib.rs" doctest = false [dependencies] +rustc_arena = { path = "../librustc_arena" } log = { version = "0.4", features = ["release_max_level_info", "std"] } -rustc = { path = "../librustc" } rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_hir = { path = "../librustc_hir" } rustc_target = { path = "../librustc_target" } diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs index a4cbae5196635..b7894eb145b0a 100644 --- a/src/librustc_ast_lowering/expr.rs +++ b/src/librustc_ast_lowering/expr.rs @@ -1,15 +1,19 @@ use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; -use rustc::bug; 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>] { @@ -21,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, span) => { + let hir_seg = self.arena.alloc(self.lower_path_segment( + e.span, + seg, + ParamMode::Optional, + 0, + ParenthesizedGenericArgs::Err, + ImplTraitContext::disallowed(), + None, + )); + let args = self.lower_exprs(args); + hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args, span) + } + ExprKind::Binary(binop, ref lhs, ref rhs) => { + let binop = self.lower_binop(binop); + let lhs = self.lower_expr(lhs); + let rhs = self.lower_expr(rhs); + hir::ExprKind::Binary(binop, lhs, rhs) + } + ExprKind::Unary(op, ref ohs) => { + let op = self.lower_unop(op); + let ohs = self.lower_expr(ohs); + hir::ExprKind::Unary(op, ohs) + } + ExprKind::Lit(ref l) => hir::ExprKind::Lit(respan(l.span, l.kind.clone())), + ExprKind::Cast(ref expr, ref ty) => { + let expr = self.lower_expr(expr); + let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + hir::ExprKind::Cast(expr, ty) + } + ExprKind::Type(ref expr, ref ty) => { + let expr = self.lower_expr(expr); + let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); + hir::ExprKind::Type(expr, ty) + } + ExprKind::AddrOf(k, m, ref ohs) => { + let ohs = self.lower_expr(ohs); + hir::ExprKind::AddrOf(k, m, ohs) + } + ExprKind::Let(ref pat, ref scrutinee) => { + self.lower_expr_let(e.span, pat, scrutinee) + } + ExprKind::If(ref cond, ref then, ref else_opt) => { + self.lower_expr_if(e.span, cond, then, else_opt.as_deref()) + } + ExprKind::While(ref cond, ref body, opt_label) => self + .with_loop_scope(e.id, |this| { + this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label) + }), + ExprKind::Loop(ref body, opt_label) => self.with_loop_scope(e.id, |this| { + hir::ExprKind::Loop( + this.lower_block(body, false), + opt_label, + hir::LoopSource::Loop, ) - } 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 { @@ -398,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) @@ -470,6 +485,15 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + /// Lower an `async` construct to a generator that is then wrapped so it implements `Future`. + /// + /// This results in: + /// + /// ```text + /// std::future::from_generator(static move? |_task_context| -> { + /// + /// }) + /// ``` pub(super) fn make_async_expr( &mut self, capture_clause: CaptureBy, @@ -480,17 +504,42 @@ impl<'hir> LoweringContext<'_, 'hir> { body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, ) -> hir::ExprKind<'hir> { let output = match ret_ty { - Some(ty) => FnRetTy::Ty(ty), - None => FnRetTy::Default(span), + Some(ty) => hir::FnRetTy::Return(self.lower_ty(&ty, ImplTraitContext::disallowed())), + None => hir::FnRetTy::DefaultReturn(span), }; - let ast_decl = FnDecl { inputs: vec![], output }; - let decl = self.lower_fn_decl(&ast_decl, None, /* impl trait allowed */ false, None); - let body_id = self.lower_fn_body(&ast_decl, |this| { + + // Resume argument type. We let the compiler infer this to simplify the lowering. It is + // fully constrained by `future::from_generator`. + let input_ty = hir::Ty { hir_id: self.next_id(), kind: hir::TyKind::Infer, span }; + + // The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`. + let decl = self.arena.alloc(hir::FnDecl { + inputs: arena_vec![self; input_ty], + output, + c_variadic: false, + implicit_self: hir::ImplicitSelfKind::None, + }); + + // Lower the argument pattern/ident. The ident is used again in the `.await` lowering. + let (pat, task_context_hid) = self.pat_ident_binding_mode( + span, + Ident::with_dummy_span(sym::_task_context), + hir::BindingAnnotation::Mutable, + ); + let param = hir::Param { attrs: &[], hir_id: self.next_id(), pat, span }; + let params = arena_vec![self; param]; + + let body_id = self.lower_body(move |this| { this.generator_kind = Some(hir::GeneratorKind::Async(async_gen_kind)); - body(this) + + let old_ctx = this.task_context; + this.task_context = Some(task_context_hid); + let res = body(this); + this.task_context = old_ctx; + (params, res) }); - // `static || -> { body }`: + // `static |_task_context| -> { body }`: let generator_kind = hir::ExprKind::Closure( capture_clause, decl, @@ -523,13 +572,14 @@ impl<'hir> LoweringContext<'_, 'hir> { /// ```rust /// match { /// mut pinned => loop { - /// match ::std::future::poll_with_tls_context(unsafe { - /// <::std::pin::Pin>::new_unchecked(&mut pinned) - /// }) { + /// match unsafe { ::std::future::Future::poll( + /// <::std::pin::Pin>::new_unchecked(&mut pinned), + /// ::std::future::get_context(task_context), + /// ) } { /// ::std::task::Poll::Ready(result) => break result, /// ::std::task::Poll::Pending => {} /// } - /// yield (); + /// task_context = yield (); /// } /// } /// ``` @@ -556,17 +606,29 @@ 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) = self.pat_ident_binding_mode(span, pinned_ident, hir::BindingAnnotation::Mutable); - // ::std::future::poll_with_tls_context(unsafe { - // ::std::pin::Pin::new_unchecked(&mut pinned) - // })` + let task_context_ident = Ident::with_dummy_span(sym::_task_context); + + // unsafe { + // ::std::future::Future::poll( + // ::std::pin::Pin::new_unchecked(&mut pinned), + // ::std::future::get_context(task_context), + // ) + // } let poll_expr = { let pinned = self.expr_ident(span, pinned_ident, pinned_pat_hid); let ref_mut_pinned = self.expr_mut_addr_of(span, pinned); + let task_context = if let Some(task_context_hid) = self.task_context { + self.expr_ident_mut(span, task_context_ident, task_context_hid) + } else { + // Use of `await` outside of an async context, we cannot use `task_context` here. + self.expr_err(span) + }; let pin_ty_id = self.next_id(); let new_unchecked_expr_kind = self.expr_call_std_assoc_fn( pin_ty_id, @@ -575,14 +637,18 @@ impl<'hir> LoweringContext<'_, 'hir> { "new_unchecked", arena_vec![self; ref_mut_pinned], ); - let new_unchecked = - self.arena.alloc(self.expr(span, new_unchecked_expr_kind, ThinVec::new())); - let unsafe_expr = self.expr_unsafe(new_unchecked); - self.expr_call_std_path( + let new_unchecked = self.expr(span, new_unchecked_expr_kind, ThinVec::new()); + let get_context = self.expr_call_std_path_mut( gen_future_span, - &[sym::future, sym::poll_with_tls_context], - arena_vec![self; unsafe_expr], - ) + &[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)) }; // `::std::task::Poll::Ready(result) => break result` @@ -622,14 +688,26 @@ impl<'hir> LoweringContext<'_, 'hir> { self.stmt_expr(span, match_expr) }; + // task_context = yield (); let yield_stmt = { 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(), ); - self.stmt_expr(span, yield_expr) + let yield_expr = self.arena.alloc(yield_expr); + + if let Some(task_context_hid) = self.task_context { + let lhs = self.expr_ident(span, task_context_ident, task_context_hid); + let assign = + self.expr(span, hir::ExprKind::Assign(lhs, yield_expr, span), AttrVec::new()); + self.stmt_expr(span, assign) + } else { + // Use of `await` outside of an async context. Return `yield_expr` so that we can + // proceed with type checking. + self.stmt(span, hir::StmtKind::Semi(yield_expr)) + } }; let loop_block = self.block_all(span, arena_vec![self; inner_match_stmt, yield_stmt], None); @@ -648,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) } @@ -700,7 +777,7 @@ impl<'hir> LoweringContext<'_, 'hir> { Some(movability) } Some(hir::GeneratorKind::Async(_)) => { - bug!("non-`async` closure body turned `async` during lowering"); + panic!("non-`async` closure body turned `async` during lowering"); } None => { if movability == Movability::Static { @@ -896,13 +973,310 @@ 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> { + if self.sess.asm_arch.is_none() { + struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit(); + } + if asm.options.contains(InlineAsmOptions::ATT_SYNTAX) + && !matches!( + self.sess.asm_arch, + Some(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( + sess.asm_arch?, + |feature| 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(sess.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()?, + ) + } + }) + }; + + // lower_reg is executed last because we need to lower all + // sub-expressions even if we throw them away later. + let op = match *op { + InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In { + expr: self.lower_expr_mut(expr), + reg: lower_reg(reg)?, + }, + InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out { + late, + expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)), + reg: lower_reg(reg)?, + }, + InlineAsmOperand::InOut { reg, late, ref expr } => { + hir::InlineAsmOperand::InOut { + late, + expr: self.lower_expr_mut(expr), + reg: lower_reg(reg)?, + } + } + InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => { + hir::InlineAsmOperand::SplitInOut { + late, + in_expr: self.lower_expr_mut(in_expr), + out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)), + reg: lower_reg(reg)?, + } + } + InlineAsmOperand::Const { ref expr } => { + hir::InlineAsmOperand::Const { expr: self.lower_expr_mut(expr) } + } + InlineAsmOperand::Sym { ref 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 + let asm_arch = sess.asm_arch.unwrap(); + 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 = "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 line_spans = self.arena.alloc_slice(&asm.line_spans[..]); + let hir_asm = hir::InlineAsm { template, operands, options: asm.options, line_spans }; + 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, @@ -916,7 +1290,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)), @@ -925,7 +1299,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> { @@ -949,7 +1323,6 @@ impl<'hir> LoweringContext<'_, 'hir> { "`async` generators are not yet supported" ) .emit(); - return hir::ExprKind::Err; } None => self.generator_kind = Some(hir::GeneratorKind::Gen), } @@ -1270,25 +1643,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 2c60fe9c07779..00665c4cafb6b 100644 --- a/src/librustc_ast_lowering/item.rs +++ b/src/librustc_ast_lowering/item.rs @@ -1,19 +1,19 @@ use super::{AnonymousLifetimeMode, LoweringContext, ParamMode}; -use super::{ImplTraitContext, ImplTraitPosition, ImplTraitTypeIdVisitor}; +use super::{ImplTraitContext, ImplTraitPosition}; +use crate::Arena; -use rustc::arena::Arena; -use rustc::bug; use rustc_ast::ast::*; use rustc_ast::attr; use rustc_ast::node_id::NodeMap; use rustc_ast::ptr::P; use rustc_ast::visit::{self, AssocCtxt, Visitor}; +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::DefId; +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; @@ -115,7 +115,7 @@ impl<'hir> LoweringContext<'_, 'hir> { _ => &[], }; let lt_def_names = parent_generics.iter().filter_map(|param| match param.kind { - hir::GenericParamKind::Lifetime { .. } => Some(param.name.modern()), + hir::GenericParamKind::Lifetime { .. } => Some(param.name.normalize_to_macros_2_0()), _ => None, }); self.in_scope_lifetimes.extend(lt_def_names); @@ -166,22 +166,6 @@ impl<'hir> LoweringContext<'_, 'hir> { } ItemKind::MacroDef(..) => SmallVec::new(), ItemKind::Fn(..) | ItemKind::Impl { of_trait: None, .. } => smallvec![i.id], - ItemKind::Static(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); - } - ids - } _ => smallvec![i.id], }; @@ -220,8 +204,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut vis = self.lower_visibility(&i.vis, None); let attrs = self.lower_attrs(&i.attrs); - if let ItemKind::MacroDef(MacroDef { ref body, legacy }) = i.kind { - if !legacy || attr::contains_name(&i.attrs, sym::macro_export) { + if let ItemKind::MacroDef(MacroDef { ref body, macro_rules }) = i.kind { + if !macro_rules || attr::contains_name(&i.attrs, sym::macro_export) { let hir_id = self.lower_node_id(i.id); let body = P(self.lower_mac_args(body)); self.exported_macros.push(hir::MacroDef { @@ -230,7 +214,7 @@ impl<'hir> LoweringContext<'_, 'hir> { attrs, hir_id, span: i.span, - ast: MacroDef { body, legacy }, + ast: MacroDef { body, macro_rules }, }); } else { self.non_exported_macro_attrs.extend(attrs.iter().cloned()); @@ -269,7 +253,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); + let fn_def_id = self.resolver.local_def_id(id); self.with_new_scopes(|this| { this.current_item = Some(ident.span); @@ -287,7 +271,12 @@ impl<'hir> LoweringContext<'_, 'hir> { AnonymousLifetimeMode::PassThrough, |this, idty| { let ret_id = asyncness.opt_return_id(); - this.lower_fn_decl(&decl, Some((fn_def_id, idty)), true, ret_id) + this.lower_fn_decl( + &decl, + Some((fn_def_id.to_def_id(), idty)), + true, + ret_id, + ) }, ); let sig = hir::FnSig { decl, header: this.lower_fn_header(header) }; @@ -297,23 +286,25 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Mod(ref m) => hir::ItemKind::Mod(self.lower_mod(m)), ItemKind::ForeignMod(ref nm) => hir::ItemKind::ForeignMod(self.lower_foreign_mod(nm)), ItemKind::GlobalAsm(ref ga) => hir::ItemKind::GlobalAsm(self.lower_global_asm(ga)), - ItemKind::TyAlias(_, ref gen, _, Some(ref ty)) => match ty.kind.opaque_top_hack() { - None => { - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); - let generics = self.lower_generics(gen, ImplTraitContext::disallowed()); - hir::ItemKind::TyAlias(ty, generics) - } - Some(bounds) => { - let ctx = || ImplTraitContext::OpaqueTy(None, hir::OpaqueTyOrigin::Misc); - let ty = hir::OpaqueTy { - generics: self.lower_generics(gen, ctx()), - bounds: self.lower_param_bounds(bounds, ctx()), - impl_trait_fn: None, - origin: hir::OpaqueTyOrigin::TypeAlias, - }; - hir::ItemKind::OpaqueTy(ty) - } - }, + ItemKind::TyAlias(_, ref gen, _, Some(ref ty)) => { + // We lower + // + // type Foo = impl Trait + // + // to + // + // type Foo = Foo1 + // opaque type Foo1: Trait + let ty = self.lower_ty( + ty, + ImplTraitContext::OtherOpaqueTy { + capturable_lifetimes: &mut FxHashSet::default(), + origin: hir::OpaqueTyOrigin::Misc, + }, + ); + let generics = self.lower_generics(gen, ImplTraitContext::disallowed()); + hir::ItemKind::TyAlias(ty, generics) + } ItemKind::TyAlias(_, ref generics, _, None) => { let ty = self.arena.alloc(self.ty(span, hir::TyKind::Err)); let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); @@ -351,7 +342,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self_ty: ref ty, items: ref impl_items, } => { - let def_id = self.resolver.definitions().local_def_id(id); + let def_id = self.resolver.local_def_id(id); // Lower the "impl header" first. This ordering is important // for in-band lifetimes! Consider `'a` here: @@ -398,10 +389,15 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }); + // `defaultness.has_value()` is never called for an `impl`, always `true` in order + // to not cause an assertion failure inside the `lower_defaultness` function. + let has_val = true; + let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val); hir::ItemKind::Impl { unsafety: self.lower_unsafety(unsafety), polarity, - defaultness: self.lower_defaultness(defaultness, true /* [1] */), + defaultness, + defaultness_span, constness: self.lower_constness(constness), generics, of_trait: trait_ref, @@ -427,12 +423,9 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_param_bounds(bounds, ImplTraitContext::disallowed()), ), ItemKind::MacroDef(..) | ItemKind::MacCall(..) => { - bug!("`TyMac` should have been expanded by now") + panic!("`TyMac` should have been expanded by now") } } - - // [1] `defaultness.has_value()` is never called for an `impl`, always `true` in order to - // not cause an assertion failure inside the `lower_defaultness` function. } fn lower_const_item( @@ -441,8 +434,13 @@ impl<'hir> LoweringContext<'_, 'hir> { span: Span, body: Option<&Expr>, ) -> (&'hir hir::Ty<'hir>, hir::BodyId) { + let mut capturable_lifetimes; let itctx = if self.sess.features_untracked().impl_trait_in_bindings { - ImplTraitContext::OpaqueTy(None, hir::OpaqueTyOrigin::Misc) + capturable_lifetimes = FxHashSet::default(); + ImplTraitContext::OtherOpaqueTy { + capturable_lifetimes: &mut capturable_lifetimes, + origin: hir::OpaqueTyOrigin::Misc, + } } else { ImplTraitContext::Disallowed(ImplTraitPosition::Binding) }; @@ -648,7 +646,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); + let def_id = self.resolver.local_def_id(i.id); hir::ForeignItem { hir_id: self.lower_node_id(i.id), ident: i.ident, @@ -749,7 +747,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); + let trait_item_def_id = self.resolver.local_def_id(i.id); let (generics, kind) = match i.kind { AssocItemKind::Const(_, ref ty, ref default) => { @@ -761,13 +759,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let names = self.lower_fn_params_to_names(&sig.decl); let (generics, sig) = self.lower_method_sig(generics, sig, trait_item_def_id, false, None); - (generics, hir::TraitItemKind::Fn(sig, hir::TraitMethod::Required(names))) + (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names))) } AssocItemKind::Fn(_, ref sig, ref generics, Some(ref body)) => { let body_id = self.lower_fn_body_block(i.span, &sig.decl, Some(body)); let (generics, sig) = self.lower_method_sig(generics, sig, trait_item_def_id, false, None); - (generics, hir::TraitItemKind::Fn(sig, hir::TraitMethod::Provided(body_id))) + (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id))) } AssocItemKind::TyAlias(_, ref generics, ref bounds, ref default) => { let ty = default.as_ref().map(|x| self.lower_ty(x, ImplTraitContext::disallowed())); @@ -779,7 +777,7 @@ impl<'hir> LoweringContext<'_, 'hir> { (generics, kind) } - AssocItemKind::MacCall(..) => bug!("macro item shouldn't exist at this point"), + AssocItemKind::MacCall(..) => panic!("macro item shouldn't exist at this point"), }; hir::TraitItem { @@ -799,7 +797,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!(), }; @@ -809,12 +807,12 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Construct `ExprKind::Err` for the given `span`. - fn expr_err(&mut self, span: Span) -> hir::Expr<'hir> { + crate fn expr_err(&mut self, span: Span) -> hir::Expr<'hir> { self.expr(span, hir::ExprKind::Err, AttrVec::new()) } fn lower_impl_item(&mut self, i: &AssocItem) -> hir::ImplItem<'hir> { - let impl_item_def_id = self.resolver.definitions().local_def_id(i.id); + let impl_item_def_id = self.resolver.local_def_id(i.id); let (generics, kind) = match &i.kind { AssocItemKind::Const(_, ty, expr) => { @@ -838,7 +836,7 @@ impl<'hir> LoweringContext<'_, 'hir> { asyncness.opt_return_id(), ); - (generics, hir::ImplItemKind::Method(sig, body_id)) + (generics, hir::ImplItemKind::Fn(sig, body_id)) } AssocItemKind::TyAlias(_, generics, _, ty) => { let generics = self.lower_generics(generics, ImplTraitContext::disallowed()); @@ -847,59 +845,56 @@ impl<'hir> LoweringContext<'_, 'hir> { let ty = self.arena.alloc(self.ty(i.span, hir::TyKind::Err)); hir::ImplItemKind::TyAlias(ty) } - Some(ty) => match ty.kind.opaque_top_hack() { - None => { - let ty = self.lower_ty(ty, ImplTraitContext::disallowed()); - hir::ImplItemKind::TyAlias(ty) - } - Some(bs) => { - let bs = self.lower_param_bounds(bs, ImplTraitContext::disallowed()); - hir::ImplItemKind::OpaqueTy(bs) - } - }, + Some(ty) => { + let ty = self.lower_ty( + ty, + ImplTraitContext::OtherOpaqueTy { + capturable_lifetimes: &mut FxHashSet::default(), + origin: hir::OpaqueTyOrigin::Misc, + }, + ); + hir::ImplItemKind::TyAlias(ty) + } }; (generics, kind) } - AssocItemKind::MacCall(..) => bug!("`TyMac` should have been expanded by now"), + AssocItemKind::MacCall(..) => panic!("`TyMac` should have been expanded by now"), }; + // Since `default impl` is not yet implemented, this is always true in impls. + let has_value = true; + let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); hir::ImplItem { hir_id: self.lower_node_id(i.id), ident: i.ident, attrs: self.lower_attrs(&i.attrs), generics, vis: self.lower_visibility(&i.vis, None), - defaultness: self.lower_defaultness(i.kind.defaultness(), true /* [1] */), + defaultness, kind, span: i.span, } - - // [1] since `default impl` is not yet implemented, this is always true in impls } fn lower_impl_item_ref(&mut self, i: &AssocItem) -> hir::ImplItemRef<'hir> { + // Since `default impl` is not yet implemented, this is always true in impls. + let has_value = true; + let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); hir::ImplItemRef { id: hir::ImplItemId { hir_id: self.lower_node_id(i.id) }, ident: i.ident, span: i.span, vis: self.lower_visibility(&i.vis, Some(i.id)), - defaultness: self.lower_defaultness(i.kind.defaultness(), true /* [1] */), + defaultness, kind: match &i.kind { AssocItemKind::Const(..) => hir::AssocItemKind::Const, - AssocItemKind::TyAlias(.., ty) => { - match ty.as_deref().and_then(|ty| ty.kind.opaque_top_hack()) { - None => hir::AssocItemKind::Type, - Some(_) => hir::AssocItemKind::OpaqueTy, - } - } + AssocItemKind::TyAlias(..) => hir::AssocItemKind::Type, AssocItemKind::Fn(_, sig, ..) => { - hir::AssocItemKind::Method { has_self: sig.decl.has_self() } + hir::AssocItemKind::Fn { has_self: sig.decl.has_self() } } AssocItemKind::MacCall(..) => unimplemented!(), }, } - - // [1] since `default impl` is not yet implemented, this is always true in impls } /// If an `explicit_owner` is given, this method allocates the `HirId` in @@ -934,12 +929,16 @@ impl<'hir> LoweringContext<'_, 'hir> { respan(v.span, node) } - fn lower_defaultness(&self, d: Defaultness, has_value: bool) -> hir::Defaultness { + fn lower_defaultness( + &self, + d: Defaultness, + has_value: bool, + ) -> (hir::Defaultness, Option) { match d { - Defaultness::Default(_) => hir::Defaultness::Default { has_value }, + Defaultness::Default(sp) => (hir::Defaultness::Default { has_value }, Some(sp)), Defaultness::Final => { assert!(has_value); - hir::Defaultness::Final + (hir::Defaultness::Final, None) } } } @@ -955,13 +954,15 @@ impl<'hir> LoweringContext<'_, 'hir> { id } - fn lower_body( + pub(super) fn lower_body( &mut self, 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 } @@ -1211,7 +1212,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, generics: &Generics, sig: &FnSig, - fn_def_id: DefId, + fn_def_id: LocalDefId, impl_trait_return_allow: bool, is_async: Option, ) -> (hir::Generics<'hir>, hir::FnSig<'hir>) { @@ -1223,7 +1224,7 @@ impl<'hir> LoweringContext<'_, 'hir> { |this, idty| { this.lower_fn_decl( &sig.decl, - Some((fn_def_id, idty)), + Some((fn_def_id.to_def_id(), idty)), impl_trait_return_allow, is_async, ) @@ -1316,21 +1317,16 @@ impl<'hir> LoweringContext<'_, 'hir> { .get_partial_res(bound_pred.bounded_ty.id) .map(|d| d.base_res()) { - if let Some(node_id) = - self.resolver.definitions().as_local_node_id(def_id) - { + if let Some(def_id) = def_id.as_local() { 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 def_id == self.resolver.local_def_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 3ef02d7c61e78..39b14ac458832 100644 --- a/src/librustc_ast_lowering/lib.rs +++ b/src/librustc_ast_lowering/lib.rs @@ -32,13 +32,9 @@ #![feature(array_value_iter)] #![feature(crate_visibility_modifier)] +#![feature(or_patterns)] #![recursion_limit = "256"] -use rustc::arena::Arena; -use rustc::dep_graph::DepGraph; -use rustc::hir::map::definitions::{DefKey, DefPathData, Definitions}; -use rustc::hir::map::Map; -use rustc::{bug, span_bug}; use rustc_ast::ast; use rustc_ast::ast::*; use rustc_ast::attr; @@ -54,17 +50,18 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res}; -use rustc_hir::def_id::{DefId, DefIdMap, DefIndex, CRATE_DEF_INDEX}; +use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::definitions::{DefKey, DefPathData, Definitions}; use rustc_hir::intravisit; use rustc_hir::{ConstArg, GenericArg, ParamName}; -use rustc_index::vec::IndexVec; +use rustc_index::vec::{Idx, IndexVec}; use rustc_session::config::nightly_options; use rustc_session::lint::{builtin::BARE_TRAIT_OBJECTS, BuiltinLintDiagnostics, LintBuffer}; 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}; @@ -86,6 +83,8 @@ mod path; const HIR_ID_COUNTER_LOCKED: u32 = 0xFFFFFFFF; +rustc_hir::arena_types!(rustc_arena::declare_arena, [], 'tcx); + struct LoweringContext<'a, 'hir: 'a> { crate_root: Option, @@ -96,7 +95,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 @@ -117,6 +116,10 @@ struct LoweringContext<'a, 'hir: 'a> { generator_kind: Option, + /// When inside an `async` context, this is the `HirId` of the + /// `task_context` local bound to the resume argument of the generator. + task_context: Option, + /// Used to get the current `fn`'s def span to point to when using `await` /// outside of an `async fn`. current_item: Option, @@ -153,7 +156,7 @@ struct LoweringContext<'a, 'hir: 'a> { /// against this list to see if it is already in-scope, or if a definition /// needs to be created for it. /// - /// We always store a `modern()` version of the param-name in this + /// We always store a `normalize_to_macros_2_0()` version of the param-name in this /// vector. in_scope_lifetimes: Vec, @@ -161,9 +164,9 @@ struct LoweringContext<'a, 'hir: 'a> { type_def_lifetime_params: DefIdMap, - current_hir_id_owner: Vec<(DefIndex, u32)>, + 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>, @@ -200,6 +203,21 @@ pub trait Resolver { fn lint_buffer(&mut self) -> &mut LintBuffer; fn next_node_id(&mut self) -> NodeId; + + fn trait_map(&self) -> &NodeMap>; + + fn opt_local_def_id(&self, node: NodeId) -> Option; + + fn local_def_id(&self, node: NodeId) -> LocalDefId; + + fn create_def( + &mut self, + parent: LocalDefId, + node_id: ast::NodeId, + data: DefPathData, + expn_id: ExpnId, + span: Span, + ) -> LocalDefId; } type NtToTokenstream = fn(&Nonterminal, &ParseSess, Span) -> TokenStream; @@ -219,11 +237,30 @@ enum ImplTraitContext<'b, 'a> { /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`. /// - /// We optionally store a `DefId` for the parent item here so we can look up necessary - /// information later. It is `None` when no information about the context should be stored - /// (e.g., for consts and statics). - OpaqueTy(Option /* fn def-ID */, hir::OpaqueTyOrigin), - + ReturnPositionOpaqueTy { + /// `DefId` for the parent function, used to look up necessary + /// information later. + fn_def_id: DefId, + /// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn, + origin: hir::OpaqueTyOrigin, + }, + /// Impl trait in type aliases, consts and statics. + OtherOpaqueTy { + /// Set of lifetimes that this opaque type can capture, if it uses + /// them. This includes lifetimes bound since we entered this context. + /// For example, in + /// + /// type A<'b> = impl for<'a> Trait<'a, Out = impl Sized + 'a>; + /// + /// the inner opaque type captures `'a` because it uses it. It doesn't + /// need to capture `'b` because it already inherits the lifetime + /// parameter from `A`. + // FIXME(impl_trait): but `required_region_bounds` will ICE later + // anyway. + capturable_lifetimes: &'b mut FxHashSet, + /// Origin: Either OpaqueTyOrigin::Misc or OpaqueTyOrigin::Binding, + origin: hir::OpaqueTyOrigin, + }, /// `impl Trait` is not accepted in this position. Disallowed(ImplTraitPosition), } @@ -248,7 +285,12 @@ impl<'a> ImplTraitContext<'_, 'a> { use self::ImplTraitContext::*; match self { Universal(params) => Universal(params), - OpaqueTy(fn_def_id, origin) => OpaqueTy(*fn_def_id, *origin), + ReturnPositionOpaqueTy { fn_def_id, origin } => { + ReturnPositionOpaqueTy { fn_def_id: *fn_def_id, origin: *origin } + } + OtherOpaqueTy { capturable_lifetimes, origin } => { + OtherOpaqueTy { capturable_lifetimes, origin: *origin } + } Disallowed(pos) => Disallowed(*pos), } } @@ -256,21 +298,15 @@ impl<'a> ImplTraitContext<'_, 'a> { pub fn lower_crate<'a, 'hir>( sess: &'a Session, - dep_graph: &'a DepGraph, krate: &'a Crate, resolver: &'a mut dyn Resolver, nt_to_tokenstream: NtToTokenstream, arena: &'hir Arena<'hir>, ) -> hir::Crate<'hir> { - // We're constructing the HIR here; we don't care what we will - // read, since we haven't even constructed the *input* to - // incr. comp. yet. - dep_graph.assert_ignored(); - let _prof_timer = sess.prof.verbose_generic_activity("hir_lowering"); LoweringContext { - crate_root: sess.parse_sess.injected_crate_name.try_get().copied(), + crate_root: sess.parse_sess.injected_crate_name.get().copied(), sess, resolver, nt_to_tokenstream, @@ -291,10 +327,11 @@ pub fn lower_crate<'a, 'hir>( anonymous_lifetime_mode: AnonymousLifetimeMode::PassThrough, type_def_lifetime_params: Default::default(), current_module: hir::CRATE_HIR_ID, - current_hir_id_owner: vec![(CRATE_DEF_INDEX, 0)], + current_hir_id_owner: vec![(LocalDefId { local_def_index: CRATE_DEF_INDEX }, 0)], item_local_id_counters: Default::default(), node_id_to_hir_id: IndexVec::new(), generator_kind: None, + task_context: None, current_item: None, lifetimes_to_define: Vec::new(), is_collecting_in_band_lifetimes: false, @@ -408,11 +445,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } impl MiscCollector<'_, '_, '_> { - fn allocate_use_tree_hir_id_counters(&mut self, tree: &UseTree, owner: DefIndex) { + fn allocate_use_tree_hir_id_counters(&mut self, tree: &UseTree, owner: LocalDefId) { match tree.kind { UseTreeKind::Simple(_, id1, id2) => { for &id in &[id1, id2] { - self.lctx.resolver.definitions().create_def_with_parent( + self.lctx.resolver.create_def( owner, id, DefPathData::Misc, @@ -464,7 +501,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); + let def_id = self.lctx.resolver.local_def_id(item.id); let count = generics .params .iter() @@ -473,7 +510,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { _ => false, }) .count(); - self.lctx.type_def_lifetime_params.insert(def_id, count); + self.lctx.type_def_lifetime_params.insert(def_id.to_def_id(), count); } ItemKind::Use(ref use_tree) => { self.allocate_use_tree_hir_id_counters(use_tree, hir_id.owner); @@ -522,7 +559,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,9 +567,28 @@ 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(); + + let trait_map = self + .resolver + .trait_map() + .iter() + .map(|(&k, v)| (self.node_id_to_hir_id[k].unwrap(), v.clone())) + .collect(); - self.resolver.definitions().init_node_id_to_hir_id_mapping(self.node_id_to_hir_id); + let mut def_id_to_hir_id = IndexVec::default(); + + for (node_id, hir_id) in self.node_id_to_hir_id.into_iter_enumerated() { + if let Some(def_id) = self.resolver.opt_local_def_id(node_id) { + if def_id_to_hir_id.len() <= def_id.index() { + def_id_to_hir_id.resize(def_id.index() + 1, None); + } + def_id_to_hir_id[def_id] = hir_id; + } + } + + self.resolver.definitions().init_def_id_to_hir_id_mapping(def_id_to_hir_id); hir::Crate { item: hir::CrateItem { module, attrs, span: c.span }, @@ -546,6 +602,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { trait_impls: self.trait_impls, modules: self.modules, proc_macros, + trait_map, } } @@ -571,26 +628,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,12 +652,12 @@ 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_index = self.resolver.definitions().opt_def_index(owner).unwrap(); - self.current_hir_id_owner.push((def_index, counter)); + let def_id = self.resolver.local_def_id(owner); + self.current_hir_id_owner.push((def_id, counter)); let ret = f(self); - let (new_def_index, new_counter) = self.current_hir_id_owner.pop().unwrap(); + let (new_def_id, new_counter) = self.current_hir_id_owner.pop().unwrap(); - debug_assert!(def_index == new_def_index); + debug_assert!(def_id == new_def_id); debug_assert!(new_counter >= counter); let prev = self.item_local_id_counters.insert(owner, new_counter).unwrap(); @@ -620,11 +673,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// properly. Calling the method twice with the same `NodeId` is fine though. fn lower_node_id(&mut self, ast_node_id: NodeId) -> hir::HirId { self.lower_node_id_generic(ast_node_id, |this| { - let &mut (def_index, ref mut local_id_counter) = + let &mut (owner, ref mut local_id_counter) = this.current_hir_id_owner.last_mut().unwrap(); let local_id = *local_id_counter; *local_id_counter += 1; - hir::HirId { owner: def_index, local_id: hir::ItemLocalId::from_u32(local_id) } + hir::HirId { owner, local_id: hir::ItemLocalId::from_u32(local_id) } }) } @@ -642,12 +695,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug_assert!(local_id != HIR_ID_COUNTER_LOCKED); *local_id_counter += 1; - let def_index = this.resolver.definitions().opt_def_index(owner).expect( - "you forgot to call `create_def_with_parent` or are lowering node-IDs \ - that do not belong to the current owner", + let owner = this.resolver.opt_local_def_id(owner).expect( + "you forgot to call `create_def` or are lowering node-IDs \ + that do not belong to the current owner", ); - hir::HirId { owner: def_index, local_id: hir::ItemLocalId::from_u32(local_id) } + hir::HirId { owner, local_id: hir::ItemLocalId::from_u32(local_id) } }) } @@ -667,7 +720,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn expect_full_res(&mut self, id: NodeId) -> Res { self.resolver.get_partial_res(id).map_or(Res::Err, |pr| { if pr.unresolved_segments() != 0 { - bug!("path not fully resolved: {:?}", pr); + panic!("path not fully resolved: {:?}", pr); } pr.base_res() }) @@ -691,7 +744,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> Span { span.fresh_expansion(ExpnData { allow_internal_unstable, - ..ExpnData::default(ExpnKind::Desugaring(reason), span, self.sess.edition()) + ..ExpnData::default(ExpnKind::Desugaring(reason), span, self.sess.edition(), None) }) } @@ -725,7 +778,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// parameter while `f` is running (and restored afterwards). fn collect_in_band_defs( &mut self, - parent_id: DefId, + parent_def_id: LocalDefId, anonymous_lifetime_mode: AnonymousLifetimeMode, f: impl FnOnce(&mut Self) -> (Vec>, T), ) -> (Vec>, T) { @@ -745,7 +798,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let params = lifetimes_to_define .into_iter() - .map(|(span, hir_name)| self.lifetime_to_generic_param(span, hir_name, parent_id.index)) + .map(|(span, hir_name)| self.lifetime_to_generic_param(span, hir_name, parent_def_id)) .chain(in_band_ty_params.into_iter()) .collect(); @@ -757,7 +810,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, span: Span, hir_name: ParamName, - parent_index: DefIndex, + parent_def_id: LocalDefId, ) -> hir::GenericParam<'hir> { let node_id = self.resolver.next_node_id(); @@ -771,8 +824,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; // Add a definition for the in-band lifetime def. - self.resolver.definitions().create_def_with_parent( - parent_index, + self.resolver.create_def( + parent_def_id, node_id, DefPathData::LifetimeNs(str_name), ExpnId::root(), @@ -803,14 +856,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { return; } - if self.in_scope_lifetimes.contains(&ParamName::Plain(ident.modern())) { + if self.in_scope_lifetimes.contains(&ParamName::Plain(ident.normalize_to_macros_2_0())) { return; } let hir_name = ParamName::Plain(ident); - if self.lifetimes_to_define.iter().any(|(_, lt_name)| lt_name.modern() == hir_name.modern()) - { + if self.lifetimes_to_define.iter().any(|(_, lt_name)| { + lt_name.normalize_to_macros_2_0() == hir_name.normalize_to_macros_2_0() + }) { return; } @@ -838,7 +892,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> T { let old_len = self.in_scope_lifetimes.len(); let lt_def_names = params.iter().filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some(ParamName::Plain(param.ident.modern())), + GenericParamKind::Lifetime { .. } => { + Some(ParamName::Plain(param.ident.normalize_to_macros_2_0())) + } _ => None, }); self.in_scope_lifetimes.extend(lt_def_names); @@ -858,13 +914,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn add_in_band_defs( &mut self, generics: &Generics, - parent_id: DefId, + parent_def_id: LocalDefId, anonymous_lifetime_mode: AnonymousLifetimeMode, f: impl FnOnce(&mut Self, &mut Vec>) -> T, ) -> (hir::Generics<'hir>, T) { let (in_band_defs, (mut lowered_generics, res)) = self.with_in_scope_lifetime_defs(&generics.params, |this| { - this.collect_in_band_defs(parent_id, anonymous_lifetime_mode, |this| { + this.collect_in_band_defs(parent_def_id, anonymous_lifetime_mode, |this| { let mut params = Vec::new(); // Note: it is necessary to lower generics *before* calling `f`. // When lowering `async fn`, there's a final step when lowering @@ -1001,6 +1057,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TypeBindingKind::Equality { ty: self.lower_ty(ty, itctx) } } AssocTyConstraintKind::Bound { ref bounds } => { + let mut capturable_lifetimes; // Piggy-back on the `impl Trait` context to figure out the correct behavior. let (desugar_to_impl_trait, itctx) = match itctx { // We are in the return position: @@ -1010,7 +1067,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // so desugar to // // fn foo() -> impl Iterator - ImplTraitContext::OpaqueTy(..) => (true, itctx), + ImplTraitContext::ReturnPositionOpaqueTy { .. } + | ImplTraitContext::OtherOpaqueTy { .. } => (true, itctx), // We are in the argument position, but within a dyn type: // @@ -1028,7 +1086,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // // FIXME: this is only needed until `impl Trait` is allowed in type aliases. ImplTraitContext::Disallowed(_) if self.is_in_dyn_type => { - (true, ImplTraitContext::OpaqueTy(None, hir::OpaqueTyOrigin::Misc)) + capturable_lifetimes = FxHashSet::default(); + ( + true, + ImplTraitContext::OtherOpaqueTy { + capturable_lifetimes: &mut capturable_lifetimes, + origin: hir::OpaqueTyOrigin::Misc, + }, + ) } // We are in the parameter position, but not within a dyn type: @@ -1046,9 +1111,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // constructing the HIR for `impl bounds...` and then lowering that. let impl_trait_node_id = self.resolver.next_node_id(); - let parent_def_index = self.current_hir_id_owner.last().unwrap().0; - self.resolver.definitions().create_def_with_parent( - parent_def_index, + let parent_def_id = self.current_hir_id_owner.last().unwrap().0; + self.resolver.create_def( + parent_def_id, impl_trait_node_id, DefPathData::ImplTrait, ExpnId::root(), @@ -1109,12 +1174,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Construct a AnonConst where the expr is the "ty"'s path. - let parent_def_index = self.current_hir_id_owner.last().unwrap().0; + let parent_def_id = self.current_hir_id_owner.last().unwrap().0; let node_id = self.resolver.next_node_id(); // Add a definition for the in-band const def. - self.resolver.definitions().create_def_with_parent( - parent_def_index, + self.resolver.create_def( + parent_def_id, node_id, DefPathData::AnonConst, ExpnId::root(), @@ -1126,6 +1191,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { kind: ExprKind::Path(qself.clone(), path.clone()), span: ty.span, attrs: AttrVec::new(), + tokens: None, }; let ct = self.with_new_scopes(|this| hir::AnonConst { @@ -1239,16 +1305,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)); @@ -1269,15 +1335,35 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::ImplTrait(def_node_id, ref bounds) => { let span = t.span; match itctx { - ImplTraitContext::OpaqueTy(fn_def_id, origin) => { - self.lower_opaque_impl_trait(span, fn_def_id, origin, def_node_id, |this| { - this.lower_param_bounds(bounds, itctx) - }) + ImplTraitContext::ReturnPositionOpaqueTy { fn_def_id, origin } => self + .lower_opaque_impl_trait( + span, + Some(fn_def_id), + origin, + def_node_id, + None, + |this| this.lower_param_bounds(bounds, itctx), + ), + ImplTraitContext::OtherOpaqueTy { ref capturable_lifetimes, origin } => { + // Reset capturable lifetimes, any nested impl trait + // types will inherit lifetimes from this opaque type, + // so don't need to capture them again. + let nested_itctx = ImplTraitContext::OtherOpaqueTy { + capturable_lifetimes: &mut FxHashSet::default(), + origin, + }; + self.lower_opaque_impl_trait( + span, + None, + origin, + def_node_id, + Some(capturable_lifetimes), + |this| this.lower_param_bounds(bounds, nested_itctx), + ) } ImplTraitContext::Universal(in_band_ty_params) => { // Add a definition for the in-band `Param`. - let def_index = - self.resolver.definitions().opt_def_index(def_node_id).unwrap(); + let def_id = self.resolver.local_def_id(def_node_id); let hir_bounds = self.lower_param_bounds( bounds, @@ -1302,7 +1388,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { None, self.arena.alloc(hir::Path { span, - res: Res::Def(DefKind::TyParam, DefId::local(def_index)), + res: Res::Def(DefKind::TyParam, def_id.to_def_id()), segments: arena_vec![self; hir::PathSegment::from_ident(ident)], }), )) @@ -1332,7 +1418,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } - TyKind::MacCall(_) => bug!("`TyKind::MacCall` should have been expanded by now"), + TyKind::MacCall(_) => panic!("`TyKind::MacCall` should have been expanded by now"), TyKind::CVarArgs => { self.sess.delay_span_bug( t.span, @@ -1351,6 +1437,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn_def_id: Option, origin: hir::OpaqueTyOrigin, opaque_ty_node_id: NodeId, + capturable_lifetimes: Option<&FxHashSet>, lower_bounds: impl FnOnce(&mut Self) -> hir::GenericBounds<'hir>, ) -> hir::TyKind<'hir> { debug!( @@ -1365,8 +1452,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_index = - self.resolver.definitions().opt_def_index(opaque_ty_node_id).unwrap(); + let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id); self.allocate_hir_id_counter(opaque_ty_node_id); @@ -1374,13 +1460,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (lifetimes, lifetime_defs) = self.lifetimes_from_impl_trait_bounds( opaque_ty_node_id, - opaque_ty_def_index, + opaque_ty_def_id, &hir_bounds, + capturable_lifetimes, ); - debug!("lower_opaque_impl_trait: lifetimes={:#?}", lifetimes,); + debug!("lower_opaque_impl_trait: lifetimes={:#?}", lifetimes); - debug!("lower_opaque_impl_trait: lifetime_defs={:#?}", lifetime_defs,); + debug!("lower_opaque_impl_trait: lifetime_defs={:#?}", lifetime_defs); self.with_hir_id_owner(opaque_ty_node_id, move |lctx| { let opaque_ty_item = hir::OpaqueTy { @@ -1394,12 +1481,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { origin, }; - trace!("lower_opaque_impl_trait: {:#?}", opaque_ty_def_index); + trace!("lower_opaque_impl_trait: {:#?}", opaque_ty_def_id); let opaque_ty_id = lctx.generate_opaque_type(opaque_ty_node_id, opaque_ty_item, span, opaque_ty_span); // `impl Trait` now just becomes `Foo<'a, 'b, ..>`. - hir::TyKind::Def(hir::ItemId { id: opaque_ty_id }, lifetimes) + hir::TyKind::OpaqueDef(hir::ItemId { id: opaque_ty_id }, lifetimes) }) } @@ -1435,14 +1522,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lifetimes_from_impl_trait_bounds( &mut self, opaque_ty_id: NodeId, - parent_index: DefIndex, + parent_def_id: LocalDefId, bounds: hir::GenericBounds<'hir>, + lifetimes_to_include: Option<&FxHashSet>, ) -> (&'hir [hir::GenericArg<'hir>], &'hir [hir::GenericParam<'hir>]) { debug!( "lifetimes_from_impl_trait_bounds(opaque_ty_id={:?}, \ - parent_index={:?}, \ + parent_def_id={:?}, \ bounds={:#?})", - opaque_ty_id, parent_index, bounds, + opaque_ty_id, parent_def_id, bounds, ); // This visitor walks over `impl Trait` bounds and creates defs for all lifetimes that @@ -1450,17 +1538,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // E.g., `'a`, `'b`, but not `'c` in `impl for<'c> SomeTrait<'a, 'b, 'c>`. struct ImplTraitLifetimeCollector<'r, 'a, 'hir> { context: &'r mut LoweringContext<'a, 'hir>, - parent: DefIndex, + parent: LocalDefId, opaque_ty_id: NodeId, collect_elided_lifetimes: bool, currently_bound_lifetimes: Vec, already_defined_lifetimes: FxHashSet, output_lifetimes: Vec>, output_lifetime_params: Vec>, + lifetimes_to_include: Option<&'r FxHashSet>, } impl<'r, 'a, 'v, 'hir> intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r, 'a, 'hir> { - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None @@ -1542,6 +1631,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { if !self.currently_bound_lifetimes.contains(&name) && !self.already_defined_lifetimes.contains(&name) + && self.lifetimes_to_include.map_or(true, |lifetimes| lifetimes.contains(&name)) { self.already_defined_lifetimes.insert(name); @@ -1554,7 +1644,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let def_node_id = self.context.resolver.next_node_id(); let hir_id = self.context.lower_node_id_with_owner(def_node_id, self.opaque_ty_id); - self.context.resolver.definitions().create_def_with_parent( + self.context.resolver.create_def( self.parent, def_node_id, DefPathData::LifetimeNs(name.ident().name), @@ -1570,7 +1660,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::LifetimeName::Param(param_name) => { (param_name, hir::LifetimeParamKind::Explicit) } - _ => bug!("expected `LifetimeName::Param` or `ParamName::Plain`"), + _ => panic!("expected `LifetimeName::Param` or `ParamName::Plain`"), }; self.output_lifetime_params.push(hir::GenericParam { @@ -1588,13 +1678,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut lifetime_collector = ImplTraitLifetimeCollector { context: self, - parent: parent_index, + parent: parent_def_id, opaque_ty_id, collect_elided_lifetimes: true, currently_bound_lifetimes: Vec::new(), already_defined_lifetimes: FxHashSet::default(), output_lifetimes: Vec::new(), output_lifetime_params: Vec::new(), + lifetimes_to_include, }; for bound in bounds { @@ -1618,12 +1709,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { visitor.visit_ty(ty); } } - let parent_def_id = DefId::local(self.current_hir_id_owner.last().unwrap().0); let ty = l.ty.as_ref().map(|t| { + let mut capturable_lifetimes; self.lower_ty( t, if self.sess.features_untracked().impl_trait_in_bindings { - ImplTraitContext::OpaqueTy(Some(parent_def_id), hir::OpaqueTyOrigin::Misc) + capturable_lifetimes = FxHashSet::default(); + ImplTraitContext::OtherOpaqueTy { + capturable_lifetimes: &mut capturable_lifetimes, + origin: hir::OpaqueTyOrigin::Binding, + } } else { ImplTraitContext::Disallowed(ImplTraitPosition::Binding) }, @@ -1726,7 +1821,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { FnRetTy::Ty(ref ty) => { let context = match in_band_ty_params { Some((def_id, _)) if impl_trait_return_allow => { - ImplTraitContext::OpaqueTy(Some(def_id), hir::OpaqueTyOrigin::FnReturn) + ImplTraitContext::ReturnPositionOpaqueTy { + fn_def_id: def_id, + origin: hir::OpaqueTyOrigin::FnReturn, + } } _ => ImplTraitContext::disallowed(), }; @@ -1742,8 +1840,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, }; @@ -1795,8 +1894,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None); - let opaque_ty_def_index = - self.resolver.definitions().opt_def_index(opaque_ty_node_id).unwrap(); + let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id); self.allocate_hir_id_counter(opaque_ty_node_id); @@ -1884,7 +1982,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let generic_params = this.arena.alloc_from_iter(lifetime_params.iter().map(|(span, hir_name)| { - this.lifetime_to_generic_param(*span, *hir_name, opaque_ty_def_index) + this.lifetime_to_generic_param(*span, *hir_name, opaque_ty_def_id) })); let opaque_ty_item = hir::OpaqueTy { @@ -1898,7 +1996,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { origin: hir::OpaqueTyOrigin::AsyncFn, }; - trace!("exist ty from async fn def index: {:#?}", opaque_ty_def_index); + trace!("exist ty from async fn def id: {:#?}", opaque_ty_def_id); let opaque_ty_id = this.generate_opaque_type(opaque_ty_node_id, opaque_ty_item, span, opaque_ty_span); @@ -1945,7 +2043,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Foo = impl Trait` is, internally, created as a child of the // async fn, so the *type parameters* are inherited. It's // only the lifetime parameters that we must supply. - let opaque_ty_ref = hir::TyKind::Def(hir::ItemId { id: opaque_ty_id }, generic_args); + let opaque_ty_ref = hir::TyKind::OpaqueDef(hir::ItemId { id: opaque_ty_id }, generic_args); let opaque_ty = self.ty(opaque_ty_span, opaque_ty_ref); hir::FnRetTy::Return(self.arena.alloc(opaque_ty)) } @@ -1963,8 +2061,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Not `OpaqueTyOrigin::AsyncFn`: that's only used for the // `impl Future` opaque type that `async fn` implicitly // generates. - let context = - ImplTraitContext::OpaqueTy(Some(fn_def_id), hir::OpaqueTyOrigin::FnReturn); + let context = ImplTraitContext::ReturnPositionOpaqueTy { + fn_def_id, + origin: hir::OpaqueTyOrigin::FnReturn, + }; self.lower_ty(ty, context) } FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])), @@ -2088,7 +2188,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | hir::LifetimeName::Underscore | hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()), hir::LifetimeName::ImplicitObjectLifetimeDefault => { - span_bug!( + self.sess.diagnostic().span_bug( param.ident.span, "object-lifetime-default should not occur here", ); @@ -2114,7 +2214,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { default: default.as_ref().map(|x| { self.lower_ty( x, - ImplTraitContext::OpaqueTy(None, hir::OpaqueTyOrigin::Misc), + ImplTraitContext::OtherOpaqueTy { + capturable_lifetimes: &mut FxHashSet::default(), + origin: hir::OpaqueTyOrigin::Misc, + }, ) }), synthetic: param @@ -2155,7 +2258,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::TraitRef<'hir> { let path = match self.lower_qpath(p.ref_id, &None, &p.path, ParamMode::Explicit, itctx) { hir::QPath::Resolved(None, path) => path, - qpath => bug!("lower_trait_ref: unexpected QPath `{:?}`", qpath), + qpath => panic!("lower_trait_ref: unexpected QPath `{:?}`", qpath), }; hir::TraitRef { path, hir_ref_id: self.lower_node_id(p.ref_id) } } @@ -2170,8 +2273,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &NodeMap::default(), itctx.reborrow(), ); + let trait_ref = self.with_in_scope_lifetime_defs(&p.bound_generic_params, |this| { - this.lower_trait_ref(&p.trait_ref, itctx) + // Any impl Trait types defined within this scope can capture + // lifetimes bound on this predicate. + let lt_def_names = p.bound_generic_params.iter().filter_map(|param| match param.kind { + GenericParamKind::Lifetime { .. } => Some(hir::LifetimeName::Param( + ParamName::Plain(param.ident.normalize_to_macros_2_0()), + )), + _ => None, + }); + if let ImplTraitContext::OtherOpaqueTy { ref mut capturable_lifetimes, .. } = itctx { + capturable_lifetimes.extend(lt_def_names.clone()); + } + + let res = this.lower_trait_ref(&p.trait_ref, itctx.reborrow()); + + if let ImplTraitContext::OtherOpaqueTy { ref mut capturable_lifetimes, .. } = itctx { + for param in lt_def_names { + capturable_lifetimes.remove(¶m); + } + } + res }); hir::PolyTraitRef { bound_generic_params, trait_ref, span: p.span } @@ -2471,7 +2594,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..55c1f80266337 100644 --- a/src/librustc_ast_lowering/pat.rs +++ b/src/librustc_ast_lowering/pat.rs @@ -2,83 +2,91 @@ use super::{ImplTraitContext, LoweringContext, ParamMode}; use rustc_ast::ast::*; use rustc_ast::ptr::P; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::Applicability; 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( @@ -95,10 +103,36 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Note that unlike for slice patterns, // where `xs @ ..` is a legal sub-slice pattern, // it is not a legal sub-tuple pattern. - if pat.is_rest() { - rest = Some((idx, pat.span)); - break; + match pat.kind { + // Found a sub-tuple rest pattern + PatKind::Rest => { + rest = Some((idx, pat.span)); + break; + } + // Found a sub-tuple pattern `$binding_mode $ident @ ..`. + // This is not allowed as a sub-tuple pattern + PatKind::Ident(ref _bm, ident, Some(ref sub)) if sub.is_rest() => { + rest = Some((idx, pat.span)); + let sp = pat.span; + self.diagnostic() + .struct_span_err( + sp, + &format!("`{} @` is not allowed in a {}", ident.name, ctx), + ) + .span_label(sp, "this is only allowed in slice patterns") + .help("remove this and bind each tuple field independently") + .span_suggestion_verbose( + sp, + &format!("if you don't need to use the contents of {}, discard the tuple's remaining fields", ident), + "..".to_string(), + Applicability::MaybeIncorrect, + ) + .emit(); + break; + } + _ => {} } + // It was not a sub-tuple pattern so lower it normally. elems.push(self.lower_pat(pat)); } @@ -194,7 +228,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 db8517bfbf0c7..e5ce51f8d2d1f 100644 --- a/src/librustc_ast_lowering/path.rs +++ b/src/librustc_ast_lowering/path.rs @@ -1,15 +1,15 @@ use super::{AnonymousLifetimeMode, ImplTraitContext, LoweringContext, ParamMode}; use super::{GenericArgsCtor, ParenthesizedGenericArgs}; -use rustc::lint::builtin::ELIDED_LIFETIMES_IN_PATHS; -use rustc::span_bug; use rustc_ast::ast::{self, *}; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, PartialRes, Res}; 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; @@ -163,12 +163,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } // We should've returned in the for loop above. - span_bug!( + + self.sess.diagnostic().span_bug( p.span, - "lower_qpath: no final extension segment in {}..{}", - proj_start, - p.segments.len() - ) + &format!( + "lower_qpath: no final extension segment in {}..{}", + proj_start, + p.segments.len() + ), + ); } crate fn lower_path_extra( @@ -271,8 +274,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .next(); if !generic_args.parenthesized && !has_lifetimes { generic_args.args = self - .elided_path_lifetimes(path_span, expected_lifetimes) - .map(|lt| GenericArg::Lifetime(lt)) + .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(); if expected_lifetimes > 0 && param_mode == ParamMode::Explicit { @@ -304,7 +310,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { E0726, "implicit elided lifetime not allowed here" ); - rustc::lint::add_elided_lifetime_in_path_suggestion( + rustc_session::lint::add_elided_lifetime_in_path_suggestion( &self.sess, &mut err, expected_lifetimes, @@ -364,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 541c681840f12..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,8 +648,77 @@ 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, +/// which is lifetimes, then types and then consts. (`<'a, T, const N: usize>`) fn validate_generic_param_order<'a>( sess: &Session, handler: &rustc_errors::Handler, @@ -718,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(); } @@ -821,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, @@ -900,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"; @@ -948,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 { @@ -1010,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); @@ -1067,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); } } @@ -1254,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 66be2b49adab7..a7b0c9cf81be6 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; @@ -121,6 +121,14 @@ impl<'a> PostExpansionVisitor<'a> { "amdgpu-kernel ABI is experimental and subject to change" ); } + "avr-interrupt" | "avr-non-blocking-interrupt" => { + gate_feature_post!( + &self, + abi_avr_interrupt, + span, + "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change" + ); + } "efiapi" => { gate_feature_post!( &self, @@ -252,12 +260,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 +294,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 +304,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 +349,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" @@ -366,7 +374,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, trait_alias, i.span, "trait aliases are experimental"); } - ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => { + ast::ItemKind::MacroDef(ast::MacroDef { macro_rules: false, .. }) => { let msg = "`macro` is experimental"; gate_feature_post!(&self, decl_macro, i.span, msg); } @@ -516,41 +524,36 @@ 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) } fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) { - if let ast::Defaultness::Default(_) = i.kind.defaultness() { - gate_feature_post!(&self, specialization, i.span, "specialization is unstable"); - } - - match i.kind { + let is_fn = match i.kind { ast::AssocItemKind::Fn(_, ref sig, _, _) => { if let (ast::Const::Yes(_), AssocCtxt::Trait) = (sig.header.constness, ctxt) { gate_feature_post!(&self, const_fn, i.span, "const fn is unstable"); } + true } ast::AssocItemKind::TyAlias(_, ref generics, _, ref ty) => { if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) { @@ -565,8 +568,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { self.check_impl_trait(ty); } self.check_gat(generics, i.span); + false } - _ => {} + _ => false, + }; + if let ast::Defaultness::Default(_) = i.kind.defaultness() { + // Limit `min_specialization` to only specializing functions. + gate_feature_fn!( + &self, + |x: &Features| x.specialization || (is_fn && x.min_specialization), + i.span, + sym::specialization, + "specialization is unstable" + ); } visit::walk_assoc_item(self, i, ctxt) } 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 a81b28d7c393b..b1abc08aa67b0 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); } @@ -148,12 +150,19 @@ pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String { // This makes comma-separated lists look slightly nicer, // and also addresses a specific regression described in issue #63896. -fn tt_prepend_space(tt: &TokenTree) -> bool { +fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool { match tt { TokenTree::Token(token) => match token.kind { token::Comma => false, _ => true, }, + TokenTree::Delimited(_, DelimToken::Paren, _) => match prev { + TokenTree::Token(token) => match token.kind { + token::Ident(_, _) => false, + _ => true, + }, + _ => true, + }, _ => true, } } @@ -382,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( @@ -630,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) => { @@ -650,11 +658,14 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } fn print_tts(&mut self, tts: tokenstream::TokenStream, convert_dollar_crate: bool) { - for (i, tt) in tts.into_trees().enumerate() { - if i != 0 && tt_prepend_space(&tt) { + let mut iter = tts.into_trees().peekable(); + while let Some(tt) = iter.next() { + let show_space = + if let Some(next) = iter.peek() { tt_prepend_space(next, &tt) } else { false }; + self.print_tt(tt, convert_dollar_crate); + if show_space { self.space(); } - self.print_tt(tt, convert_dollar_crate); } } @@ -662,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, @@ -773,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)) } @@ -786,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(">") } @@ -881,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), @@ -999,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>, @@ -1030,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>, @@ -1238,7 +1242,7 @@ impl<'a> State<'a> { } } ast::ItemKind::MacroDef(ref macro_def) => { - let (kw, has_bang) = if macro_def.legacy { + let (kw, has_bang) = if macro_def.macro_rules { ("macro_rules", true) } else { self.print_visibility(&item.vis); @@ -1279,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, ) { @@ -1335,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, ) { @@ -1387,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) } } @@ -1736,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 @@ -1816,7 +1818,7 @@ impl<'a> State<'a> { ast::ExprKind::Call(ref func, ref args) => { self.print_expr_call(func, &args[..]); } - ast::ExprKind::MethodCall(ref segment, ref args) => { + ast::ExprKind::MethodCall(ref segment, ref args, _) => { self.print_expr_method_call(segment, &args[..]); } ast::ExprKind::Binary(op, ref lhs, ref rhs) => { @@ -2015,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(":"); @@ -2056,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"); } @@ -2079,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) => { @@ -2118,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)) } @@ -2136,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) } } @@ -2325,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, @@ -2350,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); @@ -2481,7 +2593,7 @@ impl<'a> State<'a> { } crate fn print_where_clause(&mut self, where_clause: &ast::WhereClause) { - if where_clause.predicates.is_empty() { + if where_clause.predicates.is_empty() && !where_clause.has_where_token { return; } @@ -2617,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); @@ -2627,7 +2739,11 @@ impl<'a> State<'a> { } let generics = ast::Generics { params: Vec::new(), - where_clause: ast::WhereClause { predicates: Vec::new(), span: rustc_span::DUMMY_SP }, + where_clause: ast::WhereClause { + has_where_token: false, + predicates: Vec::new(), + span: rustc_span::DUMMY_SP, + }, span: rustc_span::DUMMY_SP, }; let header = ast::FnHeader { unsafety, ext, ..ast::FnHeader::default() }; diff --git a/src/librustc_ast_pretty/pprust/tests.rs b/src/librustc_ast_pretty/pprust/tests.rs index 455f2e3da3680..f92e40ed6ffab 100644 --- a/src/librustc_ast_pretty/pprust/tests.rs +++ b/src/librustc_ast_pretty/pprust/tests.rs @@ -2,13 +2,13 @@ use super::*; 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 +26,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 +41,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..677796a8df0b3 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" @@ -11,12 +12,12 @@ doctest = false [dependencies] rustc_ast_pretty = { path = "../librustc_ast_pretty" } -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } rustc_errors = { path = "../librustc_errors" } 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 08eae24da9b68..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,36 +120,22 @@ pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Op }) } -/// Represents the #[stable], #[unstable], #[rustc_deprecated] attributes. -#[derive( - RustcEncodable, - RustcDecodable, - Copy, - Clone, - Debug, - PartialEq, - Eq, - Hash, - HashStable_Generic -)] +/// Represents the following attributes: +/// +/// - `#[stable]` +/// - `#[unstable]` +/// - `#[rustc_deprecated]` +#[derive(RustcEncodable, RustcDecodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] pub struct Stability { pub level: StabilityLevel, pub feature: Symbol, pub rustc_depr: Option, } -/// Represents the #[rustc_const_unstable] and #[rustc_const_stable] attributes. -#[derive( - RustcEncodable, - RustcDecodable, - Copy, - Clone, - Debug, - PartialEq, - Eq, - Hash, - HashStable_Generic -)] +/// 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 { pub level: StabilityLevel, pub feature: Symbol, @@ -159,18 +146,8 @@ pub struct ConstStability { } /// The available stability levels. -#[derive( - RustcEncodable, - RustcDecodable, - PartialEq, - PartialOrd, - Copy, - Clone, - Debug, - Eq, - Hash, - HashStable_Generic -)] +#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] pub enum StabilityLevel { // Reason for the current stability level and the relevant rust-lang issue Unstable { reason: Option, issue: Option, is_soft: bool }, @@ -186,18 +163,8 @@ impl StabilityLevel { } } -#[derive( - RustcEncodable, - RustcDecodable, - PartialEq, - PartialOrd, - Copy, - Clone, - Debug, - Eq, - Hash, - HashStable_Generic -)] +#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] pub struct RustcDeprecation { pub since: Symbol, pub reason: Symbol, @@ -602,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 @@ -637,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) { @@ -650,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() { @@ -668,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!( @@ -686,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!( @@ -908,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 { @@ -1024,7 +1030,7 @@ pub enum TransparencyError { pub fn find_transparency( attrs: &[Attribute], - is_legacy: bool, + macro_rules: bool, ) -> (Transparency, Option) { let mut transparency = None; let mut error = None; @@ -1049,7 +1055,7 @@ pub fn find_transparency( } } } - let fallback = if is_legacy { Transparency::SemiTransparent } else { Transparency::Opaque }; + let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque }; (transparency.map_or(fallback, |t| t.0), error) } 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/Cargo.toml b/src/librustc_builtin_macros/Cargo.toml index c15438bde440e..fdb6c359052d0 100644 --- a/src/librustc_builtin_macros/Cargo.toml +++ b/src/librustc_builtin_macros/Cargo.toml @@ -10,7 +10,7 @@ path = "lib.rs" doctest = false [dependencies] -fmt_macros = { path = "../libfmt_macros" } +rustc_parse_format = { path = "../librustc_parse_format" } log = "0.4" rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_attr = { path = "../librustc_attr" } diff --git a/src/librustc_builtin_macros/asm.rs b/src/librustc_builtin_macros/asm.rs index 9811a6621afa5..52f86aa7e06b9 100644 --- a/src/librustc_builtin_macros/asm.rs +++ b/src/librustc_builtin_macros/asm.rs @@ -1,300 +1,632 @@ -// Inline assembly support. -// -use State::*; - -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_parse_format as parse; 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 { + templates: Vec>, + operands: Vec<(ast::InlineAsmOperand, Span)>, + named_args: FxHashMap, + reg_args: FxHashSet, + options: ast::InlineAsmOptions, + options_spans: Vec, } -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, "the legacy LLVM-style asm! syntax is no longer supported"); + err.note("consider migrating to the new asm! syntax specified in RFC 2873"); + err.note("alternatively, switch to llvm_asm! to keep your code working as it is"); -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 first_template = p.parse_expr()?; + let mut args = AsmArgs { + templates: vec![first_template], + operands: vec![], + named_args: FxHashMap::default(), + reg_args: FxHashSet::default(), + options: ast::InlineAsmOptions::empty(), + options_spans: vec![], + }; + + let mut allow_templates = true; + while p.token != token::Eof { + if !p.eat(&token::Comma) { + if allow_templates { + // After a template string, 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()); + } + } + if p.token == token::Eof { + break; + } // accept trailing commas + + // Parse options + if p.eat(&token::Ident(sym::options, false)) { + parse_options(&mut p, &mut args)?; + allow_templates = false; + continue; + } - 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"); + 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)?; + allow_templates = false; + 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 if p.eat(&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 } + } else if allow_templates { + let template = p.parse_expr()?; + // If it can't possibly expand to a string, provide diagnostics here to include other + // things it could have been. + match template.kind { + ast::ExprKind::Lit(ast::Lit { kind: ast::LitKind::Str(..), .. }) => {} + ast::ExprKind::MacCall(..) => {} + _ => { + let errstr = "expected operand, options, or additional template string"; + let mut err = ecx.struct_span_err(template.span, errstr); + err.span_label(template.span, errstr); + return Err(err); + } + } + args.templates.push(template); + continue; + } else { + return Err(p.expect_one_of(&[], &[]).unwrap_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), - }; + allow_templates = false; + let span = span_start.to(p.prev_token.span); + let slot = args.operands.len(); + args.operands.push((op, span)); - // 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()); + // 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 !args.options_spans.is_empty() { + ecx.struct_span_err(span, "arguments are not allowed after options") + .span_labels(args.options_spans.clone(), "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"); } - - asm = s; - asm_str_style = Some(style); + err.emit(); } - Outputs => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !outputs.is_empty() { - p.eat(&token::Comma); - } + 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(); + } + } + } - 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 - } - }; + if args.options.contains(ast::InlineAsmOptions::NOMEM) + && args.options.contains(ast::InlineAsmOptions::READONLY) + { + let spans = args.options_spans.clone(); + ecx.struct_span_err(spans, "the `nomem` and `readonly` options are mutually exclusive") + .emit(); + } + if args.options.contains(ast::InlineAsmOptions::PURE) + && args.options.contains(ast::InlineAsmOptions::NORETURN) + { + let spans = args.options_spans.clone(); + ecx.struct_span_err(spans, "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 spans = args.options_spans.clone(); + ecx.struct_span_err( + spans, + "the `pure` option must be combined with either `nomem` or `readonly`", + ) + .emit(); + } - 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, - }); - } + 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(); } - Inputs => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !inputs.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_spans.clone(), + "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)?; + // Bail out now since this is likely to confuse MIR + return Err(err); + } - 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(); - } + Ok(args) +} - p.expect(&token::OpenDelim(token::Paren))?; - let input = p.parse_expr()?; - p.expect(&token::CloseDelim(token::Paren))?; +/// Report a duplicate option error. +/// +/// This function must be called immediately after the option token is parsed. +/// Otherwise, the suggestion will be incorrect. +fn err_duplicate_option<'a>(p: &mut Parser<'a>, symbol: Symbol, span: Span) { + let mut err = p + .sess + .span_diagnostic + .struct_span_err(span, &format!("the `{}` option was already provided", symbol)); + err.span_label(span, "this option was already provided"); + + // Tool-only output + let mut full_span = span; + if p.token.kind == token::Comma { + full_span = full_span.to(p.token.span); + } + err.tool_only_span_suggestion( + full_span, + "remove this option", + String::new(), + Applicability::MachineApplicable, + ); + + err.emit(); +} - inputs.push((constraint, input)); +/// Try to set the provided option in the provided `AsmArgs`. +/// If it is already set, report a duplicate option error. +/// +/// This function must be called immediately after the option token is parsed. +/// Otherwise, the error will not point to the correct spot. +fn try_set_option<'a>( + p: &mut Parser<'a>, + args: &mut AsmArgs, + symbol: Symbol, + option: ast::InlineAsmOptions, +) { + if !args.options.contains(option) { + args.options |= option; + } else { + err_duplicate_option(p, symbol, p.prev_token.span); + } +} + +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)) { + try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE); + } else if p.eat(&token::Ident(sym::nomem, false)) { + try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM); + } else if p.eat(&token::Ident(sym::readonly, false)) { + try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY); + } else if p.eat(&token::Ident(sym::preserves_flags, false)) { + try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS); + } else if p.eat(&token::Ident(sym::noreturn, false)) { + try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN); + } else if p.eat(&token::Ident(sym::nostack, false)) { + try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK); + } else { + p.expect(&token::Ident(sym::att_syntax, false))?; + try_set_option(p, args, sym::att_syntax, 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); + args.options_spans.push(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 mut template = vec![]; + // 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: FxHashMap = + args.named_args.iter().map(|(&sym, &idx)| (idx, sym)).collect(); + let mut line_spans = Vec::with_capacity(args.templates.len()); + let mut curarg = 0; + + for template_expr in args.templates.into_iter() { + if !template.is_empty() { + template.push(ast::InlineAsmTemplatePiece::String("\n".to_string())); + } + + let msg = "asm template must be a string literal"; + let template_sp = template_expr.span; + let (template_str, template_style, template_span) = + match expr_to_spanned_string(ecx, template_expr, msg) { + Ok(template_part) => template_part, + Err(err) => { + if let Some(mut err) = err { + err.emit(); + } + return DummyResult::raw_expr(sp, true); } + }; + + let str_style = match template_style { + ast::StrStyle::Cooked => None, + ast::StrStyle::Raw(raw) => Some(raw as usize), + }; + + 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, + ); + parser.curarg = curarg; + + let mut unverified_pieces = Vec::new(); + while let Some(piece) = parser.next() { + if !parser.errors.is_empty() { + break; + } else { + unverified_pieces.push(piece); } - Clobbers => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !clobs.is_empty() { - p.eat(&token::Comma); - } + } + + if !parser.errors.is_empty() { + let err = parser.errors.remove(0); + let err_sp = template_span.from_inner(err.span); + let msg = &format!("invalid asm template string: {}", err.description); + let mut e = ecx.struct_span_err(err_sp, msg); + 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); + } - let s = parse_asm_str(&mut p)?; + curarg = parser.curarg; - 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" + let mut arg_spans = parser.arg_places.iter().map(|span| template_span.from_inner(*span)); + for piece in unverified_pieces { + match piece { + parse::Piece::String(s) => { + template.push(ast::InlineAsmTemplatePiece::String(s.to_string())) + } + parse::Piece::NextArgument(arg) => { + let span = arg_spans.next().unwrap_or(template_sp); + + let operand_idx = match arg.position { + parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => { + if idx >= args.operands.len() + || named_pos.contains_key(&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_key(&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 + } + }, + }; + + let mut chars = arg.format.ty.chars(); + let mut modifier = chars.next(); + if chars.next().is_some() { + 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; } - 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"); - } - - 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, + if parser.line_spans.is_empty() { + let template_num_lines = 1 + template_str.matches('\n').count(); + line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines)); + } else { + line_spans.extend(parser.line_spans.iter().map(|span| template_span.from_inner(*span))); + }; + } + + let mut unused_operands = vec![]; + let mut help_str = String::new(); + for (idx, used) in used.into_iter().enumerate() { + if !used { + let msg = if let Some(sym) = named_pos.get(&idx) { + help_str.push_str(&format!(" {{{}}}", sym)); + "named argument never used" + } else { + help_str.push_str(&format!(" {{{}}}", idx)); + "argument never used" + }; + unused_operands.push((args.operands[idx].1, msg)); + } + } + 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.help(&format!( + "if this argument is intentionally unused, \ + consider using it in an asm comment: `\"/*{} */\"`", + help_str + )); + 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.help(&format!( + "if these arguments are intentionally unused, \ + consider using them in an asm comment: `\"/*{} */\"`", + help_str + )); + 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: args.operands, options: args.options, line_spans }; + P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: ast::ExprKind::InlineAsm(P(inline_asm)), + span: sp, + attrs: ast::AttrVec::new(), + tokens: None, + }) +} + +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/cfg_accessible.rs b/src/librustc_builtin_macros/cfg_accessible.rs new file mode 100644 index 0000000000000..3607a4d0d15b6 --- /dev/null +++ b/src/librustc_builtin_macros/cfg_accessible.rs @@ -0,0 +1,54 @@ +//! Implementation of the `#[cfg_accessible(path)]` attribute macro. + +use rustc_ast::ast; +use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; +use rustc_feature::AttributeTemplate; +use rustc_parse::validate_attr; +use rustc_span::symbol::sym; +use rustc_span::Span; + +crate struct Expander; + +fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { + match mi.meta_item_list() { + None => {} + Some([]) => ecx.span_err(mi.span, "`cfg_accessible` path is not specified"), + Some([_, .., l]) => ecx.span_err(l.span(), "multiple `cfg_accessible` paths are specified"), + Some([nmi]) => match nmi.meta_item() { + None => ecx.span_err(nmi.span(), "`cfg_accessible` path cannot be a literal"), + Some(mi) => { + if !mi.is_word() { + ecx.span_err(mi.span, "`cfg_accessible` path cannot accept arguments"); + } + return Some(&mi.path); + } + }, + } + None +} + +impl MultiItemModifier for Expander { + fn expand( + &self, + ecx: &mut ExtCtxt<'_>, + _span: Span, + meta_item: &ast::MetaItem, + item: Annotatable, + ) -> ExpandResult, Annotatable> { + let template = AttributeTemplate { list: Some("path"), ..Default::default() }; + let attr = &ecx.attribute(meta_item.clone()); + validate_attr::check_builtin_attribute(ecx.parse_sess, attr, sym::cfg_accessible, template); + + let path = match validate_input(ecx, meta_item) { + Some(path) => path, + None => return ExpandResult::Ready(Vec::new()), + }; + + let failure_msg = "cannot determine whether the path is accessible or not"; + match ecx.resolver.cfg_accessible(ecx.current_expansion.id, path) { + Ok(true) => ExpandResult::Ready(vec![item]), + Ok(false) => ExpandResult::Ready(Vec::new()), + Err(_) => ExpandResult::Retry(item, failure_msg.into()), + } + } +} diff --git a/src/librustc_builtin_macros/cmdline_attrs.rs b/src/librustc_builtin_macros/cmdline_attrs.rs index 7ddbf08306b72..093815dbbcd4f 100644 --- a/src/librustc_builtin_macros/cmdline_attrs.rs +++ b/src/librustc_builtin_macros/cmdline_attrs.rs @@ -3,7 +3,6 @@ use rustc_ast::ast::{self, AttrItem, AttrStyle}; use rustc_ast::attr::mk_attr; use rustc_ast::token; -use rustc_expand::panictry; use rustc_session::parse::ParseSess; use rustc_span::FileName; @@ -16,7 +15,13 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) - ); let start_span = parser.token.span; - let AttrItem { path, args } = panictry!(parser.parse_attr_item()); + let AttrItem { path, args } = match parser.parse_attr_item() { + Ok(ai) => ai, + Err(mut err) => { + err.emit(); + continue; + } + }; let end_span = parser.token.span; if parser.token != token::Eof { parse_sess.span_diagnostic.span_err(start_span.to(end_span), "invalid crate attribute"); 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..fdf05ac3880b5 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 { @@ -52,6 +52,7 @@ pub fn expand_concat_idents<'cx>( kind: ast::ExprKind::Path(None, ast::Path::from_ident(self.ident)), span: self.ident.span, attrs: ast::AttrVec::new(), + tokens: None, })) } 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/decodable.rs b/src/librustc_builtin_macros/deriving/decodable.rs index ac5d08ba62d0d..64a810bdcf687 100644 --- a/src/librustc_builtin_macros/deriving/decodable.rs +++ b/src/librustc_builtin_macros/deriving/decodable.rs @@ -87,7 +87,7 @@ fn decodable_substructure( let blkarg = cx.ident_of("_d", trait_span); let blkdecoder = cx.expr_ident(trait_span, blkarg); - return match *substr.fields { + match *substr.fields { StaticStruct(_, ref summary) => { let nfields = match *summary { Unnamed(ref fields, _) => fields.len(), @@ -178,7 +178,7 @@ fn decodable_substructure( ) } _ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)"), - }; + } } /// Creates a decoder for a single enum variant/struct: diff --git a/src/librustc_builtin_macros/deriving/default.rs b/src/librustc_builtin_macros/deriving/default.rs index cb85a0b1a10cc..27d5263320041 100644 --- a/src/librustc_builtin_macros/deriving/default.rs +++ b/src/librustc_builtin_macros/deriving/default.rs @@ -53,7 +53,7 @@ fn default_substructure( let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]); let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new()); - return match *substr.fields { + match *substr.fields { StaticStruct(_, ref summary) => match *summary { Unnamed(ref fields, is_tuple) => { if !is_tuple { @@ -83,5 +83,5 @@ fn default_substructure( DummyResult::raw_expr(trait_span, true) } _ => cx.span_bug(trait_span, "method in `derive(Default)`"), - }; + } } diff --git a/src/librustc_builtin_macros/deriving/encodable.rs b/src/librustc_builtin_macros/deriving/encodable.rs index 9073085381ac1..54926ec3fd502 100644 --- a/src/librustc_builtin_macros/deriving/encodable.rs +++ b/src/librustc_builtin_macros/deriving/encodable.rs @@ -173,7 +173,7 @@ fn encodable_substructure( ], )); - return match *substr.fields { + match *substr.fields { Struct(_, ref fields) => { let emit_struct_field = cx.ident_of("emit_struct_field", trait_span); let mut stmts = Vec::new(); @@ -283,5 +283,5 @@ fn encodable_substructure( } _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"), - }; + } } diff --git a/src/librustc_builtin_macros/deriving/generic/mod.rs b/src/librustc_builtin_macros/deriving/generic/mod.rs index 84ed6e96aafc8..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 { @@ -489,7 +481,6 @@ impl<'a> TraitDef<'a> { // set earlier; see // librustc_expand/expand.rs:MacroExpander::fully_expand_fragment() // librustc_expand/base.rs:Annotatable::derive_allowed() - return; } } } @@ -621,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 { @@ -770,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, @@ -802,7 +792,6 @@ impl<'a> TraitDef<'a> { cx, self, enum_def, - type_attrs, type_ident, self_args, &nonself_args[..], @@ -817,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, @@ -1057,8 +1014,9 @@ impl<'a> MethodDef<'a> { self_: field, other: other_fields .iter_mut() - .map(|l| match l.next().unwrap() { - (.., ex, _) => ex, + .map(|l| { + let (.., ex, _) = l.next().unwrap(); + ex }) .collect(), attrs, @@ -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 bd54a73531197..609feb6f259d6 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 @@ -76,8 +76,8 @@ impl<'a> Path<'a> { self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)).collect(); let params = lt .into_iter() - .map(|lt| GenericArg::Lifetime(lt)) - .chain(tys.into_iter().map(|ty| GenericArg::Type(ty))) + .map(GenericArg::Lifetime) + .chain(tys.into_iter().map(GenericArg::Type)) .collect(); match self.kind { @@ -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 @@ -216,7 +216,11 @@ fn mk_ty_param( } fn mk_generics(params: Vec, span: Span) -> Generics { - Generics { params, where_clause: ast::WhereClause { predicates: Vec::new(), span }, span } + Generics { + params, + where_clause: ast::WhereClause { has_where_token: false, predicates: Vec::new(), span }, + span, + } } /// Lifetimes and bounds on type parameters diff --git a/src/librustc_builtin_macros/deriving/mod.rs b/src/librustc_builtin_macros/deriving/mod.rs index 5ba9d3800e118..9660cade38241 100644 --- a/src/librustc_builtin_macros/deriving/mod.rs +++ b/src/librustc_builtin_macros/deriving/mod.rs @@ -2,8 +2,8 @@ use rustc_ast::ast::{self, ItemKind, MetaItem}; use rustc_ast::ptr::P; -use rustc_expand::base::{Annotatable, ExtCtxt, MultiItemModifier}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; macro path_local($x:ident) { @@ -48,13 +48,13 @@ impl MultiItemModifier for BuiltinDerive { span: Span, meta_item: &MetaItem, item: Annotatable, - ) -> Vec { + ) -> ExpandResult, Annotatable> { // FIXME: Built-in derives often forget to give spans contexts, // so we are doing it here in a centralized way. let span = ecx.with_def_site_ctxt(span); let mut items = Vec::new(); (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a)); - items + ExpandResult::Ready(items) } } @@ -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..6c3a1ce0958ec 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; @@ -22,8 +22,10 @@ pub fn expand_option_env<'cx>( }; let sp = cx.with_def_site_ctxt(sp); - let e = match env::var(&var.as_str()) { - Err(..) => { + let value = env::var(&var.as_str()).ok().as_deref().map(Symbol::intern); + cx.parse_sess.env_depinfo.borrow_mut().insert((Symbol::intern(&var), value)); + let e = match value { + None => { let lt = cx.lifetime(sp, Ident::new(kw::StaticLifetime, sp)); cx.expr_path(cx.path_all( sp, @@ -37,10 +39,10 @@ pub fn expand_option_env<'cx>( ))], )) } - Ok(s) => cx.expr_call_global( + Some(value) => cx.expr_call_global( sp, cx.std_path(&[sym::option, sym::Option, sym::Some]), - vec![cx.expr_str(sp, Symbol::intern(&s))], + vec![cx.expr_str(sp, value)], ), }; MacEager::expr(e) @@ -77,12 +79,15 @@ pub fn expand_env<'cx>( return DummyResult::any(sp); } - let e = match env::var(&*var.as_str()) { - Err(_) => { + let sp = cx.with_def_site_ctxt(sp); + let value = env::var(&*var.as_str()).ok().as_deref().map(Symbol::intern); + cx.parse_sess.env_depinfo.borrow_mut().insert((var, value)); + let e = match value { + None => { cx.span_err(sp, &msg.as_str()); return DummyResult::any(sp); } - Ok(s) => cx.expr_str(sp, Symbol::intern(&s)), + Some(value) => cx.expr_str(sp, value), }; MacEager::expr(e) } diff --git a/src/librustc_builtin_macros/format.rs b/src/librustc_builtin_macros/format.rs index 2883159a9f34c..e574b076bf84c 100644 --- a/src/librustc_builtin_macros/format.rs +++ b/src/librustc_builtin_macros/format.rs @@ -1,8 +1,6 @@ use ArgumentType::*; use Position::*; -use fmt_macros as parse; - use rustc_ast::ast; use rustc_ast::ptr::P; use rustc_ast::token; @@ -10,7 +8,8 @@ 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_parse_format as parse; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span}; use std::borrow::Cow; @@ -108,8 +107,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 +321,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 +369,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 +499,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 +528,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 +787,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 +885,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 +951,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 cc3c403450e04..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) @@ -359,7 +355,7 @@ pub mod printf { // // Note: `move` used to capture copies of the cursors as they are *now*. let fallback = move || { - return Some(( + Some(( Substitution::Format(Format { span: start.slice_between(next).unwrap(), parameter: None, @@ -371,7 +367,7 @@ pub mod printf { position: InnerSpan::new(start.at, next.at), }), next.slice_after(), - )); + )) }; // Next parsing state. @@ -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 9a8b0a87cb793..f56d8a372a73e 100644 --- a/src/librustc_builtin_macros/lib.rs +++ b/src/librustc_builtin_macros/lib.rs @@ -5,7 +5,9 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] +#![feature(inner_deref)] #![feature(nll)] +#![feature(or_patterns)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] @@ -13,15 +15,15 @@ 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; mod cfg; +mod cfg_accessible; mod compile_error; mod concat; mod concat_idents; @@ -31,6 +33,7 @@ mod format; mod format_foreign; mod global_allocator; mod global_asm; +mod llvm_asm; mod log_syntax; mod source_util; mod test; @@ -76,6 +79,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, @@ -85,6 +89,7 @@ pub fn register_builtin_macros(resolver: &mut dyn Resolver, edition: Edition) { register_attr! { bench: test::expand_bench, + cfg_accessible: cfg_accessible::Expander, global_allocator: global_allocator::expand, test: test::expand_test, test_case: test::expand_test_case, diff --git a/src/librustc_builtin_macros/llvm_asm.rs b/src/librustc_builtin_macros/llvm_asm.rs new file mode 100644 index 0000000000000..0f4efc153b941 --- /dev/null +++ b/src/librustc_builtin_macros/llvm_asm.rs @@ -0,0 +1,300 @@ +// 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(), + tokens: None, + })) +} + +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 179b013342633..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; @@ -10,17 +10,18 @@ use rustc_expand::base::{ExtCtxt, Resolver}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_session::parse::ParseSess; use rustc_span::hygiene::AstPass; -use rustc_span::symbol::{kw, sym}; +use rustc_span::source_map::SourceMap; +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 { @@ -44,6 +45,7 @@ struct CollectProcMacros<'a> { macros: Vec, in_root: bool, handler: &'a rustc_errors::Handler, + source_map: &'a SourceMap, is_proc_macro_crate: bool, is_test_crate: bool, } @@ -59,12 +61,13 @@ pub fn inject( handler: &rustc_errors::Handler, ) -> ast::Crate { let ecfg = ExpansionConfig::default("proc_macro".to_string()); - let mut cx = ExtCtxt::new(sess, ecfg, resolver); + let mut cx = ExtCtxt::new(sess, ecfg, resolver, None); let mut collect = CollectProcMacros { macros: Vec::new(), in_root: true, handler, + source_map: sess.source_map(), is_proc_macro_crate, is_test_crate, }; @@ -195,7 +198,7 @@ impl<'a> CollectProcMacros<'a> { } else { "functions tagged with `#[proc_macro_derive]` must be `pub`" }; - self.handler.span_err(item.span, msg); + self.handler.span_err(self.source_map.guess_head_span(item.span), msg); } } @@ -214,7 +217,7 @@ impl<'a> CollectProcMacros<'a> { } else { "functions tagged with `#[proc_macro_attribute]` must be `pub`" }; - self.handler.span_err(item.span, msg); + self.handler.span_err(self.source_map.guess_head_span(item.span), msg); } } @@ -233,7 +236,7 @@ impl<'a> CollectProcMacros<'a> { } else { "functions tagged with `#[proc_macro]` must be `pub`" }; - self.handler.span_err(item.span, msg); + self.handler.span_err(self.source_map.guess_head_span(item.span), msg); } } } @@ -244,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(item.span, msg); + self.handler.span_err(self.source_map.guess_head_span(item.span), msg); } } @@ -295,7 +298,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { let attr = match found_attr { None => { - self.check_not_pub_in_root(&item.vis, 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; @@ -477,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/source_util.rs b/src/librustc_builtin_macros/source_util.rs index 5ad72a7443dd2..1b164eae5a345 100644 --- a/src/librustc_builtin_macros/source_util.rs +++ b/src/librustc_builtin_macros/source_util.rs @@ -4,13 +4,14 @@ use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; use rustc_expand::base::{self, *}; -use rustc_expand::panictry; -use rustc_parse::{self, new_sub_parser_from_file, parser::Parser, DirectoryOwnership}; +use rustc_expand::module::DirectoryOwnership; +use rustc_parse::{self, new_parser_from_file, parser::Parser}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_span::symbol::Symbol; use rustc_span::{self, Pos, Span}; use smallvec::SmallVec; +use std::rc::Rc; use rustc_data_structures::sync::Lrc; @@ -101,27 +102,36 @@ pub fn expand_include<'cx>( None => return DummyResult::any(sp), }; // The file will be added to the code map by the parser - let file = match cx.resolve_path(file, sp) { + let mut file = match cx.resolve_path(file, sp) { Ok(f) => f, Err(mut err) => { err.emit(); return DummyResult::any(sp); } }; - let directory_ownership = DirectoryOwnership::Owned { relative: None }; - let p = new_sub_parser_from_file(cx.parse_sess(), &file, directory_ownership, None, sp); + let p = new_parser_from_file(cx.parse_sess(), &file, Some(sp)); + + // If in the included file we have e.g., `mod bar;`, + // then the path of `bar.rs` should be relative to the directory of `file`. + // See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion. + // `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained. + file.pop(); + cx.current_expansion.directory_ownership = DirectoryOwnership::Owned { relative: None }; + let mod_path = cx.current_expansion.module.mod_path.clone(); + cx.current_expansion.module = Rc::new(ModuleData { mod_path, directory: file }); struct ExpandResult<'a> { p: Parser<'a>, + node_id: ast::NodeId, } impl<'a> base::MacResult for ExpandResult<'a> { fn make_expr(mut self: Box>) -> Option> { - let r = panictry!(self.p.parse_expr()); + let r = base::parse_expr(&mut self.p)?; if self.p.token != token::Eof { self.p.sess.buffer_lint( &INCOMPLETE_INCLUDE, self.p.token.span, - ast::CRATE_NODE_ID, + self.node_id, "include macro expected single expression in source", ); } @@ -131,18 +141,17 @@ pub fn expand_include<'cx>( fn make_items(mut self: Box>) -> Option; 1]>> { let mut ret = SmallVec::new(); while self.p.token != token::Eof { - match panictry!(self.p.parse_item()) { - Some(item) => ret.push(item), - None => { + match self.p.parse_item() { + Err(mut err) => { + err.emit(); + break; + } + Ok(Some(item)) => ret.push(item), + Ok(None) => { let token = pprust::token_to_string(&self.p.token); - self.p - .sess - .span_diagnostic - .span_fatal( - self.p.token.span, - &format!("expected item, found `{}`", token), - ) - .raise(); + let msg = format!("expected item, found `{}`", token); + self.p.struct_span_err(self.p.token.span, &msg).emit(); + break; } } } @@ -150,7 +159,7 @@ pub fn expand_include<'cx>( } } - Box::new(ExpandResult { p }) + Box::new(ExpandResult { p, node_id: cx.resolver.lint_node_id(cx.current_expansion.id) }) } // include_str! : read the given file, insert it as a literal string expr diff --git a/src/librustc_builtin_macros/standard_library_imports.rs b/src/librustc_builtin_macros/standard_library_imports.rs index 30403f6dc41c1..cd3773c76c483 100644 --- a/src/librustc_builtin_macros/standard_library_imports.rs +++ b/src/librustc_builtin_macros/standard_library_imports.rs @@ -39,7 +39,7 @@ pub fn inject( let call_site = DUMMY_SP.with_call_site_ctxt(expn_id); let ecfg = ExpansionConfig::default("std_lib_injection".to_string()); - let cx = ExtCtxt::new(sess, ecfg, resolver); + let cx = ExtCtxt::new(sess, ecfg, resolver, None); // .rev() to preserve ordering above in combination with insert(0, ...) for &name in names.iter().rev() { @@ -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 39009ca27f102..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; @@ -74,16 +74,16 @@ pub fn expand_test_or_bench( return vec![]; } - let item = if let Annotatable::Item(i) = item { - i - } else { - cx.parse_sess - .span_diagnostic - .span_fatal( - item.span(), + let item = match item { + Annotatable::Item(i) => i, + other => { + cx.struct_span_err( + other.span(), "`#[test]` attribute is only allowed on non associated functions", ) - .raise(); + .emit(); + return vec![other]; + } }; if let ast::ItemKind::MacCall(_) = item.kind { @@ -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 15997a27fadf2..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}; @@ -202,7 +202,7 @@ fn generate_test_harness( let mut econfig = ExpansionConfig::default("test".to_string()); econfig.features = Some(features); - let ext_cx = ExtCtxt::new(sess, econfig, resolver); + let ext_cx = ExtCtxt::new(sess, econfig, resolver, None); let expn_id = ext_cx.resolver.expansion_for_ast_pass( DUMMY_SP, @@ -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; @@ -345,14 +347,14 @@ fn is_test_case(i: &ast::Item) -> bool { fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option { let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?; - test_attr.meta_item_list().map(|meta_list| { - if meta_list.len() != 1 { - sd.span_fatal(test_attr.span, "`#![test_runner(..)]` accepts exactly 1 argument") - .raise() - } - match meta_list[0].meta_item() { - Some(meta_item) if meta_item.is_word() => meta_item.path.clone(), - _ => sd.span_fatal(test_attr.span, "`test_runner` argument must be a path").raise(), - } - }) + let meta_list = test_attr.meta_item_list()?; + let span = test_attr.span; + match &*meta_list { + [single] => match single.meta_item() { + Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()), + _ => sd.struct_span_err(span, "`test_runner` argument must be a path").emit(), + }, + _ => sd.struct_span_err(span, "`#![test_runner(..)]` accepts exactly 1 argument").emit(), + } + None } diff --git a/src/librustc_builtin_macros/util.rs b/src/librustc_builtin_macros/util.rs index 8ef76a8657e1e..b486eadd1a8be 100644 --- a/src/librustc_builtin_macros/util.rs +++ b/src/librustc_builtin_macros/util.rs @@ -6,7 +6,7 @@ use rustc_span::Symbol; pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) { // All the built-in macro attributes are "words" at the moment. - let template = AttributeTemplate::only_word(); + let template = AttributeTemplate { word: true, ..Default::default() }; let attr = ecx.attribute(meta_item.clone()); validate_attr::check_builtin_attribute(ecx.parse_sess, &attr, name, template); } diff --git a/src/librustc_codegen_llvm/Cargo.toml b/src/librustc_codegen_llvm/Cargo.toml index 0776cb19760d5..bedefcc30ed8c 100644 --- a/src/librustc_codegen_llvm/Cargo.toml +++ b/src/librustc_codegen_llvm/Cargo.toml @@ -16,11 +16,10 @@ 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" } -rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_errors = { path = "../librustc_errors" } rustc_feature = { path = "../librustc_feature" } @@ -30,7 +29,7 @@ rustc_incremental = { path = "../librustc_incremental" } rustc_index = { path = "../librustc_index" } rustc_llvm = { path = "../librustc_llvm" } rustc_session = { path = "../librustc_session" } -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } rustc_target = { path = "../librustc_target" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_ast = { path = "../librustc_ast" } 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..099c402703d09 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) })+ @@ -377,6 +375,8 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> { match self.conv { Conv::C | Conv::Rust => llvm::CCallConv, Conv::AmdGpuKernel => llvm::AmdGpuKernel, + Conv::AvrInterrupt => llvm::AvrInterrupt, + Conv::AvrNonBlockingInterrupt => llvm::AvrNonBlockingInterrupt, Conv::ArmAapcs => llvm::ArmAapcsCallConv, Conv::Msp430Intr => llvm::Msp430Intr, Conv::PtxKernel => llvm::PtxKernel, @@ -396,6 +396,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 +436,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 +450,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..2f9e49591c381 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_span::Span; +use rustc_middle::span_bug; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_span::{Pos, 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,216 @@ 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, + line_spans: &[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 => {} + InlineAsmArch::Nvptx64 => {} + InlineAsmArch::Hexagon => {} + } + } + 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, + line_spans, + ) + .unwrap_or_else(|| span_bug!(line_spans[0], "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 +341,8 @@ fn inline_asm_call( output: &'ll llvm::Type, volatile: bool, alignstack: bool, - dia: ::rustc_ast::ast::AsmDialect, + dia: LlvmAsmDialect, + line_spans: &[Span], ) -> Option<&'ll Value> { let volatile = if volatile { llvm::True } else { llvm::False }; let alignstack = if alignstack { llvm::True } else { llvm::False }; @@ -168,10 +372,365 @@ 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, + ); + + // srcloc contains one integer for each line of assembly code. + // Unfortunately this isn't enough to encode a full span so instead + // we just encode the start position of each line. + // FIXME: Figure out a way to pass the entire line spans. + let mut srcloc = vec![]; + if dia == LlvmAsmDialect::Intel && line_spans.len() > 1 { + // LLVM inserts an extra line to add the ".intel_syntax", so add + // a dummy srcloc entry for it. + // + // Don't do this if we only have 1 line span since that may be + // due to the asm template string coming from a macro. LLVM will + // default to the first srcloc for lines that don't have an + // associated srcloc. + srcloc.push(bx.const_i32(0)); + } + srcloc.extend(line_spans.iter().map(|span| bx.const_i32(span.lo().to_u32() as i32))); + let md = llvm::LLVMMDNodeInContext(bx.llcx, srcloc.as_ptr(), srcloc.len() as u32); + llvm::LLVMSetMetadata(call, kind, md); + + 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::Hexagon(HexagonInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => "h", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => "r", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => "l", + 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::Hexagon(_) => None, + InlineAsmRegClass::Nvptx(_) => None, + 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::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(), + 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 a9e4fdba03036..6234ade8a1612 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::session::config::{OptLevel, Sanitizer}; -use rustc::session::Session; -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_target::abi::call::Conv; -use rustc_target::spec::PanicStrategy; +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, SanitizerSet}; +use rustc_session::Session; -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; @@ -48,26 +45,16 @@ fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) { /// Apply LLVM sanitize attributes. #[inline] -pub fn sanitize(cx: &CodegenCx<'ll, '_>, codegen_fn_flags: CodegenFnAttrFlags, llfn: &'ll Value) { - if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer { - match *sanitizer { - Sanitizer::Address => { - if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) { - llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn); - } - } - Sanitizer::Memory => { - if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) { - llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn); - } - } - Sanitizer::Thread => { - if !codegen_fn_flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) { - llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); - } - } - Sanitizer::Leak => {} - } +pub fn sanitize(cx: &CodegenCx<'ll, '_>, no_sanitize: SanitizerSet, llfn: &'ll Value) { + let enabled = cx.tcx.sess.opts.debugging_opts.sanitizer - no_sanitize; + if enabled.contains(SanitizerSet::ADDRESS) { + llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn); + } + if enabled.contains(SanitizerSet::MEMORY) { + llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn); + } + if enabled.contains(SanitizerSet::THREAD) { + llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); } } @@ -77,12 +64,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 +72,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"), + ); } } @@ -141,9 +113,14 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { // Currently stack probes seem somewhat incompatible with the address // 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, - _ => {} + if cx + .sess() + .opts + .debugging_opts + .sanitizer + .intersects(SanitizerSet::ADDRESS | SanitizerSet::THREAD) + { + return; } // probestack doesn't play nice either with `-C profile-generate`. @@ -246,12 +223,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 +247,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 +265,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,53 +279,19 @@ 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); } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { Attribute::NoAlias.apply_llfn(llvm::AttributePlace::ReturnValue, llfn); } - 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 - } - }, - ); + sanitize(cx, codegen_fn_attrs.no_sanitize, llfn); // Always annotate functions with the target-cpu they are compiled for. // Without this, ThinLTO won't inline Rust functions into Clang generated @@ -410,15 +348,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() } }; @@ -427,8 +362,8 @@ pub fn provide(providers: &mut Providers<'_>) { pub fn provide_extern(providers: &mut Providers<'_>) { providers.wasm_import_module_map = |tcx, cnum| { - // Build up a map from DefId to a `NativeLibrary` structure, where - // `NativeLibrary` internally contains information about + // Build up a map from DefId to a `NativeLib` structure, where + // `NativeLib` internally contains information about // `#[link(wasm_import_module = "...")]` for example. let native_libs = tcx.native_libraries(cnum); @@ -450,7 +385,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 455b7086212ba..a115a1e95163e 100644 --- a/src/librustc_codegen_llvm/back/archive.rs +++ b/src/librustc_codegen_llvm/back/archive.rs @@ -9,9 +9,9 @@ use std::str; use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind}; -use rustc::session::Session; 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; struct ArchiveConfig<'a> { @@ -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; } @@ -146,7 +146,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> { } // ok, don't skip this - return false; + false }) } diff --git a/src/librustc_codegen_llvm/back/bytecode.rs b/src/librustc_codegen_llvm/back/bytecode.rs deleted file mode 100644 index db29556e70ccc..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); - } - - return 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(); - return 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 310cae978bf5e..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::session::config::{self, Lto}; 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, 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 0c243128104e7..54271d3dd0491 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,26 @@ use crate::back::profiling::{ use crate::base; use crate::common; use crate::consts; -use crate::context::{get_reloc_model, is_pie_binary}; 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::session::config::{self, Lto, OutputType, Passes, Sanitizer, SwitchWithOptPath}; -use rustc::session::Session; -use rustc::ty::TyCtxt; -use rustc_codegen_ssa::back::write::{run_assembler, CodegenContext, 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_errors::{FatalError, Handler, Level}; 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, SanitizerSet, SwitchWithOptPath}; +use rustc_session::Session; +use rustc_span::symbol::sym; +use rustc_span::InnerSpan; +use rustc_target::spec::{CodeModel, RelocModel}; use libc::{c_char, c_int, c_uint, c_void, size_t}; use std::ffi::CString; @@ -35,30 +36,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 +61,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 +97,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 +131,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; @@ -165,7 +141,7 @@ pub fn target_machine_factory( // lower atomic operations to single-threaded operations. if singlethread && sess.target.target.llvm_target.contains("wasm32") - && features.iter().any(|s| *s == "+atomics") + && sess.target_features.contains(&sym::atomics) { singlethread = false; } @@ -175,12 +151,18 @@ 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 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,6 @@ pub fn target_machine_factory( reloc_model, opt_level, use_softfp, - is_pie_binary, ffunction_sections, fdata_sections, trap_unreachable, @@ -200,6 +181,7 @@ pub fn target_machine_factory( asm_comments, emit_stack_size_section, relax_elf_relocations, + use_init_array, ) }; @@ -258,12 +240,25 @@ impl<'a> Drop for DiagnosticHandlers<'a> { } } -unsafe extern "C" fn report_inline_asm( +fn report_inline_asm( cgcx: &CodegenContext, - msg: &str, - cookie: c_uint, + msg: String, + level: llvm::DiagnosticLevel, + mut cookie: c_uint, + source: Option<(String, Vec)>, ) { - cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_owned()); + // In LTO build we may get srcloc values from other crates which are invalid + // since they use a different source map. To be safe we just suppress these + // in LTO builds. + if matches!(cgcx.lto, Lto::Fat | Lto::Thin) { + cookie = 0; + } + let level = match level { + llvm::DiagnosticLevel::Error => Level::Error, + llvm::DiagnosticLevel::Warning => Level::Warning, + llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note, + }; + cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, level, source); } unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, user: *const c_void, cookie: c_uint) { @@ -272,10 +267,39 @@ unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, user: *const c_void } let (cgcx, _) = *(user as *const (&CodegenContext, &Handler)); - let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s)) - .expect("non-UTF8 SMDiagnostic"); + // Recover the post-substitution assembly code from LLVM for better + // diagnostics. + let mut have_source = false; + let mut buffer = String::new(); + let mut level = llvm::DiagnosticLevel::Error; + let mut loc = 0; + let mut ranges = [0; 8]; + let mut num_ranges = ranges.len() / 2; + let msg = llvm::build_string(|msg| { + buffer = llvm::build_string(|buffer| { + have_source = llvm::LLVMRustUnpackSMDiagnostic( + diag, + msg, + buffer, + &mut level, + &mut loc, + ranges.as_mut_ptr(), + &mut num_ranges, + ); + }) + .expect("non-UTF8 inline asm"); + }) + .expect("non-UTF8 SMDiagnostic"); - report_inline_asm(cgcx, &msg, cookie); + let source = have_source.then(|| { + let mut spans = vec![InnerSpan::new(loc as usize, loc as usize)]; + for i in 0..num_ranges { + spans.push(InnerSpan::new(ranges[i * 2] as usize, ranges[i * 2 + 1] as usize)); + } + (buffer, spans) + }); + + report_inline_asm(cgcx, msg, level, cookie, source); } unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) { @@ -286,7 +310,13 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void match llvm::diagnostic::Diagnostic::unpack(info) { llvm::diagnostic::InlineAsm(inline) => { - report_inline_asm(cgcx, &llvm::twine_to_string(inline.message), inline.cookie); + report_inline_asm( + cgcx, + llvm::twine_to_string(inline.message), + inline.level, + inline.cookie, + None, + ); } llvm::diagnostic::Optimization(opt) => { @@ -347,7 +377,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( @@ -365,12 +395,13 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO; // Sanitizer instrumentation is only inserted during the pre-link optimization stage. let sanitizer_options = if !is_lto { - config.sanitizer.as_ref().map(|s| llvm::SanitizerOptions { - sanitize_memory: *s == Sanitizer::Memory, - sanitize_thread: *s == Sanitizer::Thread, - sanitize_address: *s == Sanitizer::Address, - sanitize_recover: config.sanitizer_recover.contains(s), + Some(llvm::SanitizerOptions { + sanitize_address: config.sanitizer.contains(SanitizerSet::ADDRESS), + sanitize_address_recover: config.sanitizer_recover.contains(SanitizerSet::ADDRESS), + sanitize_memory: config.sanitizer.contains(SanitizerSet::MEMORY), + sanitize_memory_recover: config.sanitizer_recover.contains(SanitizerSet::MEMORY), sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int, + sanitize_thread: config.sanitizer.contains(SanitizerSet::THREAD), }) } else { None @@ -402,6 +433,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()), @@ -570,25 +602,18 @@ pub(crate) unsafe fn optimize( } unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static mut llvm::Pass>) { - let sanitizer = match &config.sanitizer { - None => return, - Some(s) => s, - }; - - let recover = config.sanitizer_recover.contains(sanitizer); - match sanitizer { - Sanitizer::Address => { - passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover)); - passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover)); - } - Sanitizer::Memory => { - let track_origins = config.sanitizer_memory_track_origins as c_int; - passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover)); - } - Sanitizer::Thread => { - passes.push(llvm::LLVMRustCreateThreadSanitizerPass()); - } - Sanitizer::Leak => {} + if config.sanitizer.contains(SanitizerSet::ADDRESS) { + let recover = config.sanitizer_recover.contains(SanitizerSet::ADDRESS); + passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover)); + passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover)); + } + if config.sanitizer.contains(SanitizerSet::MEMORY) { + let track_origins = config.sanitizer_memory_track_origins as c_int; + let recover = config.sanitizer_recover.contains(SanitizerSet::MEMORY); + passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover)); + } + if config.sanitizer.contains(SanitizerSet::THREAD) { + passes.push(llvm::LLVMRustCreateThreadSanitizerPass()); } } @@ -634,30 +659,24 @@ pub(crate) unsafe fn codegen( f(cpm) } - // If we don't have the integrated assembler, then we need to emit asm - // from LLVM and use `gcc` to create the object file. - let asm_to_obj = config.emit_obj && config.no_integrated_as; - - // Change what we write and cleanup based on whether obj files are - // just llvm bitcode. In that case write bitcode, and possibly - // delete the bitcode if it wasn't requested. Don't generate the - // machine code, instead copy the .o file from the .bc - let write_bc = config.emit_bc || config.obj_is_bitcode; - let rm_bc = !config.emit_bc && config.obj_is_bitcode; - let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm_to_obj; - let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode; + // Two things to note: + // - If object files are just LLVM bitcode we write bitcode, copy it to + // the .o file, and delete the bitcode if it wasn't otherwise + // requested. + // - If we don't have the integrated assembler then we need to emit + // asm from LLVM and use `gcc` to create the object file. let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); - if write_bc || config.emit_bc_compressed || config.embed_bitcode { + if config.bitcode_needed() { let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &module.name[..]); let thin = ThinBuffer::new(llmod); let data = thin.data(); - if write_bc { + if config.emit_bc || config.emit_obj == EmitObj::Bitcode { let _timer = cgcx.prof.generic_activity_with_arg( "LLVM_module_codegen_emit_bitcode", &module.name[..], @@ -668,101 +687,83 @@ pub(crate) unsafe fn codegen( } } - if config.embed_bitcode { + 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_marker { - embed_bitcode(cgcx, llcx, llmod, None); } - { - if config.emit_ir { - let _timer = cgcx - .prof - .generic_activity_with_arg("LLVM_module_codegen_emit_ir", &module.name[..]); - let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); - let out_c = path_to_c_string(&out); - - extern "C" fn demangle_callback( - input_ptr: *const c_char, - input_len: size_t, - output_ptr: *mut c_char, - output_len: size_t, - ) -> size_t { - let input = unsafe { - slice::from_raw_parts(input_ptr as *const u8, input_len as usize) - }; - - let input = match str::from_utf8(input) { - Ok(s) => s, - Err(_) => return 0, - }; - - let output = unsafe { - slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize) - }; - let mut cursor = io::Cursor::new(output); - - let demangled = match rustc_demangle::try_demangle(input) { - Ok(d) => d, - Err(_) => return 0, - }; - - if write!(cursor, "{:#}", demangled).is_err() { - // Possible only if provided buffer is not big enough - return 0; - } - - cursor.position() as size_t + if config.emit_ir { + let _timer = cgcx + .prof + .generic_activity_with_arg("LLVM_module_codegen_emit_ir", &module.name[..]); + let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); + let out_c = path_to_c_string(&out); + + extern "C" fn demangle_callback( + input_ptr: *const c_char, + input_len: size_t, + output_ptr: *mut c_char, + output_len: size_t, + ) -> size_t { + let input = + unsafe { slice::from_raw_parts(input_ptr as *const u8, input_len as usize) }; + + let input = match str::from_utf8(input) { + Ok(s) => s, + Err(_) => return 0, + }; + + let output = unsafe { + slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize) + }; + let mut cursor = io::Cursor::new(output); + + let demangled = match rustc_demangle::try_demangle(input) { + Ok(d) => d, + Err(_) => return 0, + }; + + if write!(cursor, "{:#}", demangled).is_err() { + // Possible only if provided buffer is not big enough + return 0; } - let result = llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback); - result.into_result().map_err(|()| { - let msg = format!("failed to write LLVM IR to {}", out.display()); - llvm_err(diag_handler, &msg) - })?; + cursor.position() as size_t } - if config.emit_asm || asm_to_obj { - let _timer = cgcx - .prof - .generic_activity_with_arg("LLVM_module_codegen_emit_asm", &module.name[..]); - let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + let result = llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback); + result.into_result().map_err(|()| { + let msg = format!("failed to write LLVM IR to {}", out.display()); + llvm_err(diag_handler, &msg) + })?; + } - // 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 }; - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file( - diag_handler, - tm, - cpm, - llmod, - &path, - llvm::FileType::AssemblyFile, - ) - })?; - } + 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 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 write_obj { + match config.emit_obj { + EmitObj::ObjectCode(_) => { let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]); @@ -776,39 +777,31 @@ pub(crate) unsafe fn codegen( llvm::FileType::ObjectFile, ) })?; - } else if asm_to_obj { - 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)); + 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 copy_bc_to_obj { - 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 rm_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, )) } @@ -823,8 +816,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. @@ -835,9 +828,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), @@ -846,14 +840,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), @@ -863,6 +858,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( @@ -921,10 +967,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 04c084e459eab..e99fb8dcae1e5 100644 --- a/src/librustc_codegen_llvm/base.rs +++ b/src/librustc_codegen_llvm/base.rs @@ -23,18 +23,18 @@ 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::session::config::DebugInfo; -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::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, SanitizerSet}; use rustc_span::symbol::Symbol; use std::ffi::CString; @@ -71,8 +71,7 @@ pub fn write_compressed_metadata<'tcx>( // flags, at least for ELF outputs, so that the // metadata doesn't get loaded into memory. let directive = format!(".section {}", section_name); - let directive = CString::new(directive).unwrap(); - llvm::LLVMSetModuleInlineAsm(metadata_llmod, directive.as_ptr()) + llvm::LLVMSetModuleInlineAsm2(metadata_llmod, directive.as_ptr().cast(), directive.len()) } } @@ -101,7 +100,7 @@ pub fn compile_codegen_unit( tcx: TyCtxt<'tcx>, cgu_name: Symbol, ) -> (ModuleCodegen, u64) { - let prof_timer = tcx.prof.generic_activity("codegen_module"); + let prof_timer = tcx.prof.generic_activity_with_arg("codegen_module", cgu_name.to_string()); let start_time = Instant::now(); let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); @@ -133,7 +132,7 @@ pub fn compile_codegen_unit( // If this codegen unit contains the main function, also create the // wrapper here if let Some(entry) = maybe_create_entry_wrapper::>(&cx) { - attributes::sanitize(&cx, CodegenFnAttrFlags::empty(), entry); + attributes::sanitize(&cx, SanitizerSet::empty(), entry); } // Run replace-all-uses-with for statics that need it diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 1c5987f26f129..ba285b5ef38d1 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -7,9 +7,6 @@ use crate::type_of::LayoutLlvmExt; use crate::value::Value; use libc::{c_char, c_uint}; use log::debug; -use rustc::session::config::{self, Sanitizer}; -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}; @@ -19,6 +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_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); @@ -997,6 +997,33 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); } + fn instrprof_increment( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + num_counters: &'ll Value, + index: &'ll Value, + ) -> &'ll Value { + debug!( + "instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})", + fn_name, hash, num_counters, index + ); + + let llfn = unsafe { llvm::LLVMRustGetInstrprofIncrementIntrinsic(self.cx().llmod) }; + let args = &[fn_name, hash, num_counters, index]; + let args = self.check_call("call", llfn, args); + + unsafe { + llvm::LLVMRustBuildCall( + self.llbuilder, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + None, + ) + } + } + fn call( &mut self, llfn: &'ll Value, @@ -1242,14 +1269,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 609ddfc1d3a80..64140747871fe 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". @@ -96,15 +94,11 @@ impl BackendTypes for CodegenCx<'ll, 'tcx> { impl CodegenCx<'ll, 'tcx> { pub fn const_array(&self, ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value { - unsafe { - return llvm::LLVMConstArray(ty, elts.as_ptr(), elts.len() as c_uint); - } + unsafe { llvm::LLVMConstArray(ty, elts.as_ptr(), elts.len() as c_uint) } } pub fn const_vector(&self, elts: &[&'ll Value]) -> &'ll Value { - unsafe { - return llvm::LLVMConstVector(elts.as_ptr(), elts.len() as c_uint); - } + unsafe { llvm::LLVMConstVector(elts.as_ptr(), elts.len() as c_uint) } } pub fn const_bytes(&self, bytes: &[u8]) -> &'ll Value { @@ -212,7 +206,7 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { let len = s.as_str().len(); let cs = consts::ptrcast( self.const_cstr(s, false), - self.type_ptr_to(self.layout_of(self.tcx.mk_str()).llvm_type(self)), + self.type_ptr_to(self.layout_of(self.tcx.types.str_).llvm_type(self)), ); (cs, self.const_usize(len as u64)) } @@ -233,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, .. } => { @@ -248,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), @@ -268,12 +256,12 @@ 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)); + assert!(!self.tcx.is_thread_local_static(def_id)); self.get_static(def_id) } - None => bug!("missing allocation {:?}", ptr.alloc_id), }; let llval = unsafe { llvm::LLVMConstInBoundsGEP( @@ -282,7 +270,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) @@ -293,7 +281,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> { @@ -330,7 +318,7 @@ pub fn val_ty(v: &Value) -> &Type { pub fn bytes_in_context(llcx: &'ll llvm::Context, bytes: &[u8]) -> &'ll Value { unsafe { let ptr = bytes.as_ptr() as *const c_char; - return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True); + llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True) } } diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index 09a84aff16811..9d9b53fc4a87c 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -1,25 +1,26 @@ use crate::base; use crate::common::CodegenCx; use crate::debuginfo; -use crate::llvm::{self, SetUnnamedAddr, True}; +use crate::llvm::{self, True}; use crate::type_::Type; 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; @@ -183,7 +184,7 @@ impl CodegenCx<'ll, 'tcx> { }; llvm::LLVMSetInitializer(gv, cv); set_global_alignment(&self, gv, align); - SetUnnamedAddr(gv, true); + llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global); gv } } @@ -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 d9c88951440f4..7ff5ac5cbdc10 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -1,35 +1,31 @@ use crate::attributes; +use crate::callee::get_fn; use crate::debuginfo; use crate::llvm; use crate::llvm_util; -use crate::value::Value; -use rustc::dep_graph::DepGraphSafe; - use crate::type_::Type; -use rustc_codegen_ssa::traits::*; +use crate::value::Value; -use crate::callee::get_fn; -use rustc::bug; -use rustc::mir::mono::CodegenUnit; -use rustc::session::config::{self, CFGuard, DebugInfo}; -use rustc::session::Session; -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_target::spec::{HasTargetSpec, Target}; - +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::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. @@ -42,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>>, @@ -53,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>, @@ -91,48 +88,15 @@ 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 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 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 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 -} - fn strip_function_ptr_alignment(data_layout: String) -> String { // FIXME: Make this more general. data_layout.replace("-Fi8-", "-") @@ -163,11 +127,11 @@ 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); - let llvm_data_layout = llvm::LLVMGetDataLayout(llmod); + let llvm_data_layout = llvm::LLVMGetDataLayoutStr(llmod); let llvm_data_layout = str::from_utf8(CStr::from_ptr(llvm_data_layout).to_bytes()) .expect("got a non-UTF8 data-layout from LLVM"); @@ -206,12 +170,13 @@ 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) { - llvm::LLVMRustSetModulePIELevel(llmod); + // 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. + if sess.crate_types().iter().all(|ty| *ty == CrateType::Executable) { + llvm::LLVMRustSetModulePIELevel(llmod); + } } // If skipping the PLT is enabled, we need to add some module metadata @@ -237,7 +202,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 @@ -287,7 +252,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()); @@ -382,6 +347,7 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { def_id, tcx.intern_substs(&[]), ) + .unwrap() .unwrap(), ), _ => { @@ -407,8 +373,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> { @@ -459,7 +425,7 @@ impl CodegenCx<'b, 'tcx> { self.type_variadic_func(&[], ret) }; let f = self.declare_cfn(name, fn_ty); - llvm::SetUnnamedAddr(f, false); + llvm::SetUnnamedAddress(f, llvm::UnnamedAddr::No); self.intrinsics.borrow_mut().insert(name, f); f } @@ -783,6 +749,8 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.lifetime.start.p0i8", fn(t_i64, i8p) -> void); ifn!("llvm.lifetime.end.p0i8", fn(t_i64, i8p) -> void); + ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void); + ifn!("llvm.expect.i1", fn(i1, i1) -> i1); ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); ifn!("llvm.localescape", fn(...) -> void); @@ -801,7 +769,7 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.dbg.declare", fn(self.type_metadata(), self.type_metadata()) -> void); ifn!("llvm.dbg.value", fn(self.type_metadata(), t_i64, self.type_metadata()) -> void); } - return None; + None } } @@ -821,8 +789,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 } } @@ -841,13 +809,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 09422f4ec3768..7f47b61de3f92 100644 --- a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs +++ b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs @@ -1,11 +1,13 @@ use super::metadata::{file_metadata, UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER}; use super::utils::DIB; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext}; +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; use rustc_index::vec::Idx; @@ -19,10 +21,17 @@ pub fn compute_mir_scopes( ) { // Find all the scopes with variables defined in them. let mut has_variables = BitSet::new_empty(mir.source_scopes.len()); - // FIXME(eddyb) take into account that arguments always have debuginfo, - // irrespective of their name (assuming full debuginfo is enabled). - for var_debug_info in &mir.var_debug_info { - has_variables.insert(var_debug_info.source_info.scope); + + // Only consider variables when they're going to be emitted. + // FIXME(eddyb) don't even allocate `has_variables` otherwise. + if cx.sess().opts.debuginfo == DebugInfo::Full { + // FIXME(eddyb) take into account that arguments always have debuginfo, + // irrespective of their name (assuming full debuginfo is enabled). + // NOTE(eddyb) actually, on second thought, those are always in the + // function scope, which always exists. + for var_debug_info in &mir.var_debug_info { + has_variables.insert(var_debug_info.source_info.scope); + } } // Instantiate all scopes. @@ -67,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 753a4e18faf5e..64d4076cbf0db 100644 --- a/src/librustc_codegen_llvm/debuginfo/gdb.rs +++ b/src/librustc_codegen_llvm/debuginfo/gdb.rs @@ -5,9 +5,9 @@ use crate::llvm; use crate::builder::Builder; use crate::common::CodegenCx; use crate::value::Value; -use rustc::bug; -use rustc::session::config::DebugInfo; use rustc_codegen_ssa::traits::*; +use rustc_middle::bug; +use rustc_session::config::DebugInfo; use rustc_ast::attr; use rustc_span::symbol::sym; @@ -50,7 +50,7 @@ pub fn get_or_insert_gdb_debug_scripts_section_global(cx: &CodegenCx<'ll, '_>) - llvm::LLVMSetSection(section_var, section_name.as_ptr().cast()); llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents)); llvm::LLVMSetGlobalConstant(section_var, llvm::True); - llvm::LLVMSetUnnamedAddr(section_var, llvm::True); + llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global); llvm::LLVMRustSetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage); // This should make sure that the whole section is not larger than // the string it contains. Otherwise we get a warning from GDB. diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 55eee13d028ca..8a957a729fb68 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1,4 +1,4 @@ -use self::EnumDiscriminantInfo::*; +use self::EnumTagInfo::*; use self::MemberDescriptionFactory::*; use self::RecursiveTypeDescription::*; @@ -16,23 +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::session::config::{self, DebugInfo}; -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; @@ -42,9 +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, HasDataLayout, Integer, LayoutOf, TagEncoding}; +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 @@ -203,7 +201,7 @@ impl TypeMap<'ll, 'tcx> { let key = self.unique_id_interner.intern(&unique_type_id); self.type_to_unique_id.insert(type_, UniqueTypeId(key)); - return UniqueTypeId(key); + UniqueTypeId(key) } /// Gets the `UniqueTypeId` for an enum variant. Enum variants are not really @@ -314,7 +312,7 @@ impl RecursiveTypeDescription<'ll, 'tcx> { member_holding_stub, member_descriptions, ); - return MetadataCreationResult::new(metadata_stub, true); + MetadataCreationResult::new(metadata_stub, true) } } } @@ -364,7 +362,7 @@ fn fixed_vec_metadata( ) }; - return MetadataCreationResult::new(metadata, false); + MetadataCreationResult::new(metadata, false) } fn vec_slice_metadata( @@ -445,16 +443,15 @@ fn subroutine_type_metadata( return_if_metadata_created_in_meantime!(cx, unique_type_id); - return MetadataCreationResult::new( + MetadataCreationResult::new( unsafe { llvm::LLVMRustDIBuilderCreateSubroutineType( DIB(cx), - unknown_file_metadata(cx), create_DIArray(DIB(cx), &signature_metadata[..]), ) }, false, - ); + ) } // FIXME(1563): This is all a bit of a hack because 'trait pointer' is an ill- @@ -637,14 +634,12 @@ pub fn type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, usage_site_span: Sp // anything reading the debuginfo for a recursive // type is going to see *something* weird - the only // question is what exactly it will see. - let (size, align) = cx.size_and_align_of(t); let name = ""; llvm::LLVMRustDIBuilderCreateBasicType( DIB(cx), name.as_ptr().cast(), name.len(), - size.bits(), - align.bits() as u32, + cx.size_of(t).bits(), DW_ATE_unsigned, ) } @@ -663,7 +658,7 @@ pub fn type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, usage_site_span: Sp MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false) } ty::Closure(def_id, substs) => { - let upvar_tys: Vec<_> = substs.as_closure().upvar_tys(def_id, cx.tcx).collect(); + let upvar_tys: Vec<_> = substs.as_closure().upvar_tys().collect(); let containing_scope = get_namespace_for_item(cx, def_id); prepare_tuple_metadata( cx, @@ -678,7 +673,7 @@ pub fn type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>, usage_site_span: Sp ty::Generator(def_id, substs, _) => { let upvar_tys: Vec<_> = substs .as_generator() - .prefix_tys(def_id, cx.tcx) + .prefix_tys() .map(|t| cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)) .collect(); prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span, upvar_tys) @@ -751,14 +746,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,22 +770,23 @@ 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); match debug_context(cx).created_files.borrow_mut().entry(key) { - Entry::Occupied(o) => return o.get(), + Entry::Occupied(o) => o.get(), Entry::Vacant(v) => { let (file_name, directory) = v.key(); debug!("file_metadata: file_name: {:?}, directory: {:?}", file_name, directory); @@ -789,6 +794,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 +812,9 @@ fn file_metadata_raw( file_name.len(), directory.as_ptr().cast(), directory.len(), + hash_kind, + hash_value.as_ptr().cast(), + hash_value.len(), ) }; @@ -819,19 +838,17 @@ fn basic_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { _ => bug!("debuginfo::basic_type_metadata - `t` is invalid type"), }; - let (size, align) = cx.size_and_align_of(t); let ty_metadata = unsafe { llvm::LLVMRustDIBuilderCreateBasicType( DIB(cx), name.as_ptr().cast(), name.len(), - size.bits(), - align.bits() as u32, + cx.size_of(t).bits(), encoding, ) }; - return ty_metadata; + ty_metadata } fn foreign_type_metadata( @@ -920,6 +937,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( @@ -939,16 +959,16 @@ pub fn compile_unit_metadata( if tcx.sess.opts.debugging_opts.profile { let cu_desc_metadata = llvm::LLVMRustMetadataAsValue(debug_context.llcontext, unit_metadata); + let default_gcda_path = &tcx.output_filenames(LOCAL_CRATE).with_extension("gcda"); + let gcda_path = + tcx.sess.opts.debugging_opts.profile_emit.as_ref().unwrap_or(default_gcda_path); let gcov_cu_info = [ path_to_mdstring( debug_context.llcontext, &tcx.output_filenames(LOCAL_CRATE).with_extension("gcno"), ), - path_to_mdstring( - debug_context.llcontext, - &tcx.output_filenames(LOCAL_CRATE).with_extension("gcda"), - ), + path_to_mdstring(debug_context.llcontext, &gcda_path), cu_desc_metadata, ]; let gcov_metadata = llvm::LLVMMDNodeInContext( @@ -1203,7 +1223,7 @@ fn prepare_tuple_metadata( //=----------------------------------------------------------------------------- struct UnionMemberDescriptionFactory<'tcx> { - layout: TyLayout<'tcx>, + layout: TyAndLayout<'tcx>, variant: &'tcx ty::VariantDef, span: Span, } @@ -1262,22 +1282,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. - return 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; + cx.sess().target.target.options.is_like_msvc } // FIXME(eddyb) maybe precompute this? Right now it's computed once @@ -1285,7 +1294,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,8 +1334,8 @@ fn generator_layout_and_saved_local_names( /// offset of zero bytes). struct EnumMemberDescriptionFactory<'ll, 'tcx> { enum_type: Ty<'tcx>, - layout: TyLayout<'tcx>, - discriminant_type_metadata: Option<&'ll DIType>, + layout: TyAndLayout<'tcx>, + tag_type_metadata: Option<&'ll DIType>, containing_scope: &'ll DIScope, span: Span, } @@ -1364,7 +1373,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![]; @@ -1376,7 +1385,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, self.layout, variant_info, - NoDiscriminant, + NoTag, self_metadata, self.span, ); @@ -1399,20 +1408,20 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { discriminant: None, }] } - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, - discr_index, + Variants::Multiple { + tag_encoding: TagEncoding::Direct, + tag_field, ref variants, .. } => { - let discriminant_info = if fallback { - RegularDiscriminant { - discr_field: Field::from(discr_index), - discr_type_metadata: self.discriminant_type_metadata.unwrap(), + let tag_info = if fallback { + RegularTag { + tag_field: Field::from(tag_field), + tag_type_metadata: self.tag_type_metadata.unwrap(), } } else { // This doesn't matter in this case. - NoDiscriminant + NoTag }; variants .iter_enumerated() @@ -1423,7 +1432,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, variant, variant_info, - discriminant_info, + tag_info, self_metadata, self.span, ); @@ -1457,12 +1466,12 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { }) .collect() } - layout::Variants::Multiple { - discr_kind: - layout::DiscriminantKind::Niche { ref niche_variants, niche_start, dataful_variant }, - ref discr, + Variants::Multiple { + tag_encoding: + TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant }, + ref tag, ref variants, - discr_index, + tag_field, } => { if fallback { let variant = self.layout.for_variant(cx, dataful_variant); @@ -1471,7 +1480,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, variant, variant_info_for(dataful_variant), - OptimizedDiscriminant, + OptimizedTag, self.containing_scope, self.span, ); @@ -1494,7 +1503,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, ) { @@ -1515,8 +1524,8 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, &mut name, self.layout, - self.layout.fields.offset(discr_index), - self.layout.field(cx, discr_index).size, + self.layout.fields.offset(tag_field), + self.layout.field(cx, tag_field).size, ); variant_info_for(*niche_variants.start()).map_struct_name(|variant_name| { name.push_str(variant_name); @@ -1543,7 +1552,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { cx, variant, variant_info, - OptimizedDiscriminant, + OptimizedTag, self_metadata, self.span, ); @@ -1564,7 +1573,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { let value = (i.as_u32() as u128) .wrapping_sub(niche_variants.start().as_u32() as u128) .wrapping_add(niche_start); - let value = truncate(value, discr.value.size(cx)); + let value = truncate(value, tag.value.size(cx)); // NOTE(eddyb) do *NOT* remove this assert, until // we pass the full 128-bit value to LLVM, otherwise // truncation will be silent and remain undetected. @@ -1592,9 +1601,9 @@ 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>, + tag_type_metadata: Option<&'ll DIType>, span: Span, } @@ -1608,7 +1617,7 @@ impl VariantMemberDescriptionFactory<'ll, 'tcx> { MemberDescription { name: name.to_string(), type_metadata: if use_enum_fallback(cx) { - match self.discriminant_type_metadata { + match self.tag_type_metadata { // Discriminant is always the first field of our variant // when using the enum fallback. Some(metadata) if i == 0 => metadata, @@ -1628,11 +1637,14 @@ impl VariantMemberDescriptionFactory<'ll, 'tcx> { } } +// FIXME: terminology here should be aligned with `abi::TagEncoding`. +// `OptimizedTag` is `TagEncoding::Niche`, `RegularTag` is `TagEncoding::Direct`. +// `NoTag` should be removed; users should use `Option` instead. #[derive(Copy, Clone)] -enum EnumDiscriminantInfo<'ll> { - RegularDiscriminant { discr_field: Field, discr_type_metadata: &'ll DIType }, - OptimizedDiscriminant, - NoDiscriminant, +enum EnumTagInfo<'ll> { + RegularTag { tag_field: Field, tag_type_metadata: &'ll DIType }, + OptimizedTag, + NoTag, } #[derive(Copy, Clone)] @@ -1641,7 +1653,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,9 +1707,9 @@ 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>, + discriminant_info: EnumTagInfo<'ll>, containing_scope: &'ll DIScope, span: Span, ) -> (&'ll DICompositeType, MemberDescriptionFactory<'ll, 'tcx>) { @@ -1713,12 +1725,12 @@ fn describe_enum_variant( let (offsets, args) = if use_enum_fallback(cx) { // If this is not a univariant enum, there is also the discriminant field. let (discr_offset, discr_arg) = match discriminant_info { - RegularDiscriminant { discr_field, .. } => { + RegularTag { tag_field, .. } => { // We have the layout of an enum variant, we need the layout of the outer enum let enum_layout = cx.layout_of(layout.ty); - let offset = enum_layout.fields.offset(discr_field.as_usize()); + let offset = enum_layout.fields.offset(tag_field.as_usize()); let args = - ("RUST$ENUM$DISR".to_owned(), enum_layout.field(cx, discr_field.as_usize()).ty); + ("RUST$ENUM$DISR".to_owned(), enum_layout.field(cx, tag_field.as_usize()).ty); (Some(offset), Some(args)) } _ => (None, None), @@ -1748,8 +1760,8 @@ fn describe_enum_variant( let member_description_factory = VariantMDF(VariantMemberDescriptionFactory { offsets, args, - discriminant_type_metadata: match discriminant_info { - RegularDiscriminant { discr_type_metadata, .. } => Some(discr_type_metadata), + tag_type_metadata: match discriminant_info { + RegularTag { tag_type_metadata, .. } => Some(tag_type_metadata), _ => None, }, span, @@ -1777,7 +1789,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 +1881,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 { tag_encoding: TagEncoding::Direct, ref tag, .. }, + ) = (&layout.abi, &layout.variants) + { + return FinalMetadata(discriminant_type_metadata(tag.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 { tag_encoding: TagEncoding::Niche { .. }, .. } => None, + Variants::Multiple { tag_encoding: TagEncoding::Direct, ref tag, .. } => { + Some(discriminant_type_metadata(tag.value)) + } }; let enum_metadata = { @@ -1927,7 +1930,7 @@ fn prepare_enum_metadata( EnumMDF(EnumMemberDescriptionFactory { enum_type, layout, - discriminant_type_metadata, + tag_type_metadata: discriminant_type_metadata, containing_scope, span, }), @@ -1940,27 +1943,24 @@ 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 { .. }, - ref discr, - discr_index, - .. + Variants::Multiple { + tag_encoding: TagEncoding::Niche { .. }, ref tag, tag_field, .. } => { // Find the integer type of the correct size. - let size = discr.value.size(cx); - 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(), + let size = tag.value.size(cx); + let align = tag.value.align(cx); + + let tag_type = match tag.value { + Int(t, _) => t, + F32 => Integer::I32, + F64 => Integer::I64, + Pointer => cx.data_layout().ptr_sized_integer(), } .to_ty(cx.tcx, false); - let discr_metadata = basic_type_metadata(cx, discr_type); + let tag_metadata = basic_type_metadata(cx, tag_type); unsafe { Some(llvm::LLVMRustDIBuilderCreateMemberType( DIB(cx), @@ -1971,20 +1971,15 @@ fn prepare_enum_metadata( UNKNOWN_LINE_NUMBER, size.bits(), align.abi.bits() as u32, - layout.fields.offset(discr_index).bits(), + layout.fields.offset(tag_field).bits(), DIFlags::FlagArtificial, - discr_metadata, + tag_metadata, )) } } - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, - ref discr, - discr_index, - .. - } => { - let discr_type = discr.value.to_ty(cx.tcx); + Variants::Multiple { tag_encoding: TagEncoding::Direct, ref tag, tag_field, .. } => { + let discr_type = tag.value.to_ty(cx.tcx); let (size, align) = cx.size_and_align_of(discr_type); let discr_metadata = basic_type_metadata(cx, discr_type); @@ -1998,7 +1993,7 @@ fn prepare_enum_metadata( UNKNOWN_LINE_NUMBER, size.bits(), align.bits() as u32, - layout.fields.offset(discr_index).bits(), + layout.fields.offset(tag_field).bits(), DIFlags::FlagArtificial, discr_metadata, )) @@ -2007,8 +2002,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, @@ -2075,7 +2070,7 @@ fn prepare_enum_metadata( } }; - return create_and_register_recursive_type_forward_declaration( + create_and_register_recursive_type_forward_declaration( cx, enum_type, unique_type_id, @@ -2084,11 +2079,11 @@ fn prepare_enum_metadata( EnumMDF(EnumMemberDescriptionFactory { enum_type, layout, - discriminant_type_metadata: None, + tag_type_metadata: None, containing_scope, span, }), - ); + ) } /// Creates debug information for a composite type, that is, anything that @@ -2185,9 +2180,6 @@ fn compute_type_parameters(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> Option<&' name.as_ptr().cast(), name.len(), actual_type_metadata, - unknown_file_metadata(cx), - 0, - 0, )) }) } else { @@ -2299,6 +2291,11 @@ pub fn create_global_var_metadata(cx: &CodegenCx<'ll, '_>, def_id: DefId, global return; } + // Only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo != DebugInfo::Full { + return; + } + let tcx = cx.tcx; let attrs = tcx.codegen_fn_attrs(def_id); @@ -2310,7 +2307,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) }; @@ -2358,6 +2355,11 @@ pub fn create_vtable_metadata(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, vtable: & return; } + // Only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo != DebugInfo::Full { + return; + } + let type_metadata = type_metadata(cx, ty, rustc_span::DUMMY_SP); unsafe { @@ -2414,6 +2416,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 bbde541c58f1e..8c580847ef8fd 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::session::config::{self, DebugInfo}; -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,11 +248,11 @@ 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); - llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(self), file_metadata, fn_signature) + llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(self), fn_signature) }; // Find the enclosing function, in case this is a closure. @@ -266,8 +265,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { // name if necessary. let generics = self.tcx().generics_of(enclosing_fn_def_id); let substs = instance.substs.truncate_to(self.tcx(), generics); - let template_parameters = - get_template_parameters(self, &generics, substs, file_metadata, &mut name); + let template_parameters = get_template_parameters(self, &generics, substs, &mut name); // Get the linkage_name, which is just the symbol name let linkage_name = mangled_name_of_instance(self, instance); @@ -290,7 +288,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; } } @@ -389,7 +387,6 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { cx: &CodegenCx<'ll, 'tcx>, generics: &ty::Generics, substs: SubstsRef<'tcx>, - file_metadata: &'ll DIFile, name_to_append_suffix_to: &mut String, ) -> &'ll DIArray { if substs.types().next().is_none() { @@ -430,9 +427,6 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { name.as_ptr().cast(), name.len(), actual_type_metadata, - file_metadata, - 0, - 0, )) }) } else { @@ -444,7 +438,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { vec![] }; - return create_DIArray(DIB(cx), &template_params[..]); + create_DIArray(DIB(cx), &template_params[..]) } fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec { @@ -475,7 +469,12 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { // so avoid methods on other types (e.g., `<*mut T>::null`). match impl_self_ty.kind { ty::Adt(def, ..) if !def.is_box() => { - Some(type_metadata(cx, impl_self_ty, rustc_span::DUMMY_SP)) + // Again, only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo == DebugInfo::Full { + Some(type_metadata(cx, impl_self_ty, rustc_span::DUMMY_SP)) + } else { + Some(namespace::item_namespace(cx, def.did)) + } } _ => None, } @@ -524,14 +523,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 bef40decdf3ab..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; @@ -24,9 +24,7 @@ pub fn is_node_local_to_unit(cx: &CodegenCx<'_, '_>, def_id: DefId) -> bool { #[allow(non_snake_case)] pub fn create_DIArray(builder: &DIBuilder<'ll>, arr: &[Option<&'ll DIDescriptor>]) -> &'ll DIArray { - return unsafe { - llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) - }; + unsafe { llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) } } #[inline] diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs index fab6321186b2c..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. /// @@ -40,7 +40,7 @@ fn declare_raw_fn( llvm::SetFunctionCallConv(llfn, callconv); // Function addresses in Rust are never significant, allowing functions to // be merged. - llvm::SetUnnamedAddr(llfn, true); + llvm::SetUnnamedAddress(llfn, llvm::UnnamedAddr::Global); if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.target.options.disable_redzone) { llvm::Attribute::NoRedZone.apply_llfn(Function, llfn); diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 5ce18a9007a62..0a1cc31044a22 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -2,31 +2,32 @@ 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 log::debug; + use rustc_ast::ast; use rustc_codegen_ssa::base::{compare_simd_types, to_immediate, wants_msvc_seh}; +use rustc_codegen_ssa::common::span_invalid_monomorphization_error; use rustc_codegen_ssa::common::{IntPredicate, 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 { @@ -87,6 +88,7 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { args: &[OperandRef<'tcx, &'ll Value>], llresult: &'ll Value, span: Span, + caller_instance: ty::Instance<'tcx>, ) { let tcx = self.tcx; let callee_ty = instance.monomorphic_ty(tcx); @@ -137,6 +139,22 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { let llfn = self.get_intrinsic(&("llvm.debugtrap")); self.call(llfn, &[], None) } + "count_code_region" => { + // FIXME(richkadel): The current implementation assumes the MIR for the given + // caller_instance represents a single function. Validate and/or correct if inlining + // and/or monomorphization invalidates these assumptions. + let coverage_data = tcx.coverage_data(caller_instance.def_id()); + let mangled_fn = tcx.symbol_name(caller_instance); + let (mangled_fn_name, _len_val) = self.const_str(mangled_fn.name); + let hash = self.const_u64(coverage_data.hash); + let num_counters = self.const_u32(coverage_data.num_counters); + let index = args[0].immediate(); + debug!( + "count_code_region to LLVM intrinsic instrprof.increment(fn_name={}, hash={:?}, num_counters={:?}, index={:?})", + mangled_fn.name, hash, num_counters, index + ); + self.instrprof_increment(mangled_fn_name, hash, num_counters, index) + } "va_start" => self.va_start(args[0].immediate()), "va_end" => self.va_end(args[0].immediate()), "va_copy" => { @@ -145,7 +163,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,32 +207,14 @@ 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) } - "init" => { - let ty = substs.type_at(0); - if !self.layout_of(ty).is_zst() { - // Just zero out the stack slot. - // If we store a zero constant, LLVM will drown in vreg allocation for large - // data structures, and the generated code will be awful. (A telltale sign of - // this is large quantities of `mov [byte ptr foo],0` in the generated code.) - memset_intrinsic( - self, - false, - ty, - llresult, - self.const_u8(0), - self.const_usize(1), - ); - } - return; - } - // Effectively no-ops - "uninit" | "forget" => { + // Effectively no-op + "forget" => { return; } "offset" => { @@ -480,46 +480,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!(), }, @@ -562,13 +530,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] @@ -589,7 +557,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 @@ -600,7 +568,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) { @@ -750,6 +724,16 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { return; } + "ptr_guaranteed_eq" | "ptr_guaranteed_ne" => { + let a = args[0].immediate(); + let b = args[1].immediate(); + if name == "ptr_guaranteed_eq" { + self.icmp(IntPredicate::IntEQ, a, b) + } else { + self.icmp(IntPredicate::IntNE, a, b) + } + } + "ptr_offset_from" => { let ty = substs.type_at(0); let pointee_size = self.size_of(ty); @@ -856,7 +840,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. @@ -1190,8 +1174,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"); @@ -1372,20 +1356,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, @@ -1396,22 +1378,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, @@ -1680,7 +1662,7 @@ fn generic_simd_intrinsic( llvm_elem_vec_ty, ), ); - llvm::SetUnnamedAddr(f, false); + llvm::SetUnnamedAddress(f, llvm::UnnamedAddr::No); let v = bx.call(f, &[args[1].immediate(), alignment, mask, args[0].immediate()], None); return Ok(v); } @@ -1802,7 +1784,7 @@ fn generic_simd_intrinsic( &llvm_intrinsic, bx.type_func(&[llvm_elem_vec_ty, llvm_pointer_vec_ty, alignment_ty, mask_ty], ret_t), ); - llvm::SetUnnamedAddr(f, false); + llvm::SetUnnamedAddress(f, llvm::UnnamedAddr::No); let v = bx.call(f, &[args[0].immediate(), args[1].immediate(), alignment, mask], None); return Ok(v); } @@ -2101,7 +2083,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, let vec_ty = bx.cx.type_vector(elem_ty, in_len as u64); let f = bx.declare_cfn(&llvm_intrinsic, bx.type_func(&[vec_ty, vec_ty], vec_ty)); - llvm::SetUnnamedAddr(f, false); + llvm::SetUnnamedAddress(f, llvm::UnnamedAddr::No); let v = bx.call(f, &[lhs, rhs], None); return Ok(v); } @@ -2117,7 +2099,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, @@ -2128,7 +2110,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, @@ -2145,7 +2127,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 f32657545745f..55ee660d9f700 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -11,38 +11,35 @@ #![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}; -use rustc_span::symbol::Symbol; pub use llvm_util::target_features; -use rustc::dep_graph::WorkProduct; 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; +use rustc_span::symbol::Symbol; + use std::any::Any; use std::ffi::CStr; use std::fs; use std::sync::Arc; -use rustc::dep_graph::DepGraph; -use rustc::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; -use rustc::session::config::{self, OptLevel, OutputFilenames, PrintRequest}; -use rustc::session::Session; -use rustc::ty::{self, TyCtxt}; -use rustc::util::common::ErrorReported; -use rustc_codegen_ssa::ModuleCodegen; -use rustc_codegen_utils::codegen_backend::CodegenBackend; -use rustc_serialize::json; - mod back { pub mod archive; - pub mod bytecode; pub mod lto; mod profiling; pub mod write; @@ -112,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) @@ -203,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!(); @@ -353,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) } } } @@ -361,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/archive_ro.rs b/src/librustc_codegen_llvm/llvm/archive_ro.rs index ab9df4162472c..64db4f7462df8 100644 --- a/src/librustc_codegen_llvm/llvm/archive_ro.rs +++ b/src/librustc_codegen_llvm/llvm/archive_ro.rs @@ -27,13 +27,13 @@ impl ArchiveRO { /// If this archive is used with a mutable method, then an error will be /// raised. pub fn open(dst: &Path) -> Result { - return unsafe { + unsafe { let s = path_to_c_string(dst); let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| { super::last_error().unwrap_or_else(|| "failed to open archive".to_owned()) })?; Ok(ArchiveRO { raw: ar }) - }; + } } pub fn iter(&self) -> Iter<'_> { diff --git a/src/librustc_codegen_llvm/llvm/diagnostic.rs b/src/librustc_codegen_llvm/llvm/diagnostic.rs index 4347cd06532da..47f5c94e70c53 100644 --- a/src/librustc_codegen_llvm/llvm/diagnostic.rs +++ b/src/librustc_codegen_llvm/llvm/diagnostic.rs @@ -88,6 +88,7 @@ impl OptimizationDiagnostic<'ll> { #[derive(Copy, Clone)] pub struct InlineAsmDiagnostic<'ll> { + pub level: super::DiagnosticLevel, pub cookie: c_uint, pub message: &'ll Twine, pub instruction: Option<&'ll Value>, @@ -98,10 +99,17 @@ impl InlineAsmDiagnostic<'ll> { let mut cookie = 0; let mut message = None; let mut instruction = None; + let mut level = super::DiagnosticLevel::Error; - super::LLVMRustUnpackInlineAsmDiagnostic(di, &mut cookie, &mut message, &mut instruction); + super::LLVMRustUnpackInlineAsmDiagnostic( + di, + &mut level, + &mut cookie, + &mut message, + &mut instruction, + ); - InlineAsmDiagnostic { cookie, message: message.unwrap(), instruction } + InlineAsmDiagnostic { level, cookie, message: message.unwrap(), instruction } } } diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 388b6c7483958..8063d97aa73a9 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -45,6 +45,8 @@ pub enum CallConv { X86_64_Win64 = 79, X86_VectorCall = 80, X86_Intr = 83, + AvrNonBlockingInterrupt = 84, + AvrInterrupt = 85, AmdGpuKernel = 91, } @@ -73,6 +75,14 @@ pub enum Visibility { Protected = 2, } +/// LLVMUnnamedAddr +#[repr(C)] +pub enum UnnamedAddr { + No, + Local, + Global, +} + /// LLVMDLLStorageClass #[derive(Copy, Clone)] #[repr(C)] @@ -116,6 +126,8 @@ pub enum Attribute { NonLazyBind = 23, OptimizeNone = 24, ReturnsTwice = 25, + ReadNone = 26, + InaccessibleMemOnly = 27, } /// LLVMIntPredicate @@ -381,10 +393,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, } } } @@ -427,18 +439,18 @@ pub enum OptStage { /// LLVMRustSanitizerOptions #[repr(C)] pub struct SanitizerOptions { - pub sanitize_memory: bool, - pub sanitize_thread: bool, pub sanitize_address: bool, - pub sanitize_recover: bool, + pub sanitize_address_recover: bool, + pub sanitize_memory: bool, + pub sanitize_memory_recover: bool, pub sanitize_memory_track_origins: c_int, + pub sanitize_thread: bool, } /// LLVMRelocMode #[derive(Copy, Clone, PartialEq)] #[repr(C)] -pub enum RelocMode { - Default, +pub enum RelocModel { Static, PIC, DynamicNoPic, @@ -451,9 +463,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, @@ -482,6 +492,17 @@ pub enum DiagnosticKind { Linker, } +/// LLVMRustDiagnosticLevel +#[derive(Copy, Clone)] +#[repr(C)] +#[allow(dead_code)] // Variants constructed by C++. +pub enum DiagnosticLevel { + Error, + Warning, + Note, + Remark, +} + /// LLVMRustArchiveKind #[derive(Copy, Clone)] #[repr(C)] @@ -538,6 +559,15 @@ pub enum ThreadLocalMode { LocalExec, } +/// LLVMRustChecksumKind +#[derive(Copy, Clone)] +#[repr(C)] +pub enum ChecksumKind { + None, + MD5, + SHA1, +} + extern "C" { type Opaque; } @@ -694,8 +724,8 @@ pub mod debuginfo { } impl DebugEmissionKind { - pub fn from_generic(kind: rustc::session::config::DebugInfo) -> Self { - use rustc::session::config::DebugInfo; + pub fn from_generic(kind: rustc_session::config::DebugInfo) -> Self { + use rustc_session::config::DebugInfo; match kind { DebugInfo::None => DebugEmissionKind::NoDebug, DebugInfo::Limited => DebugEmissionKind::LineTablesOnly, @@ -727,11 +757,11 @@ extern "C" { pub fn LLVMCloneModule(M: &Module) -> &Module; /// Data layout. See Module::getDataLayout. - pub fn LLVMGetDataLayout(M: &Module) -> *const c_char; + pub fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; pub fn LLVMSetDataLayout(M: &Module, Triple: *const c_char); /// See Module::setModuleInlineAsm. - pub fn LLVMSetModuleInlineAsm(M: &Module, Asm: *const c_char); + pub fn LLVMSetModuleInlineAsm2(M: &Module, Asm: *const c_char, AsmLen: size_t); pub fn LLVMRustAppendModuleInlineAsm(M: &Module, Asm: *const c_char, AsmLen: size_t); /// See llvm::LLVMTypeKind::getTypeID. @@ -1331,6 +1361,7 @@ extern "C" { // Miscellaneous instructions pub fn LLVMBuildPhi(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value; + pub fn LLVMRustGetInstrprofIncrementIntrinsic(M: &Module) -> &'a Value; pub fn LLVMRustBuildCall( B: &Builder<'a>, Fn: &'a Value, @@ -1632,11 +1663,13 @@ 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( Builder: &DIBuilder<'a>, - File: &'a DIFile, ParameterTypes: &'a DIArray, ) -> &'a DICompositeType; @@ -1663,7 +1696,6 @@ extern "C" { Name: *const c_char, NameLen: size_t, SizeInBits: u64, - AlignInBits: u32, Encoding: c_uint, ) -> &'a DIBasicType; @@ -1853,7 +1885,7 @@ extern "C" { UniqueIdLen: size_t, ) -> &'a DIDerivedType; - pub fn LLVMSetUnnamedAddr(GlobalVar: &Value, UnnamedAddr: Bool); + pub fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); pub fn LLVMRustDIBuilderCreateTemplateTypeParameter( Builder: &DIBuilder<'a>, @@ -1861,9 +1893,6 @@ extern "C" { Name: *const c_char, NameLen: size_t, Ty: &'a DIType, - File: &'a DIFile, - LineNo: c_uint, - ColumnNo: c_uint, ) -> &'a DITemplateTypeParameter; pub fn LLVMRustDIBuilderCreateNameSpace( @@ -1926,10 +1955,9 @@ extern "C" { Features: *const c_char, Abi: *const c_char, Model: CodeModel, - Reloc: RelocMode, + Reloc: RelocModel, Level: CodeGenOptLevel, UseSoftFP: bool, - PositionIndependentExecutable: bool, FunctionSections: bool, DataSections: bool, TrapUnreachable: bool, @@ -1937,6 +1965,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( @@ -1980,6 +2009,7 @@ extern "C" { SLPVectorize: bool, LoopVectorize: bool, DisableSimplifyLibCalls: bool, + EmitLifetimeMarkers: bool, SanitizerOptions: Option<&SanitizerOptions>, PGOGenPath: *const c_char, PGOUsePath: *const c_char, @@ -2039,6 +2069,7 @@ extern "C" { pub fn LLVMRustUnpackInlineAsmDiagnostic( DI: &'a DiagnosticInfo, + level_out: &mut DiagnosticLevel, cookie_out: &mut c_uint, message_out: &mut Option<&'a Twine>, instruction_out: &mut Option<&'a Value>, @@ -2055,7 +2086,15 @@ extern "C" { ); #[allow(improper_ctypes)] - pub fn LLVMRustWriteSMDiagnosticToString(d: &SMDiagnostic, s: &RustString); + pub fn LLVMRustUnpackSMDiagnostic( + d: &SMDiagnostic, + message_out: &RustString, + buffer_out: &RustString, + level_out: &mut DiagnosticLevel, + loc_out: &mut c_uint, + ranges_out: *mut c_uint, + num_ranges: &mut usize, + ) -> bool; pub fn LLVMRustWriteArchive( Dst: *const c_char, @@ -2118,6 +2157,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/mod.rs b/src/librustc_codegen_llvm/llvm/mod.rs index a083e14979c0b..b7f1e1789c9e2 100644 --- a/src/librustc_codegen_llvm/llvm/mod.rs +++ b/src/librustc_codegen_llvm/llvm/mod.rs @@ -106,9 +106,9 @@ pub fn UnsetComdat(val: &'a Value) { } } -pub fn SetUnnamedAddr(global: &'a Value, unnamed: bool) { +pub fn SetUnnamedAddress(global: &'a Value, unnamed: UnnamedAddr) { unsafe { - LLVMSetUnnamedAddr(global, unnamed as Bool); + LLVMSetUnnamedAddress(global, unnamed); } } diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs index 0081a75a4da11..67a2251e8593e 100644 --- a/src/librustc_codegen_llvm/llvm_util.rs +++ b/src/librustc_codegen_llvm/llvm_util.rs @@ -1,11 +1,11 @@ use crate::back::write::create_informational_target_machine; use crate::llvm; use libc::c_int; -use rustc::bug; -use rustc::session::config::PrintRequest; -use rustc::session::Session; 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; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; @@ -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); } } @@ -173,6 +170,7 @@ const AARCH64_WHITELIST: &[(&str, Option)] = &[ ("fp16", Some(sym::aarch64_target_feature)), ("rcpc", Some(sym::aarch64_target_feature)), ("dotprod", Some(sym::aarch64_target_feature)), + ("tme", Some(sym::aarch64_target_feature)), ("v8.1a", Some(sym::aarch64_target_feature)), ("v8.2a", Some(sym::aarch64_target_feature)), ("v8.3a", Some(sym::aarch64_target_feature)), @@ -239,6 +237,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 +263,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 +308,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 +334,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 3181d568b013a..eeb6b4aabcf29 100644 --- a/src/librustc_codegen_ssa/Cargo.toml +++ b/src/librustc_codegen_ssa/Cargo.toml @@ -15,17 +15,17 @@ 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_serialize = { path = "../librustc_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_codegen_utils = { path = "../librustc_codegen_utils" } +rustc_symbol_mangling = { path = "../librustc_symbol_mangling" } rustc_data_structures = { path = "../librustc_data_structures"} rustc_errors = { path = "../librustc_errors" } rustc_fs_util = { path = "../librustc_fs_util" } 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/archive.rs b/src/librustc_codegen_ssa/back/archive.rs index a357c350287fb..f83b4b2b0c025 100644 --- a/src/librustc_codegen_ssa/back/archive.rs +++ b/src/librustc_codegen_ssa/back/archive.rs @@ -1,4 +1,4 @@ -use rustc::session::Session; +use rustc_session::Session; use rustc_span::symbol::Symbol; use std::io; diff --git a/src/librustc_codegen_ssa/back/command.rs b/src/librustc_codegen_ssa/back/command.rs index 30b055b313149..0208bb73abdbe 100644 --- a/src/librustc_codegen_ssa/back/command.rs +++ b/src/librustc_codegen_ssa/back/command.rs @@ -119,7 +119,7 @@ impl Command { for k in &self.env_remove { ret.env_remove(k); } - return ret; + ret } // extensions diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index a4eef2374c241..6c995be913c9e 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1,42 +1,34 @@ -use rustc::middle::cstore::{EncodedMetadata, LibSource, NativeLibrary, NativeLibraryKind}; -use rustc::middle::dependency_format::Linkage; -use rustc::session::config::{ - self, CFGuard, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer, -}; -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_data_structures::fx::FxHashSet; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::CrateNum; +use rustc_middle::middle::cstore::{EncodedMetadata, LibSource, NativeLib}; +use rustc_middle::middle::dependency_format::Linkage; +use rustc_session::config::{self, CFGuard, CrateType, DebugInfo}; +use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SanitizerSet}; +use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; +use rustc_session::search_paths::PathKind; +use rustc_session::utils::NativeLibKind; +/// 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::{CrtObjects, 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}; -use std::ascii; -use std::char; -use std::env; use std::ffi::OsString; -use std::fmt; -use std::fs; -use std::io; use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output, Stdio}; -use std::str; - -pub use rustc_codegen_utils::link::*; +use std::{ascii, char, env, fmt, fs, io, mem, str}; pub fn remove(sess: &Session, path: &Path) { if let Err(e) = fs::remove_file(path) { @@ -55,11 +47,11 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( ) { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); - for &crate_type in sess.crate_types.borrow().iter() { + for &crate_type in sess.crate_types().iter() { // 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; } @@ -86,7 +78,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, @@ -97,7 +89,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( ) .build(); } - config::CrateType::Staticlib => { + CrateType::Staticlib => { link_staticlib::(sess, codegen_results, &out_filename, &tmpdir); } _ => { @@ -131,10 +123,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); @@ -144,9 +132,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); - } } } }); @@ -155,7 +140,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 @@ -183,7 +168,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) { @@ -191,9 +178,11 @@ pub fn get_linker(sess: &Session, linker: &Path, flavor: LinkerFlavor) -> (PathB "x86_64" => Some("x64".to_string()), "x86" => Some("x86".to_string()), "aarch64" => Some("arm64".to_string()), + "arm" => Some("arm".to_string()), _ => 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); @@ -233,7 +222,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( @@ -244,10 +233,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; } @@ -260,7 +249,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()), } @@ -285,11 +274,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); @@ -338,11 +323,12 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( // metadata of the rlib we're generating somehow. for lib in codegen_results.crate_info.used_libraries.iter() { match lib.kind { - NativeLibraryKind::NativeStatic => {} - NativeLibraryKind::NativeStaticNobundle - | NativeLibraryKind::NativeFramework - | NativeLibraryKind::NativeRawDylib - | NativeLibraryKind::NativeUnknown => continue, + NativeLibKind::StaticBundle => {} + NativeLibKind::StaticNoBundle + | NativeLibKind::Dylib + | NativeLibKind::Framework + | NativeLibKind::RawDylib + | NativeLibKind::Unspecified => continue, } if let Some(name) = lib.name { ab.add_native_library(name); @@ -380,14 +366,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 @@ -449,7 +427,7 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>( // object files come from where and selectively skip them. let skip_object_files = native_libs .iter() - .any(|lib| lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib)); + .any(|lib| lib.kind == NativeLibKind::StaticBundle && !relevant_lib(sess, lib)); ab.add_rlib( path, &name.as_str(), @@ -481,105 +459,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() { - 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() { - 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() { - 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); } @@ -601,7 +501,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, @@ -637,6 +537,61 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( continue; } + // Detect '-static-pie' used with an older version of gcc or clang not supporting it. + // Fallback from '-static-pie' to '-static' in that case. + if sess.target.target.options.linker_is_gnu + && flavor != LinkerFlavor::Ld + && (out.contains("unrecognized command line option") + || out.contains("unknown argument")) + && (out.contains("-static-pie") || out.contains("--no-dynamic-linker")) + && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-static-pie") + { + info!("linker output: {:?}", out); + warn!( + "Linker does not support -static-pie command line option. Retrying with -static instead." + ); + // Mirror `add_(pre,post)_link_objects` to replace CRT objects. + let fallback = crt_objects_fallback(sess, crate_type); + let opts = &sess.target.target.options; + let pre_objects = + if fallback { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects }; + let post_objects = + if fallback { &opts.post_link_objects_fallback } else { &opts.post_link_objects }; + let get_objects = |objects: &CrtObjects, kind| { + objects + .get(&kind) + .iter() + .copied() + .flatten() + .map(|obj| get_object_file_path(sess, obj).into_os_string()) + .collect::>() + }; + let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe); + let post_objects_static_pie = get_objects(post_objects, LinkOutputKind::StaticPicExe); + let mut pre_objects_static = get_objects(pre_objects, LinkOutputKind::StaticNoPicExe); + let mut post_objects_static = get_objects(post_objects, LinkOutputKind::StaticNoPicExe); + // Assume that we know insertion positions for the replacement arguments from replaced + // arguments, which is true for all supported targets. + assert!(pre_objects_static.is_empty() || !pre_objects_static_pie.is_empty()); + assert!(post_objects_static.is_empty() || !post_objects_static_pie.is_empty()); + for arg in cmd.take_args() { + if arg.to_string_lossy() == "-static-pie" { + // Replace the output kind. + cmd.arg("-static"); + } else if pre_objects_static_pie.contains(&arg) { + // Replace the pre-link objects (replace the first and remove the rest). + cmd.args(mem::take(&mut pre_objects_static)); + } else if post_objects_static_pie.contains(&arg) { + // Replace the post-link objects (replace the first and remove the rest). + cmd.args(mem::take(&mut post_objects_static)); + } else { + cmd.arg(arg); + } + } + info!("{:?}", &cmd); + continue; + } + // Here's a terribly awful hack that really shouldn't be present in any // compiler. Here an environment variable is supported to automatically // retry the linker invocation if the linker looks like it segfaulted. @@ -702,12 +657,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)); @@ -718,9 +722,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() + )) } }; @@ -759,23 +766,26 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( } } -fn link_sanitizer_runtime(sess: &Session, crate_type: config::CrateType, linker: &mut dyn Linker) { - let sanitizer = match &sess.opts.debugging_opts.sanitizer { - Some(s) => s, - None => return, - }; - - if crate_type != config::CrateType::Executable { +fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) { + if crate_type != CrateType::Executable { return; } + let sanitizer = sess.opts.debugging_opts.sanitizer; + if sanitizer.contains(SanitizerSet::ADDRESS) { + link_sanitizer_runtime(sess, linker, "asan"); + } + if sanitizer.contains(SanitizerSet::LEAK) { + link_sanitizer_runtime(sess, linker, "lsan"); + } + if sanitizer.contains(SanitizerSet::MEMORY) { + link_sanitizer_runtime(sess, linker, "msan"); + } + if sanitizer.contains(SanitizerSet::THREAD) { + link_sanitizer_runtime(sess, linker, "tsan"); + } +} - let name = match sanitizer { - Sanitizer::Address => "asan", - Sanitizer::Leak => "lsan", - Sanitizer::Memory => "msan", - Sanitizer::Thread => "tsan", - }; - +fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { let default_sysroot = filesearch::get_or_default_sysroot(); let default_tlib = filesearch::make_target_lib_path(&default_sysroot, sess.opts.target_triple.triple()); @@ -791,10 +801,13 @@ 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" => { + "aarch64-fuchsia" + | "aarch64-unknown-linux-gnu" + | "x86_64-fuchsia" + | "x86_64-unknown-linux-gnu" => { let filename = format!("librustc{}_rt.{}.a", channel, name); let path = default_tlib.join(&filename); linker.link_whole_rlib(&path); @@ -821,7 +834,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, @@ -839,7 +852,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", @@ -897,7 +922,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; @@ -905,11 +930,8 @@ pub fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { // If we're only producing artifacts that are archives, no need to preserve // the objects as they're losslessly contained inside the archives. - let output_linked = sess - .crate_types - .borrow() - .iter() - .any(|&x| x != config::CrateType::Rlib && x != config::CrateType::Staticlib); + let output_linked = + sess.crate_types().iter().any(|&x| x != CrateType::Rlib && x != CrateType::Staticlib); if !output_linked { return false; } @@ -925,18 +947,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 @@ -951,26 +962,28 @@ 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: &[NativeLib]) { let lib_args: Vec<_> = all_native_libs .iter() .filter(|l| relevant_lib(sess, l)) .filter_map(|lib| { let name = lib.name?; match lib.kind { - NativeLibraryKind::NativeStaticNobundle | NativeLibraryKind::NativeUnknown => { + NativeLibKind::StaticNoBundle + | NativeLibKind::Dylib + | NativeLibKind::Unspecified => { if sess.target.target.options.is_like_msvc { Some(format!("{}.lib", name)) } else { Some(format!("-l{}", name)) } } - NativeLibraryKind::NativeFramework => { + NativeLibKind::Framework => { // ld-only syntax, since there are no frameworks in MSVC Some(format!("-framework {}", name)) } // These are included, no need to print them - NativeLibraryKind::NativeStatic | NativeLibraryKind::NativeRawDylib => None, + NativeLibKind::StaticBundle | NativeLibKind::RawDylib => None, } }) .collect(); @@ -1053,7 +1066,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) { @@ -1068,6 +1081,10 @@ pub fn get_file_path(sess: &Session, name: &str) -> PathBuf { if file_path.exists() { return file_path; } + let file_path = fs.get_selfcontained_lib_path().join(name); + if file_path.exists() { + return file_path; + } for search_path in fs.search_paths() { let file_path = search_path.dir.join(name); if file_path.exists() { @@ -1077,9 +1094,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 { @@ -1225,134 +1242,192 @@ 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; - - // 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); - } - } - - cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); +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, + }; - for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { - cmd.add_object(obj); + // Adjust the output kind to target capabilities. + let opts = &sess.target.target.options; + let pic_exe_supported = opts.position_independent_executables; + let static_pic_exe_supported = opts.static_position_independent_executables; + let static_dylib_supported = opts.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.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); - } +/// 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, } +} - // 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 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)); } +} - 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 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)); } +} - // 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); +/// 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) { + if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { + 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() && !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); + } +} + +/// 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); + } +} - // Pass debuginfo flags down to the linker. - cmd.debuginfo(); +/// 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); + } +} - // 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 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 +1454,55 @@ 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() { - 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)); + + let lib_path = sess.target_filesearch(PathKind::All).get_selfcontained_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 +1526,172 @@ 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); + + // 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 = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) { + "asan/" + } else { + "" + }; + 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_sanitizers(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 + cmd.set_output_kind(link_output_kind, out_filename); + + // 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, + ); + + // 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 +1705,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, @@ -1470,11 +1732,11 @@ pub fn add_local_native_libraries( None => continue, }; match lib.kind { - NativeLibraryKind::NativeUnknown => cmd.link_dylib(name), - NativeLibraryKind::NativeFramework => cmd.link_framework(name), - NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(name), - NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(name, &search_path), - NativeLibraryKind::NativeRawDylib => { + NativeLibKind::Dylib | NativeLibKind::Unspecified => cmd.link_dylib(name), + NativeLibKind::Framework => cmd.link_framework(name), + NativeLibKind::StaticNoBundle => cmd.link_staticlib(name), + NativeLibKind::StaticBundle => cmd.link_whole_staticlib(name, &search_path), + NativeLibKind::RawDylib => { // FIXME(#58713): Proper handling for raw dylibs. bug!("raw_dylib feature not yet implemented"); } @@ -1491,7 +1753,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 +1914,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]; @@ -1664,11 +1926,11 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; let skip_native = native_libs .iter() - .any(|lib| lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib)); + .any(|lib| lib.kind == NativeLibKind::StaticBundle && !relevant_lib(sess, lib)); 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 +1947,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 +1991,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 +2035,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, @@ -1806,9 +2068,9 @@ pub fn add_upstream_native_libraries( continue; } match lib.kind { - NativeLibraryKind::NativeUnknown => cmd.link_dylib(name), - NativeLibraryKind::NativeFramework => cmd.link_framework(name), - NativeLibraryKind::NativeStaticNobundle => { + NativeLibKind::Dylib | NativeLibKind::Unspecified => cmd.link_dylib(name), + NativeLibKind::Framework => cmd.link_framework(name), + NativeLibKind::StaticNoBundle => { // Link "static-nobundle" native libs only if the crate they originate from // is being linked statically to the current crate. If it's linked dynamically // or is an rlib already included via some other dylib crate, the symbols from @@ -1820,8 +2082,8 @@ pub fn add_upstream_native_libraries( // ignore statically included native libraries here as we've // already included them when we included the rust library // previously - NativeLibraryKind::NativeStatic => {} - NativeLibraryKind::NativeRawDylib => { + NativeLibKind::StaticBundle => {} + NativeLibKind::RawDylib => { // FIXME(#58713): Proper handling for raw dylibs. bug!("raw_dylib feature not yet implemented"); } @@ -1830,14 +2092,14 @@ pub fn add_upstream_native_libraries( } } -pub fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { +fn relevant_lib(sess: &Session, lib: &NativeLib) -> 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 +2110,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 5aafb8a12d74b..d6ef94bfd1727 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/src/librustc_codegen_ssa/back/linker.rs @@ -1,22 +1,37 @@ use super::archive; use super::command::Command; use super::symbol_export; +use rustc_span::symbol::sym; -use rustc_data_structures::fx::FxHashMap; 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::session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel}; -use rustc::session::Session; -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, Strip}; +use rustc_session::Session; use rustc_span::symbol::Symbol; -use rustc_target::spec::{LinkerFlavor, LldFlavor}; +use rustc_target::spec::{LinkOutputKind, 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. @@ -30,8 +45,7 @@ impl LinkerInfo { LinkerInfo { exports: tcx .sess - .crate_types - .borrow() + .crate_types() .iter() .map(|&c| (c, exported_symbols(tcx, c))) .collect(), @@ -87,6 +101,8 @@ 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 set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path); fn link_dylib(&mut self, lib: Symbol); fn link_rust_dylib(&mut self, lib: Symbol, path: &Path); fn link_framework(&mut self, framework: Symbol); @@ -99,26 +115,35 @@ pub trait Linker { fn output_filename(&mut self, path: &Path); fn add_object(&mut self, path: &Path); fn gc_sections(&mut self, keep_metadata: bool); - fn position_independent_executable(&mut self); - fn no_position_independent_executable(&mut self); fn full_relro(&mut self); fn partial_relro(&mut self); fn no_relro(&mut self); 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> { @@ -205,9 +230,109 @@ impl<'a> GccLinker<'a> { let target_cpu = self.target_cpu; self.linker_arg(&format!("-plugin-opt=mcpu={}", target_cpu)); } + + fn build_dylib(&mut self, out_filename: &Path) { + // On mac we need to tell the linker to let this library be rpathed + if self.sess.target.target.options.is_like_osx { + self.cmd.arg("-dynamiclib"); + self.linker_arg("-dylib"); + + // Note that the `osx_rpath_install_name` option here is a hack + // purely to support rustbuild right now, we should get a more + // principled solution at some point to force the compiler to pass + // the right `-Wl,-install_name` with an `@rpath` in it. + if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name { + self.linker_arg("-install_name"); + let mut v = OsString::from("@rpath/"); + v.push(out_filename.file_name().unwrap()); + self.linker_arg(&v); + } + } else { + self.cmd.arg("-shared"); + if self.sess.target.target.options.is_like_windows { + // The output filename already contains `dll_suffix` so + // the resulting import library will have a name in the + // form of libfoo.dll.a + let implib_name = + out_filename.file_name().and_then(|file| file.to_str()).map(|file| { + format!( + "{}{}{}", + self.sess.target.target.options.staticlib_prefix, + file, + self.sess.target.target.options.staticlib_suffix + ) + }); + if let Some(implib_name) = implib_name { + let implib = out_filename.parent().map(|dir| dir.join(&implib_name)); + if let Some(implib) = implib { + self.linker_arg(&format!("--out-implib,{}", (*implib).to_str().unwrap())); + } + } + } + } + } } impl<'a> Linker for GccLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + + fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) { + match output_kind { + LinkOutputKind::DynamicNoPicExe => { + if !self.is_ld && self.sess.target.target.options.linker_is_gnu { + self.cmd.arg("-no-pie"); + } + } + LinkOutputKind::DynamicPicExe => { + // `-pie` works for both gcc wrapper and ld. + self.cmd.arg("-pie"); + } + LinkOutputKind::StaticNoPicExe => { + // `-static` works for both gcc wrapper and ld. + self.cmd.arg("-static"); + if !self.is_ld && self.sess.target.target.options.linker_is_gnu { + self.cmd.arg("-no-pie"); + } + } + LinkOutputKind::StaticPicExe => { + if !self.is_ld { + // Note that combination `-static -pie` doesn't work as expected + // for the gcc wrapper, `-static` in that case suppresses `-pie`. + self.cmd.arg("-static-pie"); + } else { + // `--no-dynamic-linker` and `-z text` are not strictly necessary for producing + // a static pie, but currently passed because gcc and clang pass them. + // The former suppresses the `INTERP` ELF header specifying dynamic linker, + // which is otherwise implicitly injected by ld (but not lld). + // The latter doesn't change anything, only ensures that everything is pic. + self.cmd.args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]); + } + } + LinkOutputKind::DynamicDylib => self.build_dylib(out_filename), + LinkOutputKind::StaticDylib => { + self.cmd.arg("-static"); + self.build_dylib(out_filename); + } + } + // VxWorks compiler driver introduced `--static-crt` flag specifically for rustc, + // it switches linking for libc and similar system libraries to static without using + // any `#[link]` attributes in the `libc` crate, see #72782 for details. + // FIXME: Switch to using `#[link]` attributes in the `libc` crate + // similarly to other targets. + if self.sess.target.target.target_os == "vxworks" + && matches!( + output_kind, + LinkOutputKind::StaticNoPicExe + | LinkOutputKind::StaticPicExe + | LinkOutputKind::StaticDylib + ) + { + self.cmd.arg("--static-crt"); + } + } + fn link_dylib(&mut self, lib: Symbol) { self.hint_dynamic(); self.cmd.arg(format!("-l{}", lib)); @@ -232,12 +357,6 @@ impl<'a> Linker for GccLinker<'a> { fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn position_independent_executable(&mut self) { - self.cmd.arg("-pie"); - } - fn no_position_independent_executable(&mut self) { - self.cmd.arg("-no-pie"); - } fn full_relro(&mut self) { self.linker_arg("-zrelro"); self.linker_arg("-znow"); @@ -248,12 +367,6 @@ impl<'a> Linker for GccLinker<'a> { fn no_relro(&mut self) { self.linker_arg("-znorelro"); } - 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,61 +478,29 @@ 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 { + fn debuginfo(&mut self, strip: Strip) { + match strip { + Strip::None => {} + Strip::Debuginfo => { + // MacOS linker does not support longhand argument --strip-debug self.linker_arg("-S"); } - }; + Strip::Symbols => { + // MacOS linker does not support longhand argument --strip-all + self.linker_arg("-s"); + } + } } - fn no_default_libraries(&mut self) { + fn no_crt_objects(&mut self) { if !self.is_ld { - self.cmd.arg("-nodefaultlibs"); + self.cmd.arg("-nostartfiles"); } } - fn build_dylib(&mut self, out_filename: &Path) { - // On mac we need to tell the linker to let this library be rpathed - if self.sess.target.target.options.is_like_osx { - self.cmd.arg("-dynamiclib"); - self.linker_arg("-dylib"); - - // Note that the `osx_rpath_install_name` option here is a hack - // purely to support rustbuild right now, we should get a more - // principled solution at some point to force the compiler to pass - // the right `-Wl,-install_name` with an `@rpath` in it. - if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name { - self.linker_arg("-install_name"); - let mut v = OsString::from("@rpath/"); - v.push(out_filename.file_name().unwrap()); - self.linker_arg(&v); - } - } else { - self.cmd.arg("-shared"); - if self.sess.target.target.options.is_like_windows { - // The output filename already contains `dll_suffix` so - // the resulting import library will have a name in the - // form of libfoo.dll.a - let implib_name = - out_filename.file_name().and_then(|file| file.to_str()).map(|file| { - format!( - "{}{}{}", - self.sess.target.target.options.staticlib_prefix, - file, - self.sess.target.target.options.staticlib_suffix - ) - }); - if let Some(implib_name) = implib_name { - let implib = out_filename.parent().map(|dir| dir.join(&implib_name)); - if let Some(implib) = implib { - self.linker_arg(&format!("--out-implib,{}", (*implib).to_str().unwrap())); - } - } - } + fn no_default_libraries(&mut self) { + if !self.is_ld { + self.cmd.arg("-nodefaultlibs"); } } @@ -505,10 +586,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,26 +624,31 @@ pub struct MsvcLinker<'a> { } impl<'a> Linker for MsvcLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + + fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) { + match output_kind { + LinkOutputKind::DynamicNoPicExe + | LinkOutputKind::DynamicPicExe + | LinkOutputKind::StaticNoPicExe + | LinkOutputKind::StaticPicExe => {} + LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { + self.cmd.arg("/DLL"); + let mut arg: OsString = "/IMPLIB:".into(); + arg.push(out_filename.with_extension("dll.lib")); + self.cmd.arg(arg); + } + } + } + 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"); - let mut arg: OsString = "/IMPLIB:".into(); - arg.push(out_filename.with_extension("dll.lib")); - self.cmd.arg(arg); - } - - fn build_static_executable(&mut self) { - // noop - } fn gc_sections(&mut self, _keep_metadata: bool) { // MSVC's ICF (Identical COMDAT Folding) link optimization is @@ -598,14 +682,6 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg(&format!("{}.lib", lib)); } - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - fn full_relro(&mut self) { // noop } @@ -618,16 +694,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) { @@ -650,12 +722,14 @@ impl<'a> Linker for MsvcLinker<'a> { } fn link_whole_staticlib(&mut self, lib: Symbol, _search_path: &[PathBuf]) { - // not supported? self.link_staticlib(lib); + self.cmd.arg(format!("/WHOLEARCHIVE:{}.lib", lib)); } fn link_whole_rlib(&mut self, path: &Path) { - // not supported? self.link_rlib(path); + let mut arg = OsString::from("/WHOLEARCHIVE:"); + arg.push(path); + self.cmd.arg(arg); } fn optimize(&mut self) { // Needs more investigation of `/OPT` arguments @@ -669,29 +743,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 +840,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 +858,12 @@ pub struct EmLinker<'a> { } impl<'a> Linker for EmLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + + fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} + fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } @@ -817,14 +903,6 @@ impl<'a> Linker for EmLinker<'a> { self.add_object(lib); } - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - fn full_relro(&mut self) { // noop } @@ -837,10 +915,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 +949,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,18 +958,12 @@ 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=[]"]); } - fn build_dylib(&mut self, _out_filename: &Path) { - bug!("building dynamic library is unsupported on Emscripten") - } - - fn build_static_executable(&mut self) { - // noop - } - fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { let symbols = &self.info.exports[&crate_type]; @@ -928,9 +996,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) {} @@ -964,9 +1030,6 @@ impl<'a> WasmLd<'a> { // sharing memory and instantiating the module multiple times. As a // result if it were exported then we'd just have no sharing. // - // * `--passive-segments` - all memory segments should be passive to - // prevent each module instantiation from reinitializing memory. - // // * `--export=__wasm_init_memory` - when using `--passive-segments` the // linker will synthesize this function, and so we need to make sure // that our usage of `--export` below won't accidentally cause this @@ -974,13 +1037,10 @@ impl<'a> WasmLd<'a> { // // * `--export=*tls*` - when `#[thread_local]` symbols are used these // symbols are how the TLS segments are initialized and configured. - let atomics = sess.opts.cg.target_feature.contains("+atomics") - || sess.target.target.options.features.contains("+atomics"); - if atomics { + if sess.target_features.contains(&sym::atomics) { cmd.arg("--shared-memory"); cmd.arg("--max-memory=1073741824"); cmd.arg("--import-memory"); - cmd.arg("--passive-segments"); cmd.arg("--export=__wasm_init_memory"); cmd.arg("--export=__wasm_init_tls"); cmd.arg("--export=__tls_size"); @@ -992,6 +1052,22 @@ impl<'a> WasmLd<'a> { } impl<'a> Linker for WasmLd<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + + fn set_output_kind(&mut self, output_kind: LinkOutputKind, _out_filename: &Path) { + match output_kind { + LinkOutputKind::DynamicNoPicExe + | LinkOutputKind::DynamicPicExe + | LinkOutputKind::StaticNoPicExe + | LinkOutputKind::StaticPicExe => {} + LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { + self.cmd.arg("--no-entry"); + } + } + } + fn link_dylib(&mut self, lib: Symbol) { self.cmd.arg("-l").sym_arg(lib); } @@ -1020,20 +1096,12 @@ impl<'a> Linker for WasmLd<'a> { self.cmd.arg(path); } - fn position_independent_executable(&mut self) {} - fn full_relro(&mut self) {} fn partial_relro(&mut self) {} fn no_relro(&mut self) {} - 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,17 +1137,25 @@ 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_default_libraries(&mut self) {} + fn no_crt_objects(&mut self) {} - fn build_dylib(&mut self, _out_filename: &Path) { - self.cmd.arg("--no-entry"); - } + fn no_default_libraries(&mut self) {} fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { for sym in self.info.exports[&crate_type].iter() { @@ -1096,11 +1172,7 @@ impl<'a> Linker for WasmLd<'a> { fn subsystem(&mut self, _subsystem: &str) {} - 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 +1202,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 +1230,12 @@ pub struct PtxLinker<'a> { } impl<'a> Linker for PtxLinker<'a> { + fn cmd(&mut self) -> &mut Command { + &mut self.cmd + } + + fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} + fn link_rlib(&mut self, path: &Path) { self.cmd.arg("--rlib").arg(path); } @@ -1174,7 +1248,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 +1256,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 +1270,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) { @@ -1234,34 +1302,28 @@ impl<'a> Linker for PtxLinker<'a> { panic!("frameworks not supported") } - fn position_independent_executable(&mut self) {} - fn full_relro(&mut self) {} fn partial_relro(&mut self) {} fn no_relro(&mut self) {} - fn build_static_executable(&mut self) {} - fn gc_sections(&mut self, _keep_metadata: bool) {} fn pgo_gen(&mut self) {} + fn no_crt_objects(&mut self) {} + fn no_default_libraries(&mut self) {} fn control_flow_guard(&mut self) { self.sess.warn("Windows Control Flow Guard is not supported by this linker."); } - fn build_dylib(&mut self, _out_filename: &Path) {} - fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) {} fn subsystem(&mut self, _subsystem: &str) {} - fn no_position_independent_executable(&mut self) {} - fn group_start(&mut self) {} fn group_end(&mut self) {} diff --git a/src/librustc_codegen_ssa/back/rpath.rs b/src/librustc_codegen_ssa/back/rpath.rs index 9d19cc25a32bc..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)], @@ -81,9 +81,7 @@ fn get_rpaths(config: &mut RPathConfig<'_>, libs: &[PathBuf]) -> Vec { rpaths.extend_from_slice(&fallback_rpaths); // Remove duplicates - let rpaths = minimize_rpaths(&rpaths); - - rpaths + minimize_rpaths(&rpaths) } fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>, libs: &[PathBuf]) -> Vec { diff --git a/src/librustc_codegen_ssa/back/symbol_export.rs b/src/librustc_codegen_ssa/back/symbol_export.rs index 3fe256ce25b96..217ad57ddc9c3 100644 --- a/src/librustc_codegen_ssa/back/symbol_export.rs +++ b/src/librustc_codegen_ssa/back/symbol_export.rs @@ -1,37 +1,36 @@ 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::session::config::{self, Sanitizer}; -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_codegen_utils::symbol_names; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; 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_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, SanitizerSet}; pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { - crates_export_threshold(&tcx.sess.crate_types.borrow()) + crates_export_threshold(&tcx.sess.crate_types()) } -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) @@ -42,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 @@ -86,15 +82,19 @@ 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::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Method(..), .. }) => { + 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) + if !generics.requires_monomorphization(tcx) + // Functions marked with #[inline] are codegened with "internal" + // linkage and are not exported unless marked with an extern + // inidicator + && (!Instance::mono(tcx, def_id.to_def_id()).def.generates_cgu_internal_copy(tcx) + || tcx.codegen_fn_attrs(def_id.to_def_id()).contains_extern_indicator()) { Some(def_id) } else { @@ -107,7 +107,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 @@ -124,14 +124,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(); @@ -143,7 +143,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 { @@ -163,11 +163,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 @@ -204,7 +204,7 @@ fn exported_symbols_provider_local( })); } - if let Some(Sanitizer::Memory) = tcx.sess.opts.debugging_opts.sanitizer { + if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) { // Similar to profiling, preserve weak msan symbol during LTO. const MSAN_WEAK_SYMBOLS: [&str; 2] = ["__msan_track_origins", "__msan_keep_going"]; @@ -214,7 +214,7 @@ fn exported_symbols_provider_local( })); } - if tcx.sess.crate_types.borrow().contains(&config::CrateType::Dylib) { + if tcx.sess.crate_types().contains(&CrateType::Dylib) { let symbol_name = metadata_symbol_name(tcx); let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); @@ -222,8 +222,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 @@ -273,13 +273,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); @@ -336,7 +336,7 @@ fn upstream_monomorphizations_provider( } } - tcx.arena.alloc(instances) + instances } fn upstream_monomorphizations_for_provider( @@ -359,8 +359,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) } @@ -423,17 +423,21 @@ pub fn symbol_name_for_instance_in_crate<'tcx>( // This is something instantiated in an upstream crate, so we have to use // the slower (because uncached) version of computing the symbol name. match symbol { - ExportedSymbol::NonGeneric(def_id) => symbol_names::symbol_name_for_instance_in_crate( - tcx, - Instance::mono(tcx, def_id), - instantiating_crate, - ), - ExportedSymbol::Generic(def_id, substs) => symbol_names::symbol_name_for_instance_in_crate( - tcx, - Instance::new(def_id, substs), - instantiating_crate, - ), - ExportedSymbol::DropGlue(ty) => symbol_names::symbol_name_for_instance_in_crate( + ExportedSymbol::NonGeneric(def_id) => { + rustc_symbol_mangling::symbol_name_for_instance_in_crate( + tcx, + Instance::mono(tcx, def_id), + instantiating_crate, + ) + } + ExportedSymbol::Generic(def_id, substs) => { + rustc_symbol_mangling::symbol_name_for_instance_in_crate( + tcx, + Instance::new(def_id, substs), + instantiating_crate, + ) + } + ExportedSymbol::DropGlue(ty) => rustc_symbol_mangling::symbol_name_for_instance_in_crate( tcx, Instance::resolve_drop_in_place(tcx, ty), instantiating_crate, diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index dbc2ef6f2b05e..23e0b9344ec91 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -1,24 +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::session::config::{ - self, Lto, OutputFilenames, OutputType, Passes, Sanitizer, SwitchWithOptPath, -}; -use rustc::session::Session; -use rustc::ty::TyCtxt; use rustc_ast::attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfilerRef; @@ -31,13 +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_span::hygiene::ExpnId; +use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; +use rustc_session::config::{Passes, SanitizerSet, SwitchWithOptPath}; +use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{sym, Symbol}; -use rustc_target::spec::MergeFunctions; +use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span}; +use rustc_target::spec::{MergeFunctions, PanicStrategy}; use std::any::Any; use std::fs; @@ -51,6 +48,30 @@ use std::thread; const PRE_LTO_BC_EXT: &str = "pre-lto.bc"; +/// What kind of object file to emit. +#[derive(Clone, Copy, PartialEq)] +pub enum EmitObj { + // No object file. + None, + + // Just uncompressed llvm bitcode. Provides easy compatibility with + // emscripten's ecc compiler, when used as the linker. + Bitcode, + + // Object code, possibly augmented with a bitcode section. + ObjectCode(BitcodeSection), +} + +/// What kind of llvm bitcode section to embed in an object file. +#[derive(Clone, Copy, PartialEq)] +pub enum BitcodeSection { + // No bitcode section. + None, + + // A full, uncompressed bitcode section. + Full, +} + /// Module-specific configuration for `optimize_and_codegen`. pub struct ModuleConfig { /// Names of additional optimization passes to run. @@ -65,19 +86,19 @@ pub struct ModuleConfig { pub pgo_gen: SwitchWithOptPath, pub pgo_use: Option, - pub sanitizer: Option, - pub sanitizer_recover: Vec, + pub sanitizer: SanitizerSet, + pub sanitizer_recover: SanitizerSet, pub sanitizer_memory_track_origins: usize, // Flags indicating which outputs to produce. pub emit_pre_lto_bc: bool, pub emit_no_opt_bc: bool, pub emit_bc: bool, - pub emit_bc_compressed: bool, - pub emit_lto_bc: 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, @@ -88,118 +109,173 @@ 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: bool, - pub embed_bitcode_marker: bool, + 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_lto_bc: false, - emit_ir: false, - emit_asm: false, - emit_obj: false, - obj_is_bitcode: false, - embed_bitcode: false, - embed_bitcode_marker: false, - 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(); - let embed_bitcode = - sess.target.target.options.embed_bitcode || sess.opts.debugging_opts.embed_bitcode; - if embed_bitcode { - match sess.opts.optimize { - config::OptLevel::No | config::OptLevel::Less => { - self.embed_bitcode_marker = embed_bitcode; - } - _ => self.embed_bitcode = embed_bitcode, - } - } + let opt_level_and_size = if_regular!(Some(sess.opts.optimize), None); - // 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 save_temps = sess.opts.cg.save_temps; + + let should_emit_obj = sess.opts.output_types.contains_key(&OutputType::Exe) + || match kind { + ModuleKind::Regular => sess.opts.output_types.contains_key(&OutputType::Object), + ModuleKind::Allocator => false, + ModuleKind::Metadata => sess.opts.output_types.contains_key(&OutputType::Metadata), + }; + + let emit_obj = if !should_emit_obj { + EmitObj::None + } else if sess.target.target.options.obj_is_bitcode + || (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins) { - MergeFunctions::Disabled => false, - MergeFunctions::Trampolines | MergeFunctions::Aliases => { - sess.opts.optimize == config::OptLevel::Default - || sess.opts.optimize == config::OptLevel::Aggressive - } + // This case is selected if the target uses objects as bitcode, or + // if linker plugin LTO is enabled. In the linker plugin LTO case + // the assumption is that the final link-step will read the bitcode + // and convert it to object code. This may be done by either the + // native linker or rustc itself. + // + // Note, however, that the linker-plugin-lto requested here is + // explicitly ignored for `#![no_builtins]` crates. These crates are + // specifically ignored by rustc's LTO passes and wouldn't work if + // loaded into the linker. These crates define symbols that LLVM + // lowers intrinsics to, and these symbol dependencies aren't known + // until after codegen. As a result any crate marked + // `#![no_builtins]` is assumed to not participate in LTO and + // instead goes on to generate object code. + EmitObj::Bitcode + } else if need_bitcode_in_object(sess) { + EmitObj::ObjectCode(BitcodeSection::Full) + } else { + EmitObj::ObjectCode(BitcodeSection::None) }; + + ModuleConfig { + passes: if_regular!( + { + let mut passes = sess.opts.cg.passes.clone(); + // compiler_builtins overrides the codegen-units settings, + // which is incompatible with -Zprofile which requires that + // only a single codegen unit is used per crate. + if sess.opts.debugging_opts.profile && !is_compiler_builtins { + passes.push("insert-gcov-profiling".to_owned()); + } + + // The rustc option `-Zinstrument_coverage` injects intrinsic calls to + // `llvm.instrprof.increment()`, which requires the LLVM `instrprof` pass. + if sess.opts.debugging_opts.instrument_coverage { + passes.push("instrprof".to_owned()); + } + passes + }, + vec![] + ), + + opt_level: opt_level_and_size, + opt_size: opt_level_and_size, + + pgo_gen: if_regular!( + sess.opts.cg.profile_generate.clone(), + SwitchWithOptPath::Disabled + ), + pgo_use: if_regular!(sess.opts.cg.profile_use.clone(), None), + + sanitizer: if_regular!(sess.opts.debugging_opts.sanitizer, SanitizerSet::empty()), + sanitizer_recover: if_regular!( + sess.opts.debugging_opts.sanitizer_recover, + SanitizerSet::empty() + ), + sanitizer_memory_track_origins: if_regular!( + sess.opts.debugging_opts.sanitizer_memory_track_origins, + 0 + ), + + emit_pre_lto_bc: if_regular!( + save_temps || need_pre_lto_bitcode_for_incr_comp(sess), + false + ), + emit_no_opt_bc: if_regular!(save_temps, false), + emit_bc: if_regular!( + save_temps || sess.opts.output_types.contains_key(&OutputType::Bitcode), + save_temps + ), + emit_ir: if_regular!( + sess.opts.output_types.contains_key(&OutputType::LlvmAssembly), + false + ), + emit_asm: if_regular!( + sess.opts.output_types.contains_key(&OutputType::Assembly), + false + ), + emit_obj, + bc_cmdline: sess.target.target.options.bitcode_llvm_cmdline.clone(), + + verify_llvm_ir: sess.verify_llvm_ir(), + no_prepopulate_passes: sess.opts.cg.no_prepopulate_passes, + no_builtins: no_builtins || sess.target.target.options.no_builtins, + + // Exclude metadata and allocator modules from time_passes output, + // since they throw off the "LLVM passes" measurement. + time_module: if_regular!(true, false), + + // Copy what clang does by turning on loop vectorization at O2 and + // slp vectorization at O3. + vectorize_loop: !sess.opts.cg.no_vectorize_loops + && (sess.opts.optimize == config::OptLevel::Default + || sess.opts.optimize == config::OptLevel::Aggressive), + vectorize_slp: !sess.opts.cg.no_vectorize_slp + && sess.opts.optimize == config::OptLevel::Aggressive, + + // Some targets (namely, NVPTX) interact badly with the + // MergeFunctions pass. This is because MergeFunctions can generate + // new function calls which may interfere with the target calling + // convention; e.g. for the NVPTX target, PTX kernels should not + // call other PTX kernels. MergeFunctions can also be configured to + // generate aliases instead, but aliases are not supported by some + // backends (again, NVPTX). Therefore, allow targets to opt out of + // the MergeFunctions pass, but otherwise keep the pass enabled (at + // O2 and O3) since it can be useful for reducing code size. + merge_functions: match sess + .opts + .debugging_opts + .merge_functions + .unwrap_or(sess.target.target.options.merge_functions) + { + MergeFunctions::Disabled => false, + MergeFunctions::Trampolines | MergeFunctions::Aliases => { + sess.opts.optimize == config::OptLevel::Default + || sess.opts.optimize == config::OptLevel::Aggressive + } + }, + + inline_threshold: sess.opts.cg.inline_threshold, + new_llvm_pass_manager: sess.opts.debugging_opts.new_llvm_pass_manager, + emit_lifetime_markers: sess.emit_lifetime_markers(), + } } pub fn bitcode_needed(&self) -> bool { - self.emit_bc || self.obj_is_bitcode || self.emit_bc_compressed || self.embed_bitcode + self.emit_bc + || 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>, @@ -225,7 +301,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, @@ -252,8 +328,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 { @@ -288,7 +362,7 @@ fn generate_lto_work( B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise()) }; - let result = lto_modules + lto_modules .into_iter() .map(|module| { let cost = module.cost(); @@ -303,9 +377,7 @@ fn generate_lto_work( 0, ) })) - .collect(); - - result + .collect() } pub struct CompiledModules { @@ -314,9 +386,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().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 { @@ -342,6 +417,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| { @@ -358,89 +435,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; - modules_config.emit_lto_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(); @@ -454,7 +454,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(), @@ -487,23 +487,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); } @@ -737,38 +727,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, @@ -778,20 +764,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. @@ -836,29 +835,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 {}", @@ -877,16 +856,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, })) } @@ -954,7 +930,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>, @@ -1011,17 +987,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() { @@ -1032,10 +997,10 @@ fn start_executing_work( }; let cgcx = CodegenContext:: { backend: backend.clone(), - crate_types: sess.crate_types.borrow().clone(), + crate_types: sess.crate_types().to_vec(), 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()), @@ -1048,16 +1013,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. @@ -1535,7 +1499,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] @@ -1591,47 +1555,9 @@ 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), + InlineAsmError(u32, String, Level, Option<(String, Vec)>), AbortIfErrors, Fatal(String), } @@ -1652,8 +1578,14 @@ impl SharedEmitter { (SharedEmitter { sender }, SharedEmitterMain { receiver }) } - pub fn inline_asm_error(&self, cookie: u32, msg: String) { - drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg))); + pub fn inline_asm_error( + &self, + cookie: u32, + msg: String, + level: Level, + source: Option<(String, Vec)>, + ) { + drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source))); } pub fn fatal(&self, msg: &str) { @@ -1706,8 +1638,35 @@ impl SharedEmitterMain { } handler.emit_diagnostic(&d); } - Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => { - sess.span_err(ExpnId::from_u32(cookie).expn_data().call_site, &msg) + Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => { + let msg = msg.strip_prefix("error: ").unwrap_or(&msg); + + let mut err = match level { + Level::Error => sess.struct_err(&msg), + Level::Warning => sess.struct_warn(&msg), + Level::Note => sess.struct_note_without_error(&msg), + _ => bug!("Invalid inline asm diagnostic level"), + }; + + // If the cookie is 0 then we don't have span information. + if cookie != 0 { + let pos = BytePos::from_u32(cookie); + let span = Span::with_root_ctxt(pos, pos); + err.set_span(span); + }; + + // Point to the generated assembly if it is available. + if let Some((buffer, spans)) = source { + let source = sess + .source_map() + .new_source_file(FileName::inline_asm_source_code(&buffer), buffer); + let source_span = Span::with_root_ctxt(source.start_pos, source.end_pos); + let spans: Vec<_> = + spans.iter().map(|sp| source_span.from_inner(*sp)).collect(); + err.span_note(spans, "instantiated into assembly here"); + } + + err.emit(); } Ok(SharedEmitterMessage::AbortIfErrors) => { sess.abort_if_errors(); @@ -1892,7 +1851,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().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 d6e1ab8909c59..5b14258bd25be 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,30 @@ 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::session::config::{self, EntryFnType, Lto}; -use rustc::session::Session; -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_codegen_utils::{check_for_rustc_errors_attr, symbol_names_test}; 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}; +use rustc_session::utils::NativeLibKind; +use rustc_session::Session; use rustc_span::Span; +use rustc_symbol_mangling::test as symbol_names_test; +use rustc_target::abi::{Abi, Align, LayoutOf, Scalar, VariantIdx}; use std::cmp; use std::ops::{Deref, DerefMut}; @@ -181,8 +183,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 +232,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 +340,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 +351,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 +365,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 +398,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 +417,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 +466,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 +506,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, @@ -514,8 +514,6 @@ pub fn codegen_crate( metadata: EncodedMetadata, need_metadata_module: bool, ) -> OngoingCodegen { - check_for_rustc_errors_attr(tcx); - // Skip crate items and just output metadata in -Z no-codegen mode. if tcx.sess.opts.debugging_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { let ongoing_codegen = start_async_codegen(backend, tcx, metadata, 1); @@ -534,7 +532,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 @@ -542,8 +539,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()); } } @@ -559,7 +556,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 { @@ -604,7 +601,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 }; @@ -852,7 +849,7 @@ impl CrateInfo { info.missing_lang_items.insert(cnum, missing); } - return info; + info } } @@ -887,7 +884,7 @@ pub fn provide_both(providers: &mut Providers<'_>) { } } } - return tcx.sess.opts.optimize; + tcx.sess.opts.optimize }; providers.dllimport_foreign_items = |tcx, krate| { @@ -899,7 +896,7 @@ pub fn provide_both(providers: &mut Providers<'_>) { .native_libraries(krate) .iter() .filter(|lib| { - if lib.kind != cstore::NativeLibraryKind::NativeUnknown { + if !matches!(lib.kind, NativeLibKind::Dylib | NativeLibKind::Unspecified) { return false; } let cfg = match lib.cfg { @@ -912,7 +909,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 = @@ -945,8 +942,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(), + 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 28b61e0b36d64..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::session::Session; -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..a64489c04c81d 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 @@ -48,7 +48,7 @@ pub fn push_debuginfo_type_name<'tcx>( } ty::Tuple(component_types) => { output.push('('); - for &component_type in component_types { + for component_type in component_types { push_debuginfo_type_name(tcx, component_type.expect_ty(), true, output, visited); output.push_str(", "); } @@ -195,10 +195,9 @@ pub fn push_debuginfo_type_name<'tcx>( tcx.def_key(def_id).disambiguated_data.disambiguator )); } - ty::Error + 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 a2bb39b9e4019..bd3721850f35f 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,18 +16,18 @@ #[macro_use] extern crate log; #[macro_use] -extern crate rustc; - -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::session::config::{OutputFilenames, OutputType, RUST_CGU_EXT}; -use rustc::ty::query::Providers; +extern crate rustc_middle; + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::CrateNum; +use rustc_hir::LangItem; +use rustc_middle::dep_graph::WorkProduct; +use rustc_middle::middle::cstore::{CrateSource, LibSource, NativeLib}; +use rustc_middle::middle::dependency_format::Dependencies; +use rustc_middle::ty::query::Providers; +use rustc_session::config::{OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_span::symbol::Symbol; use std::path::{Path, PathBuf}; @@ -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 { @@ -125,9 +112,9 @@ pub struct CrateInfo { pub compiler_builtins: Option, pub profiler_runtime: Option, pub is_no_builtins: FxHashSet, - pub native_libraries: FxHashMap>>, + pub native_libraries: FxHashMap>>, pub crate_name: FxHashMap, - pub used_libraries: Lrc>, + pub used_libraries: Lrc>, pub link_args: Lrc>, pub used_crate_source: FxHashMap>, pub used_crates_static: Vec<(CrateNum, LibSource)>, @@ -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..db935c2b3e265 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); @@ -103,7 +104,7 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { ) { let cx = self.fx.cx; - if let [proj_base @ .., elem] = place_ref.projection { + if let &[ref proj_base @ .., elem] = place_ref.projection { let mut base_context = if context.is_mutating_use() { PlaceContext::MutatingUse(MutatingUseContext::Projection) } else { @@ -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. @@ -184,7 +186,7 @@ impl> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { // now that we have moved to the "slice of projections" representation. if let mir::ProjectionElem::Index(local) = elem { self.visit_local( - local, + &local, PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy), location, ); @@ -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); } } @@ -232,8 +234,8 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> self.visit_rvalue(rvalue, location); } - fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) { - let check = match *kind { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + let check = match terminator.kind { mir::TerminatorKind::Call { func: mir::Operand::Constant(ref c), ref args, .. } => { match c.literal.ty.kind { ty::FnDef(did, _) => Some((did, args)), @@ -257,7 +259,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> } } - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: 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); } @@ -349,8 +357,9 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec { /* nothing to do */ } + | TerminatorKind::FalseEdge { .. } + | 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..5125ce779ed8e 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], ) { @@ -195,6 +200,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { targets: &Vec, ) { let discr = self.codegen_operand(&mut bx, &discr); + // `switch_ty` is redundant, sanity-check that. + assert_eq!(discr.layout.ty, switch_ty); if targets.len() == 2 { // If there are two targets, emit br instead of switch let lltrue = helper.llblock(self, targets[0]); @@ -299,11 +306,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); @@ -525,6 +532,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args: &Vec>, destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>, cleanup: Option, + fn_span: Span, ) { let span = terminator.source_info.span; // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. @@ -534,6 +542,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 +577,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 +589,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 +600,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 +628,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 { @@ -628,7 +637,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if intrinsic == Some("caller_location") { if let Some((_, target)) = destination.as_ref() { - let location = self.get_caller_location(&mut bx, span); + let location = self.get_caller_location(&mut bx, fn_span); if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { location.val.store(&mut bx, tmp); @@ -686,6 +695,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &args, dest, terminator.source_info.span, + self.instance, ); if let ReturnDest::IndirectOperand(dst, _) = ret_dest { @@ -792,7 +802,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args.len() + 1, "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR", ); - let location = self.get_caller_location(&mut bx, span); + let location = self.get_caller_location(&mut bx, fn_span); + debug!( + "codegen_call_terminator({:?}): location={:?} (fn_span {:?})", + terminator, location, fn_span + ); + let last_arg = fn_abi.args.last().unwrap(); self.codegen_argument(&mut bx, location, &mut llargs, last_arg); } @@ -816,6 +831,119 @@ 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, + line_spans: &[Span], + destination: Option, + ) { + let span = terminator.source_info.span; + + let operands: Vec<_> = operands + .iter() + .map(|op| match *op { + mir::InlineAsmOperand::In { reg, ref value } => { + let value = self.codegen_operand(&mut bx, value); + InlineAsmOperandRef::In { reg, value } + } + mir::InlineAsmOperand::Out { reg, late, ref place } => { + let place = place.map(|place| self.codegen_place(&mut bx, place.as_ref())); + InlineAsmOperandRef::Out { reg, late, place } + } + mir::InlineAsmOperand::InOut { reg, late, ref in_value, ref out_place } => { + let in_value = self.codegen_operand(&mut bx, in_value); + let out_place = + out_place.map(|out_place| self.codegen_place(&mut bx, out_place.as_ref())); + InlineAsmOperandRef::InOut { reg, late, in_value, out_place } + } + mir::InlineAsmOperand::Const { ref value } => { + if let mir::Operand::Constant(constant) = value { + let const_value = self + .eval_mir_constant(constant) + .unwrap_or_else(|_| span_bug!(span, "asm const cannot be resolved")); + let ty = constant.literal.ty; + let size = bx.layout_of(ty).size; + let scalar = match const_value { + // Promoted constants are evaluated into a ByRef instead of a Scalar, + // but we want the scalar value here. + ConstValue::ByRef { alloc, offset } => { + let ptr = Pointer::new(AllocId(0), offset); + alloc + .read_scalar(&bx, ptr, size) + .and_then(|s| s.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_for_fn_ptr( + bx.tcx(), + ty::ParamEnv::reveal_all(), + def_id, + substs, + ) + .unwrap(); + InlineAsmOperandRef::SymFn { instance } + } else { + span_bug!(span, "invalid type for asm sym (fn)"); + } + } + mir::InlineAsmOperand::SymStatic { def_id } => { + InlineAsmOperandRef::SymStatic { def_id } + } + }) + .collect(); + + bx.codegen_inline_asm(template, &operands, options, line_spans); + + if let Some(target) = destination { + helper.funclet_br(self, &mut bx, target); + } else { + bx.unreachable(); + } + } } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -873,8 +1001,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.unreachable(); } - mir::TerminatorKind::Drop { ref location, target, unwind } => { - self.codegen_drop_terminator(helper, bx, location, target, unwind); + mir::TerminatorKind::Drop { place, target, unwind } => { + self.codegen_drop_terminator(helper, bx, place, target, unwind); } mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { @@ -893,6 +1021,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ref destination, cleanup, from_hir_call: _, + fn_span, } => { self.codegen_call_terminator( helper, @@ -902,14 +1031,34 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, destination, cleanup, + fn_span, ); } mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Yield { .. } => { bug!("generator ops in codegen") } - mir::TerminatorKind::FalseEdges { .. } | mir::TerminatorKind::FalseUnwind { .. } => { + mir::TerminatorKind::FalseEdge { .. } | mir::TerminatorKind::FalseUnwind { .. } => { bug!("borrowck false edges in codegen") } + + mir::TerminatorKind::InlineAsm { + template, + ref operands, + options, + line_spans, + destination, + } => { + self.codegen_asm_terminator( + helper, + bx, + terminator, + template, + operands, + options, + line_spans, + destination, + ); + } } } @@ -994,7 +1143,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 +1272,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 +1333,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 4248627dccaf2..11ec62f96ed38 100644 --- a/src/librustc_codegen_ssa/mir/constant.rs +++ b/src/librustc_codegen_ssa/mir/constant.rs @@ -1,11 +1,11 @@ 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,56 +15,35 @@ 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( &mut self, constant: &mir::Constant<'tcx>, ) -> Result, ErrorHandled> { - match constant.literal.val { - ty::ConstKind::Unevaluated(def_id, substs, promoted) => { - let substs = self.monomorphize(&substs); - self.cx - .tcx() - .const_eval_resolve(ty::ParamEnv::reveal_all(), def_id, substs, promoted, None) - .map_err(|err| { - if promoted.is_none() { - self.cx - .tcx() - .sess - .span_err(constant.span, "erroneous constant encountered"); - } - err - }) - } + match self.monomorphize(&constant.literal).val { + ty::ConstKind::Unevaluated(def_id, substs, promoted) => self + .cx + .tcx() + .const_eval_resolve(ty::ParamEnv::reveal_all(), def_id, substs, promoted, None) + .map_err(|err| { + if promoted.is_none() { + self.cx + .tcx() + .sess + .span_err(constant.span, "erroneous constant encountered"); + } + err + }), ty::ConstKind::Value(value) => Ok(value), - _ => { - let const_ = self.monomorphize(&constant.literal); - if let ty::ConstKind::Value(value) = const_.val { - Ok(value) - } else { - span_bug!(constant.span, "encountered bad ConstKind in codegen: {:?}", const_); - } - } + err => span_bug!( + constant.span, + "encountered bad ConstKind after monomorphizing: {:?}", + err + ), } } @@ -79,20 +58,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { constant .map(|val| { let field_ty = ty.builtin_index().unwrap(); - let fields = match ty.kind { - ty::Array(_, n) => n.eval_usize(bx.tcx(), ty::ParamEnv::reveal_all()), - _ => bug!("invalid simd shuffle type: {}", ty), - }; let c = ty::Const::from_value(bx.tcx(), val, ty); - let values: Vec<_> = (0..fields) + let values: Vec<_> = bx + .tcx() + .destructure_const(ty::ParamEnv::reveal_all().and(&c)) + .fields + .iter() .map(|field| { - let field = bx.tcx().const_field( - ty::ParamEnv::reveal_all().and((&c, mir::Field::new(field as usize))), - ); - if let Some(prim) = field.try_to_scalar() { + if let Some(prim) = field.val.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 2dc1405f4e438..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::session::config::DebugInfo; -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 64ead19b35869..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>, @@ -86,13 +88,18 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn monomorphize(&self, value: &T) -> T where - T: TypeFoldable<'tcx>, + T: Copy + TypeFoldable<'tcx>, { - self.cx.tcx().subst_and_normalize_erasing_regions( - self.instance.substs, - ty::ParamEnv::reveal_all(), - value, - ) + debug!("monomorphize: self.instance={:?}", self.instance); + if let Some(substs) = self.instance.substs_for_mir_body() { + self.cx.tcx().subst_and_normalize_erasing_regions( + substs, + ty::ParamEnv::reveal_all(), + &value, + ) + } else { + self.cx.tcx().normalize_erasing_regions(ty::ParamEnv::reveal_all(), *value) + } } } @@ -109,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 @@ -150,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() @@ -164,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, @@ -185,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 @@ -192,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()); @@ -218,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() }; @@ -230,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 { @@ -241,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..0c8638b673d4f 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, FieldsShape, Int, TagEncoding}; +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) @@ -193,8 +199,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { if self.layout.abi.is_uninhabited() { return bx.cx().const_undef(cast_to); } - let (discr_scalar, discr_kind, discr_index) = match self.layout.variants { - layout::Variants::Single { index } => { + let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants { + Variants::Single { index } => { let discr_val = self .layout .ty @@ -202,37 +208,33 @@ 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, .. } => { - (discr, discr_kind, discr_index) + Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => { + (tag, tag_encoding, tag_field) } }; // Read the tag/niche-encoded discriminant from memory. - let encoded_discr = self.project_field(bx, discr_index); - let encoded_discr = bx.load_operand(encoded_discr); + let tag = self.project_field(bx, tag_field); + let tag = bx.load_operand(tag); // Decode the discriminant (specifically if it's niche-encoded). - match *discr_kind { - layout::DiscriminantKind::Tag => { - let signed = match discr_scalar.value { + match *tag_encoding { + TagEncoding::Direct => { + let signed = match tag_scalar.value { // We use `i1` for bytes that are always `0` or `1`, // e.g., `#[repr(i8)] enum E { A, B }`, but we can't // let LLVM interpret the `i1` as signed, because // then `i1 1` (i.e., `E::B`) is effectively `i8 -1`. - layout::Int(_, signed) => !discr_scalar.is_bool() && signed, + Int(_, signed) => !tag_scalar.is_bool() && signed, _ => false, }; - bx.intcast(encoded_discr.immediate(), cast_to, signed) + bx.intcast(tag.immediate(), cast_to, signed) } - layout::DiscriminantKind::Niche { - dataful_variant, - ref niche_variants, - niche_start, - } => { + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { // Rebase from niche values to discriminants, and check // whether the result is in range for the niche variants. - let niche_llty = bx.cx().immediate_backend_type(encoded_discr.layout); - let encoded_discr = encoded_discr.immediate(); + let niche_llty = bx.cx().immediate_backend_type(tag.layout); + let tag = tag.immediate(); // We first compute the "relative discriminant" (wrt `niche_variants`), // that is, if `n = niche_variants.end() - niche_variants.start()`, @@ -246,9 +248,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { let relative_discr = if niche_start == 0 { // Avoid subtracting `0`, which wouldn't work for pointers. // FIXME(eddyb) check the actual primitive type here. - encoded_discr + tag } else { - bx.sub(encoded_discr, bx.cx().const_uint_big(niche_llty, niche_start)) + bx.sub(tag, bx.cx().const_uint_big(niche_llty, niche_start)) }; let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); let is_niche = { @@ -307,15 +309,11 @@ 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, - .. - } => { - let ptr = self.project_field(bx, discr_index); + Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => { + let ptr = self.project_field(bx, tag_field); let to = self.layout.ty.discriminant_for_variant(bx.tcx(), variant_index).unwrap().val; bx.store( @@ -324,10 +322,10 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { ptr.align, ); } - layout::Variants::Multiple { - discr_kind: - layout::DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start }, - discr_index, + Variants::Multiple { + tag_encoding: + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, + tag_field, .. } => { if variant_index != dataful_variant { @@ -341,7 +339,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx.memset(self.llval, fill_byte, size, self.align, MemFlags::empty()); } - let niche = self.project_field(bx, discr_index); + let niche = self.project_field(bx, tag_field); let niche_llty = bx.cx().immediate_backend_type(niche.layout); let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); let niche_value = (niche_value as u128).wrapping_add(niche_start); @@ -431,7 +429,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_consume(bx, mir::PlaceRef { local, projection: proj_base }) .deref(bx.cx()) } - mir::PlaceRef { local, projection: [proj_base @ .., elem] } => { + mir::PlaceRef { local, projection: &[ref proj_base @ .., elem] } => { // FIXME turn this recursion into iteration let cg_base = self.codegen_place(bx, mir::PlaceRef { local, projection: proj_base }); @@ -442,7 +440,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cg_base.project_field(bx, field.index()) } mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Copy(mir::Place::from(*index)); + let index = &mir::Operand::Copy(mir::Place::from(index)); let index = self.codegen_operand(bx, index); let llindex = index.immediate(); cg_base.project_index(bx, llindex) @@ -452,7 +450,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { from_end: false, min_length: _, } => { - let lloffset = bx.cx().const_usize(*offset as u64); + let lloffset = bx.cx().const_usize(offset as u64); cg_base.project_index(bx, lloffset) } mir::ProjectionElem::ConstantIndex { @@ -460,14 +458,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { from_end: true, min_length: _, } => { - let lloffset = bx.cx().const_usize(*offset as u64); + let lloffset = bx.cx().const_usize(offset as u64); let lllen = cg_base.len(bx.cx()); let llindex = bx.sub(lllen, lloffset); cg_base.project_index(bx, llindex) } mir::ProjectionElem::Subslice { from, to, from_end } => { let mut subslice = - cg_base.project_index(bx, bx.cx().const_usize(*from as u64)); + cg_base.project_index(bx, bx.cx().const_usize(from as u64)); let projected_ty = PlaceTy::from_ty(cg_base.layout.ty).projection_ty(tcx, elem).ty; subslice.layout = bx.cx().layout_of(self.monomorphize(&projected_ty)); @@ -476,7 +474,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { assert!(from_end, "slice subslices should be `from_end`"); subslice.llextra = Some(bx.sub( cg_base.llextra.unwrap(), - bx.cx().const_usize((*from as u64) + (*to as u64)), + bx.cx().const_usize((from as u64) + (to as u64)), )); } @@ -489,7 +487,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { subslice } - mir::ProjectionElem::Downcast(_, v) => cg_base.project_downcast(bx, *v), + mir::ProjectionElem::Downcast(_, v) => cg_base.project_downcast(bx, v), } } }; @@ -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 6d0046063989b..57f72b1065d05 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( @@ -106,6 +105,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } + let count = + self.monomorphize(&count).eval_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all()); + bx.write_operand_repeatedly(cg_elem, count, dest) } @@ -275,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); @@ -289,11 +292,16 @@ 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) { - let discr_val = bx.cx().const_uint_big(ll_t_out, discr.val); + let discr_layout = bx.cx().layout_of(discr.ty); + let discr_t = bx.cx().immediate_backend_type(discr_layout); + let discr_val = bx.cx().const_uint_big(discr_t, discr.val); + let discr_val = + bx.intcast(discr_val, ll_t_out, discr.ty.is_signed()); + return ( bx, OperandRef { @@ -303,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 @@ -351,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(_)) => { @@ -375,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, @@ -385,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), @@ -465,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); @@ -514,6 +522,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let operand = OperandRef { val: OperandValue::Immediate(val), layout: box_layout }; (bx, operand) } + mir::Rvalue::ThreadLocalRef(def_id) => { + assert!(bx.cx().tcx().is_static(def_id)); + let static_ = bx.get_static(def_id); + let layout = bx.layout_of(bx.cx().tcx().static_ptr_ty(def_id)); + let operand = OperandRef::from_immediate_or_packed_pair(&mut bx, static_, layout); + (bx, operand) + } mir::Rvalue::Use(ref operand) => { let operand = self.codegen_operand(&mut bx, operand); (bx, operand) @@ -521,7 +536,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) @@ -529,7 +544,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() { @@ -549,7 +564,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()); @@ -737,11 +752,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) | mir::Rvalue::NullaryOp(..) | + mir::Rvalue::ThreadLocalRef(_) | mir::Rvalue::Use(..) => // (*) 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() } @@ -760,7 +776,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..b6b57744f95b6 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, + line_spans: &[Span], + ); } pub trait AsmMethods { diff --git a/src/librustc_codegen_ssa/traits/backend.rs b/src/librustc_codegen_ssa/traits/backend.rs index 5535841156acd..6cbb47efa99f2 100644 --- a/src/librustc_codegen_ssa/traits/backend.rs +++ b/src/librustc_codegen_ssa/traits/backend.rs @@ -2,15 +2,23 @@ use super::write::WriteBackendMethods; use super::CodegenObject; use crate::ModuleCodegen; -use rustc::middle::cstore::EncodedMetadata; -use rustc::session::{config, Session}; -use rustc::ty::layout::{HasTyCtxt, LayoutOf, TyLayout}; -use rustc::ty::Ty; -use rustc::ty::TyCtxt; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_codegen_utils::codegen_backend::CodegenBackend; +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; + +use std::any::Any; use std::sync::Arc; pub trait BackendTypes { @@ -28,15 +36,59 @@ 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>> { } +pub trait CodegenBackend { + fn init(&self, _sess: &Session) {} + fn print(&self, _req: PrintRequest, _sess: &Session) {} + fn target_features(&self, _sess: &Session) -> Vec { + vec![] + } + fn print_passes(&self) {} + fn print_version(&self) {} + + fn metadata_loader(&self) -> Box; + fn provide(&self, _providers: &mut Providers<'_>); + fn provide_extern(&self, _providers: &mut Providers<'_>); + fn codegen_crate<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + metadata: EncodedMetadata, + need_metadata_module: bool, + ) -> Box; + + /// This is called on the returned `Box` from `codegen_backend` + /// + /// # Panics + /// + /// Panics when the passed `Box` was not returned by `codegen_backend`. + fn join_codegen( + &self, + ongoing_codegen: Box, + sess: &Session, + dep_graph: &DepGraph, + ) -> Result, ErrorReported>; + + /// This is called on the returned `Box` from `join_codegen` + /// + /// # Panics + /// + /// Panics when the passed `Box` was not returned by `join_codegen`. + fn link( + &self, + sess: &Session, + codegen_results: Box, + outputs: &OutputFilenames, + ) -> Result<(), ErrorReported>; +} + pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send + Sync { fn new_metadata(&self, sess: TyCtxt<'_>, mod_name: &str) -> Self::Module; fn write_compressed_metadata<'tcx>( @@ -58,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..7ffc9f15bffdc 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; @@ -259,6 +260,14 @@ pub trait BuilderMethods<'a, 'tcx>: /// Called for `StorageDead` fn lifetime_end(&mut self, ptr: Self::Value, size: Size); + fn instrprof_increment( + &mut self, + fn_name: Self::Value, + hash: Self::Value, + num_counters: Self::Value, + index: Self::Value, + ) -> Self::Value; + fn call( &mut self, llfn: Self::Value, 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..f62019498511c 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; @@ -15,6 +15,7 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes { args: &[OperandRef<'tcx, Self::Value>], llresult: Self::Value, span: Span, + caller_instance: ty::Instance<'tcx>, ); fn abort(&mut self); diff --git a/src/librustc_codegen_ssa/traits/misc.rs b/src/librustc_codegen_ssa/traits/misc.rs index d7587163ba001..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::session::Session; -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 d03ff8d4d37d8..6b782731d535c 100644 --- a/src/librustc_codegen_ssa/traits/mod.rs +++ b/src/librustc_codegen_ssa/traits/mod.rs @@ -28,8 +28,8 @@ mod type_; mod write; pub use self::abi::AbiBuilderMethods; -pub use self::asm::{AsmBuilderMethods, AsmMethods}; -pub use self::backend::{Backend, BackendTypes, ExtraBackendMethods}; +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; pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoMethods}; @@ -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..c55bf9858b972 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(), @@ -73,7 +74,7 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { } fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { - ty.is_freeze(self.tcx(), ty::ParamEnv::reveal_all(), DUMMY_SP) + ty.is_freeze(self.tcx().at(DUMMY_SP), ty::ParamEnv::reveal_all()) } fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool { @@ -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_codegen_utils/Cargo.toml b/src/librustc_codegen_utils/Cargo.toml deleted file mode 100644 index b5533a8307c3d..0000000000000 --- a/src/librustc_codegen_utils/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_codegen_utils" -version = "0.0.0" -edition = "2018" - -[lib] -name = "rustc_codegen_utils" -path = "lib.rs" -test = false - -[dependencies] -log = "0.4" -punycode = "0.4.0" -rustc-demangle = "0.1.16" - -rustc_ast = { path = "../librustc_ast" } -rustc_span = { path = "../librustc_span" } -rustc = { path = "../librustc" } -rustc_hir = { path = "../librustc_hir" } -rustc_target = { path = "../librustc_target" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_metadata = { path = "../librustc_metadata" } diff --git a/src/librustc_codegen_utils/codegen_backend.rs b/src/librustc_codegen_utils/codegen_backend.rs deleted file mode 100644 index 96166e04c2e3c..0000000000000 --- a/src/librustc_codegen_utils/codegen_backend.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! The Rust compiler. -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] - -use std::any::Any; - -use rustc::dep_graph::DepGraph; -use rustc::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; -use rustc::session::config::{OutputFilenames, PrintRequest}; -use rustc::session::Session; -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; -use rustc::util::common::ErrorReported; -use rustc_span::symbol::Symbol; - -pub use rustc_data_structures::sync::MetadataRef; - -pub trait CodegenBackend { - fn init(&self, _sess: &Session) {} - fn print(&self, _req: PrintRequest, _sess: &Session) {} - fn target_features(&self, _sess: &Session) -> Vec { - vec![] - } - fn print_passes(&self) {} - fn print_version(&self) {} - - fn metadata_loader(&self) -> Box; - fn provide(&self, _providers: &mut Providers<'_>); - fn provide_extern(&self, _providers: &mut Providers<'_>); - fn codegen_crate<'tcx>( - &self, - tcx: TyCtxt<'tcx>, - metadata: EncodedMetadata, - need_metadata_module: bool, - ) -> Box; - - /// This is called on the returned `Box` from `codegen_backend` - /// - /// # Panics - /// - /// Panics when the passed `Box` was not returned by `codegen_backend`. - fn join_codegen( - &self, - ongoing_codegen: Box, - sess: &Session, - dep_graph: &DepGraph, - ) -> Result, ErrorReported>; - - /// This is called on the returned `Box` from `join_codegen` - /// - /// # Panics - /// - /// Panics when the passed `Box` was not returned by `join_codegen`. - fn link( - &self, - sess: &Session, - codegen_results: Box, - outputs: &OutputFilenames, - ) -> Result<(), ErrorReported>; -} diff --git a/src/librustc_codegen_utils/lib.rs b/src/librustc_codegen_utils/lib.rs deleted file mode 100644 index 38906bbaef810..0000000000000 --- a/src/librustc_codegen_utils/lib.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] -#![feature(never_type)] -#![feature(nll)] -#![feature(in_band_lifetimes)] -#![recursion_limit = "256"] - -#[macro_use] -extern crate rustc; - -use rustc::ty::query::Providers; -use rustc::ty::TyCtxt; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::symbol::sym; - -pub mod codegen_backend; -pub mod link; -pub mod symbol_names; -pub mod symbol_names_test; - -pub fn trigger_delay_span_bug(tcx: TyCtxt<'_>, key: DefId) { - tcx.sess.delay_span_bug( - tcx.def_span(key), - "delayed span bug triggered by #[rustc_error(delay_span_bug_from_inside_query)]", - ); -} - -/// check for the #[rustc_error] annotation, which forces an -/// error in codegen. This is used to write compile-fail tests -/// that actually test that compilation succeeds without -/// reporting an error. -pub fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { - if let Some((def_id, _)) = tcx.entry_fn(LOCAL_CRATE) { - let attrs = &*tcx.get_attrs(def_id); - for attr in attrs { - if attr.check_name(sym::rustc_error) { - match attr.meta_item_list() { - // check if there is a #[rustc_error(delayed)] - Some(list) => { - if list.iter().any(|list_item| { - list_item.ident().map(|i| i.name) - == Some(sym::delay_span_bug_from_inside_query) - }) { - tcx.ensure().trigger_delay_span_bug(def_id); - } - } - // bare #[rustc_error] - None => { - tcx.sess.span_fatal( - tcx.def_span(def_id), - "fatal error triggered by #[rustc_error]", - ); - } - } - } - } - } -} - -pub fn provide(providers: &mut Providers<'_>) { - crate::symbol_names::provide(providers); - *providers = Providers { trigger_delay_span_bug, ..*providers }; -} diff --git a/src/librustc_codegen_utils/link.rs b/src/librustc_codegen_utils/link.rs deleted file mode 100644 index 524fb0a59c2f1..0000000000000 --- a/src/librustc_codegen_utils/link.rs +++ /dev/null @@ -1,189 +0,0 @@ -use rustc::session::config::{self, Input, OutputFilenames, OutputType}; -use rustc::session::Session; -use rustc_ast::{ast, attr}; -use rustc_span::symbol::sym; -use rustc_span::Span; -use std::path::{Path, PathBuf}; - -pub fn out_filename( - sess: &Session, - crate_type: config::CrateType, - outputs: &OutputFilenames, - crate_name: &str, -) -> PathBuf { - let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); - let out_filename = outputs - .outputs - .get(&OutputType::Exe) - .and_then(|s| s.to_owned()) - .or_else(|| outputs.single_output_file.clone()) - .unwrap_or(default_filename); - - check_file_is_writeable(&out_filename, sess); - - out_filename -} - -// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers -// check this already -- however, the Linux linker will happily overwrite a -// read-only file. We should be consistent. -pub fn check_file_is_writeable(file: &Path, sess: &Session) { - if !is_writeable(file) { - sess.fatal(&format!( - "output file {} is not writeable -- check its \ - permissions", - file.display() - )); - } -} - -fn is_writeable(p: &Path) -> bool { - match p.metadata() { - Err(..) => true, - Ok(m) => !m.permissions().readonly(), - } -} - -pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input: &Input) -> String { - let validate = |s: String, span: Option| { - rustc_metadata::validate_crate_name(sess, &s, span); - s - }; - - // Look in attributes 100% of the time to make sure the attribute is marked - // as used. After doing this, however, we still prioritize a crate name from - // the command line over one found in the #[crate_name] attribute. If we - // find both we ensure that they're the same later on as well. - let attr_crate_name = - attr::find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s))); - - if let Some(sess) = sess { - if let Some(ref s) = sess.opts.crate_name { - if let Some((attr, name)) = attr_crate_name { - if name.as_str() != *s { - let msg = format!( - "`--crate-name` and `#[crate_name]` are \ - required to match, but `{}` != `{}`", - s, name - ); - sess.span_err(attr.span, &msg); - } - } - return validate(s.clone(), None); - } - } - - if let Some((attr, s)) = attr_crate_name { - return validate(s.to_string(), Some(attr.span)); - } - if let Input::File(ref path) = *input { - if let Some(s) = path.file_stem().and_then(|s| s.to_str()) { - if s.starts_with('-') { - let msg = format!( - "crate names cannot start with a `-`, but \ - `{}` has a leading hyphen", - s - ); - if let Some(sess) = sess { - sess.err(&msg); - } - } else { - return validate(s.replace("-", "_"), None); - } - } - } - - "rust_out".to_string() -} - -pub fn filename_for_metadata( - sess: &Session, - crate_name: &str, - outputs: &OutputFilenames, -) -> PathBuf { - let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); - - let out_filename = outputs - .single_output_file - .clone() - .unwrap_or_else(|| outputs.out_directory.join(&format!("lib{}.rmeta", libname))); - - check_file_is_writeable(&out_filename, sess); - - out_filename -} - -pub fn filename_for_input( - sess: &Session, - crate_type: config::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 => { - 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 => { - 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 => { - 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..]) } - } - } -} - -/// Returns default crate type for target -/// -/// Default crate type is used when crate type isn't provided neither -/// through cmd line arguments nor through crate attributes -/// -/// It is CrateType::Executable for all platforms but iOS as there is no -/// 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 { - if !sess.target.target.options.executables { - config::CrateType::Staticlib - } else { - config::CrateType::Executable - } -} - -/// Checks if target supports crate_type as output -pub fn invalid_output_for_target(sess: &Session, crate_type: config::CrateType) -> bool { - match crate_type { - config::CrateType::Cdylib | config::CrateType::Dylib | config::CrateType::ProcMacro => { - if !sess.target.target.options.dynamic_linking { - return true; - } - if sess.crt_static() && !sess.target.target.options.crt_static_allows_dylibs { - return true; - } - } - _ => {} - } - if sess.target.target.options.only_cdylib { - match crate_type { - config::CrateType::ProcMacro | config::CrateType::Dylib => return true, - _ => {} - } - } - if !sess.target.target.options.executables { - if crate_type == config::CrateType::Executable { - return true; - } - } - - false -} diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs deleted file mode 100644 index cfde09fad62cc..0000000000000 --- a/src/librustc_codegen_utils/symbol_names.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! The Rust Linkage Model and Symbol Names -//! ======================================= -//! -//! The semantic model of Rust linkage is, broadly, that "there's no global -//! namespace" between crates. Our aim is to preserve the illusion of this -//! model despite the fact that it's not *quite* possible to implement on -//! modern linkers. We initially didn't use system linkers at all, but have -//! been convinced of their utility. -//! -//! There are a few issues to handle: -//! -//! - Linkers operate on a flat namespace, so we have to flatten names. -//! We do this using the C++ namespace-mangling technique. Foo::bar -//! symbols and such. -//! -//! - Symbols for distinct items with the same *name* need to get different -//! linkage-names. Examples of this are monomorphizations of functions or -//! items within anonymous scopes that end up having the same path. -//! -//! - Symbols in different crates but with same names "within" the crate need -//! to get different linkage-names. -//! -//! - Symbol names should be deterministic: Two consecutive runs of the -//! compiler over the same code base should produce the same symbol names for -//! the same items. -//! -//! - Symbol names should not depend on any global properties of the code base, -//! so that small modifications to the code base do not result in all symbols -//! changing. In previous versions of the compiler, symbol names incorporated -//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be -//! infeasible when used in conjunction with incremental compilation because -//! small code changes would invalidate all symbols generated previously. -//! -//! - Even symbols from different versions of the same crate should be able to -//! live next to each other without conflict. -//! -//! In order to fulfill the above requirements the following scheme is used by -//! the compiler: -//! -//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit -//! hash value into every exported symbol name. Anything that makes a difference -//! to the symbol being named, but does not show up in the regular path needs to -//! be fed into this hash: -//! -//! - Different monomorphizations of the same item have the same path but differ -//! in their concrete type parameters, so these parameters are part of the -//! data being digested for the symbol hash. -//! -//! - Rust allows items to be defined in anonymous scopes, such as in -//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have -//! the path `foo::bar`, since the anonymous scopes do not contribute to the -//! path of an item. The compiler already handles this case via so-called -//! disambiguating `DefPaths` which use indices to distinguish items with the -//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` -//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation -//! information into the symbol name too, these indices are fed into the -//! symbol hash, so that the above two symbols would end up with different -//! hash values. -//! -//! The two measures described above suffice to avoid intra-crate conflicts. In -//! order to also avoid inter-crate conflicts two more measures are taken: -//! -//! - The name of the crate containing the symbol is prepended to the symbol -//! name, i.e., symbols are "crate qualified". For example, a function `foo` in -//! module `bar` in crate `baz` would get a symbol name like -//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids -//! simple conflicts between functions from different crates. -//! -//! - In order to be able to also use symbols from two versions of the same -//! crate (which naturally also have the same name), a stronger measure is -//! required: The compiler accepts an arbitrary "disambiguator" value via the -//! `-C metadata` command-line argument. This disambiguator is then fed into -//! the symbol hash of every exported item. Consequently, the symbols in two -//! identical crates but with different disambiguators are not in conflict -//! with each other. This facility is mainly intended to be used by build -//! tools like Cargo. -//! -//! A note on symbol name stability -//! ------------------------------- -//! Previous versions of the compiler resorted to feeding NodeIds into the -//! symbol hash in order to disambiguate between items with the same path. The -//! current version of the name generation algorithm takes great care not to do -//! that, since NodeIds are notoriously unstable: A small change to the -//! code base will offset all NodeIds after the change and thus, much as using -//! the SVH in the hash, invalidate an unbounded number of symbol names. This -//! makes re-using previously compiled code for incremental compilation -//! virtually impossible. Thus, symbol hash generation exclusively relies on -//! DefPaths which are much more robust in the face of changes to the code base. - -use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::mir::mono::{InstantiationMode, MonoItem}; -use rustc::session::config::SymbolManglingVersion; -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_span::symbol::Symbol; - -use log::debug; - -mod legacy; -mod v0; - -/// This function computes the symbol name for the given `instance` and the -/// given instantiating crate. That is, if you know that instance X is -/// instantiated in crate Y, this is the symbol name this instance would have. -pub fn symbol_name_for_instance_in_crate( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - instantiating_crate: CrateNum, -) -> String { - compute_symbol_name(tcx, instance, || instantiating_crate) -} - -pub fn provide(providers: &mut Providers<'_>) { - *providers = Providers { symbol_name: symbol_name_provider, ..*providers }; -} - -// The `symbol_name` query provides the symbol name for calling a given -// instance from the local crate. In particular, it will also look up the -// correct symbol name of instances from upstream crates. -fn symbol_name_provider(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::SymbolName { - let symbol_name = compute_symbol_name(tcx, instance, || { - // This closure determines the instantiating crate for instances that - // need an instantiating-crate-suffix for their symbol name, in order - // to differentiate between local copies. - if is_generic(instance.substs) { - // For generics we might find re-usable upstream instances. If there - // is one, we rely on the symbol being instantiated locally. - instance.upstream_monomorphization(tcx).unwrap_or(LOCAL_CRATE) - } else { - // For non-generic things that need to avoid naming conflicts, we - // always instantiate a copy in the local crate. - LOCAL_CRATE - } - }); - - ty::SymbolName { name: Symbol::intern(&symbol_name) } -} - -/// Computes the symbol name for the given instance. This function will call -/// `compute_instantiating_crate` if it needs to factor the instantiating crate -/// into the symbol name. -fn compute_symbol_name( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - compute_instantiating_crate: impl FnOnce() -> CrateNum, -) -> String { - let def_id = instance.def_id(); - let substs = instance.substs; - - debug!("symbol_name(def_id={:?}, substs={:?})", def_id, substs); - - 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) { - 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) { - 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) { - Node::ForeignItem(_) => true, - _ => false, - } - } else { - tcx.is_foreign_item(def_id) - }; - - let attrs = tcx.codegen_fn_attrs(def_id); - - // Foreign items by default use no mangling for their symbol name. There's a - // few exceptions to this rule though: - // - // * This can be overridden with the `#[link_name]` attribute - // - // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the - // same-named symbol when imported from different wasm modules will get - // hooked up incorrectly. As a result foreign symbols, on the wasm target, - // with a wasm import module, get mangled. Additionally our codegen will - // deduplicate symbols based purely on the symbol name, but for wasm this - // isn't quite right because the same-named symbol on wasm can come from - // different modules. For these reasons if `#[link(wasm_import_module)]` - // is present we mangle everything on wasm because the demangled form will - // show up in the `wasm-import-name` custom attribute in LLVM IR. - // - // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 - if is_foreign { - if tcx.sess.target.target.arch != "wasm32" - || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id) - { - if let Some(name) = attrs.link_name { - return name.to_string(); - } - return tcx.item_name(def_id).to_string(); - } - } - - if let Some(name) = attrs.export_name { - // Use provided name - return name.to_string(); - } - - if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { - // Don't mangle - return tcx.item_name(def_id).to_string(); - } - - let avoid_cross_crate_conflicts = - // If this is an instance of a generic function, we also hash in - // the ID of the instantiating crate. This avoids symbol conflicts - // in case the same instances is emitted in two crates of the same - // project. - is_generic(substs) || - - // If we're dealing with an instance of a function that's inlined from - // another crate but we're marking it as globally shared to our - // compliation (aka we're not making an internal copy in each of our - // codegen units) then this symbol may become an exported (but hidden - // visibility) symbol. This means that multiple crates may do the same - // and we want to be sure to avoid any symbol conflicts here. - match MonoItem::Fn(instance).instantiation_mode(tcx) { - InstantiationMode::GloballyShared { may_conflict: true } => true, - _ => false, - }; - - let instantiating_crate = - if avoid_cross_crate_conflicts { Some(compute_instantiating_crate()) } else { None }; - - // Pick the crate responsible for the symbol mangling version, which has to: - // 1. be stable for each instance, whether it's being defined or imported - // 2. obey each crate's own `-Z symbol-mangling-version`, as much as possible - // We solve these as follows: - // 1. because symbol names depend on both `def_id` and `instantiating_crate`, - // both their `CrateNum`s are stable for any given instance, so we can pick - // either and have a stable choice of symbol mangling version - // 2. we favor `instantiating_crate` where possible (i.e. when `Some`) - let mangling_version_crate = instantiating_crate.unwrap_or(def_id.krate); - let mangling_version = if mangling_version_crate == LOCAL_CRATE { - tcx.sess.opts.debugging_opts.symbol_mangling_version - } else { - tcx.symbol_mangling_version(mangling_version_crate) - }; - - match mangling_version { - SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate), - SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate), - } -} - -fn is_generic(substs: SubstsRef<'_>) -> bool { - substs.non_erasable_generics().next().is_some() -} diff --git a/src/librustc_codegen_utils/symbol_names_test.rs b/src/librustc_codegen_utils/symbol_names_test.rs deleted file mode 100644 index 8f2f2628e7b7f..0000000000000 --- a/src/librustc_codegen_utils/symbol_names_test.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! Walks the crate looking for items/impl-items/trait-items that have -//! either a `rustc_symbol_name` or `rustc_def_path` attribute and -//! generates an error giving, respectively, the symbol name or -//! def-path. This is used for unit testing the code that generates -//! paths etc in all kinds of annoying scenarios. - -use rustc::ty::{Instance, TyCtxt}; -use rustc_hir as hir; -use rustc_span::symbol::{sym, Symbol}; - -const SYMBOL_NAME: Symbol = sym::rustc_symbol_name; -const DEF_PATH: Symbol = sym::rustc_def_path; - -pub fn report_symbol_names(tcx: TyCtxt<'_>) { - // if the `rustc_attrs` feature is not enabled, then the - // attributes we are interested in cannot be present anyway, so - // skip the walk. - if !tcx.features().rustc_attrs { - return; - } - - tcx.dep_graph.with_ignore(|| { - let mut visitor = SymbolNamesTest { tcx }; - tcx.hir().krate().visit_all_item_likes(&mut visitor); - }) -} - -struct SymbolNamesTest<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl SymbolNamesTest<'tcx> { - fn process_attrs(&mut self, hir_id: hir::HirId) { - let tcx = self.tcx; - let def_id = tcx.hir().local_def_id(hir_id); - for attr in tcx.get_attrs(def_id).iter() { - if attr.check_name(SYMBOL_NAME) { - // for now, can only use on monomorphic names - let instance = Instance::mono(tcx, 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()) { - tcx.sess.span_err(attr.span, &format!("demangling({})", demangling)); - tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); - } - } else if attr.check_name(DEF_PATH) { - let path = tcx.def_path_str(def_id); - tcx.sess.span_err(attr.span, &format!("def-path({})", path)); - } - - // (*) The formatting of `tag({})` is chosen so that tests can elect - // to test the entirety of the string, if they choose, or else just - // some subset. - } - } -} - -impl hir::itemlikevisit::ItemLikeVisitor<'tcx> for SymbolNamesTest<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - self.process_attrs(item.hir_id); - } - - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - self.process_attrs(trait_item.hir_id); - } - - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - self.process_attrs(impl_item.hir_id); - } -} diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index fb4f818c4b249..1c2fb90b2d8b4 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -10,26 +10,29 @@ 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" } lazy_static = "1" -rustc_serialize = { path = "../libserialize", package = "serialize" } -graphviz = { path = "../libgraphviz" } +once_cell = { version = "1", features = ["parking_lot"] } +rustc_serialize = { path = "../librustc_serialize" } +rustc_graphviz = { path = "../librustc_graphviz" } 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" } -rustc-hash = "1.0.1" +rustc-hash = "1.1.0" 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/base_n/tests.rs b/src/librustc_data_structures/base_n/tests.rs index a86f991cd0e0d..b68ef1eb7f4c4 100644 --- a/src/librustc_data_structures/base_n/tests.rs +++ b/src/librustc_data_structures/base_n/tests.rs @@ -12,8 +12,8 @@ fn test_encode() { test(35, base); test(36, base); test(37, base); - test(u64::max_value() as u128, base); - test(u128::max_value(), base); + test(u64::MAX as u128, base); + test(u128::MAX, base); for i in 0..1_000 { test(i * 983, base); diff --git a/src/librustc_data_structures/box_region.rs b/src/librustc_data_structures/box_region.rs index dbc54291f4087..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,40 +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 { - #[cfg(bootstrap)] - 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() { - GeneratorState::Yielded(YieldType::Initial(y)) => y, - _ => panic!(), - }; - - (init, result) - } - - #[cfg(not(bootstrap))] - 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!(), }; @@ -55,45 +49,18 @@ impl PinnedGenerator { (init, result) } - #[cfg(bootstrap)] 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() { - panic!() - } - } - - #[cfg(not(bootstrap))] - 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!() } } - #[cfg(bootstrap)] 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(); - if let GeneratorState::Complete(r) = result { r } else { panic!() } - } - - #[cfg(not(bootstrap))] - 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!() } } } @@ -128,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); @@ -137,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,)*)) = @@ -179,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()) @@ -191,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/flock.rs b/src/librustc_data_structures/flock.rs index 2a0139fa90d5a..9383be474fd5a 100644 --- a/src/librustc_data_structures/flock.rs +++ b/src/librustc_data_structures/flock.rs @@ -7,18 +7,22 @@ #![allow(non_camel_case_types)] #![allow(nonstandard_style)] +use std::fs::{File, OpenOptions}; use std::io; use std::path::Path; cfg_if! { - if #[cfg(unix)] { - use std::ffi::{CString, OsStr}; - use std::mem; + // We use `flock` rather than `fcntl` on Linux, because WSL1 does not support + // `fcntl`-style advisory locks properly (rust-lang/rust#72157). + // + // For other Unix targets we still use `fcntl` because it's more portable than + // `flock`. + if #[cfg(target_os = "linux")] { use std::os::unix::prelude::*; #[derive(Debug)] pub struct Lock { - fd: libc::c_int, + _file: File, } impl Lock { @@ -27,22 +31,55 @@ cfg_if! { create: bool, exclusive: bool) -> io::Result { - let os: &OsStr = p.as_ref(); - let buf = CString::new(os.as_bytes()).unwrap(); - let open_flags = if create { - libc::O_RDWR | libc::O_CREAT + let file = OpenOptions::new() + .read(true) + .write(true) + .create(create) + .mode(libc::S_IRWXU as u32) + .open(p)?; + + let mut operation = if exclusive { + libc::LOCK_EX } else { - libc::O_RDWR - }; - - let fd = unsafe { - libc::open(buf.as_ptr(), open_flags, - libc::S_IRWXU as libc::c_int) + libc::LOCK_SH }; + if !wait { + operation |= libc::LOCK_NB + } - if fd < 0 { - return Err(io::Error::last_os_error()); + let ret = unsafe { libc::flock(file.as_raw_fd(), operation) }; + if ret == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(Lock { _file: file }) } + } + } + + // Note that we don't need a Drop impl to execute `flock(fd, LOCK_UN)`. Lock acquired by + // `flock` is associated with the file descriptor and closing the file release it + // automatically. + } else if #[cfg(unix)] { + use std::mem; + use std::os::unix::prelude::*; + + #[derive(Debug)] + pub struct Lock { + file: File, + } + + impl Lock { + pub fn new(p: &Path, + wait: bool, + create: bool, + exclusive: bool) + -> io::Result { + let file = OpenOptions::new() + .read(true) + .write(true) + .create(create) + .mode(libc::S_IRWXU as u32) + .open(p)?; let lock_type = if exclusive { libc::F_WRLCK @@ -58,14 +95,12 @@ cfg_if! { let cmd = if wait { libc::F_SETLKW } else { libc::F_SETLK }; let ret = unsafe { - libc::fcntl(fd, cmd, &flock) + libc::fcntl(file.as_raw_fd(), cmd, &flock) }; if ret == -1 { - let err = io::Error::last_os_error(); - unsafe { libc::close(fd); } - Err(err) + Err(io::Error::last_os_error()) } else { - Ok(Lock { fd }) + Ok(Lock { file }) } } } @@ -79,15 +114,13 @@ cfg_if! { flock.l_len = 0; unsafe { - libc::fcntl(self.fd, libc::F_SETLK, &flock); - libc::close(self.fd); + libc::fcntl(self.file.as_raw_fd(), libc::F_SETLK, &flock); } } } } else if #[cfg(windows)] { use std::mem; use std::os::windows::prelude::*; - use std::fs::{File, OpenOptions}; use winapi::um::minwinbase::{OVERLAPPED, LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK}; use winapi::um::fileapi::LockFileEx; diff --git a/src/librustc_data_structures/graph/dominators/mod.rs b/src/librustc_data_structures/graph/dominators/mod.rs index 5283bd78a3029..a7f9340dead88 100644 --- a/src/librustc_data_structures/graph/dominators/mod.rs +++ b/src/librustc_data_structures/graph/dominators/mod.rs @@ -125,9 +125,9 @@ impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> { } else { self.node = Some(dom); } - return Some(node); + Some(node) } else { - return None; + None } } } 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..0b2e7cda1b4cc 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)] @@ -22,17 +22,14 @@ #![feature(test)] #![feature(associated_type_bounds)] #![feature(thread_id_value)] +#![feature(extend_one)] #![allow(rustc::default_hash_types)] #[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 +66,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 +81,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 +114,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/graphviz.rs b/src/librustc_data_structures/obligation_forest/graphviz.rs index e1784f44fd112..3a268e4b4f432 100644 --- a/src/librustc_data_structures/obligation_forest/graphviz.rs +++ b/src/librustc_data_structures/obligation_forest/graphviz.rs @@ -1,5 +1,5 @@ use crate::obligation_forest::{ForestObligation, ObligationForest}; -use graphviz as dot; +use rustc_graphviz as dot; use std::env::var_os; use std::fs::File; use std::io::BufWriter; 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 a70314c35c07c..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; @@ -345,7 +349,7 @@ impl SelfProfilerRef { ) { drop(self.exec(event_filter, |profiler| { let event_id = StringId::new_virtual(query_invocation_id.0); - let thread_id = std::thread::current().id().as_u64() as u32; + let thread_id = std::thread::current().id().as_u64().get() as u32; profiler.profiler.record_instant_event( event_kind(profiler), @@ -522,7 +526,7 @@ impl<'a> TimingGuard<'a> { event_kind: StringId, event_id: EventId, ) -> TimingGuard<'a> { - let thread_id = std::thread::current().id().as_u64() as u32; + let thread_id = std::thread::current().id().as_u64().get() as u32; let raw_profiler = &profiler.profiler; let timing_guard = raw_profiler.start_recording_interval_event(event_kind, event_id, thread_id); @@ -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/sharded.rs b/src/librustc_data_structures/sharded.rs index d08d46a7414d0..485719c517564 100644 --- a/src/librustc_data_structures/sharded.rs +++ b/src/librustc_data_structures/sharded.rs @@ -30,7 +30,7 @@ pub struct Sharded { impl Default for Sharded { #[inline] fn default() -> Self { - Self::new(|| T::default()) + Self::new(T::default) } } diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index b71163a8f9433..b4cc85293f7c1 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -1,77 +1,76 @@ 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 { + #[inline] + 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 +78,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/sync.rs b/src/librustc_data_structures/sync.rs index 9051b1751b119..53d831749ceb7 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -20,7 +20,6 @@ use crate::owning_ref::{Erased, OwningRef}; use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; -use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; pub use std::sync::atomic::Ordering; @@ -230,6 +229,8 @@ cfg_if! { pub use std::cell::RefMut as LockGuard; pub use std::cell::RefMut as MappedLockGuard; + pub use once_cell::unsync::OnceCell; + use std::cell::RefCell as InnerRwLock; use std::cell::RefCell as InnerLock; @@ -313,6 +314,8 @@ cfg_if! { pub use parking_lot::MutexGuard as LockGuard; pub use parking_lot::MappedMutexGuard as MappedLockGuard; + pub use once_cell::sync::OnceCell; + pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32, AtomicU64}; pub use crossbeam_utils::atomic::AtomicCell; @@ -355,7 +358,6 @@ cfg_if! { use parking_lot::Mutex as InnerLock; use parking_lot::RwLock as InnerRwLock; - use std; use std::thread; pub use rayon::{join, scope}; @@ -432,134 +434,6 @@ impl HashMapExt for HashMap } } -/// A type whose inner value can be written once and then will stay read-only -// This contains a PhantomData since this type conceptually owns a T outside the Mutex once -// initialized. This ensures that Once is Sync only if T is. If we did not have PhantomData -// we could send a &Once> to multiple threads and call `get` on it to get access -// to &Cell on those threads. -pub struct Once(Lock>, PhantomData); - -impl Once { - /// Creates an Once value which is uninitialized - #[inline(always)] - pub fn new() -> Self { - Once(Lock::new(None), PhantomData) - } - - /// Consumes the value and returns Some(T) if it was initialized - #[inline(always)] - pub fn into_inner(self) -> Option { - self.0.into_inner() - } - - /// Tries to initialize the inner value to `value`. - /// Returns `None` if the inner value was uninitialized and `value` was consumed setting it - /// otherwise if the inner value was already set it returns `value` back to the caller - #[inline] - pub fn try_set(&self, value: T) -> Option { - let mut lock = self.0.lock(); - if lock.is_some() { - return Some(value); - } - *lock = Some(value); - None - } - - /// Tries to initialize the inner value to `value`. - /// Returns `None` if the inner value was uninitialized and `value` was consumed setting it - /// otherwise if the inner value was already set it asserts that `value` is equal to the inner - /// value and then returns `value` back to the caller - #[inline] - pub fn try_set_same(&self, value: T) -> Option - where - T: Eq, - { - let mut lock = self.0.lock(); - if let Some(ref inner) = *lock { - assert!(*inner == value); - return Some(value); - } - *lock = Some(value); - None - } - - /// Tries to initialize the inner value to `value` and panics if it was already initialized - #[inline] - pub fn set(&self, value: T) { - assert!(self.try_set(value).is_none()); - } - - /// Initializes the inner value if it wasn't already done by calling the provided closure. It - /// ensures that no-one else can access the value in the mean time by holding a lock for the - /// duration of the closure. - /// A reference to the inner value is returned. - #[inline] - pub fn init_locking T>(&self, f: F) -> &T { - { - let mut lock = self.0.lock(); - if lock.is_none() { - *lock = Some(f()); - } - } - - self.borrow() - } - - /// Tries to initialize the inner value by calling the closure without ensuring that no-one - /// else can access it. This mean when this is called from multiple threads, multiple - /// closures may concurrently be computing a value which the inner value should take. - /// Only one of these closures are used to actually initialize the value. - /// If some other closure already set the value, - /// we return the value our closure computed wrapped in a `Option`. - /// If our closure set the value, `None` is returned. - /// If the value is already initialized, the closure is not called and `None` is returned. - #[inline] - pub fn init_nonlocking T>(&self, f: F) -> Option { - if self.0.lock().is_some() { None } else { self.try_set(f()) } - } - - /// Tries to initialize the inner value by calling the closure without ensuring that no-one - /// else can access it. This mean when this is called from multiple threads, multiple - /// closures may concurrently be computing a value which the inner value should take. - /// Only one of these closures are used to actually initialize the value. - /// If some other closure already set the value, we assert that it our closure computed - /// a value equal to the value already set and then - /// we return the value our closure computed wrapped in a `Option`. - /// If our closure set the value, `None` is returned. - /// If the value is already initialized, the closure is not called and `None` is returned. - #[inline] - pub fn init_nonlocking_same T>(&self, f: F) -> Option - where - T: Eq, - { - if self.0.lock().is_some() { None } else { self.try_set_same(f()) } - } - - /// Tries to get a reference to the inner value, returns `None` if it is not yet initialized - #[inline(always)] - pub fn try_get(&self) -> Option<&T> { - let lock = &*self.0.lock(); - if let Some(ref inner) = *lock { - // This is safe since we won't mutate the inner value - unsafe { Some(&*(inner as *const T)) } - } else { - None - } - } - - /// Gets reference to the inner value, panics if it is not yet initialized - #[inline(always)] - pub fn get(&self) -> &T { - self.try_get().expect("value was not set") - } - - /// Gets reference to the inner value, panics if it is not yet initialized - #[inline(always)] - pub fn borrow(&self) -> &T { - self.get() - } -} - #[derive(Debug)] pub struct Lock(InnerLock); diff --git a/src/librustc_data_structures/thin_vec.rs b/src/librustc_data_structures/thin_vec.rs index 2befc0aa50487..43002178eb971 100644 --- a/src/librustc_data_structures/thin_vec.rs +++ b/src/librustc_data_structures/thin_vec.rs @@ -53,6 +53,20 @@ impl Extend for ThinVec { ThinVec(None) => *self = iter.into_iter().collect::>().into(), } } + + fn extend_one(&mut self, item: T) { + match *self { + ThinVec(Some(ref mut vec)) => vec.push(item), + ThinVec(None) => *self = vec![item].into(), + } + } + + fn extend_reserve(&mut self, additional: usize) { + match *self { + ThinVec(Some(ref mut vec)) => vec.reserve(additional), + ThinVec(None) => *self = Vec::with_capacity(additional).into(), + } + } } impl, CTX> HashStable for ThinVec { 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 26f1741153c8c..75d6592076655 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,16 +22,17 @@ 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" } rustc_plugin_impl = { path = "../librustc_plugin_impl" } rustc_save_analysis = { path = "../librustc_save_analysis" } rustc_codegen_ssa = { path = "../librustc_codegen_ssa" } -rustc_codegen_utils = { path = "../librustc_codegen_utils" } +rustc_session = { path = "../librustc_session" } rustc_error_codes = { path = "../librustc_error_codes" } rustc_interface = { path = "../librustc_interface" } -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 8acb2260cbe89..b45ab0f80ffac 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,31 +15,31 @@ extern crate lazy_static; pub extern crate rustc_plugin_impl as plugin; -use rustc::lint::{Lint, LintId}; -use rustc::middle::cstore::MetadataLoader; -use rustc::session::config::nightly_options; -use rustc::session::config::{ErrorOutputType, Input, OutputType, PrintRequest}; -use rustc::session::{config, DiagnosticOutput, Session}; -use rustc::session::{early_error, early_warn}; -use rustc::ty::TyCtxt; -use rustc::util::common::ErrorReported; -use rustc_codegen_ssa::CodegenResults; -use rustc_codegen_utils::codegen_backend::CodegenBackend; +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}; +use rustc_span::source_map::{FileLoader, FileName}; +use rustc_span::symbol::sym; use std::borrow::Cow; use std::cmp::max; @@ -58,11 +55,6 @@ use std::process::{self, Command, Stdio}; use std::str; use std::time::Instant; -use rustc_ast::ast; -use rustc_span::source_map::FileLoader; -use rustc_span::symbol::sym; -use rustc_span::FileName; - mod args; pub mod pretty; @@ -142,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], @@ -316,6 +307,7 @@ pub fn run_compiler( compiler.output_file().as_ref().map(|p| &**p), ); } + trace!("finished pretty-printing"); return early_exit(); } @@ -355,12 +347,15 @@ pub fn run_compiler( queries.global_ctxt()?; + // Drop AST after creating GlobalCtxt to free memory + let _timer = sess.prof.generic_activity("drop_ast"); + mem::drop(queries.expansion()?.take()); + if sess.opts.debugging_opts.no_analysis || sess.opts.debugging_opts.ast_json { return early_exit(); } if sess.opts.debugging_opts.save_analysis { - let expanded_crate = &queries.expansion()?.peek().0; let crate_name = queries.crate_name()?.peek().clone(); queries.global_ctxt()?.peek_mut().enter(|tcx| { let result = tcx.analysis(LOCAL_CRATE); @@ -368,7 +363,6 @@ pub fn run_compiler( sess.time("save_analysis", || { save::process_crate( tcx, - &expanded_crate, &crate_name, &compiler.input(), None, @@ -380,13 +374,7 @@ pub fn run_compiler( }); result - // AST will be dropped *after* the `after_analysis` callback - // (needed by the RLS) })?; - } else { - // Drop AST after creating GlobalCtxt to free memory - let _timer = sess.prof.generic_activity("drop_ast"); - mem::drop(queries.expansion()?.take()); } queries.global_ctxt()?.peek_mut().enter(|tcx| tcx.analysis(LOCAL_CRATE))?; @@ -395,10 +383,6 @@ pub fn run_compiler( return early_exit(); } - if sess.opts.debugging_opts.save_analysis { - mem::drop(queries.expansion()?.take()); - } - queries.ongoing_codegen()?; if sess.opts.debugging_opts.print_type_sizes { @@ -595,7 +579,7 @@ impl RustcDefaultCalls { if let Input::File(file) = compiler.input() { // FIXME: #![crate_type] and #![crate_name] support not implemented yet let attrs = vec![]; - sess.crate_types.set(collect_crate_types(sess, &attrs)); + sess.init_crate_types(collect_crate_types(sess, &attrs)); let outputs = compiler.build_output_filenames(&sess, &attrs); let rlink_data = fs::read_to_string(file).unwrap_or_else(|err| { sess.fatal(&format!("failed to read rlink file: {}", err)); @@ -627,15 +611,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"); } } @@ -652,7 +636,7 @@ impl RustcDefaultCalls { odir: &Option, ofile: &Option, ) -> Compilation { - use rustc::session::config::PrintRequest::*; + use rustc_session::config::PrintRequest::*; // PrintRequest::NativeStaticLibs is special - printed during linking // (empty iterator returns true) if sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs) { @@ -693,16 +677,15 @@ impl RustcDefaultCalls { let t_outputs = rustc_interface::util::build_output_filenames( input, odir, ofile, attrs, sess, ); - let id = rustc_codegen_utils::link::find_crate_name(Some(sess), attrs, input); + let id = rustc_session::output::find_crate_name(Some(sess), attrs, input); if *req == PrintRequest::CrateName { println!("{}", id); continue; } let crate_types = collect_crate_types(sess, attrs); for &style in &crate_types { - let fname = rustc_codegen_utils::link::filename_for_input( - sess, style, &id, &t_outputs, - ); + let fname = + rustc_session::output::filename_for_input(sess, style, &id, &t_outputs); println!("{}", fname.file_name().unwrap().to_string_lossy()); } } @@ -752,7 +735,7 @@ impl RustcDefaultCalls { PrintRequest::NativeStaticLibs => {} } } - return Compilation::Stop; + Compilation::Stop } } @@ -965,32 +948,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 ); } } @@ -1163,6 +1131,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(); @@ -1253,12 +1231,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)| { @@ -1271,13 +1249,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 2361046c0366c..0a21eb8de059c 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::session::config::{Input, PpMode, PpSourceMode}; -use rustc::session::Session; -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(); } } } @@ -400,7 +396,7 @@ pub fn print_after_parsing( annotation.pp_ann(), false, parse.edition, - parse.injected_crate_name.try_get().is_some(), + parse.injected_crate_name.get().is_some(), ) }) } else { @@ -442,7 +438,7 @@ pub fn print_after_hir_lowering<'tcx>( annotation.pp_ann(), true, parse.edition, - parse.injected_crate_name.try_get().is_some(), + parse.injected_crate_name.get().is_some(), ) }) } diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index 59a030bc4c683..6a5e23adafa53 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"), @@ -118,7 +117,10 @@ E0220: include_str!("./error_codes/E0220.md"), E0221: include_str!("./error_codes/E0221.md"), 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"), @@ -280,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"), @@ -349,6 +352,7 @@ E0623: include_str!("./error_codes/E0623.md"), E0624: include_str!("./error_codes/E0624.md"), E0626: include_str!("./error_codes/E0626.md"), E0627: include_str!("./error_codes/E0627.md"), +E0628: include_str!("./error_codes/E0628.md"), E0631: include_str!("./error_codes/E0631.md"), E0633: include_str!("./error_codes/E0633.md"), E0634: include_str!("./error_codes/E0634.md"), @@ -364,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"), @@ -383,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"), @@ -400,6 +409,7 @@ E0718: include_str!("./error_codes/E0718.md"), E0719: include_str!("./error_codes/E0719.md"), E0720: include_str!("./error_codes/E0720.md"), E0723: include_str!("./error_codes/E0723.md"), +E0724: include_str!("./error_codes/E0724.md"), E0725: include_str!("./error_codes/E0725.md"), E0727: include_str!("./error_codes/E0727.md"), E0728: include_str!("./error_codes/E0728.md"), @@ -412,7 +422,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"), @@ -423,6 +432,20 @@ 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"), +E0758: include_str!("./error_codes/E0758.md"), +E0759: include_str!("./error_codes/E0759.md"), +E0760: include_str!("./error_codes/E0760.md"), +E0761: include_str!("./error_codes/E0761.md"), +E0762: include_str!("./error_codes/E0762.md"), +E0763: include_str!("./error_codes/E0763.md"), +E0764: include_str!("./error_codes/E0764.md"), +E0765: include_str!("./error_codes/E0765.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -457,6 +480,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, @@ -468,10 +492,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 - E0224, // at least one non-builtin train is required for an object type - 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 @@ -536,7 +557,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 @@ -560,7 +581,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' @@ -583,7 +603,6 @@ E0748: include_str!("./error_codes/E0748.md"), // E0612, // merged into E0609 // E0613, // Removed (merged with E0609) E0625, // thread-local statics cannot be accessed at compile-time - E0628, // generators cannot have explicit parameters E0629, // missing 'feature' (rustc_const_unstable) // rustc_const_unstable attribute must be paired with stable/unstable // attribute @@ -592,23 +611,20 @@ 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/E0055.md b/src/librustc_error_codes/error_codes/E0055.md index d5b863081a616..223ba40002986 100644 --- a/src/librustc_error_codes/error_codes/E0055.md +++ b/src/librustc_error_codes/error_codes/E0055.md @@ -6,7 +6,7 @@ recursion limit (which can be set via the `recursion_limit` attribute). For a somewhat artificial example: ```compile_fail,E0055 -#![recursion_limit="5"] +#![recursion_limit="4"] struct Foo; 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/E0081.md b/src/librustc_error_codes/error_codes/E0081.md index fd5eca68e21fd..b834a734cefc4 100644 --- a/src/librustc_error_codes/error_codes/E0081.md +++ b/src/librustc_error_codes/error_codes/E0081.md @@ -1,4 +1,4 @@ -A discrimant value is present more than once. +A discriminant value is present more than once. Erroneous code example: 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/E0207.md b/src/librustc_error_codes/error_codes/E0207.md index 21e7e461c753b..cb4f5d5157d9b 100644 --- a/src/librustc_error_codes/error_codes/E0207.md +++ b/src/librustc_error_codes/error_codes/E0207.md @@ -1,4 +1,4 @@ -A type or lifetime parameter that is specified for `impl` is not constrained. +A type parameter that is specified for `impl` is not constrained. Erroneous code example: @@ -14,7 +14,7 @@ impl Foo { } ``` -Any type parameter or lifetime parameter of an `impl` must meet at least one of +Any type parameter parameter of an `impl` must meet at least one of the following criteria: - it appears in the _implementing type_ of the impl, e.g. `impl Foo` diff --git a/src/librustc_error_codes/error_codes/E0224.md b/src/librustc_error_codes/error_codes/E0224.md new file mode 100644 index 0000000000000..fd89c1d52560f --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0224.md @@ -0,0 +1,15 @@ +A trait object was declaired with no traits. + +Erroneous code example: + +```compile_fail,E0224 +type Foo = dyn 'static +; +``` + +Rust does not currently support this. + +To solve ensure the the trait object has at least one trait: + +``` +type Foo = dyn 'static + Copy; +``` 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 a907ca272970e..e2c40f03019c1 100644 --- a/src/librustc_error_codes/error_codes/E0308.md +++ b/src/librustc_error_codes/error_codes/E0308.md @@ -1,10 +1,6 @@ -This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = "I am not a number!"; @@ -15,3 +11,8 @@ let x: i32 = "I am not a number!"; // | // type `i32` assigned to variable `x` ``` + +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/E0437.md b/src/librustc_error_codes/error_codes/E0437.md index 834cf33dbc7f0..0f924ba692064 100644 --- a/src/librustc_error_codes/error_codes/E0437.md +++ b/src/librustc_error_codes/error_codes/E0437.md @@ -1,7 +1,5 @@ -Trait implementations can only implement associated types that are members of -the trait in question. This error indicates that you attempted to implement -an associated type whose name does not match the name of any associated type -in the trait. +An associated type whose name does not match any of the associated types +in the trait was used when implementing the trait. Erroneous code example: @@ -13,6 +11,9 @@ impl Foo for i32 { } ``` +Trait implementations can only implement associated types that are members of +the trait in question. + The solution to this problem is to remove the extraneous associated type: ``` diff --git a/src/librustc_error_codes/error_codes/E0438.md b/src/librustc_error_codes/error_codes/E0438.md index cb141a5d24aed..13723bc30090e 100644 --- a/src/librustc_error_codes/error_codes/E0438.md +++ b/src/librustc_error_codes/error_codes/E0438.md @@ -1,7 +1,5 @@ -Trait implementations can only implement associated constants that are -members of the trait in question. This error indicates that you -attempted to implement an associated constant whose name does not -match the name of any associated constant in the trait. +An associated constant whose name does not match any of the associated constants +in the trait was used when implementing the trait. Erroneous code example: @@ -13,6 +11,9 @@ impl Foo for i32 { } ``` +Trait implementations can only implement associated constants that are +members of the trait in question. + The solution to this problem is to remove the extraneous associated constant: ``` diff --git a/src/librustc_error_codes/error_codes/E0439.md b/src/librustc_error_codes/error_codes/E0439.md index e6da2117ac5f2..3e663df866caa 100644 --- a/src/librustc_error_codes/error_codes/E0439.md +++ b/src/librustc_error_codes/error_codes/E0439.md @@ -1,5 +1,6 @@ -The length of the platform-intrinsic function `simd_shuffle` -wasn't specified. Erroneous code example: +The length of the platform-intrinsic function `simd_shuffle` wasn't specified. + +Erroneous code example: ```compile_fail,E0439 #![feature(platform_intrinsics)] diff --git a/src/librustc_error_codes/error_codes/E0446.md b/src/librustc_error_codes/error_codes/E0446.md index 77a1834ece42f..6ec47c4962c06 100644 --- a/src/librustc_error_codes/error_codes/E0446.md +++ b/src/librustc_error_codes/error_codes/E0446.md @@ -4,10 +4,10 @@ Erroneous code example: ```compile_fail,E0446 #![deny(private_in_public)] +struct Bar(u32); -mod Foo { - struct Bar(u32); - +mod foo { + use crate::Bar; pub fn bar() -> Bar { // error: private type in public interface Bar(0) } @@ -16,15 +16,31 @@ mod Foo { fn main() {} ``` -To solve this error, please ensure that the type is also public. The type -can be made inaccessible if necessary by placing it into a private inner -module, but it still has to be marked with `pub`. +There are two ways to solve this error. The first is to make the public type +signature only public to a module that also has access to the private type. +This is done by using pub(crate) or pub(in crate::my_mod::etc) Example: ``` -mod Foo { - pub struct Bar(u32); // we set the Bar type public +struct Bar(u32); + +mod foo { + use crate::Bar; + pub(crate) fn bar() -> Bar { // only public to crate root + Bar(0) + } +} +fn main() {} +``` + +The other way to solve this error is to make the private type public. +Example: + +``` +pub struct Bar(u32); // we set the Bar type public +mod foo { + use crate::Bar; pub fn bar() -> Bar { // ok! Bar(0) } diff --git a/src/librustc_error_codes/error_codes/E0449.md b/src/librustc_error_codes/error_codes/E0449.md index a3eb51961d32e..9afc67689bf85 100644 --- a/src/librustc_error_codes/error_codes/E0449.md +++ b/src/librustc_error_codes/error_codes/E0449.md @@ -1,5 +1,6 @@ -A visibility qualifier was used when it was unnecessary. Erroneous code -examples: +A visibility qualifier was used when it was unnecessary. + +Erroneous code examples: ```compile_fail,E0449 struct Bar; diff --git a/src/librustc_error_codes/error_codes/E0452.md b/src/librustc_error_codes/error_codes/E0452.md index be3d573e10d2d..429813a7cdd4e 100644 --- a/src/librustc_error_codes/error_codes/E0452.md +++ b/src/librustc_error_codes/error_codes/E0452.md @@ -1,4 +1,6 @@ -An invalid lint attribute has been given. Erroneous code example: +An invalid lint attribute has been given. + +Erroneous code example: ```compile_fail,E0452 #![allow(foo = "")] // error: malformed lint attribute 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/E0493.md b/src/librustc_error_codes/error_codes/E0493.md index 90a0cbce623d0..0dcc3b62b4b2f 100644 --- a/src/librustc_error_codes/error_codes/E0493.md +++ b/src/librustc_error_codes/error_codes/E0493.md @@ -1,5 +1,4 @@ -A type with a `Drop` implementation was destructured when trying to initialize -a static item. +A value with a custom `Drop` implementation may be dropped during const-eval. Erroneous code example: @@ -16,13 +15,14 @@ struct Foo { field1: DropType, } -static FOO: Foo = Foo { ..Foo { field1: DropType::A } }; // error! +static FOO: Foo = Foo { field1: (DropType::A, DropType::A).1 }; // error! ``` The problem here is that if the given type or one of its fields implements the -`Drop` trait, this `Drop` implementation cannot be called during the static -type initialization which might cause a memory leak. To prevent this issue, -you need to instantiate all the static type's fields by hand. +`Drop` trait, this `Drop` implementation cannot be called within a const +context since it may run arbitrary, non-const-checked code. To prevent this +issue, ensure all values with custom a custom `Drop` implementation escape the +initializer. ``` enum DropType { 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/E0583.md b/src/librustc_error_codes/error_codes/E0583.md index 2dcbbf8855669..701900bb0cd4b 100644 --- a/src/librustc_error_codes/error_codes/E0583.md +++ b/src/librustc_error_codes/error_codes/E0583.md @@ -2,7 +2,7 @@ A file wasn't found for an out-of-line module. Erroneous code example: -```ignore (compile_fail not working here; see Issue #43707) +```compile_fail,E0583 mod file_that_doesnt_exist; // error: file not found for module fn main() {} 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..11005b8336fca 100644 --- a/src/librustc_error_codes/error_codes/E0590.md +++ b/src/librustc_error_codes/error_codes/E0590.md @@ -1,13 +1,17 @@ -`break` or `continue` must include a label when used in the condition of a -`while` loop. +`break` or `continue` keywords were used in a condition of a `while` loop +without a label. -Example of erroneous code: +Erroneous code code: -```compile_fail +```compile_fail,E0590 while break {} ``` +`break` or `continue` must include a label when used in the condition of a +`while` loop. + To fix this, add a label specifying which loop is being broken out of: + ``` 'foo: while break 'foo {} ``` diff --git a/src/librustc_error_codes/error_codes/E0593.md b/src/librustc_error_codes/error_codes/E0593.md index b32923a9e5ff9..1902d73f4d00c 100644 --- a/src/librustc_error_codes/error_codes/E0593.md +++ b/src/librustc_error_codes/error_codes/E0593.md @@ -11,3 +11,14 @@ fn main() { foo(|y| { }); } ``` + +You have to provide the same number of arguments as expected by the `Fn`-based +type. So to fix the previous example, we need to remove the `y` argument: + +``` +fn foo(x: F) { } + +fn main() { + foo(|| { }); // ok! +} +``` diff --git a/src/librustc_error_codes/error_codes/E0599.md b/src/librustc_error_codes/error_codes/E0599.md index c44e60571e1e3..5b1590b29998f 100644 --- a/src/librustc_error_codes/error_codes/E0599.md +++ b/src/librustc_error_codes/error_codes/E0599.md @@ -9,3 +9,18 @@ let x = Mouth; x.chocolate(); // error: no method named `chocolate` found for type `Mouth` // in the current scope ``` + +In this case, you need to implement the `chocolate` method to fix the error: + +``` +struct Mouth; + +impl Mouth { + fn chocolate(&self) { // We implement the `chocolate` method here. + println!("Hmmm! I love chocolate!"); + } +} + +let x = Mouth; +x.chocolate(); // ok! +``` diff --git a/src/librustc_error_codes/error_codes/E0600.md b/src/librustc_error_codes/error_codes/E0600.md index 172e2a346c5a7..356006c72f3d1 100644 --- a/src/librustc_error_codes/error_codes/E0600.md +++ b/src/librustc_error_codes/error_codes/E0600.md @@ -1,6 +1,6 @@ An unary operator was used on a type which doesn't implement it. -Example of erroneous code: +Erroneous code example: ```compile_fail,E0600 enum Question { diff --git a/src/librustc_error_codes/error_codes/E0601.md b/src/librustc_error_codes/error_codes/E0601.md index 8180c5db46fba..7194b7971d38f 100644 --- a/src/librustc_error_codes/error_codes/E0601.md +++ b/src/librustc_error_codes/error_codes/E0601.md @@ -1,5 +1,6 @@ -No `main` function was found in a binary crate. To fix this error, add a -`main` function. For example: +No `main` function was found in a binary crate. + +To fix this error, add a `main` function: ``` fn main() { diff --git a/src/librustc_error_codes/error_codes/E0602.md b/src/librustc_error_codes/error_codes/E0602.md index ca7138a60cc4e..dcaf251a96b5b 100644 --- a/src/librustc_error_codes/error_codes/E0602.md +++ b/src/librustc_error_codes/error_codes/E0602.md @@ -1,9 +1,9 @@ An unknown lint was used on the command line. -Erroneous example: +Erroneous code example: ```sh -rustc -D bogus omse_file.rs +rustc -D bogus rust_file.rs ``` Maybe you just misspelled the lint name or the lint doesn't exist anymore. diff --git a/src/librustc_error_codes/error_codes/E0608.md b/src/librustc_error_codes/error_codes/E0608.md index 598f1e655e964..d0ebc3a26f082 100644 --- a/src/librustc_error_codes/error_codes/E0608.md +++ b/src/librustc_error_codes/error_codes/E0608.md @@ -1,4 +1,4 @@ -An attempt to index into a type which doesn't implement the `std::ops::Index` +An attempt to use index on a type which doesn't implement the `std::ops::Index` trait was performed. Erroneous code example: diff --git a/src/librustc_error_codes/error_codes/E0617.md b/src/librustc_error_codes/error_codes/E0617.md index f4357ff755e29..61b56766c26e2 100644 --- a/src/librustc_error_codes/error_codes/E0617.md +++ b/src/librustc_error_codes/error_codes/E0617.md @@ -17,3 +17,14 @@ Certain Rust types must be cast before passing them to a variadic function, because of arcane ABI rules dictated by the C standard. To fix the error, cast the value to the type specified by the error message (which you may need to import from `std::os::raw`). + +In this case, `c_double` has the same size as `f64` so we can use it directly: + +```no_run +# extern { +# fn printf(c: *const i8, ...); +# } +unsafe { + printf(::std::ptr::null(), 0f64); // ok! +} +``` diff --git a/src/librustc_error_codes/error_codes/E0619.md b/src/librustc_error_codes/error_codes/E0619.md index 8727692c0a5b8..f516de43095bd 100644 --- a/src/librustc_error_codes/error_codes/E0619.md +++ b/src/librustc_error_codes/error_codes/E0619.md @@ -1,4 +1,5 @@ #### Note: this error code is no longer emitted by the compiler. + The type-checker needed to know the type of an expression, but that type had not yet been inferred. diff --git a/src/librustc_error_codes/error_codes/E0622.md b/src/librustc_error_codes/error_codes/E0622.md index 1de81dabb291f..990a25494129e 100644 --- a/src/librustc_error_codes/error_codes/E0622.md +++ b/src/librustc_error_codes/error_codes/E0622.md @@ -5,8 +5,7 @@ Erroneous code example: ```compile_fail,E0622 #![feature(intrinsics)] extern "rust-intrinsic" { - pub static breakpoint : unsafe extern "rust-intrinsic" fn(); - // error: intrinsic must be a function + pub static breakpoint : fn(); // error: intrinsic must be a function } fn main() { unsafe { breakpoint(); } } @@ -14,4 +13,13 @@ fn main() { unsafe { breakpoint(); } } An intrinsic is a function available for use in a given programming language whose implementation is handled specially by the compiler. In order to fix this -error, just declare a function. +error, just declare a function. Example: + +```no_run +#![feature(intrinsics)] +extern "rust-intrinsic" { + pub fn breakpoint(); // ok! +} + +fn main() { unsafe { breakpoint(); } } +``` diff --git a/src/librustc_error_codes/error_codes/E0628.md b/src/librustc_error_codes/error_codes/E0628.md new file mode 100644 index 0000000000000..40040c9a56aac --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0628.md @@ -0,0 +1,30 @@ +More than one parameter was used for a generator. + +Erroneous code example: + +```compile_fail,E0628 +#![feature(generators, generator_trait)] + +fn main() { + let generator = |a: i32, b: i32| { + // error: too many parameters for a generator + // Allowed only 0 or 1 parameter + yield a; + }; +} +``` + +At present, it is not permitted to pass more than one explicit +parameter for a generator.This can be fixed by using +at most 1 parameter for the generator. For example, we might resolve +the previous example by passing only one parameter. + +``` +#![feature(generators, generator_trait)] + +fn main() { + let generator = |a: i32| { + yield a; + }; +} +``` diff --git a/src/librustc_error_codes/error_codes/E0637.md b/src/librustc_error_codes/error_codes/E0637.md index e114d3d0f94ae..d9068950bdfee 100644 --- a/src/librustc_error_codes/error_codes/E0637.md +++ b/src/librustc_error_codes/error_codes/E0637.md @@ -1,6 +1,7 @@ An underscore `_` character has been used as the identifier for a lifetime. -Erroneous example: +Erroneous code example: + ```compile_fail,E0106,E0637 fn longest<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { //^^ `'_` is a reserved lifetime name @@ -11,6 +12,7 @@ fn longest<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { } } ``` + `'_`, cannot be used as a lifetime identifier because it is a reserved for the anonymous lifetime. To fix this, use a lowercase letter such as 'a, or a series of lowercase letters such as `'foo`. For more information, see [the @@ -18,6 +20,7 @@ book][bk-no]. For more information on using the anonymous lifetime in rust nightly, see [the nightly book][bk-al]. Corrected example: + ``` fn longest<'a>(str1: &'a str, str2: &'a str) -> &'a str { if str1.len() > str2.len() { 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/E0641.md b/src/librustc_error_codes/error_codes/E0641.md index e2110042c7e8d..5848e9b5c05ca 100644 --- a/src/librustc_error_codes/error_codes/E0641.md +++ b/src/librustc_error_codes/error_codes/E0641.md @@ -1,19 +1,19 @@ Attempted to cast to/from a pointer with an unknown kind. -Erroneous code examples: +Erroneous code example: ```compile_fail,E0641 let b = 0 as *const _; // error ``` -Must give information for type of pointer that is being cast from/to if the -type cannot be inferred. +Type information must be provided if a pointer type being cast from/into another +type which cannot be inferred: ``` // Creating a pointer from reference: type can be inferred -let a = &(String::from("Hello world!")) as *const _; // Ok +let a = &(String::from("Hello world!")) as *const _; // ok! -let b = 0 as *const i32; // Ok +let b = 0 as *const i32; // ok! -let c: *const i32 = 0 as *const _; // Ok +let c: *const i32 = 0 as *const _; // ok! ``` diff --git a/src/librustc_error_codes/error_codes/E0642.md b/src/librustc_error_codes/error_codes/E0642.md index 592ada6ecadc7..c790aa154bde9 100644 --- a/src/librustc_error_codes/error_codes/E0642.md +++ b/src/librustc_error_codes/error_codes/E0642.md @@ -1,6 +1,6 @@ Trait methods currently cannot take patterns as arguments. -Example of erroneous code: +Erroneous code example: ```compile_fail,E0642 trait Foo { diff --git a/src/librustc_error_codes/error_codes/E0644.md b/src/librustc_error_codes/error_codes/E0644.md index 7a653bd2264fd..8c68da3b2f310 100644 --- a/src/librustc_error_codes/error_codes/E0644.md +++ b/src/librustc_error_codes/error_codes/E0644.md @@ -1,18 +1,18 @@ A closure or generator was constructed that references its own type. -Erroneous example: +Erroneous code 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/E0646.md b/src/librustc_error_codes/error_codes/E0646.md index e01dbae8b9060..1e9ec7d4380ad 100644 --- a/src/librustc_error_codes/error_codes/E0646.md +++ b/src/librustc_error_codes/error_codes/E0646.md @@ -1,4 +1,5 @@ It is not possible to define `main` with a where clause. + Erroneous code example: ```compile_fail,E0646 diff --git a/src/librustc_error_codes/error_codes/E0647.md b/src/librustc_error_codes/error_codes/E0647.md index 131db38c00d2e..8ca6e777f301d 100644 --- a/src/librustc_error_codes/error_codes/E0647.md +++ b/src/librustc_error_codes/error_codes/E0647.md @@ -1,4 +1,5 @@ -It is not possible to define `start` with a where clause. +The `start` function was defined with a where clause. + Erroneous code example: ```compile_fail,E0647 diff --git a/src/librustc_error_codes/error_codes/E0648.md b/src/librustc_error_codes/error_codes/E0648.md index 319bae103f3b3..d99dc19503ddb 100644 --- a/src/librustc_error_codes/error_codes/E0648.md +++ b/src/librustc_error_codes/error_codes/E0648.md @@ -1,6 +1,15 @@ -`export_name` attributes may not contain null characters (`\0`). +An `export_name` attribute contains null characters (`\0`). + +Erroneous code example: ```compile_fail,E0648 #[export_name="\0foo"] // error: `export_name` may not contain null characters pub fn bar() {} ``` + +To fix this error, remove the null characters: + +``` +#[export_name="foo"] // ok! +pub fn bar() {} +``` 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/E0666.md b/src/librustc_error_codes/error_codes/E0666.md index 22133683dc5ac..1a0dc5a522962 100644 --- a/src/librustc_error_codes/error_codes/E0666.md +++ b/src/librustc_error_codes/error_codes/E0666.md @@ -1,21 +1,25 @@ -`impl Trait` types cannot appear nested in the -generic arguments of other `impl Trait` types. +`impl Trait` types cannot appear nested in the generic arguments of other +`impl Trait` types. -Example of erroneous code: +Erroneous code example: ```compile_fail,E0666 trait MyGenericTrait {} trait MyInnerTrait {} -fn foo(bar: impl MyGenericTrait) {} +fn foo( + bar: impl MyGenericTrait, // error! +) {} ``` -Type parameters for `impl Trait` types must be -explicitly defined as named generic parameters: +Type parameters for `impl Trait` types must be explicitly defined as named +generic parameters: ``` trait MyGenericTrait {} trait MyInnerTrait {} -fn foo(bar: impl MyGenericTrait) {} +fn foo( + bar: impl MyGenericTrait, // ok! +) {} ``` diff --git a/src/librustc_error_codes/error_codes/E0668.md b/src/librustc_error_codes/error_codes/E0668.md index f5d26244fb961..b6fedfe53fce8 100644 --- a/src/librustc_error_codes/error_codes/E0668.md +++ b/src/librustc_error_codes/error_codes/E0668.md @@ -1,19 +1,22 @@ Malformed inline assembly rejected by LLVM. -LLVM checks the validity of the constraints and the assembly string passed to -it. This error implies that LLVM seems something wrong with the inline -assembly call. +Erroneous code example: -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); } } ``` + +LLVM checks the validity of the constraints and the assembly string passed to +it. This error implies that LLVM seems something wrong with the inline +assembly call. + +In particular, it can happen if you forgot the closing bracket of a register +constraint (see issue #51430), like in the previous code example. 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/E0670.md b/src/librustc_error_codes/error_codes/E0670.md index 2026370bec3b8..74c1af06cf4ec 100644 --- a/src/librustc_error_codes/error_codes/E0670.md +++ b/src/librustc_error_codes/error_codes/E0670.md @@ -1,6 +1,6 @@ Rust 2015 does not permit the use of `async fn`. -Example of erroneous code: +Erroneous code example: ```compile_fail,E0670 async fn foo() {} diff --git a/src/librustc_error_codes/error_codes/E0689.md b/src/librustc_error_codes/error_codes/E0689.md index 26c2c15ccfaac..a680a20421127 100644 --- a/src/librustc_error_codes/error_codes/E0689.md +++ b/src/librustc_error_codes/error_codes/E0689.md @@ -1,13 +1,16 @@ -This error indicates that the numeric value for the method being passed exists -but the type of the numeric value or binding could not be identified. +A method was called on an ambiguous numeric type. -The error happens on numeric literals: +Erroneous code example: ```compile_fail,E0689 -2.0.neg(); +2.0.neg(); // error! ``` -and on numeric bindings without an identified concrete type: +This error indicates that the numeric value for the method being passed exists +but the type of the numeric value or binding could not be identified. + +The error happens on numeric literals and on numeric bindings without an +identified concrete type: ```compile_fail,E0689 let x = 2.0; @@ -19,8 +22,8 @@ Because of this, you must give the numeric literal or binding a type: ``` use std::ops::Neg; -let _ = 2.0_f32.neg(); +let _ = 2.0_f32.neg(); // ok! let x: f32 = 2.0; -let _ = x.neg(); -let _ = (2.0 as f32).neg(); +let _ = x.neg(); // ok! +let _ = (2.0 as f32).neg(); // ok! ``` 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/E0695.md b/src/librustc_error_codes/error_codes/E0695.md index 208f7f4c7b165..5013e83ca0362 100644 --- a/src/librustc_error_codes/error_codes/E0695.md +++ b/src/librustc_error_codes/error_codes/E0695.md @@ -1,6 +1,6 @@ A `break` statement without a label appeared inside a labeled block. -Example of erroneous code: +Erroneous code example: ```compile_fail,E0695 # #![feature(label_break_value)] 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/E0699.md b/src/librustc_error_codes/error_codes/E0699.md index f90fd3b562469..454d2507e5e2e 100644 --- a/src/librustc_error_codes/error_codes/E0699.md +++ b/src/librustc_error_codes/error_codes/E0699.md @@ -1,14 +1,16 @@ A method was called on a raw pointer whose inner type wasn't completely known. -For example, you may have done something like: +Erroneous code example: -```compile_fail +```compile_fail,edition2018,E0699 # #![deny(warnings)] +# fn main() { let foo = &1; let bar = foo as *const _; if bar.is_null() { // ... } +# } ``` Here, the type of `bar` isn't known; it could be a pointer to anything. Instead, 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/E0724.md b/src/librustc_error_codes/error_codes/E0724.md new file mode 100644 index 0000000000000..7a7ba15485434 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0724.md @@ -0,0 +1,24 @@ +`#[ffi_returns_twice]` was used on non-foreign function. + +Erroneous code example: + +```compile_fail,E0724 +#![feature(ffi_returns_twice)] +#![crate_type = "lib"] + +#[ffi_returns_twice] // error! +pub fn foo() {} +``` + +`#[ffi_returns_twice]` can only be used on foreign function declarations. +For example, we might correct the previous example by declaring +the function inside of an `extern` block. + +``` +#![feature(ffi_returns_twice)] + +extern { + #[ffi_returns_twice] // ok! + pub fn foo(); +} +``` 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 4c9588ef7b63b..0000000000000 --- a/src/librustc_error_codes/error_codes/E0738.md +++ /dev/null @@ -1,48 +0,0 @@ -`#[track_caller]` cannot be used in traits yet. This is due to limitations in -the compiler which are likely to be temporary. See [RFC 2091] for details on -this and other restrictions. - -Erroneous example with a trait method implementation: - -```compile_fail,E0738 -#![feature(track_caller)] - -trait Foo { - fn bar(&self); -} - -impl Foo for u64 { - #[track_caller] - fn bar(&self) {} -} -``` - -Erroneous example with a blanket trait method implementation: - -```compile_fail,E0738 -#![feature(track_caller)] - -trait Foo { - #[track_caller] - fn bar(&self) {} - fn baz(&self); -} -``` - -Erroneous example with a trait method declaration: - -```compile_fail,E0738 -#![feature(track_caller)] - -trait Foo { - fn bar(&self) {} - - #[track_caller] - fn baz(&self); -} -``` - -Note that while the compiler may be able to support the attribute in traits in -the future, [RFC 2091] prohibits their implementation without a follow-up RFC. - -[RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md 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_error_codes/error_codes/E0758.md b/src/librustc_error_codes/error_codes/E0758.md new file mode 100644 index 0000000000000..ddca4b3d75f77 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0758.md @@ -0,0 +1,20 @@ +A multi-line (doc-)comment is unterminated. + +Erroneous code example: + +```compile_fail,E0758 +/* I am not terminated! +``` + +The same goes for doc comments: + +```compile_fail,E0758 +/*! I am not terminated! +``` + +You need to end your multi-line comment with `*/` in order to fix this error: + +``` +/* I am terminated! */ +/*! I am also terminated! */ +``` diff --git a/src/librustc_error_codes/error_codes/E0759.md b/src/librustc_error_codes/error_codes/E0759.md new file mode 100644 index 0000000000000..a74759bdf634b --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0759.md @@ -0,0 +1,67 @@ +A `'static` requirement in a return type involving a trait is not fulfilled. + +Erroneous code examples: + +```compile_fail,E0759 +use std::fmt::Debug; + +fn foo(x: &i32) -> impl Debug { + x +} +``` + +```compile_fail,E0759 +# use std::fmt::Debug; +fn bar(x: &i32) -> Box { + Box::new(x) +} +``` + +These examples have the same semantics as the following: + +```compile_fail,E0759 +# use std::fmt::Debug; +fn foo(x: &i32) -> impl Debug + 'static { + x +} +``` + +```compile_fail,E0759 +# use std::fmt::Debug; +fn bar(x: &i32) -> Box { + Box::new(x) +} +``` + +Both [`dyn Trait`] and [`impl Trait`] in return types have a an implicit +`'static` requirement, meaning that the value implementing them that is being +returned has to be either a `'static` borrow or an owned value. + +In order to change the requirement from `'static` to be a lifetime derived from +its arguments, you can add an explicit bound, either to an anonymous lifetime +`'_` or some appropriate named lifetime. + +``` +# use std::fmt::Debug; +fn foo(x: &i32) -> impl Debug + '_ { + x +} +fn bar(x: &i32) -> Box { + Box::new(x) +} +``` + +These are equivalent to the following explicit lifetime annotations: + +``` +# use std::fmt::Debug; +fn foo<'a>(x: &'a i32) -> impl Debug + 'a { + x +} +fn bar<'a>(x: &'a i32) -> Box { + Box::new(x) +} +``` + +[`dyn Trait`]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types +[`impl Trait`]: https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits diff --git a/src/librustc_error_codes/error_codes/E0760.md b/src/librustc_error_codes/error_codes/E0760.md new file mode 100644 index 0000000000000..e1dcfefebcd76 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0760.md @@ -0,0 +1,32 @@ +`async fn`/`impl trait` return type cannot contain a projection +or `Self` that references lifetimes from a parent scope. + +Erroneous code example: + +```compile_fail,E0760,edition2018 +struct S<'a>(&'a i32); + +impl<'a> S<'a> { + async fn new(i: &'a i32) -> Self { + S(&22) + } +} +``` + +To fix this error we need to spell out `Self` to `S<'a>`: + +```edition2018 +struct S<'a>(&'a i32); + +impl<'a> S<'a> { + async fn new(i: &'a i32) -> S<'a> { + S(&22) + } +} +``` + +This will be allowed at some point in the future, +but the implementation is not yet complete. +See the [issue-61949] for this limitation. + +[issue-61949]: https://github.com/rust-lang/rust/issues/61949 diff --git a/src/librustc_error_codes/error_codes/E0761.md b/src/librustc_error_codes/error_codes/E0761.md new file mode 100644 index 0000000000000..c01574e413cfa --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0761.md @@ -0,0 +1,25 @@ +Multiple candidate files were found for an out-of-line module. + +Erroneous code example: + +```rust +// file: ambiguous_module/mod.rs + +fn foo() {} +``` + +```rust +// file: ambiguous_module.rs + +fn foo() {} +``` + +```ignore (multiple source files required for compile_fail) +mod ambiguous_module; // error: file for module `ambiguous_module` + // found at both ambiguous_module.rs and + // ambiguous_module.rs/mod.rs + +fn main() {} +``` + +Please remove this ambiguity by deleting/renaming one of the candidate files. diff --git a/src/librustc_error_codes/error_codes/E0762.md b/src/librustc_error_codes/error_codes/E0762.md new file mode 100644 index 0000000000000..b01ded4a86616 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0762.md @@ -0,0 +1,13 @@ +A character literal wasn't ended with a quote. + +Erroneous code example: + +```compile_fail,E0762 +static C: char = '●; // error! +``` + +To fix this error, add the missing quote: + +``` +static C: char = '●'; // ok! +``` diff --git a/src/librustc_error_codes/error_codes/E0763.md b/src/librustc_error_codes/error_codes/E0763.md new file mode 100644 index 0000000000000..095b779f3e78a --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0763.md @@ -0,0 +1,13 @@ +A byte constant wasn't correctly ended. + +Erroneous code example: + +```compile_fail,E0763 +let c = b'a; // error! +``` + +To fix this error, add the missing quote: + +``` +let c = b'a'; // ok! +``` diff --git a/src/librustc_error_codes/error_codes/E0764.md b/src/librustc_error_codes/error_codes/E0764.md new file mode 100644 index 0000000000000..e9061f988ac59 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0764.md @@ -0,0 +1,39 @@ +Mutable references (`&mut`) can only be used in constant functions, not statics +or constants. This limitation exists to prevent the creation of constants that +have a mutable reference in their final value. If you had a constant of `&mut +i32` type, you could modify the value through that reference, making the +constant essentially mutable. While there could be a more fine-grained scheme +in the future that allows mutable references if they are not "leaked" to the +final value, a more conservative approach was chosen for now. `const fn` do not +have this problem, as the borrow checker will prevent the `const fn` from +returning new mutable references. + +Erroneous code example: + +```compile_fail,E0764 +#![feature(const_fn)] +#![feature(const_mut_refs)] + +fn main() { + const OH_NO: &'static mut usize = &mut 1; // error! +} +``` + +Remember: you cannot use a function call inside a constant or static. However, +you can totally use it in constant functions: + +``` +#![feature(const_fn)] +#![feature(const_mut_refs)] + +const fn foo(x: usize) -> usize { + let mut y = 1; + let z = &mut y; + *z += x; + y +} + +fn main() { + const FOO: usize = foo(10); // ok! +} +``` diff --git a/src/librustc_error_codes/error_codes/E0765.md b/src/librustc_error_codes/error_codes/E0765.md new file mode 100644 index 0000000000000..456e3f3e9e408 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0765.md @@ -0,0 +1,13 @@ +A double quote string (`"`) was not terminated. + +Erroneous code example: + +```compile_fail,E0765 +let s = "; // error! +``` + +To fix this error, add the missing double quote at the end of the string: + +``` +let s = ""; // ok! +``` diff --git a/src/librustc_errors/Cargo.toml b/src/librustc_errors/Cargo.toml index b8340b1a1df6a..7f72161aff826 100644 --- a/src/librustc_errors/Cargo.toml +++ b/src/librustc_errors/Cargo.toml @@ -11,13 +11,13 @@ doctest = false [dependencies] log = "0.4" -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } rustc_span = { path = "../librustc_span" } rustc_data_structures = { path = "../librustc_data_structures" } unicode-width = "0.1.4" atty = "0.2" termcolor = "1.0" -annotate-snippets = "0.6.1" +annotate-snippets = "0.8.0" termize = "0.1.1" [target.'cfg(windows)'.dependencies] diff --git a/src/librustc_errors/annotate_snippet_emitter_writer.rs b/src/librustc_errors/annotate_snippet_emitter_writer.rs index d83175694f407..265ba59cccb2a 100644 --- a/src/librustc_errors/annotate_snippet_emitter_writer.rs +++ b/src/librustc_errors/annotate_snippet_emitter_writer.rs @@ -8,12 +8,11 @@ use crate::emitter::FileWithAnnotatedLines; use crate::snippet::Line; use crate::{CodeSuggestion, Diagnostic, DiagnosticId, Emitter, Level, SubDiagnostic}; -use annotate_snippets::display_list::DisplayList; -use annotate_snippets::formatter::DisplayListFormatter; +use annotate_snippets::display_list::{DisplayList, FormatOptions}; use annotate_snippets::snippet::*; use rustc_data_structures::sync::Lrc; use rustc_span::source_map::SourceMap; -use rustc_span::{Loc, MultiSpan, SourceFile}; +use rustc_span::{MultiSpan, SourceFile}; /// Generates diagnostics using annotate-snippet pub struct AnnotateSnippetEmitterWriter { @@ -59,112 +58,20 @@ impl Emitter for AnnotateSnippetEmitterWriter { } } -/// Collects all the data needed to generate the data structures needed for the -/// `annotate-snippets` library. -struct DiagnosticConverter<'a> { - source_map: Option>, - level: Level, - message: String, - code: Option, - msp: MultiSpan, - #[allow(dead_code)] - children: &'a [SubDiagnostic], - #[allow(dead_code)] - suggestions: &'a [CodeSuggestion], +/// Provides the source string for the given `line` of `file` +fn source_string(file: Lrc, line: &Line) -> String { + file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default() } -impl<'a> DiagnosticConverter<'a> { - /// Turns rustc Diagnostic information into a `annotate_snippets::snippet::Snippet`. - fn to_annotation_snippet(&self) -> Option { - if let Some(source_map) = &self.source_map { - // Make sure our primary file comes first - let primary_lo = if let Some(ref primary_span) = self.msp.primary_span().as_ref() { - source_map.lookup_char_pos(primary_span.lo()) - } else { - // FIXME(#59346): Not sure when this is the case and what - // should be done if it happens - return None; - }; - let annotated_files = - FileWithAnnotatedLines::collect_annotations(&self.msp, &self.source_map); - let slices = self.slices_for_files(annotated_files, primary_lo); - - Some(Snippet { - title: Some(Annotation { - label: Some(self.message.to_string()), - id: self.code.clone().map(|c| match c { - DiagnosticId::Error(val) | DiagnosticId::Lint(val) => val, - }), - annotation_type: Self::annotation_type_for_level(self.level), - }), - footer: vec![], - slices, - }) - } else { - // FIXME(#59346): Is it ok to return None if there's no source_map? - None - } - } - - fn slices_for_files( - &self, - annotated_files: Vec, - primary_lo: Loc, - ) -> Vec { - // FIXME(#64205): Provide a test case where `annotated_files` is > 1 - annotated_files - .iter() - .flat_map(|annotated_file| { - annotated_file - .lines - .iter() - .map(|line| { - let line_source = Self::source_string(annotated_file.file.clone(), &line); - Slice { - source: line_source, - line_start: line.line_index, - origin: Some(primary_lo.file.name.to_string()), - // FIXME(#59346): Not really sure when `fold` should be true or false - fold: false, - annotations: line - .annotations - .iter() - .map(|a| self.annotation_to_source_annotation(a.clone())) - .collect(), - } - }) - .collect::>() - }) - .collect::>() - } - - /// Turns a `crate::snippet::Annotation` into a `SourceAnnotation` - fn annotation_to_source_annotation( - &self, - annotation: crate::snippet::Annotation, - ) -> SourceAnnotation { - SourceAnnotation { - range: (annotation.start_col, annotation.end_col), - label: annotation.label.unwrap_or("".to_string()), - annotation_type: Self::annotation_type_for_level(self.level), - } - } - - /// Provides the source string for the given `line` of `file` - fn source_string(file: Lrc, line: &Line) -> String { - file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or(String::new()) - } - - /// Maps `Diagnostic::Level` to `snippet::AnnotationType` - fn annotation_type_for_level(level: Level) -> AnnotationType { - match level { - Level::Bug | Level::Fatal | Level::Error => AnnotationType::Error, - Level::Warning => AnnotationType::Warning, - Level::Note => AnnotationType::Note, - Level::Help => AnnotationType::Help, - // FIXME(#59346): Not sure how to map these two levels - Level::Cancelled | Level::FailureNote => AnnotationType::Error, - } +/// Maps `Diagnostic::Level` to `snippet::AnnotationType` +fn annotation_type_for_level(level: Level) -> AnnotationType { + match level { + Level::Bug | Level::Fatal | Level::Error => AnnotationType::Error, + Level::Warning => AnnotationType::Warning, + Level::Note => AnnotationType::Note, + Level::Help => AnnotationType::Help, + // FIXME(#59346): Not sure how to map these two levels + Level::Cancelled | Level::FailureNote => AnnotationType::Error, } } @@ -191,25 +98,83 @@ impl AnnotateSnippetEmitterWriter { message: String, code: &Option, msp: &MultiSpan, - children: &[SubDiagnostic], - suggestions: &[CodeSuggestion], + _children: &[SubDiagnostic], + _suggestions: &[CodeSuggestion], ) { - let converter = DiagnosticConverter { - source_map: self.source_map.clone(), - level: *level, - message, - code: code.clone(), - msp: msp.clone(), - children, - suggestions, - }; - if let Some(snippet) = converter.to_annotation_snippet() { - let dl = DisplayList::from(snippet); - let dlf = DisplayListFormatter::new(true, self.ui_testing); + if let Some(source_map) = &self.source_map { + // Make sure our primary file comes first + let primary_lo = if let Some(ref primary_span) = msp.primary_span().as_ref() { + if primary_span.is_dummy() { + // FIXME(#59346): Not sure when this is the case and what + // should be done if it happens + return; + } else { + source_map.lookup_char_pos(primary_span.lo()) + } + } else { + // FIXME(#59346): Not sure when this is the case and what + // should be done if it happens + return; + }; + let mut annotated_files = + FileWithAnnotatedLines::collect_annotations(msp, &self.source_map); + if let Ok(pos) = + annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name)) + { + annotated_files.swap(0, pos); + } + // owned: line source, line index, annotations + type Owned = (String, usize, Vec); + let origin = primary_lo.file.name.to_string(); + let annotated_files: Vec = annotated_files + .into_iter() + .flat_map(|annotated_file| { + let file = annotated_file.file; + annotated_file + .lines + .into_iter() + .map(|line| { + (source_string(file.clone(), &line), line.line_index, line.annotations) + }) + .collect::>() + }) + .collect(); + let snippet = Snippet { + title: Some(Annotation { + label: Some(&message), + id: code.as_ref().map(|c| match c { + DiagnosticId::Error(val) | DiagnosticId::Lint(val) => val.as_str(), + }), + annotation_type: annotation_type_for_level(*level), + }), + footer: vec![], + opt: FormatOptions { color: true, anonymized_line_numbers: self.ui_testing }, + slices: annotated_files + .iter() + .map(|(source, line_index, annotations)| { + Slice { + source, + line_start: *line_index, + origin: Some(&origin), + // FIXME(#59346): Not really sure when `fold` should be true or false + fold: false, + annotations: annotations + .iter() + .map(|annotation| SourceAnnotation { + range: (annotation.start_col, annotation.end_col), + label: annotation.label.as_deref().unwrap_or_default(), + annotation_type: annotation_type_for_level(*level), + }) + .collect(), + } + }) + .collect(), + }; // FIXME(#59346): Figure out if we can _always_ print to stderr or not. // `emitter.rs` has the `Destination` enum that lists various possible output // destinations. - eprintln!("{}", dlf.format(&dl)); - }; + eprintln!("{}", DisplayList::from(snippet)) + } + // FIXME(#59346): Is it ok to return None if there's no source_map? } } diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 1cc5daafed14e..acaa26c6ad2fc 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -193,9 +193,18 @@ impl Diagnostic { expected_extra: &dyn fmt::Display, found_extra: &dyn fmt::Display, ) -> &mut Self { - let expected_label = format!("expected {}", expected_label); - - let found_label = format!("found {}", found_label); + let expected_label = expected_label.to_string(); + let expected_label = if expected_label.is_empty() { + "expected".to_string() + } else { + format!("expected {}", expected_label) + }; + let found_label = found_label.to_string(); + let found_label = if found_label.is_empty() { + "found".to_string() + } else { + format!("found {}", found_label) + }; let (found_padding, expected_padding) = if expected_label.len() > found_label.len() { (expected_label.len() - found_label.len(), 0) } else { @@ -287,6 +296,29 @@ impl Diagnostic { self } + pub fn multipart_suggestions( + &mut self, + msg: &str, + suggestions: Vec>, + applicability: Applicability, + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: suggestions + .into_iter() + .map(|suggestion| Substitution { + parts: suggestion + .into_iter() + .map(|(span, snippet)| SubstitutionPart { snippet, span }) + .collect(), + }) + .collect(), + msg: msg.to_owned(), + style: SuggestionStyle::ShowCode, + applicability, + }); + self + } + /// Prints out a message with for a multipart suggestion without showing the suggested code. /// /// This is intended to be used for suggestions that are obvious in what the changes need to diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index 008d2e92418f9..22bf8fe34aa15 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -162,7 +162,7 @@ impl<'a> DiagnosticBuilder<'a> { message: &str, span: Option, ) -> &mut Self { - let span = span.map(|s| s.into()).unwrap_or_else(|| MultiSpan::new()); + let span = span.map(|s| s.into()).unwrap_or_else(MultiSpan::new); self.0.diagnostic.sub(level, message, span, None); self } @@ -260,6 +260,19 @@ impl<'a> DiagnosticBuilder<'a> { self } + pub fn multipart_suggestions( + &mut self, + msg: &str, + suggestions: Vec>, + applicability: Applicability, + ) -> &mut Self { + if !self.0.allow_suggestions { + return self; + } + self.0.diagnostic.multipart_suggestions(msg, suggestions, applicability); + self + } + pub fn tool_only_multipart_suggestion( &mut self, msg: &str, @@ -315,6 +328,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 03f83e616365b..b22da86c09187 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -5,7 +5,7 @@ //! There are various `Emitter` implementations that generate different output formats such as //! JSON and human readable output. //! -//! The output types are defined in `librustc::session::config::ErrorOutputType`. +//! The output types are defined in `rustc_session::config::ErrorOutputType`. use Destination::*; @@ -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( @@ -1574,7 +1571,7 @@ impl EmitterWriter { .span_to_lines(parts[0].span) .expect("span_to_lines failed when emitting suggestion"); - assert!(!lines.lines.is_empty()); + assert!(!lines.lines.is_empty() || parts[0].span.is_dummy()); let line_start = sm.lookup_char_pos(parts[0].span.lo()).line; draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1); @@ -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 bed26c3736b83..0c1418d3cad27 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -5,6 +5,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(crate_visibility_modifier)] #![feature(nll)] +#![feature(track_caller)] pub use emitter::ColorConfig; @@ -194,7 +195,7 @@ impl CodeSuggestion { let bounding_span = Span::with_root_ctxt(lo, hi); // The different spans might belong to different contexts, if so ignore suggestion. let lines = sm.span_to_lines(bounding_span).ok()?; - assert!(!lines.lines.is_empty()); + assert!(!lines.lines.is_empty() || bounding_span.is_dummy()); // We can't splice anything if the source is unavailable. if !sm.ensure_source_file_source_present(lines.file.clone()) { @@ -213,8 +214,8 @@ impl CodeSuggestion { let sf = &lines.file; let mut prev_hi = sm.lookup_char_pos(bounding_span.lo()); prev_hi.col = CharPos::from_usize(0); - - let mut prev_line = sf.get_line(lines.lines[0].line_index); + let mut prev_line = + lines.lines.get(0).and_then(|line0| sf.get_line(line0.line_index)); let mut buf = String::new(); for part in &substitution.parts { @@ -231,7 +232,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 +316,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 +421,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 +433,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 +447,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(); @@ -573,6 +582,11 @@ impl Handler { DiagnosticBuilder::new(self, Level::Help, msg) } + /// Construct a builder at the `Note` level with the `msg`. + pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Level::Note, msg) + } + pub fn span_fatal(&self, span: impl Into, msg: &str) -> FatalError { self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span); FatalError @@ -608,6 +622,7 @@ impl Handler { self.inner.borrow_mut().span_bug(span, msg) } + #[track_caller] pub fn delay_span_bug(&self, span: impl Into, msg: &str) { self.inner.borrow_mut().delay_span_bug(span, msg) } @@ -745,6 +760,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 +780,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 +794,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 +833,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] )); } @@ -844,13 +875,18 @@ impl HandlerInner { self.emit_diagnostic(diag.set_span(sp)); } + #[track_caller] 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); } let mut diagnostic = Diagnostic::new(Level::Bug, msg); diagnostic.set_span(sp.into()); + diagnostic.note(&format!("delayed at {}", std::panic::Location::caller())); self.delay_as_bug(diagnostic) } 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/Cargo.toml b/src/librustc_expand/Cargo.toml index 3cb79030771b8..ef617acfe1314 100644 --- a/src/librustc_expand/Cargo.toml +++ b/src/librustc_expand/Cargo.toml @@ -11,7 +11,7 @@ path = "lib.rs" doctest = false [dependencies] -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } log = "0.4" rustc_span = { path = "../librustc_span" } rustc_ast_pretty = { path = "../librustc_ast_pretty" } diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs index 2d27fe09f98c8..a57ae798ffceb 100644 --- a/src/librustc_expand/base.rs +++ b/src/librustc_expand/base.rs @@ -1,6 +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; @@ -9,9 +10,10 @@ use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{self, Lrc}; -use rustc_errors::{DiagnosticBuilder, DiagnosticId}; -use rustc_parse::{self, parser, DirectoryOwnership, MACRO_ARGUMENTS}; -use rustc_session::parse::ParseSess; +use rustc_errors::{DiagnosticBuilder, ErrorReported}; +use rustc_parse::{self, parser, MACRO_ARGUMENTS}; +use rustc_session::{parse::ParseSess, Limit}; +use rustc_span::def_id::DefId; use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind}; use rustc_span::source_map::SourceMap; @@ -258,8 +260,17 @@ impl Annotatable { } } -// `meta_item` is the annotation, and `item` is the item being modified. -// FIXME Decorators should follow the same pattern too. +/// Result of an expansion that may need to be retried. +/// Consider using this for non-`MultiItemModifier` expanders as well. +pub enum ExpandResult { + /// Expansion produced a result (possibly dummy). + Ready(T), + /// Expansion could not produce a result and needs to be retried. + /// The string is an explanation that will be printed if we are stuck in an infinite retry loop. + Retry(U, String), +} + +// `meta_item` is the attribute, and `item` is the item being modified. pub trait MultiItemModifier { fn expand( &self, @@ -267,13 +278,12 @@ pub trait MultiItemModifier { span: Span, meta_item: &ast::MetaItem, item: Annotatable, - ) -> Vec; + ) -> ExpandResult, Annotatable>; } -impl MultiItemModifier for F +impl MultiItemModifier for F where - F: Fn(&mut ExtCtxt<'_>, Span, &ast::MetaItem, Annotatable) -> T, - T: Into>, + F: Fn(&mut ExtCtxt<'_>, Span, &ast::MetaItem, Annotatable) -> Vec, { fn expand( &self, @@ -281,28 +291,32 @@ where span: Span, meta_item: &ast::MetaItem, item: Annotatable, - ) -> Vec { - (*self)(ecx, span, meta_item, item).into() - } -} - -impl Into> for Annotatable { - fn into(self) -> Vec { - vec![self] + ) -> ExpandResult, Annotatable> { + ExpandResult::Ready(self(ecx, span, meta_item, item)) } } pub trait ProcMacro { - fn expand<'cx>(&self, ecx: &'cx mut ExtCtxt<'_>, span: Span, ts: TokenStream) -> TokenStream; + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + ts: TokenStream, + ) -> Result; } impl ProcMacro for F where F: Fn(TokenStream) -> TokenStream, { - fn expand<'cx>(&self, _ecx: &'cx mut ExtCtxt<'_>, _span: Span, ts: TokenStream) -> TokenStream { + fn expand<'cx>( + &self, + _ecx: &'cx mut ExtCtxt<'_>, + _span: Span, + ts: TokenStream, + ) -> Result { // FIXME setup implicit context in TLS before calling self. - (*self)(ts) + Ok((*self)(ts)) } } @@ -313,7 +327,7 @@ pub trait AttrProcMacro { span: Span, annotation: TokenStream, annotated: TokenStream, - ) -> TokenStream; + ) -> Result; } impl AttrProcMacro for F @@ -326,9 +340,9 @@ where _span: Span, annotation: TokenStream, annotated: TokenStream, - ) -> TokenStream { + ) -> Result { // FIXME setup implicit context in TLS before calling self. - (*self)(annotation, annotated) + Ok((*self)(annotation, annotated)) } } @@ -580,6 +594,7 @@ impl DummyResult { kind: if is_error { ast::ExprKind::Err } else { ast::ExprKind::Tup(Vec::new()) }, span: sp, attrs: ast::AttrVec::new(), + tokens: None, }) } @@ -783,7 +798,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) @@ -844,7 +859,13 @@ impl SyntaxExtension { SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr { mark_used }, edition) } - pub fn expn_data(&self, parent: ExpnId, call_site: Span, descr: Symbol) -> ExpnData { + pub fn expn_data( + &self, + parent: ExpnId, + call_site: Span, + descr: Symbol, + macro_def_id: Option, + ) -> ExpnData { ExpnData { kind: ExpnKind::Macro(self.macro_kind(), descr), parent, @@ -854,6 +875,7 @@ impl SyntaxExtension { allow_internal_unsafe: self.allow_internal_unsafe, local_inner_macros: self.local_inner_macros, edition: self.edition, + macro_def_id, } } } @@ -872,7 +894,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, @@ -893,13 +915,17 @@ pub trait Resolver { fn check_unused_macros(&mut self); + /// Some parent node that is close enough to the given macro call. + fn lint_node_id(&mut self, expn_id: ExpnId) -> NodeId; + fn has_derive_copy(&self, expn_id: ExpnId) -> bool; fn add_derive_copy(&mut self, expn_id: ExpnId); + fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result; } #[derive(Clone)] pub struct ModuleData { - pub mod_path: Vec, + pub mod_path: Vec, pub directory: PathBuf, } @@ -918,10 +944,13 @@ pub struct ExpansionData { pub struct ExtCtxt<'a> { pub parse_sess: &'a ParseSess, pub ecfg: expand::ExpansionConfig<'a>, + pub reduced_recursion_limit: Option, pub root_path: PathBuf, pub resolver: &'a mut dyn Resolver, pub current_expansion: ExpansionData, pub expansions: FxHashMap>, + /// Called directly after having parsed an external `mod foo;` in expansion. + pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>, } impl<'a> ExtCtxt<'a> { @@ -929,12 +958,15 @@ impl<'a> ExtCtxt<'a> { parse_sess: &'a ParseSess, ecfg: expand::ExpansionConfig<'a>, resolver: &'a mut dyn Resolver, + extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>, ) -> ExtCtxt<'a> { ExtCtxt { parse_sess, ecfg, - root_path: PathBuf::new(), + reduced_recursion_limit: None, resolver, + extern_mod_loaded, + root_path: PathBuf::new(), current_expansion: ExpansionData { id: ExpnId::root(), depth: 0, @@ -994,31 +1026,9 @@ impl<'a> ExtCtxt<'a> { self.current_expansion.id.expansion_cause() } - pub fn struct_span_warn>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> { - self.parse_sess.span_diagnostic.struct_span_warn(sp, msg) - } pub fn struct_span_err>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> { self.parse_sess.span_diagnostic.struct_span_err(sp, msg) } - pub fn struct_span_fatal>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> { - self.parse_sess.span_diagnostic.struct_span_fatal(sp, msg) - } - - /// Emit `msg` attached to `sp`, and stop compilation immediately. - /// - /// `span_err` should be strongly preferred where-ever possible: - /// this should *only* be used when: - /// - /// - continuing has a high risk of flow-on errors (e.g., errors in - /// declaring a macro would cause all uses of that macro to - /// complain about "undefined macro"), or - /// - there is literally nothing else that can be done (however, - /// in most cases one can construct a dummy expression/item to - /// substitute; we never hit resolve/type-checking so the dummy - /// value doesn't have to match anything) - pub fn span_fatal>(&self, sp: S, msg: &str) -> ! { - self.parse_sess.span_diagnostic.span_fatal(sp, msg).raise(); - } /// Emit `msg` attached to `sp`, without immediately stopping /// compilation. @@ -1028,9 +1038,6 @@ impl<'a> ExtCtxt<'a> { pub fn span_err>(&self, sp: S, msg: &str) { self.parse_sess.span_diagnostic.span_err(sp, msg); } - pub fn span_err_with_code>(&self, sp: S, msg: &str, code: DiagnosticId) { - self.parse_sess.span_diagnostic.span_err_with_code(sp, msg, code); - } pub fn span_warn>(&self, sp: S, msg: &str) { self.parse_sess.span_diagnostic.span_warn(sp, msg); } @@ -1057,16 +1064,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) } @@ -1091,7 +1098,7 @@ impl<'a> ExtCtxt<'a> { if !path.is_absolute() { let callsite = span.source_callsite(); let mut result = match self.source_map().span_to_unmapped_path(callsite) { - FileName::Real(path) => path, + FileName::Real(name) => name.into_local_path(), FileName::DocTest(path, _) => path, other => { return Err(self.struct_span_err( @@ -1158,6 +1165,18 @@ pub fn check_zero_tts(cx: &ExtCtxt<'_>, sp: Span, tts: TokenStream, name: &str) } } +/// Parse an expression. On error, emit it, advancing to `Eof`, and return `None`. +pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option> { + match p.parse_expr() { + Ok(e) => return Some(e), + Err(mut err) => err.emit(), + } + while p.token != token::Eof { + p.bump(); + } + None +} + /// Interpreting `tts` as a comma-separated sequence of expressions, /// expect exactly one string literal, or emit an error and return `None`. pub fn get_single_str_from_tts( @@ -1171,7 +1190,7 @@ pub fn get_single_str_from_tts( cx.span_err(sp, &format!("{} takes 1 argument", name)); return None; } - let ret = panictry!(p.parse_expr()); + let ret = parse_expr(&mut p)?; let _ = p.eat(&token::Comma); if p.token != token::Eof { @@ -1180,8 +1199,8 @@ pub fn get_single_str_from_tts( expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s.to_string()) } -/// Extracts comma-separated expressions from `tts`. If there is a -/// parsing error, emit a non-fatal error and return `None`. +/// Extracts comma-separated expressions from `tts`. +/// On error, emit it, and return `None`. pub fn get_exprs_from_tts( cx: &mut ExtCtxt<'_>, sp: Span, @@ -1190,7 +1209,7 @@ pub fn get_exprs_from_tts( let mut p = cx.new_parser_from_tts(tts); let mut es = Vec::new(); while p.token != token::Eof { - let expr = panictry!(p.parse_expr()); + let expr = parse_expr(&mut p)?; // Perform eager expansion on the expression. // We want to be able to handle e.g., `concat!("foo", "bar")`. diff --git a/src/librustc_expand/build.rs b/src/librustc_expand/build.rs index 48ceaf5fccd8e..20d2ea0a215d4 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,18 +63,24 @@ 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)) } pub fn anon_const(&self, span: Span, kind: ast::ExprKind) -> ast::AnonConst { ast::AnonConst { id: ast::DUMMY_NODE_ID, - value: P(ast::Expr { id: ast::DUMMY_NODE_ID, kind, span, attrs: AttrVec::new() }), + value: P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind, + span, + attrs: AttrVec::new(), + tokens: None, + }), } } - 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 +101,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 +135,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 +161,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) @@ -210,14 +211,14 @@ impl<'a> ExtCtxt<'a> { } pub fn expr(&self, span: Span, kind: ast::ExprKind) -> P { - P(ast::Expr { id: ast::DUMMY_NODE_ID, kind, span, attrs: AttrVec::new() }) + P(ast::Expr { id: ast::DUMMY_NODE_ID, kind, span, attrs: AttrVec::new(), tokens: None }) } pub fn expr_path(&self, path: ast::Path) -> P { self.expr(path.span, ast::ExprKind::Path(None, path)) } - pub fn expr_ident(&self, span: Span, id: 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 +251,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,12 +267,12 @@ impl<'a> ExtCtxt<'a> { &self, span: Span, expr: P, - ident: ast::Ident, + ident: Ident, mut args: Vec>, ) -> P { args.insert(0, expr); let segment = ast::PathSegment::from_ident(ident.with_span_pos(span)); - self.expr(span, ast::ExprKind::MethodCall(segment, args)) + self.expr(span, ast::ExprKind::MethodCall(segment, args, span)) } pub fn expr_block(&self, b: P) -> P { self.expr(b.span, ast::ExprKind::Block(b, None)) @@ -303,7 +299,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 +400,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 +408,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 +512,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 +539,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 +643,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 new file mode 100644 index 0000000000000..d79dabb509267 --- /dev/null +++ b/src/librustc_expand/config.rs @@ -0,0 +1,532 @@ +//! Conditional compilation stripping. + +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_attr as attr; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::map_in_place::MapInPlace; +use rustc_errors::{error_code, struct_span_err, Applicability, Handler}; +use rustc_feature::{Feature, Features, State as FeatureState}; +use rustc_feature::{ + ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES, +}; +use rustc_parse::{parse_in, validate_attr}; +use rustc_session::parse::{feature_err, ParseSess}; +use rustc_span::edition::{Edition, ALL_EDITIONS}; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{Span, DUMMY_SP}; + +use smallvec::SmallVec; + +/// A folder that strips out items that do not belong in the current configuration. +pub struct StripUnconfigured<'a> { + pub sess: &'a ParseSess, + pub features: Option<&'a Features>, +} + +fn get_features( + span_handler: &Handler, + krate_attrs: &[ast::Attribute], + crate_edition: Edition, + allow_features: &Option>, +) -> Features { + fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) { + let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed"); + err.span_label(span, "feature has been removed"); + if let Some(reason) = reason { + err.note(reason); + } + err.emit(); + } + + fn active_features_up_to(edition: Edition) -> impl Iterator { + ACTIVE_FEATURES.iter().filter(move |feature| { + if let Some(feature_edition) = feature.edition { + feature_edition <= edition + } else { + false + } + }) + } + + let mut features = Features::default(); + let mut edition_enabled_features = FxHashMap::default(); + + for &edition in ALL_EDITIONS { + if edition <= crate_edition { + // The `crate_edition` implies its respective umbrella feature-gate + // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX). + edition_enabled_features.insert(edition.feature_name(), edition); + } + } + + for feature in active_features_up_to(crate_edition) { + feature.set(&mut features, DUMMY_SP); + edition_enabled_features.insert(feature.name, crate_edition); + } + + // Process the edition umbrella feature-gates first, to ensure + // `edition_enabled_features` is completed before it's queried. + for attr in krate_attrs { + if !attr.check_name(sym::feature) { + continue; + } + + let list = match attr.meta_item_list() { + Some(list) => list, + None => continue, + }; + + for mi in list { + if !mi.is_word() { + continue; + } + + let name = mi.name_or_empty(); + + let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied(); + if let Some(edition) = edition { + if edition <= crate_edition { + continue; + } + + for feature in active_features_up_to(edition) { + // FIXME(Manishearth) there is currently no way to set + // lib features by edition + feature.set(&mut features, DUMMY_SP); + edition_enabled_features.insert(feature.name, edition); + } + } + } + } + + for attr in krate_attrs { + if !attr.check_name(sym::feature) { + continue; + } + + let list = match attr.meta_item_list() { + Some(list) => list, + None => continue, + }; + + let bad_input = |span| { + struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input") + }; + + for mi in list { + let name = match mi.ident() { + Some(ident) if mi.is_word() => ident.name, + Some(ident) => { + bad_input(mi.span()) + .span_suggestion( + mi.span(), + "expected just one word", + format!("{}", ident.name), + Applicability::MaybeIncorrect, + ) + .emit(); + continue; + } + None => { + bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit(); + continue; + } + }; + + if let Some(edition) = edition_enabled_features.get(&name) { + let msg = + &format!("the feature `{}` is included in the Rust {} edition", name, edition); + span_handler.struct_span_warn_with_code(mi.span(), msg, error_code!(E0705)).emit(); + continue; + } + + if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) { + // Handled in the separate loop above. + continue; + } + + let removed = REMOVED_FEATURES.iter().find(|f| name == f.name); + let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name); + if let Some(Feature { state, .. }) = removed.or(stable_removed) { + if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } = + state + { + feature_removed(span_handler, mi.span(), *reason); + continue; + } + } + + if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) { + let since = Some(Symbol::intern(since)); + features.declared_lang_features.push((name, mi.span(), since)); + continue; + } + + if let Some(allowed) = allow_features.as_ref() { + if allowed.iter().find(|&f| name.as_str() == *f).is_none() { + struct_span_err!( + span_handler, + mi.span(), + E0725, + "the feature `{}` is not in the list of allowed features", + name + ) + .emit(); + continue; + } + } + + if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) { + f.set(&mut features, mi.span()); + features.declared_lang_features.push((name, mi.span(), None)); + continue; + } + + features.declared_lib_features.push((name, mi.span())); + } + } + + features +} + +// `cfg_attr`-process the crate's attributes and compute the crate's features. +pub fn features( + mut krate: ast::Crate, + sess: &ParseSess, + edition: Edition, + allow_features: &Option>, +) -> (ast::Crate, Features) { + let mut strip_unconfigured = StripUnconfigured { sess, features: None }; + + let unconfigured_attrs = krate.attrs.clone(); + let diag = &sess.span_diagnostic; + let err_count = diag.err_count(); + let features = match strip_unconfigured.configure(krate.attrs) { + None => { + // The entire crate is unconfigured. + krate.attrs = Vec::new(); + krate.module.items = Vec::new(); + Features::default() + } + Some(attrs) => { + krate.attrs = attrs; + let features = get_features(diag, &krate.attrs, edition, allow_features); + if err_count == diag.err_count() { + // Avoid reconfiguring malformed `cfg_attr`s. + strip_unconfigured.features = Some(&features); + strip_unconfigured.configure(unconfigured_attrs); + } + features + } + }; + (krate, features) +} + +#[macro_export] +macro_rules! configure { + ($this:ident, $node:ident) => { + match $this.configure($node) { + Some(node) => node, + None => return Default::default(), + } + }; +} + +const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; +const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ + "; + +impl<'a> StripUnconfigured<'a> { + pub fn configure(&mut self, mut node: T) -> Option { + self.process_cfg_attrs(&mut node); + self.in_cfg(node.attrs()).then_some(node) + } + + /// Parse and expand all `cfg_attr` attributes into a list of attributes + /// that are within each `cfg_attr` that has a true configuration predicate. + /// + /// Gives compiler warnings if any `cfg_attr` does not contain any + /// attributes and is in the original source code. Gives compiler errors if + /// the syntax of any `cfg_attr` is incorrect. + pub fn process_cfg_attrs(&mut self, node: &mut T) { + node.visit_attrs(|attrs| { + attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); + }); + } + + /// Parse and expand a single `cfg_attr` attribute into a list of attributes + /// when the configuration predicate is true, or otherwise expand into an + /// empty list of attributes. + /// + /// Gives a compiler warning when the `cfg_attr` contains no attributes and + /// is in the original source file. Gives a compiler error if the syntax of + /// the attribute is incorrect. + fn process_cfg_attr(&mut self, attr: Attribute) -> Vec { + if !attr.has_name(sym::cfg_attr) { + return vec![attr]; + } + + let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) { + None => return vec![], + Some(r) => r, + }; + + // Lint on zero attributes in source. + if expanded_attrs.is_empty() { + return vec![attr]; + } + + // At this point we know the attribute is considered used. + attr::mark_used(&attr); + + if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) { + return vec![]; + } + + // We call `process_cfg_attr` recursively in case there's a + // `cfg_attr` inside of another `cfg_attr`. E.g. + // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. + expanded_attrs + .into_iter() + .flat_map(|(item, span)| { + let attr = attr::mk_attr_from_item(attr.style, item, span); + self.process_cfg_attr(attr) + }) + .collect() + } + + fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { + match attr.get_normal_item().args { + ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => { + let msg = "wrong `cfg_attr` delimiters"; + validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg); + match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { + Ok(r) => return Some(r), + Err(mut e) => { + e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) + .note(CFG_ATTR_NOTE_REF) + .emit(); + } + } + } + _ => self.error_malformed_cfg_attr_missing(attr.span), + } + None + } + + fn error_malformed_cfg_attr_missing(&self, span: Span) { + self.sess + .span_diagnostic + .struct_span_err(span, "malformed `cfg_attr` attribute input") + .span_suggestion( + span, + "missing condition and attribute", + CFG_ATTR_GRAMMAR_HELP.to_string(), + Applicability::HasPlaceholders, + ) + .note(CFG_ATTR_NOTE_REF) + .emit(); + } + + /// Determines if a node with the given attributes should be included in this configuration. + pub fn in_cfg(&self, attrs: &[Attribute]) -> bool { + attrs.iter().all(|attr| { + if !is_cfg(attr) { + return true; + } + let meta_item = match validate_attr::parse_meta(self.sess, attr) { + Ok(meta_item) => meta_item, + Err(mut err) => { + err.emit(); + return true; + } + }; + let error = |span, msg, suggestion: &str| { + let mut err = self.sess.span_diagnostic.struct_span_err(span, msg); + if !suggestion.is_empty() { + err.span_suggestion( + span, + "expected syntax is", + suggestion.into(), + Applicability::MaybeIncorrect, + ); + } + err.emit(); + true + }; + let span = meta_item.span; + match meta_item.meta_item_list() { + None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"), + Some([]) => error(span, "`cfg` predicate is not specified", ""), + Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""), + Some([single]) => match single.meta_item() { + Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features), + None => error(single.span(), "`cfg` predicate key cannot be a literal", ""), + }, + } + }) + } + + /// Visit attributes on expression and statements (but not attributes on items in blocks). + fn visit_expr_attrs(&mut self, attrs: &[Attribute]) { + // flag the offending attributes + for attr in attrs.iter() { + self.maybe_emit_expr_attr_err(attr); + } + } + + /// If attributes are not allowed on expressions, emit an error for `attr` + pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { + if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) { + let mut err = feature_err( + self.sess, + sym::stmt_expr_attributes, + attr.span, + "attributes on expressions are experimental", + ); + + if attr.is_doc_comment() { + err.help("`///` is for documentation comments. For a plain comment, use `//`."); + } + + err.emit(); + } + } + + pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { + let ast::ForeignMod { abi: _, items } = foreign_mod; + items.flat_map_in_place(|item| self.configure(item)); + } + + pub fn configure_generic_params(&mut self, params: &mut Vec) { + params.flat_map_in_place(|param| self.configure(param)); + } + + fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) { + match vdata { + ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => { + fields.flat_map_in_place(|field| self.configure(field)) + } + ast::VariantData::Unit(_) => {} + } + } + + pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) { + match item { + ast::ItemKind::Struct(def, _generics) | ast::ItemKind::Union(def, _generics) => { + self.configure_variant_data(def) + } + ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => { + variants.flat_map_in_place(|variant| self.configure(variant)); + for variant in variants { + self.configure_variant_data(&mut variant.data); + } + } + _ => {} + } + } + + pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) { + match expr_kind { + ast::ExprKind::Match(_m, arms) => { + arms.flat_map_in_place(|arm| self.configure(arm)); + } + ast::ExprKind::Struct(_path, fields, _base) => { + fields.flat_map_in_place(|field| self.configure(field)); + } + _ => {} + } + } + + pub fn configure_expr(&mut self, expr: &mut P) { + self.visit_expr_attrs(expr.attrs()); + + // If an expr is valid to cfg away it will have been removed by the + // outer stmt or expression folder before descending in here. + // Anything else is always required, and thus has to error out + // in case of a cfg attr. + // + // N.B., this is intentionally not part of the visit_expr() function + // in order for filter_map_expr() to be able to avoid this check + if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) { + let msg = "removing an expression is not supported in this position"; + self.sess.span_diagnostic.span_err(attr.span, msg); + } + + self.process_cfg_attrs(expr) + } + + pub fn configure_pat(&mut self, pat: &mut P) { + if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.kind { + fields.flat_map_in_place(|field| self.configure(field)); + } + } + + pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) { + fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg)); + } +} + +impl<'a> MutVisitor for StripUnconfigured<'a> { + fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { + self.configure_foreign_mod(foreign_mod); + noop_visit_foreign_mod(foreign_mod, self); + } + + fn visit_item_kind(&mut self, item: &mut ast::ItemKind) { + self.configure_item_kind(item); + noop_visit_item_kind(item, self); + } + + fn visit_expr(&mut self, expr: &mut P) { + self.configure_expr(expr); + self.configure_expr_kind(&mut expr.kind); + noop_visit_expr(expr, self); + } + + fn filter_map_expr(&mut self, expr: P) -> Option> { + let mut expr = configure!(self, expr); + self.configure_expr_kind(&mut expr.kind); + noop_visit_expr(&mut expr, self); + Some(expr) + } + + fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { + noop_flat_map_stmt(configure!(self, stmt), self) + } + + fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { + noop_flat_map_item(configure!(self, item), self) + } + + fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { + noop_flat_map_assoc_item(configure!(self, item), self) + } + + fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { + noop_flat_map_assoc_item(configure!(self, item), self) + } + + fn visit_mac(&mut self, _mac: &mut ast::MacCall) { + // Don't configure interpolated AST (cf. issue #34171). + // Interpolated AST will get configured once the surrounding tokens are parsed. + } + + fn visit_pat(&mut self, pat: &mut P) { + self.configure_pat(pat); + noop_visit_pat(pat, self) + } + + fn visit_fn_decl(&mut self, mut fn_decl: &mut P) { + self.configure_fn_decl(&mut fn_decl); + noop_visit_fn_decl(fn_decl, self); + } +} + +fn is_cfg(attr: &Attribute) -> bool { + attr.check_name(sym::cfg) +} diff --git a/src/librustc_expand/expand.rs b/src/librustc_expand/expand.rs index 73197160a0269..4e41bd4bbfa08 100644 --- a/src/librustc_expand/expand.rs +++ b/src/librustc_expand/expand.rs @@ -1,31 +1,32 @@ use crate::base::*; use crate::config::StripUnconfigured; +use crate::configure; use crate::hygiene::{ExpnData, ExpnId, ExpnKind, SyntaxContext}; use crate::mbe::macro_rules::annotate_err_with_kind; +use crate::module::{parse_external_mod, push_directory, Directory, DirectoryOwnership}; use crate::placeholders::{placeholder, PlaceholderExpander}; use crate::proc_macro::collect_derives; -use rustc_ast::ast::{self, AttrItem, Block, 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_errors::{Applicability, FatalError, PResult}; +use rustc_data_structures::map_in_place::MapInPlace; +use rustc_errors::{Applicability, PResult}; use rustc_feature::Features; -use rustc_parse::configure; use rustc_parse::parser::Parser; use rustc_parse::validate_attr; -use rustc_parse::DirectoryOwnership; use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::{feature_err, ParseSess}; +use rustc_session::Limit; use rustc_span::source_map::respan; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{FileName, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; @@ -204,7 +205,7 @@ ast_fragments! { } impl AstFragmentKind { - fn dummy(self, span: Span) -> AstFragment { + crate fn dummy(self, span: Span) -> AstFragment { self.make_from(DummyResult::any(span)).expect("couldn't create a dummy AST fragment") } @@ -340,7 +341,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let mut module = ModuleData { mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)], directory: match self.cx.source_map().span_to_unmapped_path(krate.span) { - FileName::Real(path) => path, + FileName::Real(name) => name.into_local_path(), other => PathBuf::from(other.to_string()), }, }; @@ -408,7 +409,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let mut undetermined_invocations = Vec::new(); let (mut progress, mut force) = (false, !self.monotonic); loop { - let invoc = if let Some(invoc) = invocations.pop() { + let (invoc, res) = if let Some(invoc) = invocations.pop() { invoc } else { self.resolve_imports(); @@ -420,30 +421,51 @@ impl<'a, 'b> MacroExpander<'a, 'b> { continue; }; - let eager_expansion_root = - if self.monotonic { invoc.expansion_data.id } else { orig_expansion_data.id }; - let res = match self.cx.resolver.resolve_macro_invocation( - &invoc, - eager_expansion_root, - force, - ) { - Ok(res) => res, - Err(Indeterminate) => { - undetermined_invocations.push(invoc); - continue; + let res = match res { + Some(res) => res, + None => { + let eager_expansion_root = if self.monotonic { + invoc.expansion_data.id + } else { + orig_expansion_data.id + }; + match self.cx.resolver.resolve_macro_invocation( + &invoc, + eager_expansion_root, + force, + ) { + Ok(res) => res, + Err(Indeterminate) => { + // Cannot resolve, will retry this invocation later. + undetermined_invocations.push((invoc, None)); + continue; + } + } } }; - progress = true; let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data; self.cx.current_expansion = invoc.expansion_data.clone(); // FIXME(jseyfried): Refactor out the following logic let (expanded_fragment, new_invocations) = match res { - InvocationRes::Single(ext) => { - let fragment = self.expand_invoc(invoc, &ext.kind); - self.collect_invocations(fragment, &[]) - } + InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) { + ExpandResult::Ready(fragment) => self.collect_invocations(fragment, &[]), + ExpandResult::Retry(invoc, explanation) => { + if force { + // We are stuck, stop retrying and produce a dummy fragment. + let span = invoc.span(); + self.cx.span_err(span, &explanation); + let fragment = invoc.fragment_kind.dummy(span); + self.collect_invocations(fragment, &[]) + } else { + // Cannot expand, will retry this invocation later. + undetermined_invocations + .push((invoc, Some(InvocationRes::Single(ext)))); + continue; + } + } + }, InvocationRes::DeriveContainer(_exts) => { // FIXME: Consider using the derive resolutions (`_exts`) immediately, // instead of enqueuing the derives to be resolved again later. @@ -463,14 +485,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> { for path in derives { let expn_id = ExpnId::fresh(None); derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id)); - invocations.push(Invocation { - kind: InvocationKind::Derive { path, item: item.clone() }, - fragment_kind: invoc.fragment_kind, - expansion_data: ExpansionData { - id: expn_id, - ..invoc.expansion_data.clone() + invocations.push(( + Invocation { + kind: InvocationKind::Derive { path, item: item.clone() }, + fragment_kind: invoc.fragment_kind, + expansion_data: ExpansionData { + id: expn_id, + ..invoc.expansion_data.clone() + }, }, - }); + None, + )); } let fragment = invoc.fragment_kind.expect_from_annotatables(::std::iter::once(item)); @@ -478,13 +503,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } }; + progress = true; if expanded_fragments.len() < depth { 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; @@ -535,7 +559,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { &mut self, mut fragment: AstFragment, extra_placeholders: &[NodeId], - ) -> (AstFragment, Vec) { + ) -> (AstFragment, Vec<(Invocation, Option)>) { // Resolve `$crate`s in the fragment for pretty-printing. self.cx.resolver.resolve_dollar_crates(); @@ -620,7 +644,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { )) .emit(); self.cx.trace_macros_diag(); - FatalError.raise(); } /// A macro's expansion does not fit in this fragment kind. @@ -635,17 +658,32 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.trace_macros_diag(); } - fn expand_invoc(&mut self, invoc: Invocation, ext: &SyntaxExtensionKind) -> AstFragment { - if self.cx.current_expansion.depth > self.cx.ecfg.recursion_limit { - self.error_recursion_limit_reached(); + fn expand_invoc( + &mut self, + invoc: Invocation, + ext: &SyntaxExtensionKind, + ) -> ExpandResult { + let recursion_limit = + self.cx.reduced_recursion_limit.unwrap_or(self.cx.ecfg.recursion_limit); + if !recursion_limit.value_within_limit(self.cx.current_expansion.depth) { + if self.cx.reduced_recursion_limit.is_none() { + self.error_recursion_limit_reached(); + } + + // Reduce the recursion limit by half each time it triggers. + self.cx.reduced_recursion_limit = Some(recursion_limit / 2); + + return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span())); } let (fragment_kind, span) = (invoc.fragment_kind, invoc.span()); - match invoc.kind { + 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 = expander.expand(self.cx, span, mac.args.inner_tokens()); + let tok_result = match expander.expand(self.cx, span, mac.args.inner_tokens()) { + Err(_) => return ExpandResult::Ready(fragment_kind.dummy(span)), + Ok(ts) => ts, + }; self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span) } SyntaxExtensionKind::LegacyBang(expander) => { @@ -663,7 +701,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } _ => unreachable!(), }, - InvocationKind::Attr { attr, mut item, .. } => match ext { + InvocationKind::Attr { attr, mut item, derives, after_derive } => match ext { SyntaxExtensionKind::Attr(expander) => { self.gate_proc_macro_input(&item); self.gate_proc_macro_attr_item(span, &item); @@ -672,15 +710,35 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if let MacArgs::Eq(..) = attr_item.args { self.cx.span_err(span, "key-value macro attributes are not supported"); } - let tok_result = - expander.expand(self.cx, span, attr_item.args.inner_tokens(), tokens); + let inner_tokens = attr_item.args.inner_tokens(); + let tok_result = match expander.expand(self.cx, span, inner_tokens, tokens) { + Err(_) => return ExpandResult::Ready(fragment_kind.dummy(span)), + Ok(ts) => ts, + }; self.parse_ast_fragment(tok_result, fragment_kind, &attr_item.path, span) } SyntaxExtensionKind::LegacyAttr(expander) => { match validate_attr::parse_meta(self.cx.parse_sess, &attr) { Ok(meta) => { - let item = expander.expand(self.cx, span, &meta, item); - fragment_kind.expect_from_annotatables(item) + let items = match expander.expand(self.cx, span, &meta, item) { + ExpandResult::Ready(items) => items, + ExpandResult::Retry(item, explanation) => { + // Reassemble the original invocation for retrying. + return ExpandResult::Retry( + Invocation { + kind: InvocationKind::Attr { + attr, + item, + derives, + after_derive, + }, + ..invoc + }, + explanation, + ); + } + }; + fragment_kind.expect_from_annotatables(items) } Err(mut err) => { err.emit(); @@ -702,19 +760,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> { SyntaxExtensionKind::Derive(expander) | SyntaxExtensionKind::LegacyDerive(expander) => { if !item.derive_allowed() { - return fragment_kind.dummy(span); + return ExpandResult::Ready(fragment_kind.dummy(span)); } if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path }; - let items = expander.expand(self.cx, span, &meta, item); + let items = match expander.expand(self.cx, span, &meta, item) { + ExpandResult::Ready(items) => items, + ExpandResult::Retry(item, explanation) => { + // Reassemble the original invocation for retrying. + return ExpandResult::Retry( + Invocation { + kind: InvocationKind::Derive { path: meta.path, item }, + ..invoc + }, + explanation, + ); + } + }; fragment_kind.expect_from_annotatables(items) } _ => unreachable!(), }, InvocationKind::DeriveContainer { .. } => unreachable!(), - } + }) } fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) { @@ -776,36 +846,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, @@ -933,7 +973,7 @@ pub fn ensure_complete_parse<'a>( struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, cfg: StripUnconfigured<'a>, - invocations: Vec, + invocations: Vec<(Invocation, Option)>, monotonic: bool, } @@ -949,21 +989,25 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ExpnKind::Macro(MacroKind::Attr, sym::derive), item.span(), self.cx.parse_sess.edition, + None, ) }), _ => None, }; let expn_id = ExpnId::fresh(expn_data); let vis = kind.placeholder_visibility(); - self.invocations.push(Invocation { - kind, - fragment_kind, - expansion_data: ExpansionData { - id: expn_id, - depth: self.cx.current_expansion.depth + 1, - ..self.cx.current_expansion.clone() + self.invocations.push(( + Invocation { + kind, + fragment_kind, + expansion_data: ExpansionData { + id: expn_id, + depth: self.cx.current_expansion.depth + 1, + ..self.cx.current_expansion.clone() + }, }, - }); + None, + )); placeholder(fragment_kind, NodeId::placeholder_from_expn_id(expn_id), vis) } @@ -1070,6 +1114,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // macros are expanded before any lint passes so this warning has to be hardcoded if attr.has_name(sym::derive) { self.cx + .parse_sess() + .span_diagnostic .struct_span_warn(attr.span, "`#[derive]` does nothing on macro invocations") .note("this may become a hard error in a future release") .emit(); @@ -1097,10 +1143,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 @@ -1247,8 +1293,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( @@ -1366,59 +1412,83 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { .make_items(); } + let mut attrs = mem::take(&mut item.attrs); // We do this to please borrowck. + let ident = item.ident; + let span = item.span; + match item.kind { ast::ItemKind::MacCall(..) => { + item.attrs = attrs; self.check_attributes(&item.attrs); item.and_then(|item| match item.kind { ItemKind::MacCall(mac) => self - .collect( - AstFragmentKind::Items, - InvocationKind::Bang { mac, span: item.span }, - ) + .collect(AstFragmentKind::Items, InvocationKind::Bang { mac, span }) .make_items(), _ => unreachable!(), }) } - ast::ItemKind::Mod(ast::Mod { inner, inline, .. }) - if item.ident != Ident::invalid() => - { - let orig_directory_ownership = self.cx.current_expansion.directory_ownership; + ast::ItemKind::Mod(ref mut old_mod @ ast::Mod { .. }) if ident != Ident::invalid() => { + let sess = self.cx.parse_sess; + let orig_ownership = self.cx.current_expansion.directory_ownership; let mut module = (*self.cx.current_expansion.module).clone(); - module.mod_path.push(item.ident); - if inline { - if let Some(path) = attr::first_attr_value_str_by_name(&item.attrs, sym::path) { - self.cx.current_expansion.directory_ownership = - DirectoryOwnership::Owned { relative: None }; - module.directory.push(&*path.as_str()); - } else { - module.directory.push(&*item.ident.as_str()); - } + let pushed = &mut false; // Record `parse_external_mod` pushing so we can pop. + let dir = Directory { ownership: orig_ownership, path: module.directory }; + let Directory { ownership, path } = if old_mod.inline { + // Inline `mod foo { ... }`, but we still need to push directories. + item.attrs = attrs; + push_directory(ident, &item.attrs, dir) } else { - let path = self.cx.parse_sess.source_map().span_to_unmapped_path(inner); - let mut path = match path { - FileName::Real(path) => path, - other => PathBuf::from(other.to_string()), + // We have an outline `mod foo;` so we need to parse the file. + let (new_mod, dir) = + parse_external_mod(sess, ident, span, dir, &mut attrs, pushed); + + let krate = ast::Crate { + span: new_mod.inner, + module: new_mod, + attrs, + proc_macros: vec![], }; - let directory_ownership = match path.file_name().unwrap().to_str() { - Some("mod.rs") => DirectoryOwnership::Owned { relative: None }, - Some(_) => DirectoryOwnership::Owned { relative: Some(item.ident) }, - None => DirectoryOwnership::UnownedViaMod, + if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded { + extern_mod_loaded(&krate); + } + + *old_mod = krate.module; + item.attrs = krate.attrs; + // File can have inline attributes, e.g., `#![cfg(...)]` & co. => Reconfigure. + item = match self.configure(item) { + Some(node) => node, + None => { + if *pushed { + sess.included_mod_stack.borrow_mut().pop(); + } + return Default::default(); + } }; - path.pop(); - module.directory = path; - self.cx.current_expansion.directory_ownership = directory_ownership; - } + dir + }; + // Set the module info before we flat map. + self.cx.current_expansion.directory_ownership = ownership; + module.directory = path; + module.mod_path.push(ident); let orig_module = mem::replace(&mut self.cx.current_expansion.module, Rc::new(module)); + let result = noop_flat_map_item(item, self); + + // Restore the module info. self.cx.current_expansion.module = orig_module; - self.cx.current_expansion.directory_ownership = orig_directory_ownership; + self.cx.current_expansion.directory_ownership = orig_ownership; + if *pushed { + sess.included_mod_stack.borrow_mut().pop(); + } result } - - _ => noop_flat_map_item(item, self), + _ => { + item.attrs = attrs; + noop_flat_map_item(item, self) + } } } @@ -1715,11 +1785,11 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { pub struct ExpansionConfig<'feat> { pub crate_name: String, pub features: Option<&'feat Features>, - pub recursion_limit: usize, + pub recursion_limit: Limit, pub trace_mac: bool, pub should_test: bool, // If false, strip `#[test]` nodes - pub single_step: bool, pub keep_macs: bool, + pub span_debug: bool, // If true, use verbose debugging for `proc_macro::Span` } impl<'feat> ExpansionConfig<'feat> { @@ -1727,11 +1797,11 @@ impl<'feat> ExpansionConfig<'feat> { ExpansionConfig { crate_name, features: None, - recursion_limit: 1024, + recursion_limit: Limit::new(1024), trace_mac: false, should_test: false, - single_step: false, keep_macs: false, + span_debug: false, } } diff --git a/src/librustc_expand/lib.rs b/src/librustc_expand/lib.rs index f119c956ced04..1e3ab2c5ec2db 100644 --- a/src/librustc_expand/lib.rs +++ b/src/librustc_expand/lib.rs @@ -1,31 +1,15 @@ +#![feature(bool_to_option)] #![feature(cow_is_borrowed)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] +#![feature(or_patterns)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] #![feature(proc_macro_span)] +#![feature(try_blocks)] extern crate proc_macro as pm; -// A variant of 'try!' that panics on an Err. This is used as a crutch on the -// way towards a non-panic!-prone parser. It should be used for fatal parsing -// errors; eventually we plan to convert all code using panictry to just use -// normal try. -#[macro_export] -macro_rules! panictry { - ($e:expr) => {{ - use rustc_errors::FatalError; - use std::result::Result::{Err, Ok}; - match $e { - Ok(e) => e, - Err(mut e) => { - e.emit(); - FatalError.raise() - } - } - }}; -} - mod placeholders; mod proc_macro_server; @@ -33,8 +17,10 @@ pub use mbe::macro_rules::compile_declarative_macro; crate use rustc_span::hygiene; pub mod base; pub mod build; +#[macro_use] +pub mod config; pub mod expand; -pub use rustc_parse::config; +pub mod module; pub mod proc_macro; crate mod mbe; 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_check.rs b/src/librustc_expand/mbe/macro_check.rs index 6eb834beac652..ca3e68fa6706e 100644 --- a/src/librustc_expand/mbe/macro_check.rs +++ b/src/librustc_expand/mbe/macro_check.rs @@ -106,13 +106,13 @@ //! bound. use crate::mbe::{KleeneToken, TokenTree}; -use rustc_ast::ast::NodeId; +use rustc_ast::ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast::token::{DelimToken, Token, TokenKind}; use rustc_data_structures::fx::FxHashMap; use rustc_session::lint::builtin::META_VARIABLE_MISUSE; use rustc_session::parse::ParseSess; use rustc_span::symbol::kw; -use rustc_span::{symbol::Ident, MultiSpan, Span}; +use rustc_span::{symbol::MacroRulesNormalizedIdent, MultiSpan, Span}; use smallvec::SmallVec; @@ -179,7 +179,7 @@ struct BinderInfo { } /// An environment of meta-variables to their binder information. -type Binders = FxHashMap; +type Binders = FxHashMap; /// The state at which we entered a macro definition in the RHS of another macro definition. struct MacroState<'a> { @@ -245,6 +245,7 @@ fn check_binders( if macros.is_empty() { sess.span_diagnostic.span_bug(span, "unexpected MetaVar in lhs"); } + let name = MacroRulesNormalizedIdent::new(name); // There are 3 possibilities: if let Some(prev_info) = binders.get(&name) { // 1. The meta-variable is already bound in the current LHS: This is an error. @@ -264,6 +265,7 @@ fn check_binders( if !macros.is_empty() { sess.span_diagnostic.span_bug(span, "unexpected MetaVarDecl in nested lhs"); } + let name = MacroRulesNormalizedIdent::new(name); if let Some(prev_info) = get_binder_info(macros, binders, name) { // Duplicate binders at the top-level macro definition are errors. The lint is only // for nested macro definitions. @@ -300,7 +302,7 @@ fn check_binders( fn get_binder_info<'a>( mut macros: &'a Stack<'a, MacroState<'a>>, binders: &'a Binders, - name: Ident, + name: MacroRulesNormalizedIdent, ) -> Option<&'a BinderInfo> { binders.get(&name).or_else(|| macros.find_map(|state| state.binders.get(&name))) } @@ -331,6 +333,7 @@ fn check_occurrences( sess.span_diagnostic.span_bug(span, "unexpected MetaVarDecl in rhs") } TokenTree::MetaVar(span, name) => { + let name = MacroRulesNormalizedIdent::new(name); check_ops_is_prefix(sess, node_id, macros, binders, ops, span, name); } TokenTree::Delimited(_, ref del) => { @@ -419,10 +422,10 @@ fn check_nested_occurrences( | (NestedMacroState::MacroName, &TokenTree::Delimited(_, ref del)) if del.delim == DelimToken::Brace => { - let legacy = state == NestedMacroState::MacroRulesNotName; + let macro_rules = state == NestedMacroState::MacroRulesNotName; state = NestedMacroState::Empty; let rest = - check_nested_macro(sess, node_id, legacy, &del.tts, &nested_macros, valid); + check_nested_macro(sess, node_id, macro_rules, &del.tts, &nested_macros, valid); // If we did not check the whole macro definition, then check the rest as if outside // the macro definition. check_nested_occurrences( @@ -493,21 +496,21 @@ fn check_nested_occurrences( /// Arguments: /// - `sess` is used to emit diagnostics and lints /// - `node_id` is used to emit lints -/// - `legacy` specifies whether the macro is legacy +/// - `macro_rules` specifies whether the macro is `macro_rules` /// - `tts` is checked as a list of (LHS) => {RHS} /// - `macros` is the stack of outer macros /// - `valid` is set in case of errors fn check_nested_macro( sess: &ParseSess, node_id: NodeId, - legacy: bool, + macro_rules: bool, tts: &[TokenTree], macros: &Stack<'_, MacroState<'_>>, valid: &mut bool, ) -> usize { let n = tts.len(); let mut i = 0; - let separator = if legacy { TokenKind::Semi } else { TokenKind::Comma }; + let separator = if macro_rules { TokenKind::Semi } else { TokenKind::Comma }; loop { // We expect 3 token trees: `(LHS) => {RHS}`. The separator is checked after. if i + 2 >= n @@ -522,7 +525,7 @@ fn check_nested_macro( let mut binders = Binders::default(); check_binders(sess, node_id, lhs, macros, &mut binders, &Stack::Empty, valid); check_occurrences(sess, node_id, rhs, macros, &binders, &Stack::Empty, valid); - // Since the last semicolon is optional for legacy macros and decl_macro are not terminated, + // Since the last semicolon is optional for `macro_rules` macros and decl_macro are not terminated, // we increment our checked position by how many token trees we already checked (the 3 // above) before checking for the separator. i += 3; @@ -552,7 +555,7 @@ fn check_ops_is_prefix( binders: &Binders, ops: &Stack<'_, KleeneToken>, span: Span, - name: Ident, + name: MacroRulesNormalizedIdent, ) { let macros = macros.push(MacroState { binders, ops: ops.into() }); // Accumulates the stacks the operators of each state until (and including when) the @@ -598,7 +601,7 @@ fn ops_is_prefix( sess: &ParseSess, node_id: NodeId, span: Span, - name: Ident, + name: MacroRulesNormalizedIdent, binder_ops: &[KleeneToken], occurrence_ops: &[KleeneToken], ) { @@ -623,5 +626,8 @@ fn ops_is_prefix( } fn buffer_lint(sess: &ParseSess, span: MultiSpan, node_id: NodeId, message: &str) { - sess.buffer_lint(&META_VARIABLE_MISUSE, span, node_id, message); + // Macros loaded from other crates have dummy node ids. + if node_id != DUMMY_NODE_ID { + sess.buffer_lint(&META_VARIABLE_MISUSE, span, node_id, message); + } } diff --git a/src/librustc_expand/mbe/macro_parser.rs b/src/librustc_expand/mbe/macro_parser.rs index 6d4d7f5b4f394..968f7c8e273a3 100644 --- a/src/librustc_expand/mbe/macro_parser.rs +++ b/src/librustc_expand/mbe/macro_parser.rs @@ -76,15 +76,14 @@ use TokenTreeOrTokenTreeSlice::*; use crate::mbe::{self, TokenTree}; -use rustc_ast::ast::{Ident, Name}; use rustc_ast::ptr::P; use rustc_ast::token::{self, DocComment, Nonterminal, Token}; use rustc_ast_pretty::pprust; use rustc_parse::parser::{FollowedByType, Parser, PathStyle}; use rustc_session::parse::ParseSess; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent, Symbol}; -use rustc_errors::{FatalError, PResult}; +use rustc_errors::PResult; use rustc_span::Span; use smallvec::{smallvec, SmallVec}; @@ -271,11 +270,13 @@ crate enum ParseResult { Failure(Token, &'static str), /// Fatal error (malformed macro?). Abort compilation. Error(rustc_span::Span, String), + ErrorReported, } -/// A `ParseResult` where the `Success` variant contains a mapping of `Ident`s to `NamedMatch`es. -/// This represents the mapping of metavars to the token trees they bind to. -crate type NamedParseResult = ParseResult>; +/// A `ParseResult` where the `Success` variant contains a mapping of +/// `MacroRulesNormalizedIdent`s to `NamedMatch`es. This represents the mapping +/// of metavars to the token trees they bind to. +crate type NamedParseResult = ParseResult>; /// Count how many metavars are named in the given matcher `ms`. pub(super) fn count_names(ms: &[TokenTree]) -> usize { @@ -368,7 +369,7 @@ fn nameize>( sess: &ParseSess, m: &TokenTree, res: &mut I, - ret_val: &mut FxHashMap, + ret_val: &mut FxHashMap, ) -> Result<(), (rustc_span::Span, String)> { match *m { TokenTree::Sequence(_, ref seq) => { @@ -382,11 +383,13 @@ fn nameize>( } } TokenTree::MetaVarDecl(span, _, id) if id.name == kw::Invalid => { - if sess.missing_fragment_specifiers.borrow_mut().remove(&span) { + if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { return Err((span, "missing fragment specifier".to_string())); } } - TokenTree::MetaVarDecl(sp, bind_name, _) => match ret_val.entry(bind_name) { + TokenTree::MetaVarDecl(sp, bind_name, _) => match ret_val + .entry(MacroRulesNormalizedIdent::new(bind_name)) + { Vacant(spot) => { spot.insert(res.next().unwrap()); } @@ -563,7 +566,7 @@ fn inner_parse_loop<'root, 'tt>( // We need to match a metavar (but the identifier is invalid)... this is an error TokenTree::MetaVarDecl(span, _, id) if id.name == kw::Invalid => { - if sess.missing_fragment_specifiers.borrow_mut().remove(&span) { + if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { return Error(span, "missing fragment specifier".to_string()); } } @@ -584,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 }); @@ -649,6 +654,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na Success(_) => {} Failure(token, msg) => return Failure(token, msg), Error(sp, msg) => return Error(sp, msg), + ErrorReported => return ErrorReported, } // inner parse loop handled all cur_items, so it's empty @@ -732,10 +738,11 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na let mut item = bb_items.pop().unwrap(); if let TokenTree::MetaVarDecl(span, _, ident) = item.top_elts.get_tt(item.idx) { let match_cur = item.match_cur; - item.push_match( - match_cur, - MatchedNonterminal(Lrc::new(parse_nt(parser.to_mut(), span, ident.name))), - ); + let nt = match parse_nt(parser.to_mut(), span, ident.name) { + Err(()) => return ErrorReported, + Ok(nt) => nt, + }; + item.push_match(match_cur, MatchedNonterminal(Lrc::new(nt))); item.idx += 1; item.match_cur += 1; } else { @@ -758,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, } } @@ -775,7 +782,7 @@ fn may_begin_with(token: &Token, name: Name) -> bool { } sym::ty => token.can_begin_type(), sym::ident => get_macro_ident(token).is_some(), - sym::literal => token.can_begin_literal_or_bool(), + sym::literal => token.can_begin_literal_maybe_minus(), sym::vis => match token.kind { // The follow-set of :vis + "priv" keyword + interpolated token::Comma | token::Ident(..) | token::Interpolated(_) => true, @@ -846,27 +853,36 @@ fn may_begin_with(token: &Token, name: Name) -> bool { /// # Returns /// /// The parsed non-terminal. -fn parse_nt(p: &mut Parser<'_>, sp: Span, name: Symbol) -> Nonterminal { +fn parse_nt(p: &mut Parser<'_>, sp: Span, name: Symbol) -> Result { // FIXME(Centril): Consider moving this to `parser.rs` to make // the visibilities of the methods used below `pub(super)` at most. - if name == sym::tt { - return token::NtTT(p.parse_token_tree()); - } - match parse_nt_inner(p, sp, name) { - Ok(nt) => nt, - Err(mut err) => { - err.emit(); - FatalError.raise(); - } + return Ok(token::NtTT(p.parse_token_tree())); } + parse_nt_inner(p, sp, name).map_err(|mut err| { + err.span_label(sp, format!("while parsing argument for this `{}` macro fragment", name)) + .emit() + }) } fn parse_nt_inner<'a>(p: &mut Parser<'a>, sp: Span, name: Symbol) -> PResult<'a, Nonterminal> { + // Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`) + // needs to have them force-captured here. + // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, + // which requires having captured tokens available. Since we cannot determine + // in advance whether or not a proc-macro will be (transitively) invoked, + // we always capture tokens for any `Nonterminal` which needs them. Ok(match name { - sym::item => match p.parse_item()? { - Some(i) => token::NtItem(i), - None => return Err(p.struct_span_err(p.token.span, "expected an item keyword")), + sym::item => match p.collect_tokens(|this| this.parse_item())? { + (Some(mut item), tokens) => { + // If we captured tokens during parsing (due to outer attributes), + // use those. + if item.tokens.is_none() { + item.tokens = Some(tokens); + } + token::NtItem(item) + } + (None, _) => return Err(p.struct_span_err(p.token.span, "expected an item keyword")), }, sym::block => token::NtBlock(p.parse_block()?), sym::stmt => match p.parse_stmt()? { @@ -874,7 +890,15 @@ fn parse_nt_inner<'a>(p: &mut Parser<'a>, sp: Span, name: Symbol) -> PResult<'a, None => return Err(p.struct_span_err(p.token.span, "expected a statement")), }, sym::pat => token::NtPat(p.parse_pat(None)?), - sym::expr => token::NtExpr(p.parse_expr()?), + sym::expr => { + let (mut expr, tokens) = p.collect_tokens(|this| this.parse_expr())?; + // If we captured tokens during parsing (due to outer attributes), + // use those. + if expr.tokens.is_none() { + expr.tokens = Some(tokens); + } + token::NtExpr(expr) + } sym::literal => token::NtLiteral(p.parse_literal_maybe_minus()?), sym::ty => token::NtTy(p.parse_ty()?), // this could be handled like a token, since it is one diff --git a/src/librustc_expand/mbe/macro_rules.rs b/src/librustc_expand/mbe/macro_rules.rs index 3040a9aefbb30..8cdb5b09c9e8b 100644 --- a/src/librustc_expand/mbe/macro_rules.rs +++ b/src/librustc_expand/mbe/macro_rules.rs @@ -1,10 +1,10 @@ -use crate::base::{DummyResult, ExpansionData, ExtCtxt, MacResult, TTMacroExpander}; +use crate::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander}; use crate::base::{SyntaxExtension, SyntaxExtensionKind}; use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstFragmentKind}; use crate::mbe; use crate::mbe::macro_check; use crate::mbe::macro_parser::parse_tt; -use crate::mbe::macro_parser::{Error, Failure, Success}; +use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success}; use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq}; use crate::mbe::transcribe::transcribe; @@ -15,14 +15,13 @@ use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Applicability, DiagnosticBuilder, FatalError}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_feature::Features; use rustc_parse::parser::Parser; -use rustc_parse::Directory; use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent, Symbol}; use rustc_span::Span; use log::debug; @@ -40,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, } @@ -84,41 +83,73 @@ 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: Ident, + arm_span: Span, + kind: AstFragmentKind, +) { + if parser.token == token::Eof && e.message().ends_with(", found ``") { + if !e.span.is_dummy() { + // early end of macro arm (#52866) + e.replace_span_with(parser.sess.source_map().next_point(parser.token.span)); + } + let msg = &e.message[0]; + e.message[0] = ( + format!( + "macro expansion ends with an incomplete expression: {}", + msg.0.replace(", found ``", ""), + ), + msg.1, + ); + } + if e.span.is_dummy() { + // Get around lack of span in error (#30128) + e.replace_span_with(site_span); + if !parser.sess.source_map().is_imported(arm_span) { + e.span_label(arm_span, "in this macro arm"); + } + } else if parser.sess.source_map().is_imported(parser.token.span) { + e.span_label(site_span, "in this macro invocation"); + } + match kind { + 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(); +} + 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 fragment = panictry!(parse_ast_fragment(parser, kind).map_err(|mut e| { - if parser.token == token::Eof && e.message().ends_with(", found ``") { - if !e.span.is_dummy() { - // early end of macro arm (#52866) - e.replace_span_with(parser.sess.source_map().next_point(parser.token.span)); - } - let msg = &e.message[0]; - e.message[0] = ( - format!( - "macro expansion ends with an incomplete expression: {}", - msg.0.replace(", found ``", ""), - ), - msg.1, - ); - } - if e.span.is_dummy() { - // Get around lack of span in error (#30128) - e.replace_span_with(site_span); - if !parser.sess.source_map().is_imported(arm_span) { - e.span_label(arm_span, "in this macro arm"); - } - } else if parser.sess.source_map().is_imported(parser.token.span) { - e.span_label(site_span, "in this macro invocation"); + let snapshot = &mut parser.clone(); + let fragment = match parse_ast_fragment(parser, kind) { + Ok(f) => f, + Err(err) => { + emit_frag_parse_err(err, parser, snapshot, site_span, macro_ident, arm_span, kind); + return kind.dummy(site_span); } - match kind { - AstFragmentKind::Pat if macro_ident.name == sym::vec => { - suggest_slice_pat(&mut e, site_span, parser); - } - _ => annotate_err_with_kind(&mut e, kind, site_span), - }; - e - })); + }; // We allow semicolons at the end of expressions -- e.g., the semicolon in // `macro_rules! m { () => { panic!(); } }` isn't parsed by `.parse_expr()`, @@ -135,7 +166,7 @@ impl<'a> ParserAnyMacro<'a> { } struct MacroRulesMacroExpander { - name: ast::Ident, + name: Ident, span: Span, transparency: Transparency, lhses: Vec, @@ -166,6 +197,14 @@ impl TTMacroExpander for MacroRulesMacroExpander { } } +fn macro_rules_dummy_expander<'cx>( + _: &'cx mut ExtCtxt<'_>, + span: Span, + _: TokenStream, +) -> Box { + DummyResult::any(span) +} + fn trace_macros_note(cx_expansions: &mut FxHashMap>, sp: Span, message: String) { let sp = sp.macro_backtrace().last().map(|trace| trace.call_site).unwrap_or(sp); cx_expansions.entry(sp).or_default().push(message); @@ -176,12 +215,14 @@ 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], rhses: &[mbe::TokenTree], ) -> Box { + let sess = cx.parse_sess; + if cx.trace_macros() { let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(arg.clone())); trace_macros_note(&mut cx.expansions, sp, msg); @@ -209,7 +250,7 @@ fn generic_extension<'cx>( // hacky, but speeds up the `html5ever` benchmark significantly. (Issue // 68836 suggests a more comprehensive but more complex change to deal with // this situation.) - let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone()); + let parser = parser_from_cx(sess, arg.clone()); for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers @@ -222,14 +263,13 @@ fn generic_extension<'cx>( // This is used so that if a matcher is not `Success(..)`ful, // then the spans which became gated when parsing the unsuccessful matcher // are not recorded. On the first `Success(..)`ful matcher, the spans are merged. - let mut gated_spans_snapshot = - mem::take(&mut *cx.parse_sess.gated_spans.spans.borrow_mut()); + let mut gated_spans_snapshot = mem::take(&mut *sess.gated_spans.spans.borrow_mut()); match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) { Success(named_matches) => { // The matcher was `Success(..)`ful. // Merge the gated spans from parsing the matcher with the pre-existing ones. - cx.parse_sess.gated_spans.merge(gated_spans_snapshot); + sess.gated_spans.merge(gated_spans_snapshot); let rhs = match rhses[i] { // ignore delimiters @@ -240,7 +280,13 @@ fn generic_extension<'cx>( let rhs_spans = rhs.iter().map(|t| t.span()).collect::>(); // rhs has holes ( `$id` and `$(...)` that need filled) - let mut tts = transcribe(cx, &named_matches, rhs, transparency); + let mut tts = match transcribe(cx, &named_matches, rhs, transparency) { + Ok(tts) => tts, + Err(mut err) => { + err.emit(); + return DummyResult::any(arm_span); + } + }; // Replace all the tokens for the corresponding positions in the macro, to maintain // proper positions in error reporting, while maintaining the macro_backtrace. @@ -258,13 +304,7 @@ fn generic_extension<'cx>( trace_macros_note(&mut cx.expansions, sp, msg); } - let directory = Directory { - path: cx.current_expansion.module.directory.clone(), - ownership: cx.current_expansion.directory_ownership, - }; - let mut p = Parser::new(cx.parse_sess(), tts, Some(directory), true, false, None); - p.root_module_name = - cx.current_expansion.module.mod_path.last().map(|id| id.to_string()); + let mut p = Parser::new(sess, tts, false, None); p.last_type_ascription = cx.current_expansion.prior_type_ascription; // Let the context choose how to interpret the result. @@ -284,12 +324,17 @@ fn generic_extension<'cx>( Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {} _ => best_failure = Some((token, msg)), }, - Error(err_sp, ref msg) => cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..]), + Error(err_sp, ref msg) => { + let span = err_sp.substitute_dummy(sp); + cx.struct_span_err(span, &msg).emit(); + return DummyResult::any(span); + } + ErrorReported => return DummyResult::any(sp), } // The matcher was not `Success(..)`ful. // Restore to the state before snapshotting and maybe try again. - mem::swap(&mut gated_spans_snapshot, &mut cx.parse_sess.gated_spans.spans.borrow_mut()); + mem::swap(&mut gated_spans_snapshot, &mut sess.gated_spans.spans.borrow_mut()); } drop(parser); @@ -298,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);` @@ -309,21 +354,19 @@ fn generic_extension<'cx>( mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..], _ => continue, }; - let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone()); - match parse_tt(&mut Cow::Borrowed(&parser), 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, + ); } - _ => {} } } } @@ -344,14 +387,26 @@ pub fn compile_declarative_macro( def: &ast::Item, edition: Edition, ) -> SyntaxExtension { + let mk_syn_ext = |expander| { + SyntaxExtension::new( + sess, + SyntaxExtensionKind::LegacyBang(expander), + def.span, + Vec::new(), + edition, + def.ident.name, + &def.attrs, + ) + }; + 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 (is_legacy, body) = match &def.kind { - ast::ItemKind::MacroDef(macro_def) => (macro_def.legacy, macro_def.body.inner_tokens()), + let (macro_rules, body) = match &def.kind { + ast::ItemKind::MacroDef(def) => (def.macro_rules, def.body.inner_tokens()), _ => unreachable!(), }; @@ -370,7 +425,7 @@ pub fn compile_declarative_macro( mbe::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec), ], separator: Some(Token::new( - if is_legacy { token::Semi } else { token::Comma }, + if macro_rules { token::Semi } else { token::Comma }, def.span, )), kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, def.span), @@ -382,7 +437,7 @@ pub fn compile_declarative_macro( DelimSpan::dummy(), Lrc::new(mbe::SequenceRepetition { tts: vec![mbe::TokenTree::token( - if is_legacy { token::Semi } else { token::Comma }, + if macro_rules { token::Semi } else { token::Comma }, def.span, )], separator: None, @@ -392,32 +447,36 @@ pub fn compile_declarative_macro( ), ]; - let parser = Parser::new(sess, body, None, true, true, rustc_parse::MACRO_ARGUMENTS); + let parser = Parser::new(sess, body, true, rustc_parse::MACRO_ARGUMENTS); let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) { Success(m) => m, Failure(token, msg) => { let s = parse_failure_msg(&token); let sp = token.span.substitute_dummy(def.span); - let mut err = sess.span_diagnostic.struct_span_fatal(sp, &s); - err.span_label(sp, msg); - err.emit(); - FatalError.raise(); + sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit(); + return mk_syn_ext(Box::new(macro_rules_dummy_expander)); } - Error(sp, s) => { - sess.span_diagnostic.span_fatal(sp.substitute_dummy(def.span), &s).raise(); + Error(sp, msg) => { + sess.span_diagnostic.struct_span_err(sp.substitute_dummy(def.span), &msg).emit(); + return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + } + ErrorReported => { + return mk_syn_ext(Box::new(macro_rules_dummy_expander)); } }; let mut valid = true; // Extract the arguments: - let lhses = match argument_map[&lhs_nm] { + let lhses = match argument_map[&MacroRulesNormalizedIdent::new(lhs_nm)] { MatchedSeq(ref s) => s .iter() .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = mbe::quoted::parse(tt.clone().into(), true, sess).pop().unwrap(); + let tt = mbe::quoted::parse(tt.clone().into(), true, sess, def.id) + .pop() + .unwrap(); valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt); return tt; } @@ -428,13 +487,15 @@ pub fn compile_declarative_macro( _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"), }; - let rhses = match argument_map[&rhs_nm] { + let rhses = match argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] { MatchedSeq(ref s) => s .iter() .map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - return mbe::quoted::parse(tt.clone().into(), false, sess).pop().unwrap(); + return mbe::quoted::parse(tt.clone().into(), false, sess, def.id) + .pop() + .unwrap(); } } sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") @@ -452,11 +513,9 @@ pub fn compile_declarative_macro( valid &= check_lhs_no_empty_seq(sess, slice::from_ref(lhs)); } - // We use CRATE_NODE_ID instead of `def.id` otherwise we may emit buffered lints for a node id - // that is not lint-checked and trigger the "failed to process buffered lint here" bug. - valid &= macro_check::check_meta_variables(sess, ast::CRATE_NODE_ID, def.span, &lhses, &rhses); + valid &= macro_check::check_meta_variables(sess, def.id, def.span, &lhses, &rhses); - let (transparency, transparency_error) = attr::find_transparency(&def.attrs, is_legacy); + let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules); match transparency_error { Some(TransparencyError::UnknownTransparency(value, span)) => { diag.span_err(span, &format!("unknown macro transparency: `{}`", value)) @@ -467,24 +526,14 @@ pub fn compile_declarative_macro( None => {} } - let expander: Box<_> = Box::new(MacroRulesMacroExpander { + mk_syn_ext(Box::new(MacroRulesMacroExpander { name: def.ident, span: def.span, transparency, lhses, rhses, valid, - }); - - SyntaxExtension::new( - sess, - SyntaxExtensionKind::LegacyBang(expander), - def.span, - Vec::new(), - edition, - def.ident.name, - &def.attrs, - ) + })) } fn check_lhs_nt_follows( @@ -1209,16 +1258,8 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { } } -fn parser_from_cx<'cx>( - current_expansion: &'cx ExpansionData, - sess: &'cx ParseSess, - tts: TokenStream, -) -> Parser<'cx> { - let directory = Directory { - path: current_expansion.module.directory.clone(), - ownership: current_expansion.directory_ownership, - }; - Parser::new(sess, tts, Some(directory), true, true, rustc_parse::MACRO_ARGUMENTS) +fn parser_from_cx(sess: &ParseSess, tts: TokenStream) -> Parser<'_> { + Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS) } /// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For diff --git a/src/librustc_expand/mbe/quoted.rs b/src/librustc_expand/mbe/quoted.rs index b19fae6559704..de66c2ada40e6 100644 --- a/src/librustc_expand/mbe/quoted.rs +++ b/src/librustc_expand/mbe/quoted.rs @@ -1,12 +1,12 @@ use crate::mbe::macro_parser; use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree}; -use rustc_ast::ast; +use rustc_ast::ast::{NodeId, DUMMY_NODE_ID}; 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; @@ -37,6 +37,7 @@ pub(super) fn parse( input: tokenstream::TokenStream, expect_matchers: bool, sess: &ParseSess, + node_id: NodeId, ) -> Vec { // Will contain the final collection of `self::TokenTree` let mut result = Vec::new(); @@ -47,7 +48,7 @@ pub(super) fn parse( while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, expect_matchers, sess); + let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id); match tree { TokenTree::MetaVar(start_sp, ident) if expect_matchers => { let span = match trees.next() { @@ -66,8 +67,11 @@ 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())); + if node_id != DUMMY_NODE_ID { + // Macros loaded from other crates have dummy node ids. + sess.missing_fragment_specifiers.borrow_mut().insert(span, node_id); + } + result.push(TokenTree::MetaVarDecl(span, ident, Ident::invalid())); } // Not a metavar or no matchers allowed, so just return the tree @@ -97,6 +101,7 @@ fn parse_tree( trees: &mut impl Iterator, expect_matchers: bool, sess: &ParseSess, + node_id: NodeId, ) -> TokenTree { // Depending on what `tree` is, we could be parsing different parts of a macro match tree { @@ -112,7 +117,7 @@ fn parse_tree( sess.span_diagnostic.span_err(span.entire(), &msg); } // Parse the contents of the sequence itself - let sequence = parse(tts, expect_matchers, sess); + let sequence = parse(tts, expect_matchers, sess, node_id); // Get the Kleene operator and optional separator let (separator, kleene) = parse_sep_and_kleene_op(trees, span.entire(), sess); // Count the number of captured "names" (i.e., named metavars) @@ -145,7 +150,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. @@ -159,7 +164,7 @@ fn parse_tree( // descend into the delimited set and further parse it. tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( span, - Lrc::new(Delimited { delim, tts: parse(tts, expect_matchers, sess) }), + Lrc::new(Delimited { delim, tts: parse(tts, expect_matchers, sess, node_id) }), ), } } diff --git a/src/librustc_expand/mbe/transcribe.rs b/src/librustc_expand/mbe/transcribe.rs index 7a64d40785e09..e2d3d5c4d644e 100644 --- a/src/librustc_expand/mbe/transcribe.rs +++ b/src/librustc_expand/mbe/transcribe.rs @@ -2,14 +2,15 @@ use crate::base::ExtCtxt; use crate::mbe; use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch}; -use rustc_ast::ast::{Ident, MacCall}; +use rustc_ast::ast::MacCall; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, NtTT, Token}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; -use rustc_errors::pluralize; +use rustc_errors::{pluralize, PResult}; use rustc_span::hygiene::{ExpnId, Transparency}; +use rustc_span::symbol::MacroRulesNormalizedIdent; use rustc_span::Span; use smallvec::{smallvec, SmallVec}; @@ -79,15 +80,15 @@ impl Iterator for Frame { /// `transcribe` would return a `TokenStream` containing `println!("{}", stringify!(bar));`. /// /// Along the way, we do some additional error checking. -pub(super) fn transcribe( - cx: &ExtCtxt<'_>, - interp: &FxHashMap, +pub(super) fn transcribe<'a>( + cx: &ExtCtxt<'a>, + interp: &FxHashMap, src: Vec, transparency: Transparency, -) -> TokenStream { +) -> PResult<'a, TokenStream> { // Nothing for us to transcribe... if src.is_empty() { - return TokenStream::default(); + return Ok(TokenStream::default()); } // We descend into the RHS (`src`), expanding things as we go. This stack contains the things @@ -151,7 +152,7 @@ pub(super) fn transcribe( Frame::Delimited { forest, span, .. } => { if result_stack.is_empty() { // No results left to compute! We are back at the top-level. - return TokenStream::new(result); + return Ok(TokenStream::new(result)); } // Step back into the parent Delimited. @@ -172,11 +173,11 @@ pub(super) fn transcribe( seq @ mbe::TokenTree::Sequence(..) => { match lockstep_iter_size(&seq, interp, &repeats) { LockstepIterSize::Unconstrained => { - cx.span_fatal( + return Err(cx.struct_span_err( seq.span(), /* blame macro writer */ "attempted to repeat an expression containing no syntax variables \ matched as repeating at this depth", - ); + )); } LockstepIterSize::Contradiction(ref msg) => { @@ -184,7 +185,7 @@ pub(super) fn transcribe( // happens when two meta-variables are used in the same repetition in a // sequence, but they come from different sequence matchers and repeat // different amounts. - cx.span_fatal(seq.span(), &msg[..]); + return Err(cx.struct_span_err(seq.span(), &msg[..])); } LockstepIterSize::Constraint(len, _) => { @@ -202,7 +203,10 @@ pub(super) fn transcribe( // FIXME: this really ought to be caught at macro definition // time... It happens when the Kleene operator in the matcher and // the body for the same meta-variable do not match. - cx.span_fatal(sp.entire(), "this must repeat at least once"); + return Err(cx.struct_span_err( + sp.entire(), + "this must repeat at least once", + )); } } else { // 0 is the initial counter (we have done 0 repretitions so far). `len` @@ -223,9 +227,10 @@ pub(super) fn transcribe( } // Replace the meta-var with the matched token tree from the invocation. - mbe::TokenTree::MetaVar(mut sp, mut ident) => { + mbe::TokenTree::MetaVar(mut sp, mut orignal_ident) => { // Find the matched nonterminal from the macro invocation, and use it to replace // the meta-var. + let ident = MacroRulesNormalizedIdent::new(orignal_ident); if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { if let MatchedNonterminal(ref nt) = cur_matched { // FIXME #2887: why do we apply a mark when matching a token tree meta-var @@ -240,18 +245,18 @@ pub(super) fn transcribe( } } else { // We were unable to descend far enough. This is an error. - cx.span_fatal( + return Err(cx.struct_span_err( sp, /* blame the macro writer */ &format!("variable '{}' is still repeating at this depth", ident), - ); + )); } } else { // If we aren't able to match the meta-var, we push it back into the result but // with modified syntax context. (I believe this supports nested macros). marker.visit_span(&mut sp); - marker.visit_ident(&mut ident); + marker.visit_ident(&mut orignal_ident); result.push(TokenTree::token(token::Dollar, sp).into()); - result.push(TokenTree::Token(Token::from_ast_ident(ident)).into()); + result.push(TokenTree::Token(Token::from_ast_ident(orignal_ident)).into()); } } @@ -287,8 +292,8 @@ pub(super) fn transcribe( /// into the right place in nested matchers. If we attempt to descend too far, the macro writer has /// made a mistake, and we return `None`. fn lookup_cur_matched<'a>( - ident: Ident, - interpolations: &'a FxHashMap, + ident: MacroRulesNormalizedIdent, + interpolations: &'a FxHashMap, repeats: &[(usize, usize)], ) -> Option<&'a NamedMatch> { interpolations.get(&ident).map(|matched| { @@ -316,7 +321,7 @@ enum LockstepIterSize { /// A `MetaVar` with an actual `MatchedSeq`. The length of the match and the name of the /// meta-var are returned. - Constraint(usize, Ident), + Constraint(usize, MacroRulesNormalizedIdent), /// Two `Constraint`s on the same sequence had different lengths. This is an error. Contradiction(String), @@ -360,7 +365,7 @@ impl LockstepIterSize { /// multiple nested matcher sequences. fn lockstep_iter_size( tree: &mbe::TokenTree, - interpolations: &FxHashMap, + interpolations: &FxHashMap, repeats: &[(usize, usize)], ) -> LockstepIterSize { use mbe::TokenTree; @@ -376,6 +381,7 @@ fn lockstep_iter_size( }) } TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => { + let name = MacroRulesNormalizedIdent::new(name); match lookup_cur_matched(name, interpolations, repeats) { Some(matched) => match matched { MatchedNonterminal(_) => LockstepIterSize::Unconstrained, diff --git a/src/librustc_expand/module.rs b/src/librustc_expand/module.rs new file mode 100644 index 0000000000000..535c1dbad04a9 --- /dev/null +++ b/src/librustc_expand/module.rs @@ -0,0 +1,306 @@ +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, Ident}; + +use std::path::{self, Path, PathBuf}; + +#[derive(Clone)] +pub struct Directory { + pub path: PathBuf, + pub ownership: DirectoryOwnership, +} + +#[derive(Copy, Clone)] +pub enum DirectoryOwnership { + Owned { + // None if `mod.rs`, `Some("foo")` if we're in `foo.rs`. + relative: Option, + }, + UnownedViaBlock, + UnownedViaMod, +} + +/// Information about the path to a module. +// Public for rustfmt usage. +pub struct ModulePath<'a> { + name: String, + path_exists: bool, + pub result: PResult<'a, ModulePathSuccess>, +} + +// Public for rustfmt usage. +pub struct ModulePathSuccess { + pub path: PathBuf, + pub ownership: DirectoryOwnership, +} + +crate fn parse_external_mod( + sess: &ParseSess, + id: Ident, + span: Span, // The span to blame on errors. + Directory { mut ownership, path }: Directory, + attrs: &mut Vec, + pop_mod_stack: &mut bool, +) -> (Mod, Directory) { + // We bail on the first error, but that error does not cause a fatal error... (1) + let result: PResult<'_, _> = try { + // Extract the file path and the new ownership. + let mp = submod_path(sess, id, span, &attrs, ownership, &path)?; + ownership = mp.ownership; + + // Ensure file paths are acyclic. + let mut included_mod_stack = sess.included_mod_stack.borrow_mut(); + error_on_circular_module(sess, span, &mp.path, &included_mod_stack)?; + included_mod_stack.push(mp.path.clone()); + *pop_mod_stack = true; // We have pushed, so notify caller. + drop(included_mod_stack); + + // Actually parse the external file as a module. + let mut module = new_parser_from_file(sess, &mp.path, Some(span)).parse_mod(&token::Eof)?; + module.0.inline = false; + module + }; + // (1) ...instead, we return a dummy module. + let (module, mut new_attrs) = result.map_err(|mut err| err.emit()).unwrap_or_default(); + attrs.append(&mut new_attrs); + + // Extract the directory path for submodules of `module`. + let path = sess.source_map().span_to_unmapped_path(module.inner); + let mut path = match path { + FileName::Real(name) => name.into_local_path(), + other => PathBuf::from(other.to_string()), + }; + path.pop(); + + (module, Directory { ownership, path }) +} + +fn error_on_circular_module<'a>( + sess: &'a ParseSess, + span: Span, + path: &Path, + included_mod_stack: &[PathBuf], +) -> PResult<'a, ()> { + if let Some(i) = included_mod_stack.iter().position(|p| *p == path) { + let mut err = String::from("circular modules: "); + for p in &included_mod_stack[i..] { + err.push_str(&p.to_string_lossy()); + err.push_str(" -> "); + } + err.push_str(&path.to_string_lossy()); + return Err(sess.span_diagnostic.struct_span_err(span, &err[..])); + } + Ok(()) +} + +crate fn push_directory( + id: Ident, + attrs: &[Attribute], + Directory { mut ownership, mut path }: Directory, +) -> Directory { + if let Some(filename) = attr::first_attr_value_str_by_name(attrs, sym::path) { + path.push(&*filename.as_str()); + ownership = DirectoryOwnership::Owned { relative: None }; + } else { + // We have to push on the current module name in the case of relative + // paths in order to ensure that any additional module paths from inline + // `mod x { ... }` come after the relative extension. + // + // For example, a `mod z { ... }` inside `x/y.rs` should set the current + // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`. + if let DirectoryOwnership::Owned { relative } = &mut ownership { + if let Some(ident) = relative.take() { + // Remove the relative offset. + path.push(&*ident.as_str()); + } + } + path.push(&*id.as_str()); + } + Directory { ownership, path } +} + +fn submod_path<'a>( + sess: &'a ParseSess, + id: Ident, + span: Span, + attrs: &[Attribute], + ownership: DirectoryOwnership, + dir_path: &Path, +) -> PResult<'a, ModulePathSuccess> { + if let Some(path) = submod_path_from_attr(attrs, dir_path) { + let ownership = match path.file_name().and_then(|s| s.to_str()) { + // All `#[path]` files are treated as though they are a `mod.rs` file. + // This means that `mod foo;` declarations inside `#[path]`-included + // files are siblings, + // + // Note that this will produce weirdness when a file named `foo.rs` is + // `#[path]` included and contains a `mod foo;` declaration. + // If you encounter this, it's your own darn fault :P + Some(_) => DirectoryOwnership::Owned { relative: None }, + _ => DirectoryOwnership::UnownedViaMod, + }; + return Ok(ModulePathSuccess { ownership, path }); + } + + let relative = match ownership { + DirectoryOwnership::Owned { relative } => relative, + DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None, + }; + let ModulePath { path_exists, name, result } = + default_submod_path(sess, id, span, relative, dir_path); + match ownership { + DirectoryOwnership::Owned { .. } => Ok(result?), + DirectoryOwnership::UnownedViaBlock => { + let _ = result.map_err(|mut err| err.cancel()); + error_decl_mod_in_block(sess, span, path_exists, &name) + } + DirectoryOwnership::UnownedViaMod => { + let _ = result.map_err(|mut err| err.cancel()); + error_cannot_declare_mod_here(sess, span, path_exists, &name) + } + } +} + +fn error_decl_mod_in_block<'a, T>( + sess: &'a ParseSess, + span: Span, + path_exists: bool, + name: &str, +) -> PResult<'a, T> { + let msg = "Cannot declare a non-inline module inside a block unless it has a path attribute"; + let mut err = sess.span_diagnostic.struct_span_err(span, msg); + if path_exists { + let msg = format!("Maybe `use` the module `{}` instead of redeclaring it", name); + err.span_note(span, &msg); + } + Err(err) +} + +fn error_cannot_declare_mod_here<'a, T>( + sess: &'a ParseSess, + span: Span, + path_exists: bool, + name: &str, +) -> PResult<'a, T> { + let mut err = + sess.span_diagnostic.struct_span_err(span, "cannot declare a new module at this location"); + if !span.is_dummy() { + if let FileName::Real(src_name) = sess.source_map().span_to_filename(span) { + let src_path = src_name.into_local_path(); + if let Some(stem) = src_path.file_stem() { + let mut dest_path = src_path.clone(); + dest_path.set_file_name(stem); + dest_path.push("mod.rs"); + err.span_note( + span, + &format!( + "maybe move this module `{}` to its own directory via `{}`", + src_path.display(), + dest_path.display() + ), + ); + } + } + } + if path_exists { + err.span_note( + span, + &format!("... or maybe `use` the module `{}` instead of possibly redeclaring it", name), + ); + } + Err(err) +} + +/// Derive a submodule path from the first found `#[path = "path_string"]`. +/// The provided `dir_path` is joined with the `path_string`. +// Public for rustfmt usage. +pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option { + // Extract path string from first `#[path = "path_string"]` attribute. + let path_string = attr::first_attr_value_str_by_name(attrs, sym::path)?; + let path_string = path_string.as_str(); + + // On windows, the base path might have the form + // `\\?\foo\bar` in which case it does not tolerate + // mixed `/` and `\` separators, so canonicalize + // `/` to `\`. + #[cfg(windows)] + let path_string = path_string.replace("/", "\\"); + + Some(dir_path.join(&*path_string)) +} + +/// Returns a path to a module. +// Public for rustfmt usage. +pub fn default_submod_path<'a>( + sess: &'a ParseSess, + id: Ident, + span: Span, + relative: Option, + dir_path: &Path, +) -> ModulePath<'a> { + // If we're in a foo.rs file instead of a mod.rs file, + // we need to look for submodules in + // `./foo/.rs` and `./foo//mod.rs` rather than + // `./.rs` and `.//mod.rs`. + let relative_prefix_string; + let relative_prefix = if let Some(ident) = relative { + relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR); + &relative_prefix_string + } else { + "" + }; + + let mod_name = id.name.to_string(); + let default_path_str = format!("{}{}.rs", relative_prefix, mod_name); + let secondary_path_str = + format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR); + let default_path = dir_path.join(&default_path_str); + let secondary_path = dir_path.join(&secondary_path_str); + let default_exists = sess.source_map().file_exists(&default_path); + let secondary_exists = sess.source_map().file_exists(&secondary_path); + + let result = match (default_exists, secondary_exists) { + (true, false) => Ok(ModulePathSuccess { + path: default_path, + ownership: DirectoryOwnership::Owned { relative: Some(id) }, + }), + (false, true) => Ok(ModulePathSuccess { + path: secondary_path, + ownership: DirectoryOwnership::Owned { relative: None }, + }), + (false, false) => { + let mut err = struct_span_err!( + sess.span_diagnostic, + span, + E0583, + "file not found for module `{}`", + mod_name, + ); + err.help(&format!( + "to create the module `{}`, create file \"{}\"", + mod_name, + default_path.display(), + )); + Err(err) + } + (true, true) => { + let mut err = struct_span_err!( + sess.span_diagnostic, + span, + E0761, + "file for module `{}` found at both {} and {}", + mod_name, + default_path_str, + secondary_path_str, + ); + err.help("delete or rename one of them to remove the ambiguity"); + Err(err) + } + }; + + ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result } +} 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..b4ffd714feffa 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; @@ -33,6 +34,7 @@ pub fn placeholder( span, attrs: ast::AttrVec::new(), kind: ast::ExprKind::MacCall(mac_placeholder()), + tokens: None, }) }; let ty = || P(ast::Ty { id, kind: ast::TyKind::MacCall(mac_placeholder()), span }); diff --git a/src/librustc_expand/proc_macro.rs b/src/librustc_expand/proc_macro.rs index 84a546345bb28..df7bf9438c3d0 100644 --- a/src/librustc_expand/proc_macro.rs +++ b/src/librustc_expand/proc_macro.rs @@ -5,7 +5,7 @@ use rustc_ast::ast::{self, ItemKind, MetaItemKind, NestedMetaItem}; use rustc_ast::token; use rustc_ast::tokenstream::{self, TokenStream}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Applicability, FatalError}; +use rustc_errors::{Applicability, ErrorReported}; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; @@ -21,21 +21,16 @@ impl base::ProcMacro for BangProcMacro { ecx: &'cx mut ExtCtxt<'_>, span: Span, input: TokenStream, - ) -> TokenStream { + ) -> Result { let server = proc_macro_server::Rustc::new(ecx); - match self.client.run(&EXEC_STRATEGY, server, input) { - Ok(stream) => stream, - Err(e) => { - let msg = "proc macro panicked"; - let mut err = ecx.struct_span_fatal(span, msg); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); - } - - err.emit(); - FatalError.raise(); + self.client.run(&EXEC_STRATEGY, server, input).map_err(|e| { + let mut err = ecx.struct_span_err(span, "proc macro panicked"); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); } - } + err.emit(); + ErrorReported + }) } } @@ -50,21 +45,16 @@ impl base::AttrProcMacro for AttrProcMacro { span: Span, annotation: TokenStream, annotated: TokenStream, - ) -> TokenStream { + ) -> Result { let server = proc_macro_server::Rustc::new(ecx); - match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) { - Ok(stream) => stream, - Err(e) => { - let msg = "custom attribute panicked"; - let mut err = ecx.struct_span_fatal(span, msg); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); - } - - err.emit(); - FatalError.raise(); + self.client.run(&EXEC_STRATEGY, server, annotation, annotated).map_err(|e| { + let mut err = ecx.struct_span_err(span, "custom attribute panicked"); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); } - } + err.emit(); + ErrorReported + }) } } @@ -79,7 +69,7 @@ impl MultiItemModifier for ProcMacroDerive { span: Span, _meta_item: &ast::MetaItem, item: Annotatable, - ) -> Vec { + ) -> ExpandResult, Annotatable> { let item = match item { Annotatable::Arm(..) | Annotatable::Field(..) @@ -96,10 +86,9 @@ impl MultiItemModifier for ProcMacroDerive { | Annotatable::Expr(_) => { ecx.span_err( span, - "proc-macro derives may only be \ - applied to a struct, enum, or union", + "proc-macro derives may only be applied to a struct, enum, or union", ); - return Vec::new(); + return ExpandResult::Ready(Vec::new()); } }; match item.kind { @@ -107,10 +96,9 @@ impl MultiItemModifier for ProcMacroDerive { _ => { ecx.span_err( span, - "proc-macro derives may only be \ - applied to a struct, enum, or union", + "proc-macro derives may only be applied to a struct, enum, or union", ); - return Vec::new(); + return ExpandResult::Ready(Vec::new()); } } @@ -121,20 +109,16 @@ impl MultiItemModifier for ProcMacroDerive { let stream = match self.client.run(&EXEC_STRATEGY, server, input) { Ok(stream) => stream, Err(e) => { - let msg = "proc-macro derive panicked"; - let mut err = ecx.struct_span_fatal(span, msg); + let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); if let Some(s) = e.as_str() { err.help(&format!("message: {}", s)); } - err.emit(); - FatalError.raise(); + return ExpandResult::Ready(vec![]); } }; let error_count_before = ecx.parse_sess.span_diagnostic.err_count(); - let msg = "proc-macro derive produced unparseable tokens"; - let mut parser = rustc_parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive")); let mut items = vec![]; @@ -144,21 +128,18 @@ impl MultiItemModifier for ProcMacroDerive { Ok(None) => break, Ok(Some(item)) => items.push(Annotatable::Item(item)), Err(mut err) => { - // FIXME: handle this better - err.cancel(); - ecx.struct_span_fatal(span, msg).emit(); - FatalError.raise(); + err.emit(); + break; } } } // fail if there have been errors emitted if ecx.parse_sess.span_diagnostic.err_count() > error_count_before { - ecx.struct_span_fatal(span, msg).emit(); - FatalError.raise(); + ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit(); } - items + ExpandResult::Ready(items) } } diff --git a/src/librustc_expand/proc_macro_server.rs b/src/librustc_expand/proc_macro_server.rs index 5f21ff503d59e..ea55674045c0f 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 { @@ -351,6 +352,7 @@ pub(crate) struct Rustc<'a> { def_site: Span, call_site: Span, mixed_site: Span, + span_debug: bool, } impl<'a> Rustc<'a> { @@ -361,6 +363,7 @@ impl<'a> Rustc<'a> { def_site: cx.with_def_site_ctxt(expn_data.def_site), call_site: cx.with_call_site_ctxt(expn_data.call_site), mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site), + span_debug: cx.ecfg.span_debug, } } @@ -495,7 +498,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 +509,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) @@ -574,10 +582,10 @@ impl server::Literal for Rustc<'_> { }; // Bounds check the values, preventing addition overflow and OOB spans. - if start > u32::max_value() as usize - || end > u32::max_value() as usize - || (u32::max_value() - start as u32) < span.lo().to_u32() - || (u32::max_value() - end as u32) < span.lo().to_u32() + if start > u32::MAX as usize + || end > u32::MAX as usize + || (u32::MAX - start as u32) < span.lo().to_u32() + || (u32::MAX - end as u32) < span.lo().to_u32() || start >= end || end > length { @@ -596,7 +604,8 @@ impl server::SourceFile for Rustc<'_> { } fn path(&mut self, file: &Self::SourceFile) -> String { match file.name { - FileName::Real(ref path) => path + FileName::Real(ref name) => name + .local_path() .to_str() .expect("non-UTF8 file path in `proc_macro::SourceFile::path`") .to_string(), @@ -639,7 +648,11 @@ impl server::Diagnostic for Rustc<'_> { impl server::Span for Rustc<'_> { fn debug(&mut self, span: Self::Span) -> String { - format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0) + if self.span_debug { + format!("{:?}", span) + } else { + format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0) + } } fn def_site(&mut self) -> Self::Span { self.def_site 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 3a0fc6f8da143..e2d497a3adab3 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) @@ -301,6 +304,11 @@ declare_features! ( /// Allows specialization of implementations (RFC 1210). (active, specialization, "1.7.0", Some(31844), None), + /// A minimal, sound subset of specialization intended to be used by the + /// standard library until the soundness issues with specialization + /// are fixed. + (active, min_specialization, "1.7.0", Some(31844), None), + /// Allows using `#[naked]` on functions. (active, naked_functions, "1.9.0", Some(32408), None), @@ -393,9 +401,6 @@ declare_features! ( /// Allows dereferencing raw pointers during const eval. (active, const_raw_ptr_deref, "1.27.0", Some(51911), None), - /// Allows comparing raw pointers during const eval. - (active, const_compare_raw_pointers, "1.27.0", Some(53020), None), - /// Allows `#[doc(alias = "...")]`. (active, doc_alias, "1.27.0", Some(50146), None), @@ -423,8 +428,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. @@ -549,6 +553,30 @@ 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), + + /// No longer treat an unsafe function as an unsafe block. + (active, unsafe_block_in_unsafe_fn, "1.45.0", Some(71668), None), + + /// Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`. + (active, abi_avr_interrupt, "1.45.0", Some(69664), None), + + /// Be more precise when looking for live drops in a const context. + (active, const_precise_live_drops, "1.46.0", Some(73255), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -565,4 +593,5 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::raw_dylib, sym::const_trait_impl, sym::const_trait_bound_opt_out, + sym::specialization, ]; diff --git a/src/librustc_feature/builtin_attrs.rs b/src/librustc_feature/builtin_attrs.rs index e9a5364c65838..524a357971029 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. @@ -85,19 +86,13 @@ impl AttributeGate { /// A template that the attribute input must match. /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub struct AttributeTemplate { pub word: bool, pub list: Option<&'static str>, pub name_value_str: Option<&'static str>, } -impl AttributeTemplate { - pub fn only_word() -> Self { - Self { word: true, list: None, name_value_str: None } - } -} - /// A convenience macro for constructing attribute templates. /// E.g., `template!(Word, List: "description")` means that the attribute /// supports forms `#[attr]` and `#[attr(description)]`. @@ -226,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)), @@ -336,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, ..."), @@ -369,7 +366,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // FIXME(#14407) ungated!(rustc_const_stable, Whitelisted, template!(List: r#"feature = "name""#)), gated!( - allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), + allow_internal_unstable, Whitelisted, template!(Word, List: "feat1, feat2, ..."), "allow_internal_unstable side-steps feature gating and stability checks", ), gated!( @@ -382,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", @@ -530,6 +522,14 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_test_marker, Normal, template!(Word), "the `#[rustc_test_marker]` attribute is used internally to track tests", ), + rustc_attr!( + rustc_unsafe_specialization_marker, Normal, template!(Word), + "the `#[rustc_unsafe_specialization_marker]` attribute is used to check specializations" + ), + rustc_attr!( + rustc_specialization_trait, Normal, template!(Word), + "the `#[rustc_specialization_trait]` attribute is used to check specializations" + ), // ========================================================================== // Internal attributes, Testing: diff --git a/src/librustc_feature/lib.rs b/src/librustc_feature/lib.rs index 01546f7825774..f8bf0315d0c9f 100644 --- a/src/librustc_feature/lib.rs +++ b/src/librustc_feature/lib.rs @@ -51,7 +51,7 @@ pub struct Feature { impl Feature { fn issue(&self) -> Option { - self.issue.and_then(|i| NonZeroU32::new(i)) + self.issue.and_then(NonZeroU32::new) } } diff --git a/src/librustc_feature/removed.rs b/src/librustc_feature/removed.rs index 4e348054fbd4b..8d410894e8b19 100644 --- a/src/librustc_feature/removed.rs +++ b/src/librustc_feature/removed.rs @@ -113,6 +113,11 @@ declare_features! ( Some("removed in favor of `#![feature(marker_trait_attr)]`")), /// Allows `#[no_debug]`. (removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")), + + /// Allows comparing raw pointers during const eval. + (removed, const_compare_raw_pointers, "1.46.0", Some(53020), None, + Some("cannot be allowed in const eval in any meaningful way")), + // ------------------------------------------------------------------------- // feature-group-end: removed features // ------------------------------------------------------------------------- diff --git a/src/librustc_graphviz/Cargo.toml b/src/librustc_graphviz/Cargo.toml new file mode 100644 index 0000000000000..9a5e78a560cf0 --- /dev/null +++ b/src/librustc_graphviz/Cargo.toml @@ -0,0 +1,9 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_graphviz" +version = "0.0.0" +edition = "2018" + +[lib] +name = "rustc_graphviz" +path = "lib.rs" diff --git a/src/librustc_graphviz/lib.rs b/src/librustc_graphviz/lib.rs new file mode 100644 index 0000000000000..4339092b63e85 --- /dev/null +++ b/src/librustc_graphviz/lib.rs @@ -0,0 +1,693 @@ +//! Generate files suitable for use with [Graphviz](http://www.graphviz.org/) +//! +//! The `render` function generates output (e.g., an `output.dot` file) for +//! use with [Graphviz](http://www.graphviz.org/) by walking a labeled +//! graph. (Graphviz can then automatically lay out the nodes and edges +//! of the graph, and also optionally render the graph as an image or +//! other [output formats]( +//! http://www.graphviz.org/content/output-formats), such as SVG.) +//! +//! Rather than impose some particular graph data structure on clients, +//! this library exposes two traits that clients can implement on their +//! own structs before handing them over to the rendering function. +//! +//! Note: This library does not yet provide access to the full +//! expressiveness of the [DOT language]( +//! http://www.graphviz.org/doc/info/lang.html). For example, there are +//! many [attributes](http://www.graphviz.org/content/attrs) related to +//! providing layout hints (e.g., left-to-right versus top-down, which +//! algorithm to use, etc). The current intention of this library is to +//! emit a human-readable .dot file with very regular structure suitable +//! for easy post-processing. +//! +//! # Examples +//! +//! The first example uses a very simple graph representation: a list of +//! pairs of ints, representing the edges (the node set is implicit). +//! Each node label is derived directly from the int representing the node, +//! while the edge labels are all empty strings. +//! +//! This example also illustrates how to use `Cow<[T]>` to return +//! an owned vector or a borrowed slice as appropriate: we construct the +//! node vector from scratch, but borrow the edge list (rather than +//! constructing a copy of all the edges from scratch). +//! +//! The output from this example renders five nodes, with the first four +//! forming a diamond-shaped acyclic graph and then pointing to the fifth +//! which is cyclic. +//! +//! ```rust +//! #![feature(rustc_private)] +//! +//! use std::io::Write; +//! use rustc_graphviz as dot; +//! +//! type Nd = isize; +//! type Ed = (isize,isize); +//! struct Edges(Vec); +//! +//! pub fn render_to(output: &mut W) { +//! let edges = Edges(vec![(0,1), (0,2), (1,3), (2,3), (3,4), (4,4)]); +//! dot::render(&edges, output).unwrap() +//! } +//! +//! impl<'a> dot::Labeller<'a> for Edges { +//! type Node = Nd; +//! type Edge = Ed; +//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example1").unwrap() } +//! +//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> { +//! dot::Id::new(format!("N{}", *n)).unwrap() +//! } +//! } +//! +//! impl<'a> dot::GraphWalk<'a> for Edges { +//! type Node = Nd; +//! type Edge = Ed; +//! fn nodes(&self) -> dot::Nodes<'a,Nd> { +//! // (assumes that |N| \approxeq |E|) +//! let &Edges(ref v) = self; +//! let mut nodes = Vec::with_capacity(v.len()); +//! for &(s,t) in v { +//! nodes.push(s); nodes.push(t); +//! } +//! nodes.sort(); +//! nodes.dedup(); +//! nodes.into() +//! } +//! +//! fn edges(&'a self) -> dot::Edges<'a,Ed> { +//! let &Edges(ref edges) = self; +//! (&edges[..]).into() +//! } +//! +//! fn source(&self, e: &Ed) -> Nd { let &(s,_) = e; s } +//! +//! fn target(&self, e: &Ed) -> Nd { let &(_,t) = e; t } +//! } +//! +//! # pub fn main() { render_to(&mut Vec::new()) } +//! ``` +//! +//! ```no_run +//! # pub fn render_to(output: &mut W) { unimplemented!() } +//! pub fn main() { +//! use std::fs::File; +//! let mut f = File::create("example1.dot").unwrap(); +//! render_to(&mut f) +//! } +//! ``` +//! +//! Output from first example (in `example1.dot`): +//! +//! ```dot +//! digraph example1 { +//! N0[label="N0"]; +//! N1[label="N1"]; +//! N2[label="N2"]; +//! N3[label="N3"]; +//! N4[label="N4"]; +//! N0 -> N1[label=""]; +//! N0 -> N2[label=""]; +//! N1 -> N3[label=""]; +//! N2 -> N3[label=""]; +//! N3 -> N4[label=""]; +//! N4 -> N4[label=""]; +//! } +//! ``` +//! +//! The second example illustrates using `node_label` and `edge_label` to +//! add labels to the nodes and edges in the rendered graph. The graph +//! here carries both `nodes` (the label text to use for rendering a +//! particular node), and `edges` (again a list of `(source,target)` +//! indices). +//! +//! This example also illustrates how to use a type (in this case the edge +//! type) that shares substructure with the graph: the edge type here is a +//! direct reference to the `(source,target)` pair stored in the graph's +//! internal vector (rather than passing around a copy of the pair +//! itself). Note that this implies that `fn edges(&'a self)` must +//! construct a fresh `Vec<&'a (usize,usize)>` from the `Vec<(usize,usize)>` +//! edges stored in `self`. +//! +//! Since both the set of nodes and the set of edges are always +//! constructed from scratch via iterators, we use the `collect()` method +//! from the `Iterator` trait to collect the nodes and edges into freshly +//! constructed growable `Vec` values (rather than using `Cow` as in the +//! first example above). +//! +//! The output from this example renders four nodes that make up the +//! Hasse-diagram for the subsets of the set `{x, y}`. Each edge is +//! labeled with the ⊆ character (specified using the HTML character +//! entity `&sube`). +//! +//! ```rust +//! #![feature(rustc_private)] +//! +//! use std::io::Write; +//! use rustc_graphviz as dot; +//! +//! type Nd = usize; +//! type Ed<'a> = &'a (usize, usize); +//! struct Graph { nodes: Vec<&'static str>, edges: Vec<(usize,usize)> } +//! +//! pub fn render_to(output: &mut W) { +//! let nodes = vec!["{x,y}","{x}","{y}","{}"]; +//! let edges = vec![(0,1), (0,2), (1,3), (2,3)]; +//! let graph = Graph { nodes: nodes, edges: edges }; +//! +//! dot::render(&graph, output).unwrap() +//! } +//! +//! impl<'a> dot::Labeller<'a> for Graph { +//! type Node = Nd; +//! type Edge = Ed<'a>; +//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example2").unwrap() } +//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> { +//! dot::Id::new(format!("N{}", n)).unwrap() +//! } +//! fn node_label<'b>(&'b self, n: &Nd) -> dot::LabelText<'b> { +//! dot::LabelText::LabelStr(self.nodes[*n].into()) +//! } +//! fn edge_label<'b>(&'b self, _: &Ed) -> dot::LabelText<'b> { +//! dot::LabelText::LabelStr("⊆".into()) +//! } +//! } +//! +//! impl<'a> dot::GraphWalk<'a> for Graph { +//! type Node = Nd; +//! type Edge = Ed<'a>; +//! fn nodes(&self) -> dot::Nodes<'a,Nd> { (0..self.nodes.len()).collect() } +//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { self.edges.iter().collect() } +//! fn source(&self, e: &Ed) -> Nd { let & &(s,_) = e; s } +//! fn target(&self, e: &Ed) -> Nd { let & &(_,t) = e; t } +//! } +//! +//! # pub fn main() { render_to(&mut Vec::new()) } +//! ``` +//! +//! ```no_run +//! # pub fn render_to(output: &mut W) { unimplemented!() } +//! pub fn main() { +//! use std::fs::File; +//! let mut f = File::create("example2.dot").unwrap(); +//! render_to(&mut f) +//! } +//! ``` +//! +//! The third example is similar to the second, except now each node and +//! edge now carries a reference to the string label for each node as well +//! as that node's index. (This is another illustration of how to share +//! structure with the graph itself, and why one might want to do so.) +//! +//! The output from this example is the same as the second example: the +//! Hasse-diagram for the subsets of the set `{x, y}`. +//! +//! ```rust +//! #![feature(rustc_private)] +//! +//! use std::io::Write; +//! use rustc_graphviz as dot; +//! +//! type Nd<'a> = (usize, &'a str); +//! type Ed<'a> = (Nd<'a>, Nd<'a>); +//! struct Graph { nodes: Vec<&'static str>, edges: Vec<(usize,usize)> } +//! +//! pub fn render_to(output: &mut W) { +//! let nodes = vec!["{x,y}","{x}","{y}","{}"]; +//! let edges = vec![(0,1), (0,2), (1,3), (2,3)]; +//! let graph = Graph { nodes: nodes, edges: edges }; +//! +//! dot::render(&graph, output).unwrap() +//! } +//! +//! impl<'a> dot::Labeller<'a> for Graph { +//! type Node = Nd<'a>; +//! type Edge = Ed<'a>; +//! fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example3").unwrap() } +//! fn node_id(&'a self, n: &Nd<'a>) -> dot::Id<'a> { +//! dot::Id::new(format!("N{}", n.0)).unwrap() +//! } +//! fn node_label<'b>(&'b self, n: &Nd<'b>) -> dot::LabelText<'b> { +//! let &(i, _) = n; +//! dot::LabelText::LabelStr(self.nodes[i].into()) +//! } +//! fn edge_label<'b>(&'b self, _: &Ed<'b>) -> dot::LabelText<'b> { +//! dot::LabelText::LabelStr("⊆".into()) +//! } +//! } +//! +//! impl<'a> dot::GraphWalk<'a> for Graph { +//! type Node = Nd<'a>; +//! type Edge = Ed<'a>; +//! fn nodes(&'a self) -> dot::Nodes<'a,Nd<'a>> { +//! self.nodes.iter().map(|s| &s[..]).enumerate().collect() +//! } +//! fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { +//! self.edges.iter() +//! .map(|&(i,j)|((i, &self.nodes[i][..]), +//! (j, &self.nodes[j][..]))) +//! .collect() +//! } +//! fn source(&self, e: &Ed<'a>) -> Nd<'a> { let &(s,_) = e; s } +//! fn target(&self, e: &Ed<'a>) -> Nd<'a> { let &(_,t) = e; t } +//! } +//! +//! # pub fn main() { render_to(&mut Vec::new()) } +//! ``` +//! +//! ```no_run +//! # pub fn render_to(output: &mut W) { unimplemented!() } +//! pub fn main() { +//! use std::fs::File; +//! let mut f = File::create("example3.dot").unwrap(); +//! render_to(&mut f) +//! } +//! ``` +//! +//! # References +//! +//! * [Graphviz](http://www.graphviz.org/) +//! +//! * [DOT language](http://www.graphviz.org/doc/info/lang.html) + +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + test(attr(allow(unused_variables), deny(warnings))) +)] +#![feature(nll)] + +use LabelText::*; + +use std::borrow::Cow; +use std::io; +use std::io::prelude::*; + +/// The text for a graphviz label on a node or edge. +pub enum LabelText<'a> { + /// This kind of label preserves the text directly as is. + /// + /// Occurrences of backslashes (`\`) are escaped, and thus appear + /// as backslashes in the rendered label. + LabelStr(Cow<'a, str>), + + /// This kind of label uses the graphviz label escString type: + /// + /// + /// Occurrences of backslashes (`\`) are not escaped; instead they + /// are interpreted as initiating an escString escape sequence. + /// + /// Escape sequences of particular interest: in addition to `\n` + /// to break a line (centering the line preceding the `\n`), there + /// are also the escape sequences `\l` which left-justifies the + /// preceding line and `\r` which right-justifies it. + EscStr(Cow<'a, str>), + + /// This uses a graphviz [HTML string label][html]. The string is + /// printed exactly as given, but between `<` and `>`. **No + /// escaping is performed.** + /// + /// [html]: http://www.graphviz.org/content/node-shapes#html + HtmlStr(Cow<'a, str>), +} + +/// The style for a node or edge. +/// See for descriptions. +/// Note that some of these are not valid for edges. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Style { + None, + Solid, + Dashed, + Dotted, + Bold, + Rounded, + Diagonals, + Filled, + Striped, + Wedged, +} + +impl Style { + pub fn as_slice(self) -> &'static str { + match self { + Style::None => "", + Style::Solid => "solid", + Style::Dashed => "dashed", + Style::Dotted => "dotted", + Style::Bold => "bold", + Style::Rounded => "rounded", + Style::Diagonals => "diagonals", + Style::Filled => "filled", + Style::Striped => "striped", + Style::Wedged => "wedged", + } + } +} + +// There is a tension in the design of the labelling API. +// +// For example, I considered making a `Labeller` trait that +// provides labels for `T`, and then making the graph type `G` +// implement `Labeller` and `Labeller`. However, this is +// not possible without functional dependencies. (One could work +// around that, but I did not explore that avenue heavily.) +// +// Another approach that I actually used for a while was to make a +// `Label` trait that is implemented by the client-specific +// Node and Edge types (as well as an implementation on Graph itself +// for the overall name for the graph). The main disadvantage of this +// second approach (compared to having the `G` type parameter +// implement a Labelling service) that I have encountered is that it +// makes it impossible to use types outside of the current crate +// directly as Nodes/Edges; you need to wrap them in newtype'd +// structs. See e.g., the `No` and `Ed` structs in the examples. (In +// practice clients using a graph in some other crate would need to +// provide some sort of adapter shim over the graph anyway to +// interface with this library). +// +// Another approach would be to make a single `Labeller` trait +// that provides three methods (graph_label, node_label, edge_label), +// and then make `G` implement `Labeller`. At first this did not +// appeal to me, since I had thought I would need separate methods on +// each data variant for dot-internal identifiers versus user-visible +// labels. However, the identifier/label distinction only arises for +// nodes; graphs themselves only have identifiers, and edges only have +// labels. +// +// So in the end I decided to use the third approach described above. + +/// `Id` is a Graphviz `ID`. +pub struct Id<'a> { + name: Cow<'a, str>, +} + +impl<'a> Id<'a> { + /// Creates an `Id` named `name`. + /// + /// The caller must ensure that the input conforms to an + /// identifier format: it must be a non-empty string made up of + /// alphanumeric or underscore characters, not beginning with a + /// digit (i.e., the regular expression `[a-zA-Z_][a-zA-Z_0-9]*`). + /// + /// (Note: this format is a strict subset of the `ID` format + /// defined by the DOT language. This function may change in the + /// future to accept a broader subset, or the entirety, of DOT's + /// `ID` format.) + /// + /// Passing an invalid string (containing spaces, brackets, + /// quotes, ...) will return an empty `Err` value. + pub fn new>>(name: Name) -> Result, ()> { + let name = name.into(); + match name.chars().next() { + Some(c) if c.is_ascii_alphabetic() || c == '_' => {} + _ => return Err(()), + } + if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + return Err(()); + } + + Ok(Id { name }) + } + + pub fn as_slice(&'a self) -> &'a str { + &*self.name + } + + pub fn name(self) -> Cow<'a, str> { + self.name + } +} + +/// Each instance of a type that implements `Label` maps to a +/// unique identifier with respect to `C`, which is used to identify +/// it in the generated .dot file. They can also provide more +/// elaborate (and non-unique) label text that is used in the graphviz +/// rendered output. + +/// The graph instance is responsible for providing the DOT compatible +/// identifiers for the nodes and (optionally) rendered labels for the nodes and +/// edges, as well as an identifier for the graph itself. +pub trait Labeller<'a> { + type Node; + type Edge; + + /// Must return a DOT compatible identifier naming the graph. + fn graph_id(&'a self) -> Id<'a>; + + /// Maps `n` to a unique identifier with respect to `self`. The + /// implementor is responsible for ensuring that the returned name + /// is a valid DOT identifier. + fn node_id(&'a self, n: &Self::Node) -> Id<'a>; + + /// Maps `n` to one of the [graphviz `shape` names][1]. If `None` + /// is returned, no `shape` attribute is specified. + /// + /// [1]: http://www.graphviz.org/content/node-shapes + fn node_shape(&'a self, _node: &Self::Node) -> Option> { + None + } + + /// Maps `n` to a label that will be used in the rendered output. + /// The label need not be unique, and may be the empty string; the + /// default is just the output from `node_id`. + fn node_label(&'a self, n: &Self::Node) -> LabelText<'a> { + LabelStr(self.node_id(n).name) + } + + /// Maps `e` to a label that will be used in the rendered output. + /// The label need not be unique, and may be the empty string; the + /// default is in fact the empty string. + fn edge_label(&'a self, _e: &Self::Edge) -> LabelText<'a> { + LabelStr("".into()) + } + + /// Maps `n` to a style that will be used in the rendered output. + fn node_style(&'a self, _n: &Self::Node) -> Style { + Style::None + } + + /// Maps `e` to a style that will be used in the rendered output. + fn edge_style(&'a self, _e: &Self::Edge) -> Style { + Style::None + } +} + +/// Escape tags in such a way that it is suitable for inclusion in a +/// Graphviz HTML label. +pub fn escape_html(s: &str) -> String { + s.replace("&", "&").replace("\"", """).replace("<", "<").replace(">", ">") +} + +impl<'a> LabelText<'a> { + pub fn label>>(s: S) -> LabelText<'a> { + LabelStr(s.into()) + } + + pub fn escaped>>(s: S) -> LabelText<'a> { + EscStr(s.into()) + } + + pub fn html>>(s: S) -> LabelText<'a> { + HtmlStr(s.into()) + } + + fn escape_char(c: char, mut f: F) + where + F: FnMut(char), + { + match c { + // not escaping \\, since Graphviz escString needs to + // interpret backslashes; see EscStr above. + '\\' => f(c), + _ => { + for c in c.escape_default() { + f(c) + } + } + } + } + fn escape_str(s: &str) -> String { + let mut out = String::with_capacity(s.len()); + for c in s.chars() { + LabelText::escape_char(c, |c| out.push(c)); + } + out + } + + /// Renders text as string suitable for a label in a .dot file. + /// This includes quotes or suitable delimiters. + pub fn to_dot_string(&self) -> String { + match *self { + LabelStr(ref s) => format!("\"{}\"", s.escape_default()), + EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(&s)), + HtmlStr(ref s) => format!("<{}>", s), + } + } + + /// Decomposes content into string suitable for making EscStr that + /// yields same content as self. The result obeys the law + /// render(`lt`) == render(`EscStr(lt.pre_escaped_content())`) for + /// all `lt: LabelText`. + fn pre_escaped_content(self) -> Cow<'a, str> { + match self { + EscStr(s) => s, + LabelStr(s) => { + if s.contains('\\') { + (&*s).escape_default().to_string().into() + } else { + s + } + } + HtmlStr(s) => s, + } + } + + /// Puts `prefix` on a line above this label, with a blank line separator. + pub fn prefix_line(self, prefix: LabelText<'_>) -> LabelText<'static> { + prefix.suffix_line(self) + } + + /// Puts `suffix` on a line below this label, with a blank line separator. + pub fn suffix_line(self, suffix: LabelText<'_>) -> LabelText<'static> { + let mut prefix = self.pre_escaped_content().into_owned(); + let suffix = suffix.pre_escaped_content(); + prefix.push_str(r"\n\n"); + prefix.push_str(&suffix); + EscStr(prefix.into()) + } +} + +pub type Nodes<'a, N> = Cow<'a, [N]>; +pub type Edges<'a, E> = Cow<'a, [E]>; + +// (The type parameters in GraphWalk should be associated items, +// when/if Rust supports such.) + +/// GraphWalk is an abstraction over a directed graph = (nodes,edges) +/// made up of node handles `N` and edge handles `E`, where each `E` +/// can be mapped to its source and target nodes. +/// +/// The lifetime parameter `'a` is exposed in this trait (rather than +/// introduced as a generic parameter on each method declaration) so +/// that a client impl can choose `N` and `E` that have substructure +/// that is bound by the self lifetime `'a`. +/// +/// The `nodes` and `edges` method each return instantiations of +/// `Cow<[T]>` to leave implementors the freedom to create +/// entirely new vectors or to pass back slices into internally owned +/// vectors. +pub trait GraphWalk<'a> { + type Node: Clone; + type Edge: Clone; + + /// Returns all the nodes in this graph. + fn nodes(&'a self) -> Nodes<'a, Self::Node>; + /// Returns all of the edges in this graph. + fn edges(&'a self) -> Edges<'a, Self::Edge>; + /// The source node for `edge`. + fn source(&'a self, edge: &Self::Edge) -> Self::Node; + /// The target node for `edge`. + fn target(&'a self, edge: &Self::Edge) -> Self::Node; +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum RenderOption { + NoEdgeLabels, + NoNodeLabels, + NoEdgeStyles, + NoNodeStyles, + + Monospace, +} + +/// Returns vec holding all the default render options. +pub fn default_options() -> Vec { + vec![] +} + +/// Renders directed graph `g` into the writer `w` in DOT syntax. +/// (Simple wrapper around `render_opts` that passes a default set of options.) +pub fn render<'a, N, E, G, W>(g: &'a G, w: &mut W) -> io::Result<()> +where + N: Clone + 'a, + E: Clone + 'a, + G: Labeller<'a, Node = N, Edge = E> + GraphWalk<'a, Node = N, Edge = E>, + W: Write, +{ + render_opts(g, w, &[]) +} + +/// Renders directed graph `g` into the writer `w` in DOT syntax. +/// (Main entry point for the library.) +pub fn render_opts<'a, N, E, G, W>(g: &'a G, w: &mut W, options: &[RenderOption]) -> io::Result<()> +where + N: Clone + 'a, + E: Clone + 'a, + G: Labeller<'a, Node = N, Edge = E> + GraphWalk<'a, Node = N, Edge = E>, + W: Write, +{ + writeln!(w, "digraph {} {{", g.graph_id().as_slice())?; + + // Global graph properties + if options.contains(&RenderOption::Monospace) { + writeln!(w, r#" graph[fontname="monospace"];"#)?; + writeln!(w, r#" node[fontname="monospace"];"#)?; + writeln!(w, r#" edge[fontname="monospace"];"#)?; + } + + for n in g.nodes().iter() { + write!(w, " ")?; + let id = g.node_id(n); + + let escaped = &g.node_label(n).to_dot_string(); + + let mut text = Vec::new(); + write!(text, "{}", id.as_slice()).unwrap(); + + if !options.contains(&RenderOption::NoNodeLabels) { + write!(text, "[label={}]", escaped).unwrap(); + } + + let style = g.node_style(n); + if !options.contains(&RenderOption::NoNodeStyles) && style != Style::None { + write!(text, "[style=\"{}\"]", style.as_slice()).unwrap(); + } + + if let Some(s) = g.node_shape(n) { + write!(text, "[shape={}]", &s.to_dot_string()).unwrap(); + } + + writeln!(text, ";").unwrap(); + w.write_all(&text[..])?; + } + + for e in g.edges().iter() { + let escaped_label = &g.edge_label(e).to_dot_string(); + write!(w, " ")?; + let source = g.source(e); + let target = g.target(e); + let source_id = g.node_id(&source); + let target_id = g.node_id(&target); + + let mut text = Vec::new(); + write!(text, "{} -> {}", source_id.as_slice(), target_id.as_slice()).unwrap(); + + if !options.contains(&RenderOption::NoEdgeLabels) { + write!(text, "[label={}]", escaped_label).unwrap(); + } + + let style = g.edge_style(e); + if !options.contains(&RenderOption::NoEdgeStyles) && style != Style::None { + write!(text, "[style=\"{}\"]", style.as_slice()).unwrap(); + } + + writeln!(text, ";").unwrap(); + w.write_all(&text[..])?; + } + + writeln!(w, "}}") +} + +#[cfg(test)] +mod tests; diff --git a/src/libgraphviz/tests.rs b/src/librustc_graphviz/tests.rs similarity index 100% rename from src/libgraphviz/tests.rs rename to src/librustc_graphviz/tests.rs diff --git a/src/librustc_hir/Cargo.toml b/src/librustc_hir/Cargo.toml index 872d114fb1800..1b91d769c7047 100644 --- a/src/librustc_hir/Cargo.toml +++ b/src/librustc_hir/Cargo.toml @@ -10,14 +10,13 @@ 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_serialize = { path = "../librustc_serialize" } rustc_ast = { path = "../librustc_ast" } lazy_static = "1" +log = { version = "0.4", features = ["release_max_level_info", "std"] } smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc_hir/arena.rs b/src/librustc_hir/arena.rs new file mode 100644 index 0000000000000..f439db715310c --- /dev/null +++ b/src/librustc_hir/arena.rs @@ -0,0 +1,52 @@ +/// This declares a list of types which can be allocated by `Arena`. +/// +/// The `few` modifier will cause allocation to use the shared arena and recording the destructor. +/// This is faster and more memory efficient if there's only a few allocations of the type. +/// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is +/// faster and more memory efficient if there is lots of allocations. +/// +/// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]`, +/// where `T` is the type listed. These impls will appear in the implement_ty_decoder! macro. +#[macro_export] +macro_rules! arena_types { + ($macro:path, $args:tt, $tcx:lifetime) => ( + $macro!($args, [ + // HIR types + [few] hir_krate: rustc_hir::Crate<$tcx>, rustc_hir::Crate<'_x>; + [] arm: rustc_hir::Arm<$tcx>, rustc_hir::Arm<'_x>; + [] asm_operand: rustc_hir::InlineAsmOperand<$tcx>, rustc_hir::InlineAsmOperand<'_x>; + [] asm_template: rustc_ast::ast::InlineAsmTemplatePiece, rustc_ast::ast::InlineAsmTemplatePiece; + [] attribute: rustc_ast::ast::Attribute, rustc_ast::ast::Attribute; + [] block: rustc_hir::Block<$tcx>, rustc_hir::Block<'_x>; + [] bare_fn_ty: rustc_hir::BareFnTy<$tcx>, rustc_hir::BareFnTy<'_x>; + [few] global_asm: rustc_hir::GlobalAsm, rustc_hir::GlobalAsm; + [] generic_arg: rustc_hir::GenericArg<$tcx>, rustc_hir::GenericArg<'_x>; + [] generic_args: rustc_hir::GenericArgs<$tcx>, rustc_hir::GenericArgs<'_x>; + [] generic_bound: rustc_hir::GenericBound<$tcx>, rustc_hir::GenericBound<'_x>; + [] generic_param: rustc_hir::GenericParam<$tcx>, rustc_hir::GenericParam<'_x>; + [] expr: rustc_hir::Expr<$tcx>, rustc_hir::Expr<'_x>; + [] field: rustc_hir::Field<$tcx>, rustc_hir::Field<'_x>; + [] field_pat: rustc_hir::FieldPat<$tcx>, rustc_hir::FieldPat<'_x>; + [] fn_decl: rustc_hir::FnDecl<$tcx>, rustc_hir::FnDecl<'_x>; + [] foreign_item: rustc_hir::ForeignItem<$tcx>, rustc_hir::ForeignItem<'_x>; + [] impl_item_ref: rustc_hir::ImplItemRef<$tcx>, rustc_hir::ImplItemRef<'_x>; + [few] inline_asm: rustc_hir::InlineAsm<$tcx>, rustc_hir::InlineAsm<'_x>; + [few] llvm_inline_asm: rustc_hir::LlvmInlineAsm<$tcx>, rustc_hir::LlvmInlineAsm<'_x>; + [] local: rustc_hir::Local<$tcx>, rustc_hir::Local<'_x>; + [few] macro_def: rustc_hir::MacroDef<$tcx>, rustc_hir::MacroDef<'_x>; + [] param: rustc_hir::Param<$tcx>, rustc_hir::Param<'_x>; + [] pat: rustc_hir::Pat<$tcx>, rustc_hir::Pat<'_x>; + [] path: rustc_hir::Path<$tcx>, rustc_hir::Path<'_x>; + [] path_segment: rustc_hir::PathSegment<$tcx>, rustc_hir::PathSegment<'_x>; + [] poly_trait_ref: rustc_hir::PolyTraitRef<$tcx>, rustc_hir::PolyTraitRef<'_x>; + [] qpath: rustc_hir::QPath<$tcx>, rustc_hir::QPath<'_x>; + [] stmt: rustc_hir::Stmt<$tcx>, rustc_hir::Stmt<'_x>; + [] struct_field: rustc_hir::StructField<$tcx>, rustc_hir::StructField<'_x>; + [] trait_item_ref: rustc_hir::TraitItemRef, rustc_hir::TraitItemRef; + [] ty: rustc_hir::Ty<$tcx>, rustc_hir::Ty<'_x>; + [] type_binding: rustc_hir::TypeBinding<$tcx>, rustc_hir::TypeBinding<'_x>; + [] variant: rustc_hir::Variant<$tcx>, rustc_hir::Variant<'_x>; + [] where_predicate: rustc_hir::WherePredicate<$tcx>, rustc_hir::WherePredicate<'_x>; + ], $tcx); + ) +} diff --git a/src/librustc_hir/def.rs b/src/librustc_hir/def.rs index 696aa5c6f7dfe..af1860ca6bfea 100644 --- a/src/librustc_hir/def.rs +++ b/src/librustc_hir/def.rs @@ -54,15 +54,11 @@ pub enum DefKind { /// Refers to the variant itself, `DefKind::Ctor` refers to its constructor if it exists. Variant, Trait, - /// `type Foo = impl Bar;` - OpaqueTy, /// `type Foo = Bar;` TyAlias, ForeignTy, TraitAlias, AssocTy, - /// `type Foo = impl Bar;` - AssocOpaqueTy, TyParam, // Value namespace @@ -77,6 +73,19 @@ pub enum DefKind { // Macro namespace Macro(MacroKind), + + // Not namespaced (or they are, but we don't treat them so) + ExternCrate, + Use, + ForeignMod, + AnonConst, + OpaqueTy, + Field, + LifetimeParam, + GlobalAsm, + Impl, + Closure, + Generator, } impl DefKind { @@ -103,7 +112,6 @@ impl DefKind { DefKind::TyAlias => "type alias", DefKind::TraitAlias => "trait alias", DefKind::AssocTy => "associated type", - DefKind::AssocOpaqueTy => "associated opaque type", DefKind::Union => "union", DefKind::Trait => "trait", DefKind::ForeignTy => "foreign type", @@ -113,6 +121,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", } } @@ -121,10 +139,12 @@ impl DefKind { match *self { DefKind::AssocTy | DefKind::AssocConst - | 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", } @@ -143,7 +163,6 @@ impl DefKind { | DefKind::ForeignTy | DefKind::TraitAlias | DefKind::AssocTy - | DefKind::AssocOpaqueTy | DefKind::TyParam => ns == Namespace::TypeNS, DefKind::Fn @@ -155,10 +174,23 @@ 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, } } } +/// The resolution of a path or export. #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(HashStable_Generic)] pub enum Res { diff --git a/src/librustc_hir/definitions.rs b/src/librustc_hir/definitions.rs new file mode 100644 index 0000000000000..79b7068273932 --- /dev/null +++ b/src/librustc_hir/definitions.rs @@ -0,0 +1,456 @@ +//! For each definition, we track the following data. A definition +//! here is defined somewhat circularly as "something with a `DefId`", +//! but it generally corresponds to things like structs, enums, etc. +//! There are also some rather random cases (like const initializer +//! expressions) that are mostly just leftovers. + +pub use crate::def_id::DefPathHash; +use crate::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use crate::hir; + +use rustc_ast::crate_disambiguator::CrateDisambiguator; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_index::vec::IndexVec; +use rustc_span::hygiene::ExpnId; +use rustc_span::symbol::{sym, Symbol}; + +use log::debug; +use std::fmt::Write; +use std::hash::Hash; + +/// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa. +/// Internally the `DefPathTable` holds a tree of `DefKey`s, where each `DefKey` +/// stores the `DefIndex` of its parent. +/// There is one `DefPathTable` for each crate. +#[derive(Clone, Default, RustcDecodable, RustcEncodable)] +pub struct DefPathTable { + index_to_key: IndexVec, + def_path_hashes: IndexVec, +} + +impl DefPathTable { + fn allocate(&mut self, key: DefKey, def_path_hash: DefPathHash) -> DefIndex { + let index = { + let index = DefIndex::from(self.index_to_key.len()); + debug!("DefPathTable::insert() - {:?} <-> {:?}", key, index); + self.index_to_key.push(key); + index + }; + self.def_path_hashes.push(def_path_hash); + debug_assert!(self.def_path_hashes.len() == self.index_to_key.len()); + index + } + + pub fn next_id(&self) -> DefIndex { + DefIndex::from(self.index_to_key.len()) + } + + #[inline(always)] + pub fn def_key(&self, index: DefIndex) -> DefKey { + self.index_to_key[index] + } + + #[inline(always)] + pub fn def_path_hash(&self, index: DefIndex) -> DefPathHash { + let hash = self.def_path_hashes[index]; + debug!("def_path_hash({:?}) = {:?}", index, hash); + hash + } + + pub fn add_def_path_hashes_to(&self, cnum: CrateNum, out: &mut FxHashMap) { + out.extend(self.def_path_hashes.iter().enumerate().map(|(index, &hash)| { + let def_id = DefId { krate: cnum, index: DefIndex::from(index) }; + (hash, def_id) + })); + } + + pub fn size(&self) -> usize { + self.index_to_key.len() + } +} + +/// The definition table containing node definitions. +/// It holds the `DefPathTable` for `LocalDefId`s/`DefPath`s. +/// It also stores mappings to convert `LocalDefId`s to/from `HirId`s. +#[derive(Clone)] +pub struct Definitions { + table: DefPathTable, + + // FIXME(eddyb) ideally all `LocalDefId`s would be HIR owners. + pub(super) def_id_to_hir_id: IndexVec>, + /// The reverse mapping of `def_id_to_hir_id`. + pub(super) hir_id_to_def_id: FxHashMap, + + /// If `ExpnId` is an ID of some macro expansion, + /// then `DefId` is the normal module (`mod`) in which the expanded macro was defined. + parent_modules_of_macro_defs: FxHashMap, + /// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`. + expansions_that_defined: FxHashMap, +} + +/// A unique identifier that we can use to lookup a definition +/// precisely. It combines the index of the definition's parent (if +/// any) with a `DisambiguatedDefPathData`. +#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] +pub struct DefKey { + /// The parent path. + pub parent: Option, + + /// The identifier of this node. + pub disambiguated_data: DisambiguatedDefPathData, +} + +impl DefKey { + fn compute_stable_hash(&self, parent_hash: DefPathHash) -> DefPathHash { + let mut hasher = StableHasher::new(); + + // We hash a `0u8` here to disambiguate between regular `DefPath` hashes, + // and the special "root_parent" below. + 0u8.hash(&mut hasher); + parent_hash.hash(&mut hasher); + + let DisambiguatedDefPathData { ref data, disambiguator } = self.disambiguated_data; + + ::std::mem::discriminant(data).hash(&mut hasher); + if let Some(name) = data.get_opt_name() { + // Get a stable hash by considering the symbol chars rather than + // the symbol index. + name.as_str().hash(&mut hasher); + } + + disambiguator.hash(&mut hasher); + + DefPathHash(hasher.finish()) + } + + fn root_parent_stable_hash( + crate_name: &str, + crate_disambiguator: CrateDisambiguator, + ) -> DefPathHash { + let mut hasher = StableHasher::new(); + // Disambiguate this from a regular `DefPath` hash; see `compute_stable_hash()` above. + 1u8.hash(&mut hasher); + crate_name.hash(&mut hasher); + crate_disambiguator.hash(&mut hasher); + DefPathHash(hasher.finish()) + } +} + +/// A pair of `DefPathData` and an integer disambiguator. The integer is +/// normally `0`, but in the event that there are multiple defs with the +/// same `parent` and `data`, we use this field to disambiguate +/// between them. This introduces some artificial ordering dependency +/// but means that if you have, e.g., two impls for the same type in +/// the same module, they do get distinct `DefId`s. +#[derive(Copy, Clone, PartialEq, Debug, RustcEncodable, RustcDecodable)] +pub struct DisambiguatedDefPathData { + pub data: DefPathData, + pub disambiguator: u32, +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +pub struct DefPath { + /// The path leading from the crate root to the item. + pub data: Vec, + + /// The crate root this path is relative to. + pub krate: CrateNum, +} + +impl DefPath { + pub fn is_local(&self) -> bool { + self.krate == LOCAL_CRATE + } + + pub fn make(krate: CrateNum, start_index: DefIndex, mut get_key: FN) -> DefPath + where + FN: FnMut(DefIndex) -> DefKey, + { + let mut data = vec![]; + let mut index = Some(start_index); + loop { + debug!("DefPath::make: krate={:?} index={:?}", krate, index); + let p = index.unwrap(); + let key = get_key(p); + debug!("DefPath::make: key={:?}", key); + match key.disambiguated_data.data { + DefPathData::CrateRoot => { + assert!(key.parent.is_none()); + break; + } + _ => { + data.push(key.disambiguated_data); + index = key.parent; + } + } + } + data.reverse(); + DefPath { data, krate } + } + + /// Returns a string representation of the `DefPath` without + /// the crate-prefix. This method is useful if you don't have + /// a `TyCtxt` available. + pub fn to_string_no_crate(&self) -> String { + let mut s = String::with_capacity(self.data.len() * 16); + + for component in &self.data { + write!(s, "::{}[{}]", component.data.as_symbol(), component.disambiguator).unwrap(); + } + + s + } + + /// Returns a filename-friendly string for the `DefPath`, with the + /// crate-prefix. + pub fn to_string_friendly(&self, crate_imported_name: F) -> String + where + F: FnOnce(CrateNum) -> Symbol, + { + let crate_name_str = crate_imported_name(self.krate).as_str(); + let mut s = String::with_capacity(crate_name_str.len() + self.data.len() * 16); + + write!(s, "::{}", crate_name_str).unwrap(); + + for component in &self.data { + if component.disambiguator == 0 { + write!(s, "::{}", component.data.as_symbol()).unwrap(); + } else { + write!(s, "{}[{}]", component.data.as_symbol(), component.disambiguator).unwrap(); + } + } + + s + } + + /// Returns a filename-friendly string of the `DefPath`, without + /// the crate-prefix. This method is useful if you don't have + /// a `TyCtxt` available. + pub fn to_filename_friendly_no_crate(&self) -> String { + let mut s = String::with_capacity(self.data.len() * 16); + + let mut opt_delimiter = None; + for component in &self.data { + s.extend(opt_delimiter); + opt_delimiter = Some('-'); + if component.disambiguator == 0 { + write!(s, "{}", component.data.as_symbol()).unwrap(); + } else { + write!(s, "{}[{}]", component.data.as_symbol(), component.disambiguator).unwrap(); + } + } + s + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +pub enum DefPathData { + // Root: these should only be used for the root nodes, because + // they are treated specially by the `def_path` function. + /// The crate root (marker). + CrateRoot, + // Catch-all for random `DefId` things like `DUMMY_NODE_ID`. + Misc, + + // Different kinds of items and item-like things: + /// An impl. + Impl, + /// Something in the type namespace. + TypeNs(Symbol), + /// Something in the value namespace. + ValueNs(Symbol), + /// Something in the macro namespace. + MacroNs(Symbol), + /// Something in the lifetime namespace. + LifetimeNs(Symbol), + /// A closure expression. + ClosureExpr, + + // Subportions of items: + /// Implicit constructor for a unit or tuple-like struct or enum variant. + Ctor, + /// A constant expression (see `{ast,hir}::AnonConst`). + AnonConst, + /// An `impl Trait` type node. + ImplTrait, +} + +impl Definitions { + pub fn def_path_table(&self) -> &DefPathTable { + &self.table + } + + /// Gets the number of definitions. + pub fn def_index_count(&self) -> usize { + self.table.index_to_key.len() + } + + pub fn def_key(&self, id: LocalDefId) -> DefKey { + self.table.def_key(id.local_def_index) + } + + #[inline(always)] + pub fn def_path_hash(&self, id: LocalDefId) -> DefPathHash { + self.table.def_path_hash(id.local_def_index) + } + + /// Returns the path from the crate root to `index`. The root + /// nodes are not included in the path (i.e., this will be an + /// empty vector for the crate root). For an inlined item, this + /// will be the path of the item in the external crate (but the + /// path will begin with the path to the external crate). + pub fn def_path(&self, id: LocalDefId) -> DefPath { + DefPath::make(LOCAL_CRATE, id.local_def_index, |index| { + self.def_key(LocalDefId { local_def_index: index }) + }) + } + + #[inline] + pub fn as_local_hir_id(&self, def_id: LocalDefId) -> hir::HirId { + self.local_def_id_to_hir_id(def_id) + } + + #[inline] + pub fn local_def_id_to_hir_id(&self, id: LocalDefId) -> hir::HirId { + self.def_id_to_hir_id[id].unwrap() + } + + #[inline] + pub fn opt_local_def_id_to_hir_id(&self, id: LocalDefId) -> Option { + self.def_id_to_hir_id[id] + } + + #[inline] + pub fn opt_hir_id_to_local_def_id(&self, hir_id: hir::HirId) -> Option { + self.hir_id_to_def_id.get(&hir_id).copied() + } + + /// Adds a root definition (no parent) and a few other reserved definitions. + pub fn new(crate_name: &str, crate_disambiguator: CrateDisambiguator) -> Definitions { + let key = DefKey { + parent: None, + disambiguated_data: DisambiguatedDefPathData { + data: DefPathData::CrateRoot, + disambiguator: 0, + }, + }; + + let parent_hash = DefKey::root_parent_stable_hash(crate_name, crate_disambiguator); + let def_path_hash = key.compute_stable_hash(parent_hash); + + // Create the root definition. + let mut table = DefPathTable::default(); + let root = LocalDefId { local_def_index: table.allocate(key, def_path_hash) }; + assert_eq!(root.local_def_index, CRATE_DEF_INDEX); + + Definitions { + table, + def_id_to_hir_id: Default::default(), + hir_id_to_def_id: Default::default(), + expansions_that_defined: Default::default(), + parent_modules_of_macro_defs: Default::default(), + } + } + + /// Retrieves the root definition. + pub fn get_root_def(&self) -> LocalDefId { + LocalDefId { local_def_index: CRATE_DEF_INDEX } + } + + /// Adds a definition with a parent definition. + pub fn create_def( + &mut self, + parent: LocalDefId, + data: DefPathData, + expn_id: ExpnId, + mut next_disambiguator: impl FnMut(LocalDefId, DefPathData) -> u32, + ) -> LocalDefId { + debug!("create_def(parent={:?}, data={:?}, expn_id={:?})", parent, data, expn_id); + + // The root node must be created with `create_root_def()`. + assert!(data != DefPathData::CrateRoot); + + let disambiguator = next_disambiguator(parent, data); + let key = DefKey { + parent: Some(parent.local_def_index), + disambiguated_data: DisambiguatedDefPathData { data, disambiguator }, + }; + + let parent_hash = self.table.def_path_hash(parent.local_def_index); + let def_path_hash = key.compute_stable_hash(parent_hash); + + debug!("create_def: after disambiguation, key = {:?}", key); + + // Create the definition. + let def_id = LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) }; + + if expn_id != ExpnId::root() { + self.expansions_that_defined.insert(def_id, expn_id); + } + + def_id + } + + /// Initializes the `LocalDefId` to `HirId` mapping once it has been generated during + /// AST to HIR lowering. + pub fn init_def_id_to_hir_id_mapping( + &mut self, + mapping: IndexVec>, + ) { + assert!( + self.def_id_to_hir_id.is_empty(), + "trying to initialize `LocalDefId` <-> `HirId` mappings twice" + ); + + // Build the reverse mapping of `def_id_to_hir_id`. + self.hir_id_to_def_id = mapping + .iter_enumerated() + .filter_map(|(def_id, hir_id)| hir_id.map(|hir_id| (hir_id, def_id))) + .collect(); + + self.def_id_to_hir_id = mapping; + } + + pub fn expansion_that_defined(&self, id: LocalDefId) -> ExpnId { + self.expansions_that_defined.get(&id).copied().unwrap_or(ExpnId::root()) + } + + pub fn parent_module_of_macro_def(&self, expn_id: ExpnId) -> DefId { + self.parent_modules_of_macro_defs[&expn_id] + } + + pub fn add_parent_module_of_macro_def(&mut self, expn_id: ExpnId, module: DefId) { + self.parent_modules_of_macro_defs.insert(expn_id, module); + } +} + +impl DefPathData { + pub fn get_opt_name(&self) -> Option { + use self::DefPathData::*; + match *self { + TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name), + + Impl | CrateRoot | Misc | ClosureExpr | Ctor | AnonConst | ImplTrait => None, + } + } + + pub fn as_symbol(&self) -> Symbol { + use self::DefPathData::*; + match *self { + TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => name, + // Note that this does not show up in user print-outs. + CrateRoot => sym::double_braced_crate, + Impl => sym::double_braced_impl, + Misc => sym::double_braced_misc, + ClosureExpr => sym::double_braced_closure, + Ctor => sym::double_braced_constructor, + AnonConst => sym::double_braced_constant, + ImplTrait => sym::double_braced_opaque, + } + } + + pub fn to_string(&self) -> String { + self.as_symbol().to_string() + } +} diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 48b423de268a7..f3dfec7ca7215 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::def_id::LocalDefId; 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; @@ -79,9 +75,9 @@ impl ParamName { } } - pub fn modern(&self) -> ParamName { + pub fn normalize_to_macros_2_0(&self) -> ParamName { match *self { - ParamName::Plain(ident) => ParamName::Plain(ident.modern()), + ParamName::Plain(ident) => ParamName::Plain(ident.normalize_to_macros_2_0()), param_name => param_name, } } @@ -151,9 +147,11 @@ impl LifetimeName { self == &LifetimeName::Static } - pub fn modern(&self) -> LifetimeName { + pub fn normalize_to_macros_2_0(&self) -> LifetimeName { match *self { - LifetimeName::Param(param_name) => LifetimeName::Param(param_name.modern()), + LifetimeName::Param(param_name) => { + LifetimeName::Param(param_name.normalize_to_macros_2_0()) + } lifetime_name => lifetime_name, } } @@ -167,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()) } } @@ -189,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. @@ -204,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)] @@ -386,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, } } @@ -544,6 +525,13 @@ impl WhereClause<'_> { pub fn span_for_predicates_or_empty_place(&self) -> Span { self.span } + + /// `Span` where further predicates would be suggested, accounting for trailing commas, like + /// in `fn foo(t: T) where T: Foo,` so we don't suggest two trailing commas. + pub fn tail_span_for_suggestion(&self) -> Span { + let end = self.span_for_predicates_or_empty_place().shrink_to_hi(); + self.predicates.last().map(|p| p.span()).unwrap_or(end).shrink_to_hi().to(end) + } } /// A single predicate in a where-clause. @@ -651,6 +639,8 @@ pub struct Crate<'hir> { /// A list of proc macro HirIds, written out in the order in which /// they are declared in the static array generated by proc_macro_harness. pub proc_macros: Vec, + + pub trait_map: BTreeMap>, } impl Crate<'hir> { @@ -756,7 +746,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, @@ -764,17 +754,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 { @@ -1116,26 +1095,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>), @@ -1334,6 +1302,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; @@ -1349,7 +1364,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>, @@ -1359,7 +1374,7 @@ pub struct Expr<'hir> { // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr<'static>, 64); +rustc_data_structures::static_assert_size!(Expr<'static>, 72); impl Expr<'_> { pub fn precedence(&self) -> ExprPrecedence { @@ -1388,6 +1403,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, @@ -1443,6 +1459,7 @@ impl Expr<'_> { | ExprKind::Loop(..) | ExprKind::Assign(..) | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) | ExprKind::AssignOp(..) | ExprKind::Lit(_) | ExprKind::Unary(..) @@ -1470,17 +1487,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()`). /// @@ -1505,13 +1511,7 @@ pub fn is_range_literal(sm: &SourceMap, expr: &Expr<'_>) -> bool { // Check whether a span corresponding to a range expression is a // range literal, rather than an explicit struct or `new()` call. fn is_lit(sm: &SourceMap, span: &Span) -> bool { - let end_point = sm.end_point(*span); - - if let Ok(end_string) = sm.span_to_snippet(end_point) { - !(end_string.ends_with('}') || end_string.ends_with(')')) - } else { - false - } + sm.span_to_snippet(*span).map(|range_src| range_src.contains("..")).unwrap_or(false) }; match expr.kind { @@ -1565,12 +1565,14 @@ pub enum ExprKind<'hir> { /// and the remaining elements are the rest of the arguments. /// Thus, `x.foo::(a, b, c, d)` is represented as /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`. + /// The final `Span` represents the span of the function and arguments + /// (e.g. `foo::(a, b, c, d)` in `x.foo::(a, b, c, d)` /// /// To resolve the called method to a `DefId`, call [`type_dependent_def_id`] with /// the `hir_id` of the `MethodCall` node itself. /// /// [`type_dependent_def_id`]: ../ty/struct.TypeckTables.html#method.type_dependent_def_id - MethodCall(&'hir PathSegment<'hir>, Span, &'hir [Expr<'hir>]), + MethodCall(&'hir PathSegment<'hir>, Span, &'hir [Expr<'hir>], Span), /// A tuple (e.g., `(a, b, c, d)`). Tup(&'hir [Expr<'hir>]), /// A binary operation (e.g., `a + b`, `a * b`). @@ -1631,6 +1633,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. /// @@ -1655,7 +1659,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` @@ -1790,15 +1794,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`", }) } @@ -1809,7 +1822,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 }, } } } @@ -1854,7 +1867,7 @@ pub struct TraitItem<'hir> { /// Represents a trait method's body (or just argument names). #[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] -pub enum TraitMethod<'hir> { +pub enum TraitFn<'hir> { /// No default body in the trait, just a signature. Required(&'hir [Ident]), @@ -1868,7 +1881,7 @@ pub enum TraitItemKind<'hir> { /// An associated constant with an optional value (otherwise `impl`s must contain a value). Const(&'hir Ty<'hir>, Option), /// An associated function with an optional body. - Fn(FnSig<'hir>, TraitMethod<'hir>), + Fn(FnSig<'hir>, TraitFn<'hir>), /// An associated type with (possibly empty) bounds and optional concrete /// type. Type(GenericBounds<'hir>, Option<&'hir Ty<'hir>>), @@ -1901,19 +1914,17 @@ pub enum ImplItemKind<'hir> { /// An associated constant of the given type, set to the constant result /// of the expression. Const(&'hir Ty<'hir>, BodyId), - /// A method implementation with the given signature and body. - Method(FnSig<'hir>, BodyId), + /// An associated function implementation with the given signature and body. + Fn(FnSig<'hir>, BodyId), /// An associated type. TyAlias(&'hir Ty<'hir>), - /// An associated `type = impl Trait`. - OpaqueTy(GenericBounds<'hir>), } impl ImplItemKind<'_> { pub fn namespace(&self) -> Namespace { match self { - ImplItemKind::OpaqueTy(..) | ImplItemKind::TyAlias(..) => Namespace::TypeNS, - ImplItemKind::Const(..) | ImplItemKind::Method(..) => Namespace::ValueNS, + ImplItemKind::TyAlias(..) => Namespace::TypeNS, + ImplItemKind::Const(..) | ImplItemKind::Fn(..) => Namespace::ValueNS, } } } @@ -1963,19 +1974,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)] @@ -2008,13 +2013,13 @@ pub struct OpaqueTy<'hir> { /// From whence the opaque type came. #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] pub enum OpaqueTyOrigin { - /// `type Foo = impl Trait;` - TypeAlias, /// `-> impl Trait` FnReturn, /// `async fn` AsyncFn, - /// Impl trait in bindings, consts, statics, bounds. + /// `let _: impl Trait = ...` + Binding, + /// Impl trait in type aliases, consts, statics, bounds. Misc, } @@ -2040,12 +2045,12 @@ pub enum TyKind<'hir> { /// /// Type parameters may be stored in each `PathSegment`. Path(QPath<'hir>), - /// A type definition itself. This is currently only used for the `type Foo = impl Trait` - /// item that `impl Trait` in return position desugars to. + /// A opaque type definition itself. This is currently only used for the + /// `opaque type Foo: Trait` item that `impl Trait` in desugars to. /// - /// The generic argument list contains the lifetimes (and in the future possibly parameters) - /// that are actually bound on the `impl Trait`. - Def(ItemId, &'hir [GenericArg<'hir>]), + /// The generic argument list contains the lifetimes (and in the future + /// possibly parameters) that are actually bound on the `impl Trait`. + OpaqueDef(ItemId, &'hir [GenericArg<'hir>]), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. TraitObject(&'hir [PolyTraitRef<'hir>], Lifetime), @@ -2058,8 +2063,58 @@ 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, + pub line_spans: &'hir [Span], +} + #[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, @@ -2069,20 +2124,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>], } @@ -2101,7 +2156,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, @@ -2151,7 +2206,7 @@ pub enum Defaultness { impl Defaultness { pub fn has_value(&self) -> bool { match *self { - Defaultness::Default { has_value, .. } => has_value, + Defaultness::Default { has_value } => has_value, Defaultness::Final => true, } } @@ -2180,15 +2235,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 { @@ -2272,13 +2318,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!(), } } @@ -2459,7 +2502,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;` /// @@ -2500,6 +2543,9 @@ pub enum ItemKind<'hir> { unsafety: Unsafety, polarity: ImplPolarity, defaultness: Defaultness, + // We do not put a `Span` in `Defaultness` because it breaks foreign crate metadata + // decoding as `Span`s cannot be decoded when a `Session` is not available. + defaultness_span: Option, constness: Constness, generics: Generics<'hir>, @@ -2512,27 +2558,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, _) @@ -2584,9 +2609,8 @@ 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, } #[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] @@ -2611,16 +2635,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 { @@ -2633,30 +2647,12 @@ pub type CaptureModeMap = NodeMap; // The TraitCandidate's import_ids is empty if the trait is defined in the same module, and // has length > 0 if the trait is found through an chain of imports, starting with the // import/use statement in the scope where the trait is used. -#[derive(Clone, Debug)] -pub struct TraitCandidate { +#[derive(RustcEncodable, RustcDecodable, Clone, Debug)] +pub struct TraitCandidate { pub def_id: DefId, - pub import_ids: SmallVec<[ID; 1]>, + pub import_ids: SmallVec<[LocalDefId; 1]>, } -impl TraitCandidate { - pub fn map_import_ids(self, f: F) -> TraitCandidate - where - F: Fn(ID) -> T, - { - let TraitCandidate { def_id, import_ids } = self; - let import_ids = import_ids.into_iter().map(f).collect(); - TraitCandidate { def_id, import_ids } - } -} - -// Trait method resolution -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>; - #[derive(Copy, Clone, Debug, HashStable_Generic)] pub enum Node<'hir> { Param(&'hir Param<'hir>), @@ -2704,7 +2700,7 @@ impl Node<'_> { pub fn fn_decl(&self) -> Option<&FnDecl<'_>> { match self { Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) - | Node::ImplItem(ImplItem { kind: ImplItemKind::Method(fn_sig, _), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl), Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { Some(fn_decl) @@ -2713,12 +2709,52 @@ impl Node<'_> { } } + pub fn body_id(&self) -> Option { + match self { + Node::TraitItem(TraitItem { + kind: TraitItemKind::Fn(_, TraitFn::Provided(body_id)), + .. + }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. }) + | Node::Item(Item { kind: ItemKind::Fn(.., body_id), .. }) => Some(*body_id), + _ => None, + } + } + pub fn generics(&self) -> Option<&Generics<'_>> { match self { Node::TraitItem(TraitItem { generics, .. }) - | Node::ImplItem(ImplItem { generics, .. }) - | Node::Item(Item { kind: ItemKind::Fn(_, generics, _), .. }) => Some(generics), + | Node::ImplItem(ImplItem { generics, .. }) => Some(generics), + Node::Item(item) => item.kind.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 6d2ec44576353..d782c3dd70a2c 100644 --- a/src/librustc_hir/hir_id.rs +++ b/src/librustc_hir/hir_id.rs @@ -1,9 +1,8 @@ -use crate::def_id::{DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX}; -use rustc_serialize::{self, Decodable, Decoder, Encodable, Encoder}; +use crate::def_id::{LocalDefId, CRATE_DEF_INDEX}; use std::fmt; /// Uniquely identifies a node in the HIR of the current crate. It is -/// composed of the `owner`, which is the `DefIndex` of the directly enclosing +/// composed of the `owner`, which is the `LocalDefId` of the directly enclosing /// `hir::Item`, `hir::TraitItem`, or `hir::ImplItem` (i.e., the closest "item-like"), /// and the `local_id` which is unique within the given owner. /// @@ -12,41 +11,12 @@ use std::fmt; /// the `local_id` part of the `HirId` changing, which is a very useful property in /// incremental compilation where we have to persist things through changes to /// the code base. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, RustcEncodable, RustcDecodable)] pub struct HirId { - pub owner: DefIndex, + pub owner: LocalDefId, pub local_id: ItemLocalId, } -impl HirId { - pub fn owner_def_id(self) -> DefId { - DefId::local(self.owner) - } - - pub fn owner_local_def_id(self) -> LocalDefId { - LocalDefId::from_def_id(DefId::local(self.owner)) - } -} - -impl rustc_serialize::UseSpecializedEncodable for HirId { - fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { - let HirId { owner, local_id } = *self; - - owner.encode(s)?; - local_id.encode(s)?; - Ok(()) - } -} - -impl rustc_serialize::UseSpecializedDecodable for HirId { - fn default_decode(d: &mut D) -> Result { - let owner = DefIndex::decode(d)?; - let local_id = ItemLocalId::decode(d)?; - - Ok(HirId { owner, local_id }) - } -} - impl fmt::Display for HirId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) @@ -70,9 +40,9 @@ rustc_index::newtype_index! { rustc_data_structures::impl_stable_hash_via_hash!(ItemLocalId); /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_INDEX`. -pub const CRATE_HIR_ID: HirId = - HirId { owner: CRATE_DEF_INDEX, local_id: ItemLocalId::from_u32_const(0) }; - -pub const DUMMY_HIR_ID: HirId = HirId { owner: CRATE_DEF_INDEX, local_id: DUMMY_ITEM_LOCAL_ID }; +pub const CRATE_HIR_ID: HirId = HirId { + owner: LocalDefId { local_def_index: CRATE_DEF_INDEX }, + local_id: ItemLocalId::from_u32(0), +}; pub const DUMMY_ITEM_LOCAL_ID: ItemLocalId = ItemLocalId::MAX; diff --git a/src/librustc_hir/intravisit.rs b/src/librustc_hir/intravisit.rs index 79c7f34dd5310..23d642731da4d 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,14 +120,38 @@ 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>; fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir>; } +/// An erased version of `Map<'hir>`, using dynamic dispatch. +/// NOTE: This type is effectively only usable with `NestedVisitorMap::None`. +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) + } + fn item(&self, id: HirId) -> &'hir Item<'hir> { + self.0.item(id) + } + fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { + self.0.trait_item(id) + } + fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { + self.0.impl_item(id) + } +} + /// Specifies what nested things a visitor wants to visit. The most /// common choice is `OnlyBodies`, which will cause the visitor to /// visit fn bodies for fns that it encounters, but skip over nested @@ -293,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) { @@ -371,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, @@ -571,6 +596,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { defaultness: _, polarity: _, constness: _, + defaultness_span: _, ref generics, ref of_trait, ref self_ty, @@ -664,7 +690,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) { TyKind::Path(ref qpath) => { visitor.visit_qpath(qpath, typ.hir_id, typ.span); } - TyKind::Def(item_id, lifetimes) => { + TyKind::OpaqueDef(item_id, lifetimes) => { visitor.visit_nested_item(item_id); walk_list!(visitor, visit_generic_arg, lifetimes); } @@ -911,14 +937,14 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai visitor.visit_ty(ty); walk_list!(visitor, visit_nested_body, default); } - TraitItemKind::Fn(ref sig, TraitMethod::Required(param_names)) => { + TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => { visitor.visit_id(trait_item.hir_id); visitor.visit_fn_decl(&sig.decl); for ¶m_name in param_names { visitor.visit_ident(param_name); } } - TraitItemKind::Fn(ref sig, TraitMethod::Provided(body_id)) => { + TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => { visitor.visit_fn( FnKind::Method(trait_item.ident, sig, None, &trait_item.attrs), &sig.decl, @@ -968,7 +994,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt visitor.visit_ty(ty); visitor.visit_nested_body(body); } - ImplItemKind::Method(ref sig, body_id) => { + ImplItemKind::Fn(ref sig, body_id) => { visitor.visit_fn( FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis), &impl_item.attrs), &sig.decl, @@ -981,10 +1007,6 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplIt visitor.visit_id(impl_item.hir_id); visitor.visit_ty(ty); } - ImplItemKind::OpaqueTy(bounds) => { - visitor.visit_id(impl_item.hir_id); - walk_list!(visitor, visit_param_bound, bounds); - } } } @@ -1064,7 +1086,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) visitor.visit_expr(callee_expression); walk_list!(visitor, visit_expr, arguments); } - ExprKind::MethodCall(ref segment, _, arguments) => { + ExprKind::MethodCall(ref segment, _, arguments, _) => { visitor.visit_path_segment(expression.span, segment); walk_list!(visitor, visit_expr, arguments); } @@ -1132,6 +1154,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..cd6f034f7a5da 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -25,7 +25,7 @@ use lazy_static::lazy_static; // So you probably just want to nip down to the end. macro_rules! language_item_table { ( - $( $variant:ident, $name:expr, $method:ident, $target:path; )* + $( $variant:ident, $name:expr, $method:ident, $target:expr; )* ) => { enum_from_u32! { @@ -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; @@ -204,6 +207,8 @@ language_item_table! { FnMutTraitLangItem, "fn_mut", fn_mut_trait, Target::Trait; FnOnceTraitLangItem, "fn_once", fn_once_trait, Target::Trait; + FnOnceOutputLangItem, "fn_once_output", fn_once_output, Target::AssocTy; + FutureTraitLangItem, "future_trait", future_trait, Target::Trait; GeneratorStateLangItem, "generator_state", gen_state, Target::Enum; GeneratorTraitLangItem, "generator", gen_trait, Target::Trait; @@ -239,6 +244,8 @@ language_item_table! { StartFnLangItem, "start", start_fn, Target::Fn; + CountCodeRegionFnLangItem, "count_code_region", count_code_region_fn, Target::Fn; + EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn; EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static; @@ -255,6 +262,5 @@ language_item_table! { TerminationTraitLangItem, "termination", termination, Target::Trait; - Arc, "arc", arc, Target::Struct; - Rc, "rc", rc, Target::Struct; + TryTraitLangItem, "try", try_trait, Target::Trait; } diff --git a/src/librustc_hir/lib.rs b/src/librustc_hir/lib.rs index 45f806b53f50c..b51c0a6e98840 100644 --- a/src/librustc_hir/lib.rs +++ b/src/librustc_hir/lib.rs @@ -3,15 +3,20 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html #![feature(crate_visibility_modifier)] +#![feature(const_if_match)] #![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] extern crate rustc_data_structures; +mod arena; pub mod def; +pub mod definitions; pub use rustc_span::def_id; mod hir; pub mod hir_id; @@ -19,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 1a2c3a38565c8..0000000000000 --- a/src/librustc_hir/print.rs +++ /dev/null @@ -1,2335 +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, - 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::TraitMethod::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::TraitMethod::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::Method(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 7ca2bfded3c2d..1d3f44a08993a 100644 --- a/src/librustc_hir/stable_hash_impls.rs +++ b/src/librustc_hir/stable_hash_impls.rs @@ -5,11 +5,11 @@ use crate::hir::{ VisibilityKind, }; use crate::hir_id::{HirId, ItemLocalId}; -use rustc_span::def_id::{DefIndex, DefPathHash}; +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 { @@ -21,7 +21,7 @@ pub trait HashStableContext: fn hash_hir_ty(&mut self, _: &Ty<'_>, hasher: &mut StableHasher); fn hash_hir_visibility_kind(&mut self, _: &VisibilityKind<'_>, hasher: &mut StableHasher); fn hash_hir_item_like(&mut self, f: F); - fn local_def_path_hash(&self, def_index: DefIndex) -> DefPathHash; + fn local_def_path_hash(&self, def_id: LocalDefId) -> DefPathHash; } impl ToStableHashKey for HirId { diff --git a/src/librustc_hir/target.rs b/src/librustc_hir/target.rs index b7bc555d7b410..3a4485a1b17fd 100644 --- a/src/librustc_hir/target.rs +++ b/src/librustc_hir/target.rs @@ -105,10 +105,10 @@ impl Target { pub fn from_trait_item(trait_item: &TraitItem<'_>) -> Target { match trait_item.kind { TraitItemKind::Const(..) => Target::AssocConst, - TraitItemKind::Fn(_, hir::TraitMethod::Required(_)) => { + TraitItemKind::Fn(_, hir::TraitFn::Required(_)) => { Target::Method(MethodKind::Trait { body: false }) } - TraitItemKind::Fn(_, hir::TraitMethod::Provided(_)) => { + TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => { Target::Method(MethodKind::Trait { body: true }) } TraitItemKind::Type(..) => Target::AssocTy, 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..c16b7c63e3147 --- /dev/null +++ b/src/librustc_hir_pretty/lib.rs @@ -0,0 +1,2495 @@ +#![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) + }) +} + +pub fn generic_params_to_string(generic_params: &[GenericParam<'_>]) -> String { + to_string(NO_ANN, |s| s.print_generic_params(generic_params)) +} + +pub fn bounds_to_string<'b>(bounds: impl IntoIterator>) -> String { + to_string(NO_ANN, |s| s.print_bounds("", bounds)) +} + +pub fn param_to_string(arg: &hir::Param<'_>) -> String { + to_string(NO_ANN, |s| s.print_param(arg)) +} + +pub fn ty_to_string(ty: &hir::Ty<'_>) -> String { + to_string(NO_ANN, |s| s.print_type(ty)) +} + +pub fn path_segment_to_string(segment: &hir::PathSegment<'_>) -> String { + to_string(NO_ANN, |s| s.print_path_segment(segment)) +} + +pub fn path_to_string(segment: &hir::Path<'_>) -> String { + to_string(NO_ANN, |s| s.print_path(segment, false)) +} + +pub fn fn_to_string( + decl: &hir::FnDecl<'_>, + header: hir::FnHeader, + name: Option, + generics: &hir::Generics<'_>, + vis: &hir::Visibility<'_>, + arg_names: &[Ident], + body_id: Option, +) -> String { + to_string(NO_ANN, |s| s.print_fn(decl, header, name, generics, vis, arg_names, body_id)) +} + +pub fn enum_def_to_string( + enum_definition: &hir::EnumDef<'_>, + generics: &hir::Generics<'_>, + name: Symbol, + span: rustc_span::Span, + visibility: &hir::Visibility<'_>, +) -> String { + to_string(NO_ANN, |s| s.print_enum_def(enum_definition, generics, name, span, visibility)) +} + +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::OpaqueDef(..) => self.s.word("/*impl Trait*/"), + 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)); + } + } + 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..7b3030fa1d9c1 100644 --- a/src/librustc_incremental/Cargo.toml +++ b/src/librustc_incremental/Cargo.toml @@ -10,13 +10,13 @@ path = "lib.rs" doctest = false [dependencies] -graphviz = { path = "../libgraphviz" } +rustc_graphviz = { path = "../librustc_graphviz" } 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" } +rustc_serialize = { path = "../librustc_serialize" } rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } rustc_fs_util = { path = "../librustc_fs_util" } diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs index a7dccaf974b82..b1665d9e1aeb3 100644 --- a/src/librustc_incremental/assert_dep_graph.rs +++ b/src/librustc_incremental/assert_dep_graph.rs @@ -33,18 +33,18 @@ //! fn baz() { foo(); } //! ``` -use graphviz as dot; -use rustc::dep_graph::debug::{DepNodeFilter, EdgeFilter}; -use rustc::dep_graph::{DepGraphQuery, DepKind, DepNode}; -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_graphviz as dot; 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 54d7e0ece5031..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() { @@ -175,6 +175,6 @@ impl AssertModuleSource<'tcx> { return true; } debug!("check_config: no match found"); - return false; + false } } 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 cfc0a5e1498c8..2ee95174dffe6 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}; -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; @@ -53,9 +53,9 @@ const BASE_FN: &[&str] = &[ /// DepNodes for Hir, which is pretty much everything const BASE_HIR: &[&str] = &[ - // hir_owner and hir_owner_items should be computed for all nodes + // hir_owner and hir_owner_nodes should be computed for all nodes label_strs::hir_owner, - label_strs::hir_owner_items, + label_strs::hir_owner_nodes, ]; /// `impl` implementation of struct/trait @@ -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. @@ -333,10 +333,9 @@ impl DirtyCleanVisitor<'tcx> { TraitItemKind::Type(..) => ("NodeTraitType", LABELS_CONST_IN_TRAIT), }, HirNode::ImplItem(item) => match item.kind { - ImplItemKind::Method(..) => ("Node::ImplItem", LABELS_FN_IN_IMPL), + ImplItemKind::Fn(..) => ("Node::ImplItem", LABELS_FN_IN_IMPL), ImplItemKind::Const(..) => ("NodeImplConst", LABELS_CONST_IN_IMPL), ImplItemKind::TyAlias(..) => ("NodeImplType", LABELS_CONST_IN_IMPL), - ImplItemKind::OpaqueTy(..) => ("NodeImplType", LABELS_CONST_IN_IMPL), }, _ => self.tcx.sess.span_fatal( attr.span, @@ -434,16 +433,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 +498,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/file_format.rs b/src/librustc_incremental/persist/file_format.rs index 5c72b049d97e9..048a81b81bab3 100644 --- a/src/librustc_incremental/persist/file_format.rs +++ b/src/librustc_incremental/persist/file_format.rs @@ -14,8 +14,8 @@ use std::fs; use std::io::{self, Read}; use std::path::Path; -use rustc::session::config::nightly_options; use rustc_serialize::opaque::Encoder; +use rustc_session::config::nightly_options; /// The first few bytes of files generated by incremental compilation. const FILE_MAGIC: &[u8] = b"RSIC"; diff --git a/src/librustc_incremental/persist/fs.rs b/src/librustc_incremental/persist/fs.rs index 8548ad392d2dd..4926f726f3593 100644 --- a/src/librustc_incremental/persist/fs.rs +++ b/src/librustc_incremental/persist/fs.rs @@ -103,11 +103,11 @@ //! unsupported file system and emit a warning in that case. This is not yet //! implemented. -use rustc::session::{CrateDisambiguator, Session}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::svh::Svh; use rustc_data_structures::{base_n, flock}; use rustc_fs_util::{link_or_copy, LinkOrCopy}; +use rustc_session::{CrateDisambiguator, Session}; use std::fs as std_fs; use std::io; diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 6c57f79e1a7fb..966faa9639d72 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -1,12 +1,12 @@ //! Code to save/load the dep-graph from files. -use rustc::dep_graph::{PreviousDepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; -use rustc::session::Session; -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; use std::path::Path; use super::data::*; @@ -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 87f39dedd0273..c43d4ad4049c9 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -1,10 +1,10 @@ -use rustc::dep_graph::{DepGraph, DepKind, WorkProduct, WorkProductId}; -use rustc::session::Session; -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; use std::fs; use std::path::PathBuf; @@ -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()) }); @@ -132,7 +130,6 @@ where } Err(err) => { sess.err(&format!("failed to write dep-graph to `{}`: {}", path_buf.display(), err)); - return; } } } diff --git a/src/librustc_incremental/persist/work_product.rs b/src/librustc_incremental/persist/work_product.rs index b1861acec0426..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::session::Session; 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/Cargo.toml b/src/librustc_index/Cargo.toml index 1435297f27ada..f0422b1af1b97 100644 --- a/src/librustc_index/Cargo.toml +++ b/src/librustc_index/Cargo.toml @@ -10,5 +10,5 @@ path = "lib.rs" doctest = false [dependencies] -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } 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/lib.rs b/src/librustc_index/lib.rs index 86dd1a29d0ce3..3effc41645011 100644 --- a/src/librustc_index/lib.rs +++ b/src/librustc_index/lib.rs @@ -1,4 +1,8 @@ #![feature(allow_internal_unstable)] +#![feature(const_if_match)] +#![feature(const_fn)] +#![feature(const_panic)] +#![feature(extend_one)] #![feature(unboxed_closures)] #![feature(test)] #![feature(fn_traits)] diff --git a/src/librustc_index/vec.rs b/src/librustc_index/vec.rs index 7020939fa20b2..4dde33283f575 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 ---- @@ -120,10 +119,10 @@ macro_rules! newtype_index { impl $type { $v const MAX_AS_U32: u32 = $max; - $v const MAX: Self = Self::from_u32_const($max); + $v const MAX: Self = Self::from_u32($max); #[inline] - $v fn from_usize(value: usize) -> Self { + $v const fn from_usize(value: usize) -> Self { assert!(value <= ($max as usize)); unsafe { Self::from_u32_unchecked(value as u32) @@ -131,31 +130,13 @@ macro_rules! newtype_index { } #[inline] - $v fn from_u32(value: u32) -> Self { + $v const fn from_u32(value: u32) -> Self { assert!(value <= $max); unsafe { Self::from_u32_unchecked(value) } } - /// Hacky variant of `from_u32` for use in constants. - /// This version checks the "max" constraint by using an - /// invalid array dereference. - #[inline] - $v const fn from_u32_const(value: u32) -> Self { - // This will fail at const eval time unless `value <= - // max` is true (in which case we get the index 0). - // It will also fail at runtime, of course, but in a - // kind of wacky way. - let _ = ["out of range value used"][ - !(value <= $max) as usize - ]; - - unsafe { - Self { private: value } - } - } - #[inline] $v const unsafe fn from_u32_unchecked(value: u32) -> Self { Self { private: value } @@ -163,19 +144,19 @@ macro_rules! newtype_index { /// Extracts the value of this index as an integer. #[inline] - $v fn index(self) -> usize { + $v const fn index(self) -> usize { self.as_usize() } /// Extracts the value of this index as a `u32`. #[inline] - $v fn as_u32(self) -> u32 { + $v const fn as_u32(self) -> u32 { self.private } /// Extracts the value of this index as a `usize`. #[inline] - $v fn as_usize(self) -> usize { + $v const fn as_usize(self) -> usize { self.as_u32() as usize } } @@ -200,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( @@ -210,33 +191,13 @@ macro_rules! newtype_index { } #[inline] - fn replace_one(&mut self) -> Self { - ::std::mem::replace(self, Self::from_u32(1)) - } - - #[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) + fn forward_checked(start: Self, u: usize) -> Option { + Self::index(start).checked_add(u).map(Self::from_usize) } #[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) } } @@ -500,7 +461,7 @@ macro_rules! newtype_index { const $name:ident = $constant:expr, $($tokens:tt)*) => ( $(#[doc = $doc])* - $v const $name: $type = $type::from_u32_const($constant); + $v const $name: $type = $type::from_u32($constant); $crate::newtype_index!( @derives [$($derives,)*] @attrs [$(#[$attrs])*] @@ -775,6 +736,16 @@ impl Extend for IndexVec { fn extend>(&mut self, iter: J) { self.raw.extend(iter); } + + #[inline] + fn extend_one(&mut self, item: T) { + self.raw.push(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.raw.reserve(additional); + } } impl FromIterator for IndexVec { diff --git a/src/librustc_infer/Cargo.toml b/src/librustc_infer/Cargo.toml index 4f97fd82874ff..06fc7ecf95f26 100644 --- a/src/librustc_infer/Cargo.toml +++ b/src/librustc_infer/Cargo.toml @@ -10,14 +10,16 @@ path = "lib.rs" doctest = false [dependencies] -graphviz = { path = "../libgraphviz" } +rustc_graphviz = { path = "../librustc_graphviz" } 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 = "../librustc_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 964e378f7abc8..2dc803b959533 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>, @@ -314,32 +314,28 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { } ty::ReVar(vid) => { - let r = self + let resolved_vid = self .infcx .unwrap() .inner .borrow_mut() .unwrap_region_constraints() - .opportunistic_resolve_var(self.tcx, vid); + .opportunistic_resolve_var(vid); debug!( "canonical: region var found with vid {:?}, \ opportunistically resolved to {:?}", vid, r ); + let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid)); self.canonicalize_region_mode.canonicalize_free_region(self, r) } ty::ReStatic | ty::ReEarlyBound(..) | ty::ReFree(_) - | ty::ReScope(_) | ty::ReEmpty(_) | ty::RePlaceholder(..) | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r), - - ty::ReClosureBound(..) => { - bug!("closure bound region encountered during canonicalization"); - } } } @@ -357,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)), @@ -379,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") } @@ -408,7 +404,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { | ty::Float(..) | ty::Adt(..) | ty::Str - | ty::Error + | ty::Error(_) | ty::Array(..) | ty::Slice(..) | ty::RawPtr(..) @@ -419,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(..) => { @@ -445,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, @@ -492,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 @@ -528,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); @@ -555,7 +552,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { // avoid allocations in those cases. We also don't use `indices` to // determine if a kind has been seen before until the limit of 8 has // been exceeded, to also avoid allocations for `indices`. - let var = if !var_values.spilled() { + if !var_values.spilled() { // `var_values` is stack-allocated. `indices` isn't used yet. Do a // direct linear search of `var_values`. if let Some(idx) = var_values.iter().position(|&k| k == kind) { @@ -589,9 +586,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { assert_eq!(variables.len(), var_values.len()); BoundVar::new(variables.len() - 1) }) - }; - - var + } } /// Shorthand helper that creates a canonical region variable for diff --git a/src/librustc_infer/infer/canonical/mod.rs b/src/librustc_infer/infer/canonical/mod.rs index 0e9593e8ea6b0..2b8c46f1de42d 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; @@ -87,7 +87,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { ) -> CanonicalVarValues<'tcx> { let var_values: IndexVec> = variables .iter() - .map(|info| self.instantiate_canonical_var(span, *info, &universe_map)) + .map(|info| self.instantiate_canonical_var(span, info, &universe_map)) .collect(); CanonicalVarValues { var_values } @@ -154,7 +154,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { self.tcx .mk_const(ty::Const { val: ty::ConstKind::Placeholder(placeholder_mapped), - ty: self.tcx.types.err, // FIXME(const_generics) + ty: self.tcx.ty_error(), // FIXME(const_generics) }) .into() } diff --git a/src/librustc_infer/infer/canonical/query_response.rs b/src/librustc_infer/infer/canonical/query_response.rs index 9322df4823511..8af526e3ad31b 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, ToPredicate, Ty, TyCtxt}; use std::fmt::Debug; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { @@ -56,7 +56,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { ) -> Fallible> where T: Debug + TypeFoldable<'tcx>, - Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable, + Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, { let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?; let canonical_result = self.canonicalize_response(&query_response); @@ -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>, @@ -464,12 +464,12 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { if info.is_existential() { match opt_values[BoundVar::new(index)] { Some(k) => k, - None => self.instantiate_canonical_var(cause.span, *info, |u| { + None => self.instantiate_canonical_var(cause.span, info, |u| { universe_map[u.as_usize()] }), } } else { - self.instantiate_canonical_var(cause.span, *info, |u| { + self.instantiate_canonical_var(cause.span, info, |u| { universe_map[u.as_usize()] }) } @@ -532,12 +532,14 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { cause.clone(), param_env, match k1.unpack() { - GenericArgKind::Lifetime(r1) => ty::Predicate::RegionOutlives( + GenericArgKind::Lifetime(r1) => ty::PredicateKind::RegionOutlives( ty::Binder::bind(ty::OutlivesPredicate(r1, r2)), - ), - GenericArgKind::Type(t1) => { - ty::Predicate::TypeOutlives(ty::Binder::bind(ty::OutlivesPredicate(t1, r2))) - } + ) + .to_predicate(self.tcx), + GenericArgKind::Type(t1) => ty::PredicateKind::TypeOutlives(ty::Binder::bind( + ty::OutlivesPredicate(t1, r2), + )) + .to_predicate(self.tcx), GenericArgKind::Const(..) => { // Consts cannot outlive one another, so we don't expect to // ecounter this branch. @@ -664,15 +666,19 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { self.obligations.push(Obligation { cause: self.cause.clone(), param_env: self.param_env, - predicate: ty::Predicate::RegionOutlives(ty::Binder::dummy(ty::OutlivesPredicate( + predicate: ty::PredicateKind::RegionOutlives(ty::Binder::dummy(ty::OutlivesPredicate( sup, sub, - ))), + ))) + .to_predicate(self.infcx.tcx), recursion_depth: 0, }); } - 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 9d06e26d9bb3c..4ef4ed47cb11a 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, ToPredicate, 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,13 +301,13 @@ 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( self.trace.cause.clone(), self.param_env, - ty::Predicate::WellFormed(b_ty), + ty::PredicateKind::WellFormed(b_ty.into()).to_predicate(self.infcx.tcx), )); } @@ -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,24 @@ 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::PredicateKind::ConstEquate(a, b) + } else { + ty::PredicateKind::ConstEquate(b, a) + }; + self.obligations.push(Obligation::new( + self.trace.cause.clone(), + self.param_env, + predicate.to_predicate(self.tcx()), + )); + } } struct Generalizer<'cx, 'tcx> { @@ -508,14 +541,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 +575,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 +589,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. @@ -581,15 +615,10 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { return Ok(r); } - ty::ReClosureBound(..) => { - span_bug!(self.span, "encountered unexpected ReClosureBound: {:?}", r,); - } - ty::RePlaceholder(..) | ty::ReVar(..) | ty::ReEmpty(_) | ty::ReStatic - | ty::ReScope(..) | ty::ReEarlyBound(..) | ty::ReFree(..) => { // see common code below @@ -622,7 +651,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), @@ -639,11 +669,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 bb0c124a1892d..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)), _) => { @@ -136,7 +136,13 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> { } else { // Fast path for the common case. self.relate(a.skip_binder(), b.skip_binder())?; - return Ok(a.clone()); + Ok(a.clone()) } } } + +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 4a39403f211cc..7fdcbd31df3c5 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -49,26 +49,24 @@ use super::lexical_region_resolve::RegionResolutionError; use super::region_constraints::GenericKind; use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; -use crate::infer::{self, SuppressRegionErrors}; +use crate::infer; use crate::traits::error_reporting::report_object_safety_error; 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_hir::{Item, ItemKind, Node}; +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}; @@ -82,57 +80,12 @@ pub mod nice_region_error; pub(super) fn note_and_explain_region( tcx: TyCtxt<'tcx>, - region_scope_tree: ®ion::ScopeTree, err: &mut DiagnosticBuilder<'_>, prefix: &str, region: ty::Region<'tcx>, suffix: &str, ) { let (description, span) = match *region { - ty::ReScope(scope) => { - let new_string; - 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)) { - Some(Node::Block(_)) => "block", - Some(Node::Expr(expr)) => match expr.kind { - hir::ExprKind::Call(..) => "call", - hir::ExprKind::MethodCall(..) => "method call", - hir::ExprKind::Match(.., hir::MatchSource::IfLetDesugar { .. }) => "if let", - hir::ExprKind::Match(.., hir::MatchSource::WhileLetDesugar) => "while let", - hir::ExprKind::Match(.., hir::MatchSource::ForLoopDesugar) => "for", - hir::ExprKind::Match(..) => "match", - _ => "expression", - }, - Some(Node::Stmt(_)) => "statement", - 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), - Some(_) | None => { - err.span_note(span, &unknown_scope()); - return; - } - }; - let scope_decorated_tag = match scope.data { - region::ScopeData::Node => tag, - region::ScopeData::CallSite => "scope of call-site for function", - region::ScopeData::Arguments => "scope of function body", - region::ScopeData::Destruction => { - new_string = format!("destruction scope surrounding {}", tag); - &new_string[..] - } - region::ScopeData::Remainder(first_statement_index) => { - new_string = format!( - "block suffix following statement {}", - first_statement_index.index() - ); - &new_string[..] - } - }; - explain_span(tcx, scope_decorated_tag, span) - } - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => { msg_span_from_free_region(tcx, region) } @@ -152,11 +105,6 @@ pub(super) fn note_and_explain_region( ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => { (format!("lifetime {:?}", region), None) } - - // We shouldn't encounter an error message with ReClosureBound. - ty::ReClosureBound(..) => { - bug!("encountered unexpected ReClosureBound: {:?}", region,); - } }; emit_msg_span(err, prefix, description, span, suffix); @@ -196,9 +144,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), @@ -206,7 +154,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)) { @@ -215,7 +163,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)) { @@ -229,7 +177,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!(), @@ -275,10 +223,8 @@ fn trait_item_scope_tag(item: &hir::TraitItem<'_>) -> &'static str { fn impl_item_scope_tag(item: &hir::ImplItem<'_>) -> &'static str { match item.kind { - hir::ImplItemKind::Method(..) => "method body", - hir::ImplItemKind::Const(..) - | hir::ImplItemKind::OpaqueTy(..) - | hir::ImplItemKind::TyAlias(..) => "associated item", + hir::ImplItemKind::Fn(..) => "method body", + hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(..) => "associated item", } } @@ -289,7 +235,6 @@ fn explain_span(tcx: TyCtxt<'tcx>, heading: &str, span: Span) -> (String, Option pub fn unexpected_hidden_region_diagnostic( tcx: TyCtxt<'tcx>, - region_scope_tree: Option<®ion::ScopeTree>, span: Span, hidden_ty: Ty<'tcx>, hidden_region: ty::Region<'tcx>, @@ -302,64 +247,56 @@ pub fn unexpected_hidden_region_diagnostic( ); // Explain the region we are capturing. - if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) = hidden_region { - // Assuming regionck succeeded (*), we ought to always be - // capturing *some* region from the fn header, and hence it - // ought to be free. So under normal circumstances, we will go - // 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. - note_and_explain_free_region( - tcx, - &mut err, - &format!("hidden type `{}` captures ", hidden_ty), - hidden_region, - "", - ); - } else { - // Ugh. This is a painful case: the hidden region is not one - // that we can easily summarize or explain. This can happen - // in a case like - // `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: - // - // ``` - // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { - // if condition() { a } else { b } - // } - // ``` - // - // Here the captured lifetime is the intersection of `'a` and - // `'b`, which we can't quite express. - - if let Some(region_scope_tree) = region_scope_tree { - // If the `region_scope_tree` is available, this is being - // invoked from the "region inferencer error". We can at - // least report a really cryptic error for now. - note_and_explain_region( + match hidden_region { + ty::ReEmpty(ty::UniverseIndex::ROOT) => { + // All lifetimes shorter than the function body are `empty` in + // lexical region resolution. The default explanation of "an empty + // lifetime" isn't really accurate here. + let message = format!( + "hidden type `{}` captures lifetime smaller than the function body", + hidden_ty + ); + err.span_note(span, &message); + } + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) => { + // Assuming regionck succeeded (*), we ought to always be + // capturing *some* region from the fn header, and hence it + // ought to be free. So under normal circumstances, we will go + // down this path which gives a decent human readable + // explanation. + // + // (*) 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, - region_scope_tree, &mut err, &format!("hidden type `{}` captures ", hidden_ty), hidden_region, "", ); - } else { - // If the `region_scope_tree` is *unavailable*, this is - // being invoked by the code that comes *after* region - // inferencing. This is a bug, as the region inferencer - // ought to have noticed the failed constraint and invoked - // error reporting, which in turn should have prevented us - // from getting trying to infer the hidden type - // completely. - tcx.sess.delay_span_bug( - span, - &format!( - "hidden type captures unexpected lifetime `{:?}` \ - but no region inference failure", - hidden_region, - ), + } + _ => { + // Ugh. This is a painful case: the hidden region is not one + // that we can easily summarize or explain. This can happen + // in a case like + // `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: + // + // ``` + // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { + // if condition() { a } else { b } + // } + // ``` + // + // Here the captured lifetime is the intersection of `'a` and + // `'b`, which we can't quite express. + + // We can at least report a really cryptic error for now. + note_and_explain_region( + tcx, + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", ); } } @@ -368,21 +305,8 @@ pub fn unexpected_hidden_region_diagnostic( } impl<'a, 'tcx> InferCtxt<'a, 'tcx> { - pub fn report_region_errors( - &self, - region_scope_tree: ®ion::ScopeTree, - errors: &Vec>, - suppress: SuppressRegionErrors, - ) { - debug!( - "report_region_errors(): {} errors to start, suppress = {:?}", - errors.len(), - suppress - ); - - if suppress.suppressed() { - return; - } + pub fn report_region_errors(&self, errors: &Vec>) { + debug!("report_region_errors(): {} errors to start", errors.len()); // try to pre-process the errors, which will group some of them // together into a `ProcessedErrors` group: @@ -404,17 +328,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // general bit of code that displays the error information RegionResolutionError::ConcreteFailure(origin, sub, sup) => { if sub.is_placeholder() || sup.is_placeholder() { - self.report_placeholder_failure(region_scope_tree, origin, sub, sup) - .emit(); + self.report_placeholder_failure(origin, sub, sup).emit(); } else { - self.report_concrete_failure(region_scope_tree, origin, sub, sup) - .emit(); + self.report_concrete_failure(origin, sub, sup).emit(); } } RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => { self.report_generic_bound_failure( - region_scope_tree, origin.span(), Some(origin), param_ty, @@ -431,29 +352,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { sup_r, ) => { if sub_r.is_placeholder() { - self.report_placeholder_failure( - region_scope_tree, - sub_origin, - sub_r, - sup_r, - ) - .emit(); + self.report_placeholder_failure(sub_origin, sub_r, sup_r).emit(); } else if sup_r.is_placeholder() { - self.report_placeholder_failure( - region_scope_tree, - sup_origin, - sub_r, - sup_r, - ) - .emit(); + self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit(); } else { self.report_sub_sup_conflict( - region_scope_tree, - var_origin, - sub_origin, - sub_r, - sup_origin, - sup_r, + var_origin, sub_origin, sub_r, sup_origin, sup_r, ); } } @@ -474,13 +378,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // value. let sub_r = self.tcx.mk_region(ty::ReEmpty(var_universe)); - self.report_placeholder_failure( - region_scope_tree, - sup_origin, - sub_r, - sup_r, - ) - .emit(); + self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit(); } RegionResolutionError::MemberConstraintFailure { @@ -491,7 +389,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let hidden_ty = self.resolve_vars_if_possible(&hidden_ty); unexpected_hidden_region_diagnostic( self.tcx, - Some(region_scope_tree), span, hidden_ty, member_region, @@ -563,7 +460,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; @@ -769,7 +666,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, @@ -855,7 +754,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// For the following code: /// - /// ```norun + /// ```no_run /// let x: Foo> = foo::>(); /// ``` /// @@ -885,7 +784,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(()); @@ -1072,24 +971,26 @@ 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, } } fn push_ty_ref<'tcx>( - r: &ty::Region<'tcx>, + region: &ty::Region<'tcx>, ty: Ty<'tcx>, mutbl: hir::Mutability, s: &mut DiagnosticStyledString, ) { - let mut r = r.to_string(); + let mut r = region.to_string(); if r == "'_" { r.clear(); } else { @@ -1105,8 +1006,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. @@ -1355,7 +1256,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { (ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => { let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); let mut values = self.cmp_fn_sig(&sig1, sig2); - values.0.push_normal(format!( + values.0.push_highlighted(format!( " {{{}}}", self.tcx.def_path_str_with_substs(*did1, substs1) )); @@ -1398,16 +1299,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>, @@ -1613,11 +1511,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, @@ -1628,60 +1526,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, + ); } } } } - _ => {} } } @@ -1771,66 +1665,107 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_generic_bound_failure( &self, - region_scope_tree: ®ion::ScopeTree, span: Span, origin: Option>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, ) { - self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub) - .emit(); + self.construct_generic_bound_failure(span, origin, bound_kind, sub).emit(); } pub fn construct_generic_bound_failure( &self, - region_scope_tree: ®ion::ScopeTree, span: Span, origin: Option>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, ) -> DiagnosticBuilder<'a> { + let hir = &self.tcx.hir(); // Attempt to obtain the span of the parameter so we can // suggest adding an explicit lifetime bound to it. - let type_param_span = match (self.in_progress_tables, bound_kind) { - (Some(ref table), GenericKind::Param(ref param)) => { - let table = table.borrow(); - table.local_id_root.and_then(|did| { - let generics = self.tcx.generics_of(did); - // Account for the case where `did` corresponds to `Self`, which doesn't have - // the expected type argument. - 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| { - // 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 mut has_bounds = false; - if let Node::GenericParam(param) = hir.get(id) { - has_bounds = !param.bounds.is_empty(); - } - let sp = hir.span(id); - // `sp` only covers `T`, change it so that it covers - // `T:` when appropriate - let is_impl_trait = bound_kind.to_string().starts_with("impl "); - let sp = if has_bounds && !is_impl_trait { - sp.to(self - .tcx - .sess - .source_map() - .next_point(self.tcx.sess.source_map().next_point(sp))) - } else { - sp - }; - (sp, has_bounds, is_impl_trait) - }) + let generics = + self.in_progress_tables.and_then(|table| table.borrow().hir_owner).map(|table_owner| { + let hir_id = hir.as_local_hir_id(table_owner); + let parent_id = hir.get_parent_item(hir_id); + ( + // Parent item could be a `mod`, so we check the HIR before calling: + if let Some(Node::Item(Item { + kind: ItemKind::Trait(..) | ItemKind::Impl { .. }, + .. + })) = hir.find(parent_id) + { + Some(self.tcx.generics_of(hir.local_def_id(parent_id).to_def_id())) } else { None - } - }) + }, + self.tcx.generics_of(table_owner.to_def_id()), + ) + }); + let type_param_span = match (generics, bound_kind) { + (Some((_, ref generics)), GenericKind::Param(ref param)) => { + // Account for the case where `param` corresponds to `Self`, + // which doesn't have the expected type argument. + if !(generics.has_self && param.index == 0) { + let type_param = generics.type_param(param, self.tcx); + 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(); + } + let sp = hir.span(id); + // `sp` only covers `T`, change it so that it covers + // `T:` when appropriate + let is_impl_trait = bound_kind.to_string().starts_with("impl "); + let sp = if has_bounds && !is_impl_trait { + sp.to(self + .tcx + .sess + .source_map() + .next_point(self.tcx.sess.source_map().next_point(sp))) + } else { + sp + }; + (sp, has_bounds, is_impl_trait) + }) + } else { + None + } } _ => None, }; + let new_lt = generics + .as_ref() + .and_then(|(parent_g, g)| { + let possible: Vec<_> = (b'a'..=b'z').map(|c| format!("'{}", c as char)).collect(); + let mut lts_names = g + .params + .iter() + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .map(|p| p.name.as_str()) + .collect::>(); + if let Some(g) = parent_g { + lts_names.extend( + g.params + .iter() + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .map(|p| p.name.as_str()), + ); + } + let lts = lts_names.iter().map(|s| -> &str { &*s }).collect::>(); + possible.into_iter().find(|candidate| !lts.contains(&candidate.as_str())) + }) + .unwrap_or("'lt".to_string()); + let add_lt_sugg = generics + .as_ref() + .and_then(|(_, g)| g.params.first()) + .and_then(|param| param.def_id.as_local()) + .map(|def_id| { + (hir.span(hir.as_local_hir_id(def_id)).shrink_to_lo(), format!("{}, ", new_lt)) + }); let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{}`", p), @@ -1887,6 +1822,29 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + let new_binding_suggestion = + |err: &mut DiagnosticBuilder<'tcx>, + type_param_span: Option<(Span, bool, bool)>, + bound_kind: GenericKind<'tcx>| { + let msg = "consider introducing an explicit lifetime bound"; + if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span { + let suggestion = if is_impl_trait { + (sp.shrink_to_hi(), format!(" + {}", new_lt)) + } else { + let tail = if has_lifetimes { " +" } else { "" }; + (sp, format!("{}: {}{}", bound_kind, new_lt, tail)) + }; + let mut sugg = + vec![suggestion, (span.shrink_to_hi(), format!(" + {}", new_lt))]; + if let Some(lt) = add_lt_sugg { + sugg.push(lt); + sugg.rotate_right(1); + } + // `MaybeIncorrect` due to issue #41966. + err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect); + } + }; + let mut err = match *sub { ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. }) | ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }) => { @@ -1928,18 +1886,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "{} may not live long enough", labeled_user_string ); - err.help(&format!( - "consider adding an explicit lifetime bound for `{}`", - bound_kind - )); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, &format!("{} must be valid for ", labeled_user_string), sub, "...", ); + if let Some(infer::RelateParamBound(_, t)) = origin { + let t = self.resolve_vars_if_possible(&t); + match t.kind { + // We've got: + // fn get_later(g: G, dest: &mut T) -> impl FnOnce() + '_ + // suggest: + // fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a + ty::Closure(_, _substs) | ty::Opaque(_, _substs) => { + new_binding_suggestion(&mut err, type_param_span, bound_kind); + } + _ => { + binding_suggestion(&mut err, type_param_span, bound_kind, new_lt); + } + } + } err } }; @@ -1952,7 +1920,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn report_sub_sup_conflict( &self, - region_scope_tree: ®ion::ScopeTree, var_origin: RegionVariableOrigin, sub_origin: SubregionOrigin<'tcx>, sub_region: Region<'tcx>, @@ -1963,56 +1930,53 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "first, the lifetime cannot outlive ", sup_region, "...", ); - 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()), - ); + 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); - err.note_expected_found(&"", sup_expected, &"", sup_found); - err.emit(); - return; - } + if let (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) = + (&sup_origin, &sub_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, + &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; } } - _ => {} } self.note_region_origin(&mut err, &sup_origin); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "but, the lifetime must be valid for ", sub_region, @@ -2071,8 +2035,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.sess, var_origin.span(), E0495, - "cannot infer an appropriate lifetime{} \ - due to conflicting requirements", + "cannot infer an appropriate lifetime{} due to conflicting requirements", var_description ) } 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 0eda4555e2565..04d941fb8a7c4 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::{struct_span_err, Applicability, DiagnosticBuilder}; +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,63 @@ 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)) + { + // FIXME: There's a trade-off here - we can either check that our target span + // is contained in `local.span` or not. If we choose to check containment + // we can avoid some spurious suggestions (see #72690), but we lose + // the ability to report on things like: + // + // ``` + // let x = vec![]; + // ``` + // + // because the target span will be in the macro expansion of `vec![]`. + // At present we choose not to check containment. 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); + if self.target_span.contains(param.pat.span) { + self.found_arg_pattern = Some(&*param.pat); + 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 +185,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 +218,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 +232,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 +258,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 +269,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 +283,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 }; @@ -254,7 +307,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // 3 | let _ = x.sum() as f64; // | ^^^ cannot infer type for `S` span - } else if let Some(ExprKind::MethodCall(_, call_span, _)) = + } else if let Some(ExprKind::MethodCall(_, call_span, _, _)) = local_visitor.found_method_call.map(|e| &e.kind) { // Point at the call instead of the whole expression: @@ -276,14 +329,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(def_id, substs), .. }) => { - let fn_sig = substs.as_closure().sig(*def_id, self.tcx); + 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 +364,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { error_code, ); - let suffix = match local_visitor.found_ty { - Some(ty::TyS { kind: ty::Closure(def_id, substs), .. }) => { - let fn_sig = substs.as_closure().sig(*def_id, self.tcx); + 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 +413,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 +542,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, + "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( @@ -462,24 +580,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { e: &Expr<'_>, err: &mut DiagnosticBuilder<'_>, ) { - if let (Ok(snippet), Some(tables), None) = ( - self.tcx.sess.source_map().span_to_snippet(segment.ident.span), - self.in_progress_tables, - &segment.args, - ) { + if let (Some(tables), None) = (self.in_progress_tables, &segment.args) { let borrow = tables.borrow(); if let Some((DefKind::AssocFn, did)) = borrow.type_dependent_def(e.hir_id) { let generics = self.tcx.generics_of(did); if !generics.params.is_empty() { - err.span_suggestion( - segment.ident.span, + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi(), &format!( "consider specifying the type argument{} in the method call", - if generics.params.len() > 1 { "s" } else { "" }, + pluralize!(generics.params.len()), ), format!( - "{}::<{}>", - snippet, + "::<{}>", generics .params .iter() @@ -495,7 +608,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 1a09729ef6443..7ab18e54f7ea2 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 @@ -3,9 +3,10 @@ use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo; use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use rustc::util::common::ErrorReported; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::SubregionOrigin; -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. @@ -47,6 +48,15 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub(super) fn try_report_anon_anon_conflict(&self) -> Option { let (span, sub, sup) = self.regions()?; + if let Some(RegionResolutionError::ConcreteFailure( + SubregionOrigin::ReferenceOutlivesReferent(..), + .., + )) = self.error + { + // This error doesn't make much sense in this case. + return None; + } + // Determine whether the sub and sup consist of both anonymous (elided) regions. let anon_reg_sup = self.tcx().is_suitable_region(sup)?; @@ -111,16 +121,14 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { (Some(ret_span), _) => ( ty_sub.span, ret_span, - "this parameter and the return type are declared \ - with different lifetimes..." + "this parameter and the return type are declared with different lifetimes..." .to_owned(), format!("...but data{} is returned here", span_label_var1), ), (_, Some(ret_span)) => ( ty_sup.span, ret_span, - "this parameter and the return type are declared \ - with different lifetimes..." + "this parameter and the return type are declared with different lifetimes..." .to_owned(), format!("...but data{} is returned here", span_label_var1), ), @@ -131,6 +139,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { .span_label(span_2, String::new()) .span_label(span, span_label) .emit(); - return Some(ErrorReported); + Some(ErrorReported) } } 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 47d6f5ccbb16d..6677c0e59f63a 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 @@ -26,10 +26,11 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { &self, region: Region<'tcx>, br: &ty::BoundRegion, - ) -> Option<(&hir::Ty<'_>, &hir::FnDecl<'_>)> { + ) -> Option<(&hir::Ty<'tcx>, &hir::FnDecl<'tcx>)> { 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 { @@ -37,7 +38,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { .. }) | Node::ImplItem(&hir::ImplItem { - kind: hir::ImplItemKind::Method(ref m, ..), + kind: hir::ImplItemKind::Fn(ref m, ..), .. }) => &m.decl, _ => return None, @@ -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 d8c314a0d2f1f..cc8f1816bc3f4 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,15 +1,13 @@ 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; mod find_anon_type; mod named_anon_conflict; -mod outlives_closure; mod placeholder_error; mod static_impl_trait; mod trait_impl_difference; @@ -17,12 +15,7 @@ mod util; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool { - if let Some(tables) = self.in_progress_tables { - let tables = tables.borrow(); - NiceRegionError::new(self, error.clone(), Some(&tables)).try_report().is_some() - } else { - NiceRegionError::new(self, error.clone(), None).try_report().is_some() - } + NiceRegionError::new(self, error.clone()).try_report().is_some() } } @@ -30,16 +23,11 @@ pub struct NiceRegionError<'cx, 'tcx> { infcx: &'cx InferCtxt<'cx, 'tcx>, error: Option>, regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>, - tables: Option<&'cx ty::TypeckTables<'tcx>>, } impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { - pub fn new( - infcx: &'cx InferCtxt<'cx, 'tcx>, - error: RegionResolutionError<'tcx>, - tables: Option<&'cx ty::TypeckTables<'tcx>>, - ) -> Self { - Self { infcx, error: Some(error), regions: None, tables } + pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>, error: RegionResolutionError<'tcx>) -> Self { + Self { infcx, error: Some(error), regions: None } } pub fn new_from_span( @@ -47,9 +35,8 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { span: Span, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>, - tables: Option<&'cx ty::TypeckTables<'tcx>>, ) -> Self { - Self { infcx, error: None, regions: Some((span, sub, sup)), tables } + Self { infcx, error: None, regions: Some((span, sub, sup)) } } fn tcx(&self) -> TyCtxt<'tcx> { @@ -68,10 +55,9 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { diag.emit(); ErrorReported }) + .or_else(|| self.try_report_impl_not_conforming_to_trait()) .or_else(|| self.try_report_anon_anon_conflict()) - .or_else(|| self.try_report_outlives_closure()) .or_else(|| self.try_report_static_impl_trait()) - .or_else(|| self.try_report_impl_not_conforming_to_trait()) } pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> { 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..3012928a09854 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,10 @@ //! 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_hir::intravisit::Visitor; +use rustc_hir::FnRetTy; +use rustc_middle::ty; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// When given a `ConcreteFailure` for a function with parameters containing a named region and @@ -21,8 +22,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // where the anonymous region appears (there must always be one; we // only introduced anonymous regions in parameters) as well as a // version new_ty of its type where the anonymous region is replaced - // with the named one.//scope_def_id - let (named, anon, anon_param_info, region_info) = if self.is_named_region(sub) + // with the named one. + let (named, anon, anon_param_info, region_info) = if sub.has_name() && self.tcx().is_suitable_region(sup).is_some() && self.find_param_with_region(sup, sub).is_some() { @@ -32,7 +33,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { self.find_param_with_region(sup, sub).unwrap(), self.tcx().is_suitable_region(sup).unwrap(), ) - } else if self.is_named_region(sup) + } else if sup.has_name() && self.tcx().is_suitable_region(sub).is_some() && self.find_param_with_region(sub, sup).is_some() { @@ -74,15 +75,27 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } if let Some((_, fndecl)) = self.find_anon_type(anon, &br) { - if self.is_return_type_anon(scope_def_id, br, fndecl).is_some() - || self.is_self_anon(is_first, scope_def_id) - { + let is_self_anon = self.is_self_anon(is_first, scope_def_id); + if is_self_anon { return None; } + if let FnRetTy::Return(ty) = &fndecl.output { - if let (TyKind::Def(_, _), ty::ReStatic) = (&ty.kind, sub) { - // This is an impl Trait return that evaluates de need of 'static. - // We handle this case better in `static_impl_trait`. + let mut v = ty::TraitObjectVisitor(vec![], self.tcx().hir()); + v.visit_ty(ty); + + debug!("try_report_named_anon_conflict: ret ty {:?}", ty); + if sub == &ty::ReStatic + && v.0 + .into_iter() + .filter(|t| t.span.desugaring_kind().is_none()) + .next() + .is_some() + { + // If the failure is due to a `'static` requirement coming from a `dyn` or + // `impl` Trait that *isn't* caused by `async fn` desugaring, handle this case + // better in `static_impl_trait`. + debug!("try_report_named_anon_conflict: impl Trait + 'static"); return None; } } @@ -114,17 +127,4 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { Some(diag) } - - // This method returns whether the given Region is Named - pub(super) fn is_named_region(&self, region: ty::Region<'tcx>) -> bool { - match *region { - ty::ReStatic => true, - ty::ReFree(ref free_region) => match free_region.bound_region { - ty::BrNamed(..) => true, - _ => false, - }, - ty::ReEarlyBound(ebr) => ebr.has_name(), - _ => false, - } - } } 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 deleted file mode 100644 index d88e6555af93e..0000000000000 --- a/src/librustc_infer/infer/error_reporting/nice_region_error/outlives_closure.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Error Reporting for Anonymous Region Lifetime Errors -//! where both the regions are anonymous. - -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_hir::{Expr, ExprKind::Closure, Node}; - -impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - /// Print the error message for lifetime errors when binding escapes a closure. - /// - /// Consider a case where we have - /// - /// ```no_run - /// fn with_int(f: F) where F: FnOnce(&isize) { - /// let x = 3; - /// f(&x); - /// } - /// fn main() { - /// let mut x = None; - /// with_int(|y| x = Some(y)); - /// } - /// ``` - /// - /// the output will be - /// - /// ```text - /// let mut x = None; - /// ----- borrowed data cannot be stored into here... - /// with_int(|y| x = Some(y)); - /// --- ^ cannot be stored outside of its closure - /// | - /// ...because it cannot outlive this closure - /// ``` - pub(super) fn try_report_outlives_closure(&self) -> Option { - if let Some(SubSupConflict(_, origin, ref sub_origin, _, ref sup_origin, sup_region)) = - self.error - { - // #45983: when trying to assign the contents of an argument to a binding outside of a - // closure, provide a specific message pointing this out. - if let ( - &SubregionOrigin::BindingTypeIsNotValidAtDecl(ref external_span), - &RegionKind::ReFree(ref free_region), - ) = (&sub_origin, sup_region) - { - let hir = &self.tcx().hir(); - if let Some(hir_id) = hir.as_local_hir_id(free_region.scope) { - if let Node::Expr(Expr { kind: Closure(_, _, _, closure_span, None), .. }) = - hir.get(hir_id) - { - let sup_sp = sup_origin.span(); - let origin_sp = origin.span(); - let mut err = self.tcx().sess.struct_span_err( - sup_sp, - "borrowed data cannot be stored outside of its closure", - ); - err.span_label(sup_sp, "cannot be stored outside of its closure"); - if origin_sp == sup_sp || origin_sp.contains(sup_sp) { - // // sup_sp == origin.span(): - // - // let mut x = None; - // ----- borrowed data cannot be stored into here... - // with_int(|y| x = Some(y)); - // --- ^ cannot be stored outside of its closure - // | - // ...because it cannot outlive this closure - // - // // origin.contains(&sup_sp): - // - // let mut f: Option<&u32> = None; - // ----- borrowed data cannot be stored into here... - // closure_expecting_bound(|x: &'x u32| { - // ------------ ... because it cannot outlive this closure - // f = Some(x); - // ^ cannot be stored outside of its closure - err.span_label( - *external_span, - "borrowed data cannot be stored into here...", - ); - err.span_label( - *closure_span, - "...because it cannot outlive this closure", - ); - } else { - // FIXME: the wording for this case could be much improved - // - // let mut lines_to_use: Vec<&CrateId> = Vec::new(); - // - cannot infer an appropriate lifetime... - // let push_id = |installed_id: &CrateId| { - // ------- ------------------------ borrowed data cannot outlive this closure - // | - // ...so that variable is valid at time of its declaration - // lines_to_use.push(installed_id); - // ^^^^^^^^^^^^ cannot be stored outside of its closure - err.span_label(origin_sp, "cannot infer an appropriate lifetime..."); - err.span_label( - *external_span, - "...so that variable is valid at time of its \ - declaration", - ); - err.span_label( - *closure_span, - "borrowed data cannot outlive this closure", - ); - } - err.emit(); - return Some(ErrorReported); - } - } - } - } - None - } -} 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..46dad81a099bb 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 @@ -1,15 +1,15 @@ //! Error Reporting for static impl Traits. -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::{struct_span_err, Applicability, ErrorReported}; +use rustc_hir::{GenericBound, ItemKind, Lifetime, LifetimeName, TyKind}; +use rustc_middle::ty::RegionKind; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when the return type is a static impl Trait. pub(super) fn try_report_static_impl_trait(&self) -> Option { + debug!("try_report_static_impl_trait(error={:?})", self.error); if let Some(ref error) = self.error { if let RegionResolutionError::SubSupConflict( _, @@ -18,50 +18,206 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { sub_r, sup_origin, sup_r, - ) = error.clone() + ) = error { + debug!( + "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})", + var_origin, sub_origin, sub_r, sup_origin, sup_r + ); let anon_reg_sup = self.tcx().is_suitable_region(sup_r)?; - let return_ty = self.tcx().return_type_impl_trait(anon_reg_sup.def_id); - if sub_r == &RegionKind::ReStatic && return_ty.is_some() { + debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup); + let fn_returns = self.tcx().return_type_impl_or_dyn_traits(anon_reg_sup.def_id); + if fn_returns.is_empty() { + return None; + } + debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns); + if **sub_r == RegionKind::ReStatic { let sp = var_origin.span(); let return_sp = sub_origin.span(); - let mut err = - self.tcx().sess.struct_span_err(sp, "cannot infer an appropriate lifetime"); + let param_info = self.find_param_with_region(sup_r, sub_r)?; + let (lifetime_name, lifetime) = if sup_r.has_name() { + (sup_r.to_string(), format!("lifetime `{}`", sup_r)) + } else { + ("'_".to_owned(), "an anonymous lifetime `'_`".to_string()) + }; + let mut err = struct_span_err!( + self.tcx().sess, + sp, + E0759, + "cannot infer an appropriate lifetime" + ); err.span_label( - return_sp, - "this return type evaluates to the `'static` lifetime...", + param_info.param_ty_span, + &format!("this data with {}...", lifetime), ); - err.span_label(sup_origin.span(), "...but this borrow..."); + debug!("try_report_static_impl_trait: param_info={:?}", param_info); - let (lifetime, lt_sp_opt) = msg_span_from_free_region(self.tcx(), sup_r); - if let Some(lifetime_sp) = lt_sp_opt { - err.span_note(lifetime_sp, &format!("...can't outlive {}", lifetime)); + // We try to make the output have fewer overlapping spans if possible. + if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span())) + && sup_origin.span() != return_sp + { + // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs` + + // Customize the spans and labels depending on their relative order so + // that split sentences flow correctly. + if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() { + // Avoid the following: + // + // error: cannot infer an appropriate lifetime + // --> $DIR/must_outlive_least_region_or_bound.rs:18:50 + // | + // LL | fn foo(x: &i32) -> Box { Box::new(x) } + // | ---- ---------^- + // + // and instead show: + // + // error: cannot infer an appropriate lifetime + // --> $DIR/must_outlive_least_region_or_bound.rs:18:50 + // | + // LL | fn foo(x: &i32) -> Box { Box::new(x) } + // | ---- ^ + err.span_label( + sup_origin.span(), + "...is captured here, requiring it to live as long as `'static`", + ); + } else { + err.span_label(sup_origin.span(), "...is captured here..."); + if return_sp < sup_origin.span() { + err.span_note( + return_sp, + "...and is required to live as long as `'static` here", + ); + } else { + err.span_label( + return_sp, + "...and is required to live as long as `'static` here", + ); + } + } + } else { + err.span_label( + return_sp, + "...is captured and required to live as long as `'static` here", + ); } - let lifetime_name = match sup_r { - RegionKind::ReFree(FreeRegion { - bound_region: BoundRegion::BrNamed(_, ref name), - .. - }) => name.to_string(), - _ => "'_".to_owned(), + // FIXME: account for the need of parens in `&(dyn Trait + '_)` + let consider = "consider changing the"; + let declare = "to declare that the"; + let arg = match param_info.param.pat.simple_ident() { + Some(simple_ident) => format!("argument `{}`", simple_ident), + None => "the argument".to_string(), }; - let fn_return_span = return_ty.unwrap().1; - if let Ok(snippet) = - self.tcx().sess.source_map().span_to_snippet(fn_return_span) - { - // only apply this suggestion onto functions with - // explicit non-desugar'able return. - if fn_return_span.desugaring_kind().is_none() { - err.span_suggestion( - fn_return_span, - &format!( - "you can add a bound to the return type to make it last \ - less than `'static` and match {}", - lifetime, - ), - format!("{} + {}", snippet, lifetime_name), - Applicability::Unspecified, - ); + let explicit = + format!("you can add an explicit `{}` lifetime bound", lifetime_name); + let explicit_static = + format!("explicit `'static` bound to the lifetime of {}", arg); + let captures = format!("captures data from {}", arg); + let add_static_bound = + "alternatively, add an explicit `'static` bound to this reference"; + let plus_lt = format!(" + {}", lifetime_name); + for fn_return in fn_returns { + if fn_return.span.desugaring_kind().is_some() { + // Skip `async` desugaring `impl Future`. + continue; + } + match fn_return.kind { + TyKind::OpaqueDef(item_id, _) => { + let item = self.tcx().hir().item(item_id.id); + let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind { + opaque + } else { + err.emit(); + return Some(ErrorReported); + }; + + if let Some(span) = opaque + .bounds + .iter() + .filter_map(|arg| match arg { + GenericBound::Outlives(Lifetime { + name: LifetimeName::Static, + span, + .. + }) => Some(*span), + _ => None, + }) + .next() + { + err.span_suggestion_verbose( + span, + &format!("{} `impl Trait`'s {}", consider, explicit_static), + lifetime_name.clone(), + Applicability::MaybeIncorrect, + ); + err.span_suggestion_verbose( + param_info.param_ty_span, + add_static_bound, + param_info.param_ty.to_string(), + Applicability::MaybeIncorrect, + ); + } else if let Some(_) = opaque + .bounds + .iter() + .filter_map(|arg| match arg { + GenericBound::Outlives(Lifetime { name, span, .. }) + if name.ident().to_string() == lifetime_name => + { + Some(*span) + } + _ => None, + }) + .next() + { + } else { + err.span_suggestion_verbose( + fn_return.span.shrink_to_hi(), + &format!( + "{declare} `impl Trait` {captures}, {explicit}", + declare = declare, + captures = captures, + explicit = explicit, + ), + plus_lt.clone(), + Applicability::MaybeIncorrect, + ); + } + } + TyKind::TraitObject(_, lt) => match lt.name { + LifetimeName::ImplicitObjectLifetimeDefault => { + err.span_suggestion_verbose( + fn_return.span.shrink_to_hi(), + &format!( + "{declare} trait object {captures}, {explicit}", + declare = declare, + captures = captures, + explicit = explicit, + ), + plus_lt.clone(), + Applicability::MaybeIncorrect, + ); + } + name if name.ident().to_string() != lifetime_name => { + // With this check we avoid suggesting redundant bounds. This + // would happen if there are nested impl/dyn traits and only + // one of them has the bound we'd suggest already there, like + // in `impl Foo + '_`. + err.span_suggestion_verbose( + lt.span, + &format!("{} trait object's {}", consider, explicit_static), + lifetime_name.clone(), + Applicability::MaybeIncorrect, + ); + err.span_suggestion_verbose( + param_info.param_ty_span, + add_static_bound, + param_info.param_ty.to_string(), + Applicability::MaybeIncorrect, + ); + } + _ => {} + }, + _ => {} } } err.emit(); 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..45aee2b39654d 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 @@ -2,11 +2,16 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; -use crate::infer::{Subtype, ValuePairs}; +use crate::infer::{Subtype, TyCtxtInferExt, ValuePairs}; use crate::traits::ObligationCauseCode::CompareImplMethodObligation; -use rustc::ty::Ty; -use rustc::util::common::ErrorReported; -use rustc_span::Span; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::{MultiSpan, Span}; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`. @@ -22,40 +27,124 @@ 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, + *trait_item_def_id, + ); + return Some(ErrorReported); } } - _ => {} } } } None } - fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, impl_sp: Span) { + fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, trait_def_id: DefId) { + let tcx = self.tcx(); + let trait_sp = self.tcx().def_span(trait_def_id); let mut err = self .tcx() .sess .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); - err.note(&format!("expected `{:?}`\n found `{:?}`", expected, found)); - err.span_label(sp, &format!("found {:?}", found)); - err.span_label(impl_sp, &format!("expected {:?}", expected)); + err.span_label(sp, &format!("found `{:?}`", found)); + err.span_label(trait_sp, &format!("expected `{:?}`", expected)); + + // Get the span of all the used type parameters in the method. + let assoc_item = self.tcx().associated_item(trait_def_id); + let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; + match assoc_item.kind { + ty::AssocKind::Fn => { + let hir = self.tcx().hir(); + if let Some(hir_id) = assoc_item.def_id.as_local().map(|id| hir.as_local_hir_id(id)) + { + if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) { + visitor.visit_fn_decl(decl); + } + } + } + _ => {} + } + let mut type_param_span: MultiSpan = visitor.types.to_vec().into(); + for &span in &visitor.types { + type_param_span.push_span_label( + span, + "consider borrowing this type parameter in the trait".to_string(), + ); + } + + if let Some((expected, found)) = tcx + .infer_ctxt() + .enter(|infcx| infcx.expected_found_str_ty(&ExpectedFound { expected, found })) + { + // Highlighted the differences when showing the "expected/found" note. + err.note_expected_found(&"", expected, &"", found); + } else { + // This fallback shouldn't be necessary, but let's keep it in just in case. + err.note(&format!("expected `{:?}`\n found `{:?}`", expected, found)); + } + err.span_help( + type_param_span, + "the lifetime requirements from the `impl` do not correspond to the requirements in \ + the `trait`", + ); + if visitor.types.is_empty() { + err.help( + "verify the lifetime relationships in the `trait` and `impl` between the `self` \ + argument, the other inputs and its output", + ); + } err.emit(); } } + +struct TypeParamSpanVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + types: Vec, +} + +impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { + type Map = rustc_middle::hir::map::Map<'tcx>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::OnlyBodies(self.tcx.hir()) + } + + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + match arg.kind { + hir::TyKind::Rptr(_, ref mut_ty) => { + // We don't want to suggest looking into borrowing `&T` or `&Self`. + hir::intravisit::walk_ty(self, mut_ty.ty); + return; + } + hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { + [segment] + if segment + .res + .map(|res| match res { + Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _) => true, + _ => false, + }) + .unwrap_or(false) => + { + self.types.push(path.span); + } + _ => {} + }, + _ => {} + } + hir::intravisit::walk_ty(self, arg); + } +} 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 cab632935fd8e..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,52 +51,40 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { }; let hir = &self.tcx().hir(); - if let Some(hir_id) = hir.as_local_hir_id(id) { - if let Some(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(); - if let Some(tables) = self.tables { - body.params - .iter() - .enumerate() - .filter_map(|(index, param)| { - // May return None; sometimes the tables are not yet populated. - let ty_hir_id = fn_decl.inputs[index].hir_id; - let param_ty_span = hir.span(ty_hir_id); - let ty = tables.node_type_opt(param.hir_id)?; - 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 is_first = index == 0; - Some(AnonymousParamInfo { - param, - param_ty: new_param_ty, - param_ty_span, - bound_region, - is_first, - }) - } else { - None - } - }) - .next() + 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().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 } + }); + 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 } - } else { - None - } + }) } // Here, we check for the case where the anonymous region @@ -126,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..9ac27030adeea 100644 --- a/src/librustc_infer/infer/error_reporting/note.rs +++ b/src/librustc_infer/infer/error_reporting/note.rs @@ -1,9 +1,8 @@ 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::ty::error::TypeError; +use rustc_middle::ty::{self, Region}; impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub(super) fn note_region_origin( @@ -11,10 +10,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, origin: &SubregionOrigin<'tcx>, ) { + let mut label_or_note = |span, msg| { + let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count(); + let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count(); + let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span); + if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { + err.span_label(span, msg); + } else if span_is_primary && expanded_sub_count == 0 { + err.note(msg); + } else { + err.span_note(span, msg); + } + }; match *origin { infer::Subtype(ref trace) => { if let Some((expected, found)) = self.values_str(&trace.values) { - err.span_note( + label_or_note( trace.cause.span, &format!("...so that the {}", trace.cause.as_requirement_str()), ); @@ -25,80 +36,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // handling of region checking when type errors are present is // *terrible*. - err.span_note( + label_or_note( trace.cause.span, &format!("...so that {}", trace.cause.as_requirement_str()), ); } } infer::Reborrow(span) => { - err.span_note(span, "...so that reference does not outlive borrowed content"); + label_or_note(span, "...so that reference does not outlive borrowed content"); } infer::ReborrowUpvar(span, ref upvar_id) => { let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); - err.span_note(span, &format!("...so that closure can access `{}`", var_name)); - } - infer::InfStackClosure(span) => { - err.span_note(span, "...so that closure does not outlive its stack frame"); - } - infer::InvokeClosure(span) => { - err.span_note(span, "...so that closure is not invoked outside its lifetime"); - } - infer::DerefPointer(span) => { - err.span_note(span, "...so that pointer is not dereferenced outside its lifetime"); - } - infer::ClosureCapture(span, id) => { - err.span_note( - span, - &format!( - "...so that captured variable `{}` does not outlive the \ - enclosing closure", - self.tcx.hir().name(id) - ), - ); - } - infer::IndexSlice(span) => { - err.span_note(span, "...so that slice is not indexed outside the lifetime"); + label_or_note(span, &format!("...so that closure can access `{}`", var_name)); } infer::RelateObjectBound(span) => { - err.span_note(span, "...so that it can be closed over into an object"); - } - infer::CallRcvr(span) => { - err.span_note(span, "...so that method receiver is valid for the method call"); - } - infer::CallArg(span) => { - err.span_note(span, "...so that argument is valid for the call"); + label_or_note(span, "...so that it can be closed over into an object"); } infer::CallReturn(span) => { - err.span_note(span, "...so that return value is valid for the call"); - } - infer::Operand(span) => { - err.span_note(span, "...so that operand is valid for operation"); - } - infer::AddrOf(span) => { - err.span_note(span, "...so that reference is valid at the time of borrow"); - } - infer::AutoBorrow(span) => { - err.span_note(span, "...so that auto-reference is valid at the time of borrow"); - } - infer::ExprTypeIsNotInScope(t, span) => { - err.span_note( - span, - &format!( - "...so type `{}` of expression is valid during the \ - expression", - self.ty_to_string(t) - ), - ); - } - infer::BindingTypeIsNotValidAtDecl(span) => { - err.span_note(span, "...so that variable is valid at time of its declaration"); - } - infer::ParameterInScope(_, span) => { - err.span_note(span, "...so that a type/lifetime parameter is in scope here"); + label_or_note(span, "...so that return value is valid for the call"); } infer::DataBorrowed(ty, span) => { - err.span_note( + label_or_note( span, &format!( "...so that the type `{}` is not borrowed for too long", @@ -107,49 +65,33 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); } infer::ReferenceOutlivesReferent(ty, span) => { - err.span_note( + label_or_note( span, &format!( - "...so that the reference type `{}` does not outlive the \ - data it points at", + "...so that the reference type `{}` does not outlive the data it points at", self.ty_to_string(ty) ), ); } infer::RelateParamBound(span, t) => { - err.span_note( - span, - &format!( - "...so that the type `{}` will meet its required \ - lifetime bounds", - self.ty_to_string(t) - ), - ); - } - infer::RelateDefaultParamBound(span, t) => { - err.span_note( + label_or_note( span, &format!( - "...so that type parameter instantiated with `{}`, will \ - meet its declared lifetime bounds", + "...so that the type `{}` will meet its required lifetime bounds", self.ty_to_string(t) ), ); } infer::RelateRegionParamBound(span) => { - err.span_note( + label_or_note( span, "...so that the declared lifetime parameter bounds are satisfied", ); } - infer::SafeDestructor(span) => { - err.span_note(span, "...so that references are valid when the destructor runs"); - } infer::CompareImplMethodObligation { span, .. } => { - err.span_note( + label_or_note( span, - "...so that the definition in impl matches the definition from the \ - trait", + "...so that the definition in impl matches the definition from the trait", ); } } @@ -157,7 +99,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub(super) fn report_concrete_failure( &self, - region_scope_tree: ®ion::ScopeTree, origin: SubregionOrigin<'tcx>, sub: Region<'tcx>, sup: Region<'tcx>, @@ -166,10 +107,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { infer::Subtype(box trace) => { let terr = TypeError::RegionsDoesNotOutlive(sup, sub); let mut err = self.report_and_explain_type_error(trace, &terr); - note_and_explain_region(self.tcx, region_scope_tree, &mut err, "", sup, "..."); + note_and_explain_region(self.tcx, &mut err, "", sup, "..."); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "...does not necessarily outlive ", sub, @@ -182,12 +122,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.sess, span, E0312, - "lifetime of reference outlives lifetime of \ - borrowed content..." + "lifetime of reference outlives lifetime of borrowed content..." ); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "...the reference is valid for ", sub, @@ -195,7 +133,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "...but the borrowed content is only valid for ", sup, @@ -209,13 +146,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.sess, span, E0313, - "lifetime of borrowed pointer outlives lifetime \ - of captured variable `{}`...", + "lifetime of borrowed pointer outlives lifetime of captured variable `{}`...", var_name ); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "...the borrowed pointer is valid for ", sub, @@ -223,7 +158,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, &format!("...but `{}` is only valid for ", var_name), sup, @@ -231,125 +165,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); err } - infer::InfStackClosure(span) => { - let mut err = - struct_span_err!(self.tcx.sess, span, E0314, "closure outlives stack frame"); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "...the closure must be valid for ", - sub, - "...", - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "...but the closure's stack frame is only valid \ - for ", - sup, - "", - ); - err - } - infer::InvokeClosure(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0315, - "cannot invoke closure outside of its lifetime" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the closure is only valid for ", - sup, - "", - ); - err - } - infer::DerefPointer(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0473, - "dereference of reference outside its lifetime" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the reference is only valid for ", - sup, - "", - ); - err - } - infer::ClosureCapture(span, id) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0474, - "captured variable `{}` does not outlive the \ - enclosing closure", - self.tcx.hir().name(id) - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "captured variable is valid for ", - sup, - "", - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "closure is valid for ", - sub, - "", - ); - err - } - infer::IndexSlice(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0475, - "index of slice outside its lifetime" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the slice is only valid for ", - sup, - "", - ); - err - } infer::RelateObjectBound(span) => { let mut err = struct_span_err!( self.tcx.sess, span, E0476, - "lifetime of the source pointer does not outlive \ - lifetime bound of the object type" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "object type is valid for ", - sub, - "", + "lifetime of the source pointer does not outlive lifetime bound of the \ + object type" ); + note_and_explain_region(self.tcx, &mut err, "object type is valid for ", sub, ""); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "source pointer is only valid for ", sup, @@ -362,27 +188,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.sess, span, E0477, - "the type `{}` does not fulfill the required \ - lifetime", + "the type `{}` does not fulfill the required lifetime", self.ty_to_string(ty) ); match *sub { - ty::ReStatic => note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "type must satisfy ", - sub, - "", - ), - _ => note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "type must outlive ", - sub, - "", - ), + ty::ReStatic => { + note_and_explain_region(self.tcx, &mut err, "type must satisfy ", sub, "") + } + _ => note_and_explain_region(self.tcx, &mut err, "type must outlive ", sub, ""), } err } @@ -391,7 +204,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied"); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "lifetime parameter instantiated with ", sup, @@ -399,7 +211,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "but lifetime parameter must outlive ", sub, @@ -407,72 +218,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); err } - infer::RelateDefaultParamBound(span, ty) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0479, - "the type `{}` (provided as the value of a type \ - parameter) is not valid at this point", - self.ty_to_string(ty) - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "type must outlive ", - sub, - "", - ); - err - } - infer::CallRcvr(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0480, - "lifetime of method receiver does not outlive the \ - method call" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the receiver is only valid for ", - sup, - "", - ); - err - } - infer::CallArg(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0481, - "lifetime of function argument does not outlive \ - the function call" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the function argument is only valid for ", - sup, - "", - ); - err - } infer::CallReturn(span) => { let mut err = struct_span_err!( self.tcx.sess, span, E0482, - "lifetime of return value does not outlive the \ - function call" + "lifetime of return value does not outlive the function call" ); note_and_explain_region( self.tcx, - region_scope_tree, &mut err, "the return value is only valid for ", sup, @@ -480,140 +234,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); err } - infer::Operand(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0483, - "lifetime of operand does not outlive the \ - operation" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the operand is only valid for ", - sup, - "", - ); - err - } - infer::AddrOf(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0484, - "reference is not valid at the time of borrow" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the borrow is only valid for ", - sup, - "", - ); - err - } - infer::AutoBorrow(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0485, - "automatically reference is not valid at the time \ - of borrow" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the automatic borrow is only valid for ", - sup, - "", - ); - err - } - infer::ExprTypeIsNotInScope(t, span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0486, - "type of expression contains references that are \ - not valid during the expression: `{}`", - self.ty_to_string(t) - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "type is only valid for ", - sup, - "", - ); - err - } - infer::SafeDestructor(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0487, - "unsafe use of destructor: destructor might be \ - called while references are dead" - ); - // FIXME (22171): terms "super/subregion" are suboptimal - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "superregion: ", - sup, - "", - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "subregion: ", - sub, - "", - ); - err - } - infer::BindingTypeIsNotValidAtDecl(span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0488, - "lifetime of variable does not enclose its \ - declaration" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the variable is only valid for ", - sup, - "", - ); - err - } - infer::ParameterInScope(_, span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0489, - "type/lifetime parameter not in scope here" - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the parameter is only valid for ", - sub, - "", - ); - err - } infer::DataBorrowed(ty, span) => { let mut err = struct_span_err!( self.tcx.sess, @@ -622,22 +242,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "a value of type `{}` is borrowed for too long", self.ty_to_string(ty) ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "the type is valid for ", - sub, - "", - ); - note_and_explain_region( - self.tcx, - region_scope_tree, - &mut err, - "but the borrow lasts for ", - sup, - "", - ); + note_and_explain_region(self.tcx, &mut err, "the type is valid for ", sub, ""); + note_and_explain_region(self.tcx, &mut err, "but the borrow lasts for ", sup, ""); err } infer::ReferenceOutlivesReferent(ty, span) => { @@ -648,17 +254,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "in type `{}`, reference has a longer lifetime than the data it references", self.ty_to_string(ty) ); + note_and_explain_region(self.tcx, &mut err, "the pointer is valid for ", sub, ""); note_and_explain_region( self.tcx, - region_scope_tree, - &mut err, - "the pointer is valid for ", - sub, - "", - ); - note_and_explain_region( - self.tcx, - region_scope_tree, &mut err, "but the referenced data is only valid for ", sup, @@ -683,7 +281,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub(super) fn report_placeholder_failure( &self, - region_scope_tree: ®ion::ScopeTree, placeholder_origin: SubregionOrigin<'tcx>, sub: Region<'tcx>, sup: Region<'tcx>, @@ -695,7 +292,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.report_and_explain_type_error(trace, &terr) } - _ => self.report_concrete_failure(region_scope_tree, placeholder_origin, sub, sup), + _ => self.report_concrete_failure(placeholder_origin, sub, sup), } } } diff --git a/src/librustc_infer/infer/free_regions.rs b/src/librustc_infer/infer/free_regions.rs new file mode 100644 index 0000000000000..d975038b010b9 --- /dev/null +++ b/src/librustc_infer/infer/free_regions.rs @@ -0,0 +1,166 @@ +//! 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::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, + + /// Free-region relationships. + pub free_regions: &'a FreeRegionMap<'tcx>, +} + +impl<'a, 'tcx> RegionRelations<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, context: DefId, free_regions: &'a FreeRegionMap<'tcx>) -> Self { + Self { tcx, context, 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 a454feea36b70..02bebe10ed04a 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; @@ -127,7 +127,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { ty::ReStatic | ty::ReEarlyBound(..) | ty::ReFree(_) - | ty::ReScope(_) | ty::ReVar(_) | ty::RePlaceholder(..) | ty::ReEmpty(_) @@ -135,10 +134,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { // replace all free regions with 'erased self.tcx().lifetimes.re_erased } - - ty::ReClosureBound(..) => { - bug!("encountered unexpected region: {:?}", r,); - } } } @@ -151,7 +146,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) } @@ -159,7 +154,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), @@ -170,16 +165,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 {} \ @@ -199,7 +192,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { | ty::Float(..) | ty::Adt(..) | ty::Str - | ty::Error + | ty::Error(_) | ty::Array(..) | ty::Slice(..) | ty::RawPtr(..) @@ -210,7 +203,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { | ty::Never | ty::Tuple(..) | ty::Projection(..) - | ty::UnnormalizedProjection(..) | ty::Foreign(..) | ty::Param(..) | ty::Closure(..) @@ -228,7 +220,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(); @@ -255,7 +247,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 105b987f85e96..b6251e34008a3 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( @@ -30,10 +30,10 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> { let span = self.trace.cause.span; - return self.infcx.commit_if_ok(|snapshot| { + self.infcx.commit_if_ok(|_| { // First, we instantiate each bound region in the supertype with a // fresh placeholder region. - let (b_prime, placeholder_map) = self.infcx.replace_bound_vars_with_placeholders(b); + let (b_prime, _) = self.infcx.replace_bound_vars_with_placeholders(b); // Next, we instantiate each bound region in the subtype // with a fresh region variable. These region variables -- @@ -48,12 +48,10 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> { // Compare types now that bound regions have been replaced. let result = self.sub(a_is_expected).relate(&a_prime, &b_prime)?; - self.infcx.leak_check(!a_is_expected, &placeholder_map, snapshot)?; - debug!("higher_ranked_sub: OK result={:?}", result); Ok(ty::Binder::bind(result)) - }); + }) } } @@ -63,14 +61,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// placeholder region. This is the first step of checking subtyping /// when higher-ranked things are involved. /// - /// **Important:** you must call this function from within a snapshot. - /// Moreover, before committing the snapshot, you must eventually call - /// either `plug_leaks` or `pop_placeholders` to remove the placeholder - /// regions. If you rollback the snapshot (or are using a probe), then - /// the pop occurs as part of the rollback, so an explicit call is not - /// needed (but is also permitted). - /// - /// For more information about how placeholders and HRTBs work, see + /// **Important:** You have to be careful to not leak these placeholders, + /// for more information about how placeholders and HRTBs work, see /// the [rustc dev guide]. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html @@ -81,7 +73,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { where T: TypeFoldable<'tcx>, { - let next_universe = self.create_next_universe(); + // Figure out what the next universe will be, but don't actually create + // it until after we've done the substitution (in particular there may + // be no bound variables). This is a performance optimization, since the + // leak check for example can be skipped if no new universes are created + // (i.e., if there are no placeholders). + let next_universe = self.universe().next_universe(); let fld_r = |br| { self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { @@ -109,6 +106,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t, fld_c); + // If there were higher-ranked regions to replace, then actually create + // the next universe (this avoids needlessly creating universes). + if !map.is_empty() { + let n_u = self.create_next_universe(); + assert_eq!(n_u, next_universe); + } + debug!( "replace_bound_vars_with_placeholders(\ next_universe={:?}, \ @@ -125,7 +129,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn leak_check( &self, overly_polymorphic: bool, - placeholder_map: &PlaceholderMap<'tcx>, snapshot: &CombinedSnapshot<'_, 'tcx>, ) -> RelateResult<'tcx, ()> { // If the user gave `-Zno-leak-check`, or we have been @@ -141,7 +144,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.inner.borrow_mut().unwrap_region_constraints().leak_check( self.tcx, overly_polymorphic, - placeholder_map, + self.universe(), snapshot, ) } 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 deleted file mode 100644 index eb52f10e40893..0000000000000 --- a/src/librustc_infer/infer/lexical_region_resolve/graphviz.rs +++ /dev/null @@ -1,253 +0,0 @@ -//! This module provides linkage between libgraphviz traits and -//! `rustc::middle::typeck::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 -//! each edge). - -/// For clarity, rename the graphviz crate locally to dot. -use graphviz as dot; - -use super::Constraint; -use crate::infer::region_constraints::RegionConstraintData; -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 std::borrow::Cow; -use std::collections::btree_map::BTreeMap; -use std::collections::hash_map::Entry::Vacant; -use std::env; -use std::fs; -use std::io; -use std::sync::atomic::{AtomicBool, Ordering}; - -fn print_help_message() { - println!( - "\ --Z print-region-graph by default prints a region constraint graph for every \n\ -function body, to the path `constraints.nodeXXX.dot`, where the XXX is \n\ -replaced with the node id of the function under analysis. \n\ - \n\ -To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`, \n\ -where XXX is the node id desired. \n\ - \n\ -To generate output to some path other than the default \n\ -`constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`; \n\ -occurrences of the character `%` in the requested path will be replaced with\n\ -the node id of the function under analysis. \n\ - \n\ -(Since you requested help via RUST_REGION_GRAPH=help, no region constraint \n\ -graphs will be printed. \n\ -" - ); -} - -pub fn maybe_print_constraints_for<'a, 'tcx>( - region_data: &RegionConstraintData<'tcx>, - region_rels: &RegionRelations<'a, 'tcx>, -) { - let tcx = region_rels.tcx; - let context = region_rels.context; - - if !tcx.sess.opts.debugging_opts.print_region_graph { - return; - } - - let requested_node = env::var("RUST_REGION_GRAPH_NODE") - .ok() - .and_then(|s| s.parse().map(DefIndex::from_u32).ok()); - - if requested_node.is_some() && requested_node != Some(context.index) { - return; - } - - let requested_output = env::var("RUST_REGION_GRAPH"); - debug!("requested_output: {:?} requested_node: {:?}", requested_output, requested_node); - - let output_path = { - let output_template = match requested_output { - Ok(ref s) if s == "help" => { - static PRINTED_YET: AtomicBool = AtomicBool::new(false); - if !PRINTED_YET.load(Ordering::SeqCst) { - print_help_message(); - PRINTED_YET.store(true, Ordering::SeqCst); - } - return; - } - - Ok(other_path) => other_path, - Err(_) => "constraints.node%.dot".to_string(), - }; - - if output_template.is_empty() { - panic!("empty string provided as RUST_REGION_GRAPH"); - } - - if output_template.contains('%') { - let mut new_str = String::new(); - for c in output_template.chars() { - if c == '%' { - new_str.push_str(&context.index.as_u32().to_string()); - } else { - new_str.push(c); - } - } - new_str - } else { - output_template - } - }; - - if let Err(e) = dump_region_data_to(region_rels, ®ion_data.constraints, &output_path) { - let msg = format!("io error dumping region constraints: {}", e); - tcx.sess.err(&msg) - } -} - -struct ConstraintGraph<'a, 'tcx> { - graph_name: String, - region_rels: &'a RegionRelations<'a, 'tcx>, - map: &'a BTreeMap, SubregionOrigin<'tcx>>, - node_ids: FxHashMap, -} - -#[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)] -enum Node { - RegionVid(ty::RegionVid), - Region(ty::RegionKind), -} - -#[derive(Clone, PartialEq, Eq, Debug, Copy)] -enum Edge<'tcx> { - Constraint(Constraint<'tcx>), - EnclScope(region::Scope, region::Scope), -} - -impl<'a, 'tcx> ConstraintGraph<'a, 'tcx> { - fn new( - name: String, - region_rels: &'a RegionRelations<'a, 'tcx>, - map: &'a ConstraintMap<'tcx>, - ) -> ConstraintGraph<'a, 'tcx> { - let mut i = 0; - let mut node_ids = FxHashMap::default(); - { - let mut add_node = |node| { - if let Vacant(e) = node_ids.entry(node) { - e.insert(i); - i += 1; - } - }; - - for (n1, n2) in map.keys().map(|c| constraint_to_nodes(c)) { - add_node(n1); - add_node(n2); - } - - region_rels.region_scope_tree.each_encl_scope(|sub, sup| { - add_node(Node::Region(ty::ReScope(sub))); - add_node(Node::Region(ty::ReScope(sup))); - }); - } - - ConstraintGraph { map, node_ids, region_rels, graph_name: name } - } -} - -impl<'a, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'tcx> { - type Node = Node; - type Edge = Edge<'tcx>; - fn graph_id(&self) -> dot::Id<'_> { - dot::Id::new(&*self.graph_name).unwrap() - } - fn node_id(&self, n: &Node) -> dot::Id<'_> { - let node_id = match self.node_ids.get(n) { - Some(node_id) => node_id, - None => bug!("no node_id found for node: {:?}", n), - }; - let name = || format!("node_{}", node_id); - - dot::Id::new(name()) - .unwrap_or_else(|_| bug!("failed to create graphviz node identified by {}", name())) - } - fn node_label(&self, n: &Node) -> dot::LabelText<'_> { - match *n { - Node::RegionVid(n_vid) => dot::LabelText::label(format!("{:?}", n_vid)), - Node::Region(n_rgn) => dot::LabelText::label(format!("{:?}", n_rgn)), - } - } - fn edge_label(&self, e: &Edge<'_>) -> dot::LabelText<'_> { - match *e { - Edge::Constraint(ref c) => { - dot::LabelText::label(format!("{:?}", self.map.get(c).unwrap())) - } - Edge::EnclScope(..) => dot::LabelText::label("(enclosed)".to_owned()), - } - } -} - -fn constraint_to_nodes(c: &Constraint<'_>) -> (Node, Node) { - match *c { - Constraint::VarSubVar(rv_1, rv_2) => (Node::RegionVid(rv_1), Node::RegionVid(rv_2)), - Constraint::RegSubVar(r_1, rv_2) => (Node::Region(*r_1), Node::RegionVid(rv_2)), - Constraint::VarSubReg(rv_1, r_2) => (Node::RegionVid(rv_1), Node::Region(*r_2)), - Constraint::RegSubReg(r_1, r_2) => (Node::Region(*r_1), Node::Region(*r_2)), - } -} - -fn edge_to_nodes(e: &Edge<'_>) -> (Node, Node) { - match *e { - Edge::Constraint(ref c) => constraint_to_nodes(c), - Edge::EnclScope(sub, sup) => { - (Node::Region(ty::ReScope(sub)), Node::Region(ty::ReScope(sup))) - } - } -} - -impl<'a, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'tcx> { - type Node = Node; - type Edge = Edge<'tcx>; - fn nodes(&self) -> dot::Nodes<'_, Node> { - let set = self.node_ids.keys().cloned().collect::>(); - debug!("constraint graph has {} nodes", set.len()); - set.into_iter().collect() - } - fn edges(&self) -> dot::Edges<'_, Edge<'tcx>> { - debug!("constraint graph has {} edges", self.map.len()); - let mut v: Vec<_> = self.map.keys().map(|e| Edge::Constraint(*e)).collect(); - self.region_rels - .region_scope_tree - .each_encl_scope(|sub, sup| v.push(Edge::EnclScope(sub, sup))); - debug!("region graph has {} edges", v.len()); - Cow::Owned(v) - } - fn source(&self, edge: &Edge<'tcx>) -> Node { - let (n1, _) = edge_to_nodes(edge); - debug!("edge {:?} has source {:?}", edge, n1); - n1 - } - fn target(&self, edge: &Edge<'tcx>) -> Node { - let (_, n2) = edge_to_nodes(edge); - debug!("edge {:?} has target {:?}", edge, n2); - n2 - } -} - -pub type ConstraintMap<'tcx> = BTreeMap, SubregionOrigin<'tcx>>; - -fn dump_region_data_to<'a, 'tcx>( - region_rels: &RegionRelations<'a, 'tcx>, - map: &ConstraintMap<'tcx>, - path: &str, -) -> io::Result<()> { - debug!("dump_region_data map (len: {}) path: {}", map.len(), path); - let g = ConstraintGraph::new("region_data".to_string(), region_rels, map); - debug!("dump_region_data calling render"); - let mut v = Vec::new(); - dot::render(&g, &mut v).unwrap(); - fs::write(path, &v) -} diff --git a/src/librustc_infer/infer/lexical_region_resolve/mod.rs b/src/librustc_infer/infer/lexical_region_resolve/mod.rs index b7278ecd5e407..fcf1949933b11 100644 --- a/src/librustc_infer/infer/lexical_region_resolve/mod.rs +++ b/src/librustc_infer/infer/lexical_region_resolve/mod.rs @@ -6,24 +6,23 @@ 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, ReVar}; +use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; use std::fmt; -mod graphviz; - /// This function performs lexical region resolution given a complete /// set of constraints and variable origins. It performs a fixed-point /// iteration to find region values which satisfy all constraints, @@ -33,12 +32,32 @@ pub fn resolve<'tcx>( region_rels: &RegionRelations<'_, 'tcx>, var_infos: VarInfos, data: RegionConstraintData<'tcx>, + mode: RegionckMode, ) -> (LexicalRegionResolutions<'tcx>, Vec>) { debug!("RegionConstraintData: resolve_regions()"); let mut errors = vec![]; let mut resolver = LexicalResolver { region_rels, var_infos, data }; - let values = resolver.infer_variable_values(&mut errors); - (values, errors) + match mode { + RegionckMode::Solve => { + let values = resolver.infer_variable_values(&mut errors); + (values, errors) + } + RegionckMode::Erase { suppress_errors: false } => { + // Do real inference to get errors, then erase the results. + let mut values = resolver.infer_variable_values(&mut errors); + let re_erased = region_rels.tcx.lifetimes.re_erased; + + values.values.iter_mut().for_each(|v| match *v { + VarValue::Value(ref mut r) => *r = re_erased, + VarValue::ErrorValue => {} + }); + (values, errors) + } + RegionckMode::Erase { suppress_errors: true } => { + // Skip region inference entirely. + (resolver.erased_data(region_rels.tcx), Vec::new()) + } + } } /// Contains the result of lexical region resolution. Offers methods @@ -128,7 +147,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.region_rels.context, self.dump_constraints(self.region_rels) ); - graphviz::maybe_print_constraints_for(&self.data, self.region_rels); let graph = self.construct_graph(); self.expand_givens(&graph); @@ -163,6 +181,19 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + /// An erased version of the lexical region resolutions. Used when we're + /// erasing regions and suppressing errors: in item bodies with + /// `-Zborrowck=mir`. + fn erased_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> { + LexicalRegionResolutions { + error_region: tcx.lifetimes.re_static, + values: IndexVec::from_elem_n( + VarValue::Value(tcx.lifetimes.re_erased), + self.num_vars(), + ), + } + } + fn dump_constraints(&self, free_regions: &RegionRelations<'_, 'tcx>) { debug!("----() Start constraint listing (context={:?}) ()----", free_regions.context); for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() { @@ -259,8 +290,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // Find all the "upper bounds" -- that is, each region `b` such that // `r0 <= b` must hold. - let (member_upper_bounds, _) = - self.collect_concrete_regions(graph, member_vid, OUTGOING, None); + let (member_upper_bounds, ..) = + self.collect_bounding_regions(graph, member_vid, OUTGOING, None); // Get an iterator over the *available choice* -- that is, // each choice region `c` where `lb <= c` and `c <= ub` for all the @@ -294,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 { @@ -379,15 +423,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { match *b_data { VarValue::Value(cur_region) => { - // Identical scopes can show up quite often, if the fixed point - // iteration converges slowly. Skip them. This is purely an - // optimization. - if let (ReScope(a_scope), ReScope(cur_scope)) = (a_region, cur_region) { - if a_scope == cur_scope { - return false; - } - } - // This is a specialized version of the `lub_concrete_regions` // check below for a common case, here purely as an // optimization. @@ -421,12 +456,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { debug!("Expanding value of {:?} from {:?} to {:?}", b_vid, cur_region, lub); *b_data = VarValue::Value(lub); - return true; + true } - VarValue::ErrorValue => { - return false; - } + VarValue::ErrorValue => false, } } @@ -464,12 +497,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// term "concrete regions"). fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let r = match (a, b) { - (&ty::ReClosureBound(..), _) - | (_, &ty::ReClosureBound(..)) - | (&ReLateBound(..), _) - | (_, &ReLateBound(..)) - | (&ReErased, _) - | (_, &ReErased) => { + (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } @@ -488,12 +516,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(_))) + | (r @ (ReEarlyBound(_) | ReFree(_)), &ReEmpty(_)) => { // All empty regions are less than early-bound, free, // and scope regions. r @@ -518,53 +542,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - (&ReEarlyBound(_), &ReScope(s_id)) - | (&ReScope(s_id), &ReEarlyBound(_)) - | (&ReFree(_), &ReScope(s_id)) - | (&ReScope(s_id), &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: - let fr_scope = match (a, b) { - (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { - self.region_rels.region_scope_tree.early_free_scope(self.tcx(), br) - } - (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { - self.region_rels.region_scope_tree.free_scope(self.tcx(), fr) - } - _ => bug!(), - }; - let r_id = - self.region_rels.region_scope_tree.nearest_common_ancestor(fr_scope, s_id); - if r_id == fr_scope { - // if the free region's scope `fr.scope` is bigger than - // the scope region `s_id`, then the LUB is the free - // region itself: - match (a, b) { - (_, &ReScope(_)) => return a, - (&ReScope(_), _) => return b, - _ => bug!(), - } - } - - // otherwise, we don't know what the free region is, - // so we must conservatively say the LUB is static: - self.tcx().lifetimes.re_static - } - - (&ReScope(a_id), &ReScope(b_id)) => { - // The region corresponding to an outer block is a - // subtype of the region corresponding to an inner - // block. - let lub = self.region_rels.region_scope_tree.nearest_common_ancestor(a_id, b_id); - self.tcx().mk_region(ReScope(lub)) + (&ReEarlyBound(_) | &ReFree(_), &ReEarlyBound(_) | &ReFree(_)) => { + self.region_rels.lub_free_regions(a, b) } - (&ReEarlyBound(_), &ReEarlyBound(_)) - | (&ReFree(_), &ReEarlyBound(_)) - | (&ReEarlyBound(_), &ReFree(_)) - | (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b), - // For these types, we cannot define any additional // relationship: (&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => { @@ -629,7 +610,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { if !self.sub_concrete_regions(a_region, b_region) { debug!( "collect_errors: region error at {:?}: \ - cannot verify that {:?}={:?} <= {:?}", + cannot verify that {:?}={:?} <= {:?}", origin, a_vid, a_region, b_region ); *a_data = VarValue::ErrorValue; @@ -686,7 +667,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { graph: &RegionGraph<'tcx>, errors: &mut Vec>, ) { - debug!("collect_var_errors"); + debug!("collect_var_errors, var_data = {:#?}", var_data.values); // This is the best way that I have found to suppress // duplicate and related errors. Basically we keep a set of @@ -773,7 +754,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - return graph; + graph } fn collect_error_for_expanding_node( @@ -785,10 +766,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ) { // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. - let (mut lower_bounds, lower_dup) = - self.collect_concrete_regions(graph, node_idx, INCOMING, Some(dup_vec)); - let (mut upper_bounds, upper_dup) = - self.collect_concrete_regions(graph, node_idx, OUTGOING, Some(dup_vec)); + let (mut lower_bounds, lower_vid_bounds, lower_dup) = + self.collect_bounding_regions(graph, node_idx, INCOMING, Some(dup_vec)); + let (mut upper_bounds, _, upper_dup) = + self.collect_bounding_regions(graph, node_idx, OUTGOING, Some(dup_vec)); if lower_dup || upper_dup { return; @@ -844,15 +825,22 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // If we have a scenario like `exists<'a> { forall<'b> { 'b: // 'a } }`, we wind up without any lower-bound -- all we have // are placeholders as upper bounds, but the universe of the - // variable `'a` doesn't permit those placeholders. + // variable `'a`, or some variable that `'a` has to outlive, doesn't + // permit those placeholders. + let min_universe = lower_vid_bounds + .into_iter() + .map(|vid| self.var_infos[vid].universe) + .min() + .expect("lower_vid_bounds should at least include `node_idx`"); + for upper_bound in &upper_bounds { if let ty::RePlaceholder(p) = upper_bound.region { - if node_universe.cannot_name(p.universe) { + if min_universe.cannot_name(p.universe) { let origin = self.var_infos[node_idx].origin; errors.push(RegionResolutionError::UpperBoundUniverseConflict( node_idx, origin, - node_universe, + min_universe, upper_bound.origin.clone(), upper_bound.region, )); @@ -874,13 +862,24 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ); } - fn collect_concrete_regions( + /// Collects all regions that "bound" the variable `orig_node_idx` in the + /// given direction. + /// + /// If `dup_vec` is `Some` it's used to track duplicates between successive + /// calls of this function. + /// + /// The return tuple fields are: + /// - a list of all concrete regions bounding the given region. + /// - the set of all region variables bounding the given region. + /// - a `bool` that's true if the returned region variables overlap with + /// those returned by a previous call for another region. + fn collect_bounding_regions( &self, graph: &RegionGraph<'tcx>, orig_node_idx: RegionVid, dir: Direction, mut dup_vec: Option<&mut IndexVec>>, - ) -> (Vec>, bool) { + ) -> (Vec>, FxHashSet, bool) { struct WalkState<'tcx> { set: FxHashSet, stack: Vec, @@ -899,9 +898,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // direction specified process_edges(&self.data, &mut state, graph, orig_node_idx, dir); - while !state.stack.is_empty() { - let node_idx = state.stack.pop().unwrap(); - + while let Some(node_idx) = state.stack.pop() { // check whether we've visited this node on some previous walk if let Some(dup_vec) = &mut dup_vec { if dup_vec[node_idx].is_none() { @@ -919,8 +916,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { process_edges(&self.data, &mut state, graph, node_idx, dir); } - let WalkState { result, dup_found, .. } = state; - return (result, dup_found); + let WalkState { result, dup_found, set, .. } = state; + return (result, set, dup_found); fn process_edges<'tcx>( this: &RegionConstraintData<'tcx>, 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 9ae131c568d0d..8f8ce03d638c0 100644 --- a/src/librustc_infer/infer/mod.rs +++ b/src/librustc_infer/infer/mod.rs @@ -5,44 +5,47 @@ pub use self::LateBoundRegionConversionTime::*; pub use self::RegionVariableOrigin::*; pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; -pub use rustc::ty::IntVarValue; + +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::session::config::BorrowckMode; -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, InternalSubsts, SubstsRef}; -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; +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::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; + use std::cell::{Cell, Ref, RefCell}; 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 +53,7 @@ pub mod canonical; mod combine; mod equate; pub mod error_reporting; +pub mod free_regions; mod freshen; mod fudge; mod glb; @@ -63,9 +67,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,31 +84,54 @@ pub type Bound = Option; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult<'tcx, T> = Result>; // "fixup result" -/// A flag that is used to suppress region errors. This is normally -/// false, but sometimes -- when we are doing region checks that the -/// NLL borrow checker will also do -- it might be set to true. -#[derive(Copy, Clone, Default, Debug)] -pub struct SuppressRegionErrors { - suppressed: bool, +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 +/// not exposed, and so that we can avoid doing work in HIR typeck that MIR +/// typeck will also do. +#[derive(Copy, Clone, Debug)] +pub enum RegionckMode { + /// The default mode: report region errors, don't erase regions. + Solve, + /// Erase the results of region after solving. + Erase { + /// A flag that is used to suppress region errors, when we are doing + /// region checks that the NLL borrow checker will also do -- it might + /// be set to true. + suppress_errors: bool, + }, +} + +impl Default for RegionckMode { + fn default() -> Self { + RegionckMode::Solve + } } -impl SuppressRegionErrors { +impl RegionckMode { pub fn suppressed(self) -> bool { - self.suppressed + match self { + Self::Solve => false, + Self::Erase { suppress_errors } => suppress_errors, + } } /// Indicates that the MIR borrowck will repeat these region /// checks, so we should ignore errors if NLL is (unconditionally) /// enabled. - pub fn when_nll_is_enabled(tcx: TyCtxt<'_>) -> Self { + pub fn for_item_body(tcx: TyCtxt<'_>) -> Self { // FIXME(Centril): Once we actually remove `::Migrate` also make // this always `true` and then proceed to eliminate the dead code. match tcx.borrowck_mode() { // If we're on Migrate mode, report AST region errors - BorrowckMode::Migrate => SuppressRegionErrors { suppressed: false }, + BorrowckMode::Migrate => RegionckMode::Erase { suppress_errors: false }, // If we're on MIR, don't report AST region errors as they should be reported by NLL - BorrowckMode::Mir => SuppressRegionErrors { suppressed: true }, + BorrowckMode::Mir => RegionckMode::Erase { suppress_errors: true }, } } } @@ -116,28 +144,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 @@ -170,24 +198,85 @@ 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") + #[inline] + pub fn region_obligations(&self) -> &[(hir::HirId, RegionObligation<'tcx>)] { + &self.region_obligations + } + + #[inline] + pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> { + self.projection_cache.with_log(&mut self.undo_log) + } + + #[inline] + fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'tcx> { + self.type_variable_storage.with_log(&mut self.undo_log) + } + + #[inline] + 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) + } + + #[inline] + 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) + } + + #[inline] + 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) + } + + #[inline] + 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) } } @@ -295,22 +384,6 @@ pub enum SubregionOrigin<'tcx> { /// Arose from a subtyping relation Subtype(Box>), - /// Stack-allocated closures cannot outlive innermost loop - /// or function so as to ensure we only require finite stack - InfStackClosure(Span), - - /// Invocation of closure must be within its lifetime - InvokeClosure(Span), - - /// Dereference of reference must be within its lifetime - DerefPointer(Span), - - /// Closure bound must not outlive captured variables - ClosureCapture(Span, hir::HirId), - - /// Index into slice must be within its lifetime - IndexSlice(Span), - /// When casting `&'a T` to an `&'b Trait` object, /// relating `'a` to `'b` RelateObjectBound(Span), @@ -323,10 +396,6 @@ pub enum SubregionOrigin<'tcx> { /// that must outlive some other region. RelateRegionParamBound(Span), - /// A bound placed on type parameters that states that must outlive - /// the moment of their instantiation. - RelateDefaultParamBound(Span, Ty<'tcx>), - /// Creating a pointer `b` to contents of another reference Reborrow(Span), @@ -339,41 +408,14 @@ pub enum SubregionOrigin<'tcx> { /// (&'a &'b T) where a >= b ReferenceOutlivesReferent(Ty<'tcx>, Span), - /// Type or region parameters must be in scope. - ParameterInScope(ParameterOrigin, Span), - - /// The type T of an expression E must outlive the lifetime for E. - ExprTypeIsNotInScope(Ty<'tcx>, Span), - - /// A `ref b` whose region does not enclose the decl site - BindingTypeIsNotValidAtDecl(Span), - - /// Regions appearing in a method receiver must outlive method call - CallRcvr(Span), - - /// Regions appearing in a function argument must outlive func call - CallArg(Span), - /// Region in return type of invoked fn must enclose call CallReturn(Span), - /// Operands must be in scope - Operand(Span), - - /// Region resulting from a `&` expr must enclose the `&` expr - AddrOf(Span), - - /// An auto-borrow that does not enclose the expr where it occurs - AutoBorrow(Span), - - /// Region constraint arriving from destructor safety - SafeDestructor(Span), - /// Comparing the signature and requirements of an impl method against /// the containing trait. CompareImplMethodObligation { span: Span, - item_name: ast::Name, + item_name: Symbol, impl_item_def_id: DefId, trait_item_def_id: DefId, }, @@ -435,7 +477,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. @@ -453,6 +495,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 @@ -474,6 +519,7 @@ impl NLLRegionVariableOrigin { NLLRegionVariableOrigin::FreeRegion => true, NLLRegionVariableOrigin::Placeholder(..) => true, NLLRegionVariableOrigin::Existential { .. } => false, + NLLRegionVariableOrigin::RootEmptyRegion => false, } } @@ -482,6 +528,7 @@ impl NLLRegionVariableOrigin { } } +// FIXME(eddyb) investigate overlap between this and `TyOrConstInferVar`. #[derive(Copy, Clone, Debug)] pub enum FixupError<'tcx> { UnresolvedIntTy(IntVid), @@ -540,7 +587,7 @@ impl TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { impl<'tcx> InferCtxtBuilder<'tcx> { /// Used only by `rustc_typeck` during body type-checking/inference, /// will initialize `in_progress_tables` with fresh `TypeckTables`. - pub fn with_fresh_in_progress_tables(mut self, table_owner: DefId) -> Self { + pub fn with_fresh_in_progress_tables(mut self, table_owner: LocalDefId) -> Self { self.fresh_tables = Some(RefCell::new(ty::TypeckTables::empty(Some(table_owner)))); self } @@ -618,16 +665,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>>, } @@ -642,7 +683,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, } } @@ -652,18 +693,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 @@ -678,21 +719,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 @@ -744,17 +785,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()), @@ -764,59 +800,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. @@ -870,10 +881,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 } @@ -889,7 +903,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) { @@ -956,7 +970,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - predicate: &ty::PolySubtypePredicate<'tcx>, + predicate: ty::PolySubtypePredicate<'tcx>, ) -> Option> { // Subtle: it's ok to skip the binder here and resolve because // `shallow_resolve` just ignores anything that is not a type @@ -977,14 +991,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return None; } - Some(self.commit_if_ok(|snapshot| { - let (ty::SubtypePredicate { a_is_expected, a, b }, placeholder_map) = - self.replace_bound_vars_with_placeholders(predicate); + Some(self.commit_if_ok(|_snapshot| { + let (ty::SubtypePredicate { a_is_expected, a, b }, _) = + self.replace_bound_vars_with_placeholders(&predicate); let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?; - self.leak_check(false, &placeholder_map, snapshot)?; - Ok(ok.unit()) })) } @@ -992,22 +1004,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn region_outlives_predicate( &self, cause: &traits::ObligationCause<'tcx>, - predicate: &ty::PolyRegionOutlivesPredicate<'tcx>, + predicate: ty::PolyRegionOutlivesPredicate<'tcx>, ) -> UnitResult<'tcx> { - self.commit_if_ok(|snapshot| { - let (ty::OutlivesPredicate(r_a, r_b), placeholder_map) = - self.replace_bound_vars_with_placeholders(predicate); + self.commit_if_ok(|_snapshot| { + let (ty::OutlivesPredicate(r_a, r_b), _) = + self.replace_bound_vars_with_placeholders(&predicate); let origin = SubregionOrigin::from_obligation_cause(cause, || { RelateRegionParamBound(cause.span) }); self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` - self.leak_check(false, &placeholder_map, snapshot)?; Ok(()) }) } 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> { @@ -1019,7 +1030,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) } @@ -1044,20 +1055,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> { @@ -1065,7 +1076,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> { @@ -1136,7 +1147,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 { @@ -1156,7 +1167,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() }, }); @@ -1205,31 +1216,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn resolve_regions_and_report_errors( &self, region_context: DefId, - region_map: ®ion::ScopeTree, outlives_env: &OutlivesEnvironment<'tcx>, - suppress: SuppressRegionErrors, + 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) = { + 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, region_context, outlives_env.free_region_map()); - let region_rels = &RegionRelations::new( - self.tcx, - region_context, - region_map, - outlives_env.free_region_map(), - ); - let (var_infos, data) = self - .inner - .borrow_mut() - .region_constraints - .take() - .expect("regions already resolved") - .into_infos_and_data(); let (lexical_region_resolutions, errors) = - lexical_region_resolve::resolve(region_rels, var_infos, data); + lexical_region_resolve::resolve(region_rels, var_infos, data, mode); let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); @@ -1240,7 +1250,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // this infcx was in use. This is totally hokey but // otherwise we have a hard time separating legit region // errors from silly ones. - self.report_region_errors(region_map, &errors, suppress); + self.report_region_errors(&errors); } } @@ -1280,12 +1290,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 @@ -1309,7 +1319,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), } @@ -1327,12 +1337,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { where T: TypeFoldable<'tcx>, { - let mut r = ShallowResolver::new(self); - value.fold_with(&mut r) + value.fold_with(&mut ShallowResolver { infcx: self }) } 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 @@ -1370,7 +1379,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), } @@ -1432,6 +1441,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, @@ -1457,7 +1477,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>, @@ -1476,33 +1496,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Obtains the latest type of the given closure; this may be a /// closure in the current function, in which case its /// `ClosureKind` may not yet be known. - pub fn closure_kind( - &self, - closure_def_id: DefId, - closure_substs: SubstsRef<'tcx>, - ) -> Option { - let closure_kind_ty = closure_substs.as_closure().kind_ty(closure_def_id, self.tcx); + pub fn closure_kind(&self, closure_substs: SubstsRef<'tcx>) -> Option { + let closure_kind_ty = closure_substs.as_closure().kind_ty(); let closure_kind_ty = self.shallow_resolve(closure_kind_ty); closure_kind_ty.to_opt_closure_kind() } - /// Obtains the signature of a closure. For closures, unlike - /// `tcx.fn_sig(def_id)`, this method will work during the - /// type-checking of the enclosing function and return the closure - /// signature in its partially inferred state. - pub fn closure_sig(&self, def_id: DefId, substs: SubstsRef<'tcx>) -> ty::PolyFnSig<'tcx> { - let closure_sig_ty = substs.as_closure().sig_ty(def_id, self.tcx); - let closure_sig_ty = self.shallow_resolve(closure_sig_ty); - closure_sig_ty.fn_sig(self.tcx) - } - /// Clears the selection, evaluation, and projection caches. This is useful when /// repeatedly attempting to select an `Obligation` while changing only /// its `ParamEnv`, since `FulfillmentContext` doesn't use probing. 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 { @@ -1545,22 +1551,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // variables, thus we don't need to substitute back the original values. self.tcx.const_eval_resolve(param_env, def_id, substs, promoted, span) } -} - -pub struct ShallowResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, -} - -impl<'a, 'tcx> ShallowResolver<'a, 'tcx> { - #[inline(always)] - pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { - ShallowResolver { infcx } - } /// If `typ` is a type variable of some kind, resolve it one level /// (but do not resolve types found in the result). If `typ` is /// not a type variable, just return it unmodified. - pub fn shallow_resolve(&mut self, typ: Ty<'tcx>) -> Ty<'tcx> { + // FIXME(eddyb) inline into `ShallowResolver::visit_ty`. + fn shallow_resolve_ty(&self, typ: Ty<'tcx>) -> Ty<'tcx> { match typ.kind { ty::Infer(ty::TyVar(v)) => { // Not entirely obvious: if `typ` is a type variable, @@ -1574,78 +1570,142 @@ impl<'a, 'tcx> ShallowResolver<'a, 'tcx> { // depth. // // Note: if these two lines are combined into one we get - // dynamic borrow errors on `self.infcx.inner`. - let known = self.infcx.inner.borrow_mut().type_variables.probe(v).known(); - known.map(|t| self.fold_ty(t)).unwrap_or(typ) + // dynamic borrow errors on `self.inner`. + let known = self.inner.borrow_mut().type_variables().probe(v).known(); + known.map(|t| self.shallow_resolve_ty(t)).unwrap_or(typ) } ty::Infer(ty::IntVar(v)) => self - .infcx .inner .borrow_mut() - .int_unification_table + .int_unification_table() .probe_value(v) - .map(|v| v.to_type(self.infcx.tcx)) + .map(|v| v.to_type(self.tcx)) .unwrap_or(typ), ty::Infer(ty::FloatVar(v)) => self - .infcx .inner .borrow_mut() - .float_unification_table + .float_unification_table() .probe_value(v) - .map(|v| v.to_type(self.infcx.tcx)) + .map(|v| v.to_type(self.tcx)) .unwrap_or(typ), _ => typ, } } - // `resolver.shallow_resolve_changed(ty)` is equivalent to - // `resolver.shallow_resolve(ty) != ty`, but more efficient. It's always - // inlined, despite being large, because it has only two call sites that - // are extremely hot. + /// `ty_or_const_infer_var_changed` is equivalent to one of these two: + /// * `shallow_resolve(ty) != ty` (where `ty.kind = ty::Infer(_)`) + /// * `shallow_resolve(ct) != ct` (where `ct.kind = ty::ConstKind::Infer(_)`) + /// + /// However, `ty_or_const_infer_var_changed` is more efficient. It's always + /// inlined, despite being large, because it has only two call sites that + /// are extremely hot (both in `traits::fulfill`'s checking of `stalled_on` + /// inference variables), and it handles both `Ty` and `ty::Const` without + /// having to resort to storing full `GenericArg`s in `stalled_on`. #[inline(always)] - pub fn shallow_resolve_changed(&self, infer: ty::InferTy) -> bool { - match infer { - ty::TyVar(v) => { + pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar<'tcx>) -> bool { + match infer_var { + TyOrConstInferVar::Ty(v) => { use self::type_variable::TypeVariableValue; - // If `inlined_probe` returns a `Known` value its `kind` never - // matches `infer`. - match self.infcx.inner.borrow_mut().type_variables.inlined_probe(v) { + // If `inlined_probe` returns a `Known` value, it never equals + // `ty::Infer(ty::TyVar(v))`. + match self.inner.borrow_mut().type_variables().inlined_probe(v) { TypeVariableValue::Unknown { .. } => false, TypeVariableValue::Known { .. } => true, } } - ty::IntVar(v) => { - // If inlined_probe_value returns a value it's always a + TyOrConstInferVar::TyInt(v) => { + // If `inlined_probe_value` returns a value it's always a // `ty::Int(_)` or `ty::UInt(_)`, which never matches a // `ty::Infer(_)`. - self.infcx.inner.borrow_mut().int_unification_table.inlined_probe_value(v).is_some() + self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_some() } - ty::FloatVar(v) => { - // If inlined_probe_value returns a value it's always a + TyOrConstInferVar::TyFloat(v) => { + // If `probe_value` returns a value it's always a // `ty::Float(_)`, which never matches a `ty::Infer(_)`. // // Not `inlined_probe_value(v)` because this call site is colder. - self.infcx.inner.borrow_mut().float_unification_table.probe_value(v).is_some() + self.inner.borrow_mut().float_unification_table().probe_value(v).is_some() } - _ => unreachable!(), + TyOrConstInferVar::Const(v) => { + // If `probe_value` returns a `Known` value, it never equals + // `ty::ConstKind::Infer(ty::InferConst::Var(v))`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + match self.inner.borrow_mut().const_unification_table().probe_value(v).val { + ConstVariableValue::Unknown { .. } => false, + ConstVariableValue::Known { .. } => true, + } + } + } + } +} + +/// Helper for `ty_or_const_infer_var_changed` (see comment on that), currently +/// used only for `traits::fulfill`'s list of `stalled_on` inference variables. +#[derive(Copy, Clone, Debug)] +pub enum TyOrConstInferVar<'tcx> { + /// Equivalent to `ty::Infer(ty::TyVar(_))`. + Ty(TyVid), + /// Equivalent to `ty::Infer(ty::IntVar(_))`. + TyInt(IntVid), + /// Equivalent to `ty::Infer(ty::FloatVar(_))`. + TyFloat(FloatVid), + + /// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`. + Const(ConstVid<'tcx>), +} + +impl TyOrConstInferVar<'tcx> { + /// Tries to extract an inference variable from a type or a constant, returns `None` + /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and + /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_generic_arg(arg: GenericArg<'tcx>) -> Option { + match arg.unpack() { + GenericArgKind::Type(ty) => Self::maybe_from_ty(ty), + GenericArgKind::Const(ct) => Self::maybe_from_const(ct), + GenericArgKind::Lifetime(_) => None, + } + } + + /// Tries to extract an inference variable from a type, returns `None` + /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). + pub fn maybe_from_ty(ty: Ty<'tcx>) -> Option { + match ty.kind { + ty::Infer(ty::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), + ty::Infer(ty::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), + ty::Infer(ty::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)), + _ => None, + } + } + + /// Tries to extract an inference variable from a constant, returns `None` + /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_const(ct: &'tcx ty::Const<'tcx>) -> Option { + match ct.val { + ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), + _ => None, } } } +struct ShallowResolver<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, +} + impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.infcx.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.shallow_resolve(ty) + self.infcx.shallow_resolve_ty(ty) } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { @@ -1653,7 +1713,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() @@ -1678,10 +1738,20 @@ 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> { + let err = tcx.ty_error(); TypeTrace { cause: ObligationCause::dummy(), - values: Types(ExpectedFound { expected: tcx.types.err, found: tcx.types.err }), + values: Types(ExpectedFound { expected: err, found: err }), } } } @@ -1690,29 +1760,14 @@ impl<'tcx> SubregionOrigin<'tcx> { pub fn span(&self) -> Span { match *self { Subtype(ref a) => a.span(), - InfStackClosure(a) => a, - InvokeClosure(a) => a, - DerefPointer(a) => a, - ClosureCapture(a, _) => a, - IndexSlice(a) => a, RelateObjectBound(a) => a, RelateParamBound(a, _) => a, RelateRegionParamBound(a) => a, - RelateDefaultParamBound(a, _) => a, Reborrow(a) => a, ReborrowUpvar(a, _) => a, DataBorrowed(_, a) => a, ReferenceOutlivesReferent(_, a) => a, - ParameterInScope(_, a) => a, - ExprTypeIsNotInScope(_, a) => a, - BindingTypeIsNotValidAtDecl(a) => a, - CallRcvr(a) => a, - CallArg(a) => a, CallReturn(a) => a, - Operand(a) => a, - AddrOf(a) => a, - AutoBorrow(a) => a, - SafeDestructor(a) => a, CompareImplMethodObligation { span, .. } => span, } } diff --git a/src/librustc_infer/infer/nll_relate/mod.rs b/src/librustc_infer/infer/nll_relate/mod.rs index 50bea300c5064..2350c28dfaaff 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, }; @@ -530,7 +522,13 @@ where } if a == b { - return Ok(a); + // Subtle: if a or b has a bound variable that we are lazilly + // substituting, then even if a == b, it could be that the values we + // will substitute for those bound variables are *not* the same, and + // hence returning `Ok(a)` is incorrect. + if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { + return Ok(a); + } } match (&a.kind, &b.kind) { @@ -668,7 +666,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 +724,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,14 +877,15 @@ 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 { // If sub-roots are equal, then `for_vid` and // `vid` are related via subtyping. debug!("TypeGeneralizer::tys: occurs check failed"); - return Err(TypeError::Mismatch); + Err(TypeError::Mismatch) } else { match variables.probe(vid) { TypeVariableValue::Known { value: u } => { @@ -898,13 +906,13 @@ where let u = self.tcx().mk_ty_var(new_var_id); debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); - return Ok(u); + Ok(u) } } } } - 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 +980,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 +994,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..fd3b38e9d67b0 100644 --- a/src/librustc_infer/infer/outlives/mod.rs +++ b/src/librustc_infer/infer/outlives/mod.rs @@ -4,23 +4,24 @@ 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>, ) -> impl Iterator> + 'tcx { debug!("explicit_outlives_bounds()"); - param_env.caller_bounds.into_iter().filter_map(move |predicate| match predicate { - ty::Predicate::Projection(..) - | ty::Predicate::Trait(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::TypeOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => None, - ty::Predicate::RegionOutlives(ref data) => data + param_env.caller_bounds.into_iter().filter_map(move |predicate| match predicate.kind() { + ty::PredicateKind::Projection(..) + | ty::PredicateKind::Trait(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => None, + ty::PredicateKind::RegionOutlives(ref data) => data .no_bound_vars() .map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)), }) diff --git a/src/librustc_infer/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..383979f864075 100644 --- a/src/librustc_infer/infer/outlives/verify.rs +++ b/src/librustc_infer/infer/outlives/verify.rs @@ -1,12 +1,9 @@ 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, 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 +41,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 +166,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 +222,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 @@ -280,19 +310,14 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn region_bounds_declared_on_associated_item( &self, assoc_item_def_id: DefId, - ) -> impl Iterator> + 'cx + Captures<'tcx> { + ) -> impl Iterator> { 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 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), - ) - .map(|b| b.1) + let predicates = tcx.projection_predicates(assoc_item_def_id); + predicates + .into_iter() + .filter_map(|p| p.to_opt_type_outlives()) + .filter_map(|p| p.no_bound_vars()) + .map(|b| b.1) } /// Searches through a predicate list for a predicate `T: 'a`. @@ -304,11 +329,10 @@ 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.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..32e708bf52b32 100644 --- a/src/librustc_infer/infer/region_constraints/leak_check.rs +++ b/src/librustc_infer/infer/region_constraints/leak_check.rs @@ -1,151 +1,446 @@ use super::*; -use crate::infer::{CombinedSnapshot, PlaceholderMap}; -use rustc::ty::error::TypeError; -use rustc::ty::relate::RelateResult; - -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 - /// placeholder or would have to escape into some parent universe - /// that cannot name them. +use crate::infer::CombinedSnapshot; +use rustc_data_structures::{ + graph::{scc::Sccs, vec_graph::VecGraph}, + undo_log::UndoLogs, +}; +use rustc_index::vec::Idx; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::relate::RelateResult; + +impl<'tcx> RegionConstraintCollector<'_, 'tcx> { + /// Searches new universes created during `snapshot`, looking for + /// placeholders that may "leak" out from the universes they are contained + /// in. If any leaking placeholders are found, then an `Err` is returned + /// (typically leading to the snapshot being reversed). + /// + /// The leak check *used* to be the only way we had to handle higher-ranked + /// obligations. Now that we have integrated universes into the region + /// solvers, this is no longer the case, but we retain the leak check for + /// backwards compatibility purposes. In particular, it lets us make "early" + /// decisions about whether a region error will be reported that are used in + /// coherence and elsewhere -- see #56105 and #59490 for more details. The + /// eventual fate of the leak checker is not yet settled. + /// + /// The leak checker works by searching for the following error patterns: + /// + /// * P1: P2, where P1 != P2 + /// * P1: R, where R is in some universe that cannot name P1 + /// + /// The idea here is that each of these patterns represents something that + /// the region solver would eventually report as an error, so we can detect + /// the error early. There is a fly in the ointment, though, in that this is + /// not entirely true. In particular, in the future, we may extend the + /// environment with implied bounds or other info about how placeholders + /// relate to regions in outer universes. In that case, `P1: R` for example + /// might become solveable. + /// + /// # Summary of the implementation + /// + /// The leak checks as follows. First, we construct a graph where `R2: R1` + /// implies `R2 -> R1`, and we compute the SCCs. + /// + /// For each SCC S, we compute: + /// + /// * what placeholder P it must be equal to, if any + /// * if there are multiple placeholders that must be equal, report an error because `P1: P2` + /// * the minimum universe of its constituents + /// + /// Then we walk the SCCs in dependency order and compute + /// + /// * what placeholder they must outlive transitively + /// * if they must also be equal to a placeholder, report an error because `P1: P2` + /// * minimum universe U of all SCCs they must outlive + /// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that + /// indicates `P: R` and `R` is in an incompatible universe /// - /// This is a temporary backwards compatibility measure to try and - /// retain the older (arguably incorrect) behavior of the - /// compiler. + /// # Historical note /// - /// NB. Although `_snapshot` isn't used, it's passed in to prove - /// that we are in a snapshot, which guarantees that we can just - /// search the "undo log" for edges. This is mostly an efficiency - /// thing -- we could search *all* region constraints, but that'd be - /// a bigger set and the data structures are not setup for that. If - /// we wind up keeping some form of this check long term, it would - /// probably be better to remove the snapshot parameter and to - /// refactor the constraint set. + /// Older variants of the leak check used to report errors for these + /// patterns, but we no longer do: + /// + /// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n + /// * R: P1, R: P2, as above pub fn leak_check( &mut self, tcx: TyCtxt<'tcx>, overly_polymorphic: bool, - placeholder_map: &PlaceholderMap<'tcx>, - _snapshot: &CombinedSnapshot<'_, 'tcx>, + max_universe: ty::UniverseIndex, + snapshot: &CombinedSnapshot<'_, 'tcx>, ) -> RelateResult<'tcx, ()> { - debug!("leak_check(placeholders={:?})", placeholder_map); - - assert!(self.in_snapshot()); - - // Go through each placeholder that we created. - for &placeholder_region in placeholder_map.values() { - // Find the universe this placeholder inhabits. - let placeholder = match placeholder_region { - ty::RePlaceholder(p) => p, - _ => bug!("leak_check: expected placeholder found {:?}", placeholder_region,), - }; - - // Find all regions that are related to this placeholder - // 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); - let tainted_regions = taint_set.into_set(); - - // Report an error if two placeholders in the same universe - // are related to one another, or if a placeholder is related - // to something from a parent universe. - for &tainted_region in &tainted_regions { - if let ty::RePlaceholder(_) = tainted_region { - // Two placeholders cannot be related: - if tainted_region == placeholder_region { - continue; - } - } else if self.universe(tainted_region).can_name(placeholder.universe) { - continue; - } + debug!( + "leak_check(max_universe={:?}, snapshot.universe={:?}, overly_polymorphic={:?})", + max_universe, snapshot.universe, overly_polymorphic + ); - return Err(if overly_polymorphic { - debug!("overly polymorphic!"); - TypeError::RegionsOverlyPolymorphic(placeholder.name, tainted_region) - } else { - debug!("not as polymorphic!"); - TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, tainted_region) - }); - } + assert!(UndoLogs::>::in_snapshot(&self.undo_log)); + + let universe_at_start_of_snapshot = snapshot.universe; + if universe_at_start_of_snapshot == max_universe { + return Ok(()); } + let mini_graph = + &MiniGraph::new(tcx, self.undo_log.region_constraints(), &self.storage.data.verifys); + + let mut leak_check = LeakCheck::new( + tcx, + universe_at_start_of_snapshot, + max_universe, + overly_polymorphic, + mini_graph, + self, + ); + leak_check.assign_placeholder_values()?; + leak_check.propagate_scc_value()?; Ok(()) } } -#[derive(Debug)] -struct TaintSet<'tcx> { - directions: TaintDirections, - regions: FxHashSet>, +struct LeakCheck<'me, 'tcx> { + tcx: TyCtxt<'tcx>, + universe_at_start_of_snapshot: ty::UniverseIndex, + overly_polymorphic: bool, + mini_graph: &'me MiniGraph<'tcx>, + rcc: &'me RegionConstraintCollector<'me, 'tcx>, + + // Initially, for each SCC S, stores a placeholder `P` such that `S = P` + // must hold. + // + // Later, during the [`LeakCheck::propagate_scc_value`] function, this array + // is repurposed to store some placeholder `P` such that the weaker + // condition `S: P` must hold. (This is true if `S: S1` transitively and `S1 + // = P`.) + scc_placeholders: IndexVec>, + + // For each SCC S, track the minimum universe that flows into it. Note that + // this is both the minimum of the universes for every region that is a + // member of the SCC, but also if you have `R1: R2`, then the universe of + // `R2` must be less than the universe of `R1` (i.e., `R1` flows `R2`). To + // see that, imagine that you have `P1: R` -- in that case, `R` must be + // either the placeholder `P1` or the empty region in that same universe. + // + // To detect errors, we look for an SCC S where the values in + // `scc_values[S]` (if any) cannot be stored into `scc_universes[S]`. + scc_universes: IndexVec>, } -impl<'tcx> TaintSet<'tcx> { - fn new(directions: TaintDirections, initial_region: ty::Region<'tcx>) -> Self { - let mut regions = FxHashSet::default(); - regions.insert(initial_region); - TaintSet { directions, regions } +impl<'me, 'tcx> LeakCheck<'me, 'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + universe_at_start_of_snapshot: ty::UniverseIndex, + max_universe: ty::UniverseIndex, + overly_polymorphic: bool, + mini_graph: &'me MiniGraph<'tcx>, + rcc: &'me RegionConstraintCollector<'me, 'tcx>, + ) -> Self { + let dummy_scc_universe = SccUniverse { universe: max_universe, region: None }; + Self { + tcx, + universe_at_start_of_snapshot, + overly_polymorphic, + mini_graph, + rcc, + scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()), + scc_universes: IndexVec::from_elem_n(dummy_scc_universe, mini_graph.sccs.num_sccs()), + } } - fn fixed_point( + /// Compute what placeholders (if any) each SCC must be equal to. + /// Also compute the minimum universe of all the regions in each SCC. + fn assign_placeholder_values(&mut self) -> RelateResult<'tcx, ()> { + // First walk: find each placeholder that is from a newly created universe. + for (region, leak_check_node) in &self.mini_graph.nodes { + let scc = self.mini_graph.sccs.scc(*leak_check_node); + + // Set the universe of each SCC to be the minimum of its constituent universes + let universe = self.rcc.universe(region); + debug!( + "assign_placeholder_values: scc={:?} universe={:?} region={:?}", + scc, universe, region + ); + self.scc_universes[scc].take_min(universe, region); + + // Detect those SCCs that directly contain a placeholder + if let ty::RePlaceholder(placeholder) = region { + if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) { + self.assign_scc_value(scc, *placeholder)?; + } + } + } + + Ok(()) + } + + // assign_scc_value(S, P): Update `scc_values` to account for the fact that `P: S` must hold. + // This may create an error. + fn assign_scc_value( &mut self, - tcx: TyCtxt<'tcx>, - undo_log: &[UndoLog<'tcx>], - verifys: &[Verify<'tcx>], - ) { - 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 { - match undo_entry { - &AddConstraint(Constraint::VarSubVar(a, b)) => { - self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b))); - } - &AddConstraint(Constraint::RegSubVar(a, b)) => { - self.add_edge(a, tcx.mk_region(ReVar(b))); - } - &AddConstraint(Constraint::VarSubReg(a, b)) => { - self.add_edge(tcx.mk_region(ReVar(a)), b); - } - &AddConstraint(Constraint::RegSubReg(a, b)) => { - self.add_edge(a, b); - } - &AddGiven(a, b) => { - self.add_edge(a, tcx.mk_region(ReVar(b))); - } - &AddVerify(i) => span_bug!( - verifys[i].origin.span(), - "we never add verifications while doing higher-ranked things", - ), - &Purged | &AddCombination(..) | &AddVar(..) => {} + scc: LeakCheckScc, + placeholder: ty::PlaceholderRegion, + ) -> RelateResult<'tcx, ()> { + match self.scc_placeholders[scc] { + Some(p) => { + assert_ne!(p, placeholder); + return Err(self.placeholder_error(p, placeholder)); + } + None => { + self.scc_placeholders[scc] = Some(placeholder); + } + }; + + Ok(()) + } + + /// For each SCC S, iterate over each successor S1 where `S: S1`: + /// + /// * Compute + /// Iterate over each SCC `S` and ensure that, for each `S1` where `S1: S`, + /// `universe(S) <= universe(S1)`. This executes after + /// `assign_placeholder_values`, so `universe(S)` is already the minimum + /// universe of any of its direct constituents. + fn propagate_scc_value(&mut self) -> RelateResult<'tcx, ()> { + // Loop invariants: + // + // On start of the loop iteration for `scc1`: + // + // * `scc_universes[scc1]` contains the minimum universe of the + // constituents of `scc1` + // * `scc_placeholder[scc1]` stores the placeholder that `scc1` must + // be equal to (if any) + // + // For each succssor `scc2` where `scc1: scc2`: + // + // * `scc_placeholder[scc2]` stores some placeholder `P` where + // `scc2: P` (if any) + // * `scc_universes[scc2]` contains the minimum universe of the + // constituents of `scc2` and any of its successors + for scc1 in self.mini_graph.sccs.all_sccs() { + debug!( + "propagate_scc_value: scc={:?} with universe {:?}", + scc1, self.scc_universes[scc1] + ); + + // Walk over each `scc2` such that `scc1: scc2` and compute: + // + // * `scc1_universe`: the minimum universe of `scc2` and the constituents of `scc1` + // * `succ_bound`: placeholder `P` that the successors must outlive, if any (if there are multiple, + // we pick one arbitrarily) + let mut scc1_universe = self.scc_universes[scc1]; + let mut succ_bound = None; + for &scc2 in self.mini_graph.sccs.successors(scc1) { + let SccUniverse { universe: scc2_universe, region: scc2_region } = + self.scc_universes[scc2]; + + scc1_universe.take_min(scc2_universe, scc2_region.unwrap()); + + if let Some(b) = self.scc_placeholders[scc2] { + succ_bound = Some(b); } } + + // Update minimum universe of scc1. + self.scc_universes[scc1] = scc1_universe; + + // At this point, `scc_placholder[scc1]` stores the placeholder that + // `scc1` must be equal to, if any. + if let Some(scc1_placeholder) = self.scc_placeholders[scc1] { + debug!( + "propagate_scc_value: scc1={:?} placeholder={:?} scc1_universe={:?}", + scc1, scc1_placeholder, scc1_universe + ); + + // Check if `P1: R` for some `R` in a universe that cannot name + // P1. That's an error. + if scc1_universe.universe.cannot_name(scc1_placeholder.universe) { + return Err(self.error(scc1_placeholder, scc1_universe.region.unwrap())); + } + + // Check if we have some placeholder where `S: P2` + // (transitively). In that case, since `S = P1`, that implies + // `P1: P2`, which is an error condition. + if let Some(scc2_placeholder) = succ_bound { + assert_ne!(scc1_placeholder, scc2_placeholder); + return Err(self.placeholder_error(scc1_placeholder, scc2_placeholder)); + } + } else { + // Otherwise, we can reach a placeholder if some successor can. + self.scc_placeholders[scc1] = succ_bound; + } + + // At this point, `scc_placeholder[scc1]` stores some placeholder that `scc1` must outlive (if any). } + Ok(()) } - fn into_set(self) -> FxHashSet> { - self.regions + fn placeholder_error( + &self, + placeholder1: ty::PlaceholderRegion, + placeholder2: ty::PlaceholderRegion, + ) -> TypeError<'tcx> { + self.error(placeholder1, self.tcx.mk_region(ty::RePlaceholder(placeholder2))) } - fn len(&self) -> usize { - self.regions.len() + fn error( + &self, + placeholder: ty::PlaceholderRegion, + other_region: ty::Region<'tcx>, + ) -> TypeError<'tcx> { + debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region); + if self.overly_polymorphic { + return TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region); + } else { + return TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region); + } } +} - fn add_edge(&mut self, source: ty::Region<'tcx>, target: ty::Region<'tcx>) { - if self.directions.incoming { - if self.regions.contains(&target) { - self.regions.insert(source); - } +// States we need to distinguish: +// +// * must be equal to a placeholder (i.e., a placeholder is in the SCC) +// * it could conflict with some other regions in the SCC in different universes +// * or a different placeholder +// * `P1: S` and `S` must be equal to a placeholder +// * `P1: S` and `S` is in an incompatible universe +// +// So if we +// +// (a) compute which placeholder (if any) each SCC must be equal to +// (b) compute its minimum universe +// (c) compute *some* placeholder where `S: P1` (any one will do) +// +// then we get an error if: +// +// - it must be equal to a placeholder `P1` and minimum universe cannot name `P1` +// - `S: P1` and minimum universe cannot name `P1` +// - `S: P1` and we must be equal to `P2` +// +// So we want to track: +// +// * Equal placeholder (if any) +// * Some bounding placeholder (if any) +// * Minimum universe +// +// * We compute equal placeholder + minimum universe of constituents in first pass +// * Then we walk in order and compute from our dependencies `S1` where `S: S1` (`S -> S1`) +// * bounding placeholder (if any) +// * minimum universe +// * And if we must be equal to a placeholder then we check it against +// * minimum universe +// * no bounding placeholder + +/// Tracks the "minimum universe" for each SCC, along with some region that +/// caused it to change. +#[derive(Copy, Clone, Debug)] +struct SccUniverse<'tcx> { + /// For some SCC S, the minimum universe of: + /// + /// * each region R in S + /// * each SCC S1 such that S: S1 + universe: ty::UniverseIndex, + + /// Some region that caused `universe` to be what it is. + region: Option>, +} + +impl<'tcx> SccUniverse<'tcx> { + /// If `universe` is less than our current universe, then update + /// `self.universe` and `self.region`. + fn take_min(&mut self, universe: ty::UniverseIndex, region: ty::Region<'tcx>) { + if universe < self.universe || self.region.is_none() { + self.universe = universe; + self.region = Some(region); } + } +} + +rustc_index::newtype_index! { + struct LeakCheckNode { + DEBUG_FORMAT = "LeakCheckNode({})" + } +} + +rustc_index::newtype_index! { + struct LeakCheckScc { + DEBUG_FORMAT = "LeakCheckScc({})" + } +} + +/// Represents the graph of constraints. For each `R1: R2` constraint we create +/// an edge `R1 -> R2` in the graph. +struct MiniGraph<'tcx> { + /// Map from a region to the index of the node in the graph. + nodes: FxHashMap, LeakCheckNode>, - if self.directions.outgoing { - if self.regions.contains(&source) { - self.regions.insert(target); + /// Map from node index to SCC, and stores the successors of each SCC. All + /// the regions in the same SCC are equal to one another, and if `S1 -> S2`, + /// then `S1: S2`. + sccs: Sccs, +} + +impl<'tcx> MiniGraph<'tcx> { + fn new<'a>( + tcx: TyCtxt<'tcx>, + undo_log: impl Iterator>, + verifys: &[Verify<'tcx>], + ) -> Self + where + 'tcx: 'a, + { + let mut nodes = FxHashMap::default(); + let mut edges = Vec::new(); + + // Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter. + Self::iterate_undo_log(tcx, undo_log, verifys, |target, source| { + let source_node = Self::add_node(&mut nodes, source); + let target_node = Self::add_node(&mut nodes, target); + edges.push((source_node, target_node)); + }); + let graph = VecGraph::new(nodes.len(), edges); + let sccs = Sccs::new(&graph); + Self { nodes, sccs } + } + + /// Invokes `each_edge(R1, R2)` for each edge where `R2: R1` + fn iterate_undo_log<'a>( + tcx: TyCtxt<'tcx>, + undo_log: impl Iterator>, + verifys: &[Verify<'tcx>], + mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>), + ) where + 'tcx: 'a, + { + for undo_entry in undo_log { + match undo_entry { + &AddConstraint(Constraint::VarSubVar(a, b)) => { + each_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::RegSubVar(a, b)) => { + each_edge(a, tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::VarSubReg(a, b)) => { + each_edge(tcx.mk_region(ReVar(a)), b); + } + &AddConstraint(Constraint::RegSubReg(a, b)) => { + each_edge(a, b); + } + &AddGiven(a, b) => { + each_edge(a, tcx.mk_region(ReVar(b))); + } + &AddVerify(i) => span_bug!( + verifys[i].origin.span(), + "we never add verifications while doing higher-ranked things", + ), + &AddCombination(..) | &AddVar(..) => {} } } } + + fn add_node( + nodes: &mut FxHashMap, LeakCheckNode>, + r: ty::Region<'tcx>, + ) -> LeakCheckNode { + let l = nodes.len(); + *nodes.entry(r).or_insert(LeakCheckNode::new(l)) + } } diff --git a/src/librustc_infer/infer/region_constraints/mod.rs b/src/librustc_infer/infer/region_constraints/mod.rs index 868b95043796b..2902c41a6bcae 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,35 +46,41 @@ 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 /// which will map R1 and R2 to some common region (i.e., either - /// R1 or R2). This is important when dropck and other such code - /// 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>, + /// R1 or R2). This is important when fulfillment, dropck and other such + /// code 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. + 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>; + #[inline] + fn deref(&self) -> &RegionConstraintStorage<'tcx> { + self.storage + } +} + +impl std::ops::DerefMut for RegionConstraintCollector<'_, 'tcx> { + #[inline] + 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 +157,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 +268,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), @@ -284,18 +289,10 @@ enum UndoLog<'tcx> { /// We added a GLB/LUB "combination variable". AddCombination(CombineMapType, TwoRegions<'tcx>), - - /// During skolemization, we sometimes purge entries from the undo - /// log in a kind of minisnapshot (unlike other snapshots, this - /// purging actually takes place *on success*). In that case, we - /// replace the corresponding entry with `Noop` so as to avoid the - /// need to do a bunch of swapping. (We can't use `swap_remove` as - /// the order of the vector is important.) - Purged, } #[derive(Copy, Clone, PartialEq)] -enum CombineMapType { +pub(crate) enum CombineMapType { Lub, Glb, } @@ -309,8 +306,6 @@ pub struct RegionVariableInfo { } pub struct RegionSnapshot { - length: usize, - region_snapshot: ut::Snapshot>, any_unifications: bool, } @@ -339,11 +334,46 @@ impl TaintDirections { } } -impl<'tcx> RegionConstraintCollector<'tcx> { +impl<'tcx> RegionConstraintStorage<'tcx> { pub fn new() -> Self { Self::default() } + #[inline] + 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 { + 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 +386,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 +403,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 +424,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,13 +460,11 @@ 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); - return vid; + vid } /// Returns the universe for the given variable. @@ -518,52 +477,6 @@ impl<'tcx> RegionConstraintCollector<'tcx> { self.var_infos[vid].origin } - /// Removes all the edges to/from the placeholder regions that are - /// in `skols`. This is used after a higher-ranked operation - /// completes to remove all trace of the placeholder regions - /// created in that time. - pub fn pop_placeholders(&mut self, placeholders: &FxHashSet>) { - debug!("pop_placeholders(placeholders={:?})", placeholders); - - assert!(self.in_snapshot()); - - let constraints_to_kill: Vec = self - .undo_log - .iter() - .enumerate() - .rev() - .filter(|&(_, undo_entry)| kill_constraint(placeholders, undo_entry)) - .map(|(index, _)| index) - .collect(); - - for index in constraints_to_kill { - let undo_entry = mem::replace(&mut self.undo_log[index], Purged); - self.rollback_undo_entry(undo_entry); - } - - return; - - fn kill_constraint<'tcx>( - placeholders: &FxHashSet>, - undo_entry: &UndoLog<'tcx>, - ) -> bool { - match undo_entry { - &AddConstraint(Constraint::VarSubVar(..)) => false, - &AddConstraint(Constraint::RegSubVar(a, _)) => placeholders.contains(&a), - &AddConstraint(Constraint::VarSubReg(_, b)) => placeholders.contains(&b), - &AddConstraint(Constraint::RegSubReg(a, b)) => { - placeholders.contains(&a) || placeholders.contains(&b) - } - &AddGiven(..) => false, - &AddVerify(_) => false, - &AddCombination(_, ref two_regions) => { - placeholders.contains(&two_regions.a) || placeholders.contains(&two_regions.b) - } - &AddVar(..) | &Purged => false, - } - } - } - fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { // cannot add constraints once regions are resolved debug!("RegionConstraintCollector: add_constraint({:?})", constraint); @@ -571,12 +484,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 +504,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 +512,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 +530,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 +593,6 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } - /// See [`Verify::VerifyGenericBound`]. pub fn verify_generic_bound( &mut self, origin: SubregionOrigin<'tcx>, @@ -742,13 +647,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } - pub fn opportunistic_resolve_var( - &mut self, - tcx: TyCtxt<'tcx>, - rid: RegionVid, - ) -> ty::Region<'tcx> { - let vid = self.unification_table.probe_value(rid).min_vid; - tcx.mk_region(ty::ReVar(vid)) + pub fn opportunistic_resolve_var(&mut self, rid: RegionVid) -> ty::RegionVid { + self.unification_table().probe_value(rid).min_vid } fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'tcx> { @@ -775,9 +675,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 { @@ -791,23 +689,22 @@ impl<'tcx> RegionConstraintCollector<'tcx> { pub fn universe(&self, region: Region<'tcx>) -> ty::UniverseIndex { match *region { - ty::ReScope(..) - | ty::ReStatic - | ty::ReErased - | ty::ReFree(..) - | ty::ReEarlyBound(..) => ty::UniverseIndex::ROOT, + ty::ReStatic | ty::ReErased | ty::ReFree(..) | ty::ReEarlyBound(..) => { + ty::UniverseIndex::ROOT + } ty::ReEmpty(ui) => ui, ty::RePlaceholder(placeholder) => placeholder.universe, - ty::ReClosureBound(vid) | ty::ReVar(vid) => self.var_universe(vid), + ty::ReVar(vid) => self.var_universe(vid), ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region), } } 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()) @@ -816,10 +713,10 @@ 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() + /// See `InferCtxt::region_constraints_added_in_snapshot`. + 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 +724,16 @@ impl<'tcx> RegionConstraintCollector<'tcx> { .max() .unwrap_or(None) } + + #[inline] + 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 +818,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..74f365ced2373 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 @@ -46,51 +46,56 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { } } -/// The opportunistic type and region resolver is similar to the -/// opportunistic type resolver, but also opportunistically resolves -/// regions. It is useful for canonicalization. -pub struct OpportunisticTypeAndRegionResolver<'a, 'tcx> { +/// The opportunistic region resolver opportunistically resolves regions +/// variables to the variable with the least variable id. It is used when +/// normlizing projections to avoid hitting the recursion limit by creating +/// many versions of a predicate for types that in the end have to unify. +/// +/// If you want to resolve type and const variables as well, call +/// [InferCtxt::resolve_vars_if_possible] first. +pub struct OpportunisticRegionResolver<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, } -impl<'a, 'tcx> OpportunisticTypeAndRegionResolver<'a, 'tcx> { +impl<'a, 'tcx> OpportunisticRegionResolver<'a, 'tcx> { pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { - OpportunisticTypeAndRegionResolver { infcx } + OpportunisticRegionResolver { infcx } } } -impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticTypeAndRegionResolver<'a, 'tcx> { +impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticRegionResolver<'a, 'tcx> { fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.infcx.tcx } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.needs_infer() { + if !t.has_infer_regions() { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { - let t0 = self.infcx.shallow_resolve(t); - t0.super_fold_with(self) + t.super_fold_with(self) } } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { - ty::ReVar(rid) => self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.tcx(), rid), + ty::ReVar(rid) => { + let resolved = self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(rid); + self.tcx().reuse_or_mk_region(r, ty::ReVar(resolved)) + } _ => r, } } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - if !ct.needs_infer() { + if !ct.has_infer_regions() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { - let c0 = self.infcx.shallow_resolve(ct); - c0.super_fold_with(self) + ct.super_fold_with(self) } } } @@ -123,7 +128,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,24 +187,22 @@ 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 { ty::Infer(ty::TyVar(vid)) => { self.err = Some(FixupError::UnresolvedTy(vid)); - self.tcx().types.err + self.tcx().ty_error() } ty::Infer(ty::IntVar(vid)) => { self.err = Some(FixupError::UnresolvedIntTy(vid)); - self.tcx().types.err + self.tcx().ty_error() } ty::Infer(ty::FloatVar(vid)) => { self.err = Some(FixupError::UnresolvedFloatTy(vid)); - self.tcx().types.err + self.tcx().ty_error() } ty::Infer(_) => { bug!("Unexpected type in full type resolver: {:?}", t); @@ -222,16 +226,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().const_error(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..90962d210b5b4 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, ToPredicate, 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,15 +96,16 @@ 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, - ty::Predicate::Subtype(ty::Binder::dummy(ty::SubtypePredicate { + ty::PredicateKind::Subtype(ty::Binder::dummy(ty::SubtypePredicate { a_is_expected: self.a_is_expected, a, b, - })), + })) + .to_predicate(self.tcx()), )); Ok(a) @@ -117,9 +119,9 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { Ok(a) } - (&ty::Error, _) | (_, &ty::Error) => { + (&ty::Error(_), _) | (_, &ty::Error(_)) => { infcx.set_tainted_by_errors(); - Ok(self.tcx().types.err) + Ok(self.tcx().ty_error()) } _ => { @@ -169,3 +171,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 b59c560669181..53c7dcc637718 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,13 @@ 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> { + storage: &'a mut TypeVariableStorage<'tcx>, + + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, } #[derive(Copy, Clone, Debug)] @@ -54,6 +108,7 @@ pub enum TypeVariableOriginKind { /// One of the upvars or closure kind parameters in a `ClosureSubsts` /// (before it has been determined). + // FIXME(eddyb) distinguish upvar inference variables from the rest. ClosureSynthetic, SubstitutionPlaceholder, AutoDeref, @@ -62,7 +117,7 @@ pub enum TypeVariableOriginKind { LatticeVariable, } -struct TypeVariableData { +pub(crate) struct TypeVariableData { origin: TypeVariableOrigin, diverging: bool, } @@ -91,33 +146,37 @@ 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(), } } + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> TypeVariableTable<'a, 'tcx> { + TypeVariableTable { storage: self, undo_log } + } +} + +impl<'tcx> TypeVariableTable<'_, 'tcx> { /// Returns the diverges flag given when `vid` was created. /// /// Note that this function does not return care whether /// `vid` has been unified with something else or not. pub fn var_diverges(&self, vid: ty::TyVid) -> bool { - self.values.get(vid.index as usize).diverging + self.storage.values.get(vid.index as usize).diverging } /// Returns the origin that was given when `vid` was created. @@ -125,7 +184,7 @@ impl<'tcx> TypeVariableTable<'tcx> { /// Note that this function does not return care whether /// `vid` has been unified with something else or not. pub fn var_origin(&self, vid: ty::TyVid) -> &TypeVariableOrigin { - &self.values.get(vid.index as usize).origin + &self.storage.values.get(vid.index as usize).origin } /// Records that `a == b`, depending on `dir`. @@ -134,8 +193,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`. @@ -144,7 +203,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`. @@ -154,18 +213,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. @@ -184,12 +243,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!( @@ -202,7 +261,7 @@ impl<'tcx> TypeVariableTable<'tcx> { /// Returns the number of type variables created thus far. pub fn num_vars(&self) -> usize { - self.values.len() + self.storage.values.len() } /// Returns the "root" variable of `vid` in the `eq_relations` @@ -211,7 +270,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` @@ -222,7 +281,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 @@ -240,7 +299,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 @@ -256,57 +315,33 @@ 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(), - } + #[inline] + fn values( + &mut self, + ) -> sv::SnapshotVec, &mut InferCtxtUndoLogs<'tcx>> { + self.storage.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); + #[inline] + fn eq_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidEqKey<'tcx>> { + self.storage.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); + #[inline] + fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> { + self.storage.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) - .map(|index| self.values.get(index as usize).origin) + range.start..range.end, + (range.start.index..range.end.index) + .map(|index| self.storage.values.get(index as usize).origin) .collect(), ) } @@ -317,14 +352,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 @@ -334,11 +370,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.storage.eq_relations, + &mut *self.undo_log, + ); + let escaping_type = match eq_relations.probe_value(vid) { TypeVariableValue::Unknown { .. } => bug!(), TypeVariableValue::Known { value } => value, }; @@ -357,7 +399,7 @@ impl<'tcx> TypeVariableTable<'tcx> { /// Returns indices of all variables that are not yet /// instantiated. pub fn unsolved_variables(&mut self) -> Vec { - (0..self.values.len()) + (0..self.storage.values.len()) .filter_map(|i| { let vid = ty::TyVid { index: i as u32 }; match self.probe(vid) { @@ -395,7 +437,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..2cfd6bb904c41 --- /dev/null +++ b/src/librustc_infer/infer/undo_log.rs @@ -0,0 +1,215 @@ +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, +{ + #[inline] + fn num_open_snapshots(&self) -> usize { + self.num_open_snapshots + } + + #[inline] + 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); + } +} + +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..0f3f3db867959 100644 --- a/src/librustc_infer/lib.rs +++ b/src/librustc_infer/lib.rs @@ -16,7 +16,12 @@ #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(const_fn)] +#![feature(const_if_match)] +#![feature(const_panic)] +#![feature(extend_one)] #![feature(never_type)] +#![feature(or_patterns)] #![feature(range_is_empty)] #![feature(in_band_lifetimes)] #![feature(crate_visibility_modifier)] @@ -30,7 +35,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..2710debea9478 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}; @@ -33,7 +33,7 @@ pub trait TraitEngine<'tcx>: 'tcx { cause, recursion_depth: 0, param_env, - predicate: trait_ref.without_const().to_predicate(), + predicate: trait_ref.without_const().to_predicate(infcx.tcx), }, ); } 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..8cd1eb9957bed 100644 --- a/src/librustc_infer/traits/mod.rs +++ b/src/librustc_infer/traits/mod.rs @@ -1,38 +1,37 @@ -//! 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::*; +pub use self::ImplSource::*; pub use self::ObligationCauseCode::*; pub use self::SelectionError::*; -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_middle::traits::*; -pub use rustc::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 +/// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for +/// which the "impl_source" must be found. The process of finding a "impl_source" is /// called "resolving" the `Obligation`. This process consists of -/// either identifying an `impl` (e.g., `impl Eq for int`) that -/// provides the required vtable, or else finding a bound that is in +/// either identifying an `impl` (e.g., `impl Eq for i32`) that +/// satisfies the obligation, or else finding a bound that is in /// scope. The eventual result is usually a `Selection` (defined below). #[derive(Clone, PartialEq, Eq, Hash)] pub struct Obligation<'tcx, T> { @@ -58,13 +57,13 @@ pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; // `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -static_assert_size!(PredicateObligation<'_>, 112); +static_assert_size!(PredicateObligation<'_>, 48); pub type Obligations<'tcx, O> = Vec>; pub type PredicateObligations<'tcx> = Vec>; pub type TraitObligations<'tcx> = Vec>; -pub type Selection<'tcx> = Vtable<'tcx, PredicateObligation<'tcx>>; +pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>; pub struct FulfillmentError<'tcx> { pub obligation: PredicateObligation<'tcx>, @@ -80,6 +79,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 183e4be189022..65284bcee912c 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> { @@ -23,7 +30,7 @@ pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>; impl<'tcx, T> Normalized<'tcx, T> { pub fn with(self, value: U) -> Normalized<'tcx, U> { - Normalized { value: value, obligations: self.obligations } + Normalized { value, obligations: self.obligations } } } @@ -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,31 @@ 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> { + #[inline] + 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> { + #[inline] + 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 +128,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 +143,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 +152,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 +166,7 @@ impl<'tcx> ProjectionCache<'tcx> { } }; - self.map.insert( + map.insert( key, ProjectionCacheEntry::NormalizedTy(Normalized { value: ty, obligations: vec![] }), ); @@ -163,7 +178,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 +193,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 a7c0267111522..4ae7e417a8f67 100644 --- a/src/librustc_infer/traits/util.rs +++ b/src/librustc_infer/traits/util.rs @@ -1,43 +1,53 @@ 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> { - match *pred { - ty::Predicate::Trait(ref data, constness) => { - ty::Predicate::Trait(tcx.anonymize_late_bound_regions(data), constness) +pub fn anonymize_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + pred: ty::Predicate<'tcx>, +) -> ty::Predicate<'tcx> { + let kind = pred.kind(); + let new = match kind { + &ty::PredicateKind::Trait(ref data, constness) => { + ty::PredicateKind::Trait(tcx.anonymize_late_bound_regions(data), constness) } - ty::Predicate::RegionOutlives(ref data) => { - ty::Predicate::RegionOutlives(tcx.anonymize_late_bound_regions(data)) + ty::PredicateKind::RegionOutlives(data) => { + ty::PredicateKind::RegionOutlives(tcx.anonymize_late_bound_regions(data)) } - ty::Predicate::TypeOutlives(ref data) => { - ty::Predicate::TypeOutlives(tcx.anonymize_late_bound_regions(data)) + ty::PredicateKind::TypeOutlives(data) => { + ty::PredicateKind::TypeOutlives(tcx.anonymize_late_bound_regions(data)) } - ty::Predicate::Projection(ref data) => { - ty::Predicate::Projection(tcx.anonymize_late_bound_regions(data)) + ty::PredicateKind::Projection(data) => { + ty::PredicateKind::Projection(tcx.anonymize_late_bound_regions(data)) } - ty::Predicate::WellFormed(data) => ty::Predicate::WellFormed(data), + &ty::PredicateKind::WellFormed(data) => ty::PredicateKind::WellFormed(data), - ty::Predicate::ObjectSafe(data) => ty::Predicate::ObjectSafe(data), + &ty::PredicateKind::ObjectSafe(data) => ty::PredicateKind::ObjectSafe(data), - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) + &ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { + ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) } - ty::Predicate::Subtype(ref data) => { - ty::Predicate::Subtype(tcx.anonymize_late_bound_regions(data)) + ty::PredicateKind::Subtype(data) => { + ty::PredicateKind::Subtype(tcx.anonymize_late_bound_regions(data)) } - ty::Predicate::ConstEvaluatable(def_id, substs) => { - ty::Predicate::ConstEvaluatable(def_id, substs) + &ty::PredicateKind::ConstEvaluatable(def_id, substs) => { + ty::PredicateKind::ConstEvaluatable(def_id, substs) } - } + + ty::PredicateKind::ConstEquate(c1, c2) => ty::PredicateKind::ConstEquate(c1, c2), + }; + + if new != *kind { new.to_predicate(tcx) } else { pred } } struct PredicateSet<'tcx> { @@ -47,17 +57,17 @@ struct PredicateSet<'tcx> { impl PredicateSet<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> Self { - Self { tcx: tcx, set: Default::default() } + Self { tcx, set: Default::default() } } - fn insert(&mut self, pred: &ty::Predicate<'tcx>) -> bool { + fn insert(&mut self, pred: ty::Predicate<'tcx>) -> bool { // We have to be careful here because we want // - // for<'a> Foo<&'a int> + // for<'a> Foo<&'a i32> // // and // - // for<'b> Foo<&'b int> + // for<'b> Foo<&'b i32> // // to be considered equivalent. So normalize all late-bound // regions before we throw things into the underlying set. @@ -65,12 +75,20 @@ impl PredicateSet<'tcx> { } } -impl>> Extend for PredicateSet<'tcx> { - fn extend>(&mut self, iter: I) { +impl Extend> for PredicateSet<'tcx> { + fn extend>>(&mut self, iter: I) { for pred in iter { - self.insert(pred.as_ref()); + self.insert(pred); } } + + fn extend_one(&mut self, pred: ty::Predicate<'tcx>) { + self.insert(pred); + } + + fn extend_reserve(&mut self, additional: usize) { + Extend::>::extend_reserve(&mut self.set, additional); + } } /////////////////////////////////////////////////////////////////////////// @@ -84,68 +102,114 @@ 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(tcx))) +} + +pub fn elaborate_trait_refs<'tcx>( + tcx: TyCtxt<'tcx>, + trait_refs: impl Iterator>, +) -> Elaborator<'tcx> { + let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate(tcx)); + elaborate_predicates(tcx, predicates) +} + pub fn elaborate_predicates<'tcx>( tcx: TyCtxt<'tcx>, - 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 cause = if let Some(span) = span { + ObligationCause::dummy_with_span(span) + } else { + ObligationCause::dummy() + }; + + Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate } } impl Elaborator<'tcx> { - 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 { - ty::Predicate::Trait(ref data, _) => { + match obligation.predicate.kind() { + ty::PredicateKind::Trait(ref data, _) => { // Get predicates declared on the trait. let predicates = tcx.super_predicates_of(data.def_id()); - let 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(..) => { + ty::PredicateKind::WellFormed(..) => { // Currently, we do not elaborate WF predicates, // although we easily could. } - ty::Predicate::ObjectSafe(..) => { + ty::PredicateKind::ObjectSafe(..) => { // Currently, we do not elaborate object-safe // predicates. } - ty::Predicate::Subtype(..) => { + ty::PredicateKind::Subtype(..) => { // Currently, we do not "elaborate" predicates like `X <: Y`, // though conceivably we might. } - ty::Predicate::Projection(..) => { + ty::PredicateKind::Projection(..) => { // Nothing to elaborate in a projection predicate. } - ty::Predicate::ClosureKind(..) => { + ty::PredicateKind::ClosureKind(..) => { // Nothing to elaborate when waiting for a closure's kind to be inferred. } - ty::Predicate::ConstEvaluatable(..) => { + ty::PredicateKind::ConstEvaluatable(..) => { // Currently, we do not elaborate const-evaluatable // predicates. } - ty::Predicate::RegionOutlives(..) => { + ty::PredicateKind::ConstEquate(..) => { + // Currently, we do not elaborate const-equate + // predicates. + } + ty::PredicateKind::RegionOutlives(..) => { // Nothing to elaborate from `'a: 'b`. } - ty::Predicate::TypeOutlives(ref data) => { + ty::PredicateKind::TypeOutlives(ref data) => { // We know that `T: 'a` for some type `T`. We can // often elaborate this. For example, if we know that // `[U]: 'a`, that implies that `U: 'a`. Similarly, if @@ -177,7 +241,7 @@ impl Elaborator<'tcx> { if r.is_late_bound() { None } else { - Some(ty::Predicate::RegionOutlives(ty::Binder::dummy( + Some(ty::PredicateKind::RegionOutlives(ty::Binder::dummy( ty::OutlivesPredicate(r, r_min), ))) } @@ -185,7 +249,7 @@ impl Elaborator<'tcx> { Component::Param(p) => { let ty = tcx.mk_ty_param(p.index, p.name); - Some(ty::Predicate::TypeOutlives(ty::Binder::dummy( + Some(ty::PredicateKind::TypeOutlives(ty::Binder::dummy( ty::OutlivesPredicate(ty, r_min), ))) } @@ -199,7 +263,9 @@ impl Elaborator<'tcx> { None } }) - .filter(|p| visited.insert(p)), + .map(|predicate_kind| predicate_kind.to_predicate(tcx)) + .filter(|&predicate| visited.insert(predicate)) + .map(|predicate| predicate_obligation(predicate, None)), ); } } @@ -207,19 +273,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::PredicateKind::Trait(data, _) = obligation.predicate.kind() { + return Some(data.to_poly_trait_ref()); + } + } + None + } + + fn size_hint(&self) -> (usize, Option) { + let (_, upper) = self.base_iterator.size_hint(); + (0, upper) + } +} diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml index e84181f1d75e7..112dc7037f588 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"] } @@ -20,18 +21,17 @@ rustc_expand = { path = "../librustc_expand" } 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_serialize = { path = "../librustc_serialize" } +rustc_middle = { path = "../librustc_middle" } rustc_ast_lowering = { path = "../librustc_ast_lowering" } rustc_ast_passes = { path = "../librustc_ast_passes" } rustc_incremental = { path = "../librustc_incremental" } rustc_traits = { path = "../librustc_traits" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_codegen_ssa = { path = "../librustc_codegen_ssa" } -rustc_codegen_utils = { path = "../librustc_codegen_utils" } +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..7fa1a3eb0f591 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,16 +9,16 @@ //! 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 { - write!(f, "{}", tcx.sess.source_map().span_to_string(span)) + rustc_span::debug_with_source_map(span, f, tcx.sess.source_map()) } else { rustc_span::default_span_debug(span, f) } @@ -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 e15217dfa67e6..920cc6021e687 100644 --- a/src/librustc_interface/interface.rs +++ b/src/librustc_interface/interface.rs @@ -1,24 +1,24 @@ pub use crate::passes::BoxedResolver; use crate::util; -use rustc::lint; -use rustc::session::config::{self, ErrorOutputType, Input, OutputFilenames}; -use rustc::session::early_error; -use rustc::session::{DiagnosticOutput, Session}; -use rustc::ty; -use rustc::util::common::ErrorReported; use rustc_ast::ast::{self, MetaItemKind}; use rustc_ast::token; -use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_codegen_ssa::traits::CodegenBackend; 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; +use rustc_session::lint; use rustc_session::parse::{CrateConfig, ParseSess}; +use rustc_session::{DiagnosticOutput, Session}; use rustc_span::edition; -use rustc_span::source_map::{FileLoader, FileName, SourceMap}; +use rustc_span::source_map::{FileLoader, FileName}; use std::path::PathBuf; use std::result; use std::sync::{Arc, Mutex}; @@ -31,7 +31,6 @@ pub type Result = result::Result; pub struct Compiler { pub(crate) sess: Lrc, codegen_backend: Lrc>, - source_map: Lrc, pub(crate) input: Input, pub(crate) input_path: Option, pub(crate) output_dir: Option, @@ -49,9 +48,6 @@ impl Compiler { pub fn codegen_backend(&self) -> &Lrc> { &self.codegen_backend } - pub fn source_map(&self) -> &Lrc { - &self.source_map - } pub fn input(&self) -> &Input { &self.input } @@ -168,7 +164,7 @@ pub fn run_compiler_in_existing_thread_pool( f: impl FnOnce(&Compiler) -> R, ) -> R { let registry = &config.registry; - let (sess, codegen_backend, source_map) = util::create_session( + let (sess, codegen_backend) = util::create_session( config.opts, config.crate_cfg, config.diagnostic_output, @@ -181,7 +177,6 @@ pub fn run_compiler_in_existing_thread_pool( let compiler = Compiler { sess, codegen_backend, - source_map, input: config.input, input_path: config.input_path, output_dir: config.output_dir, @@ -191,20 +186,23 @@ pub fn run_compiler_in_existing_thread_pool( override_queries: config.override_queries, }; - let r = { - let _sess_abort_error = OnDrop(|| { - compiler.sess.diagnostic().print_error_count(registry); - }); + rustc_span::with_source_map(compiler.sess.parse_sess.clone_source_map(), move || { + let r = { + let _sess_abort_error = OnDrop(|| { + compiler.sess.finish_diagnostics(registry); + }); - f(&compiler) - }; + f(&compiler) + }; - let prof = compiler.sess.prof.clone(); - prof.generic_activity("drop_compiler").run(move || drop(compiler)); - r + let prof = compiler.sess.prof.clone(); + prof.generic_activity("drop_compiler").run(move || drop(compiler)); + r + }) } pub fn run_compiler(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { + log::trace!("run_compiler"); let stderr = config.stderr.take(); util::spawn_thread_pool( config.opts.edition, 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 4fe7a06e5609e..ed5e715ce7084 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -3,39 +3,37 @@ 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::lint; -use rustc::middle; -use rustc::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn}; -use rustc::session::config::{self, CrateType, Input, OutputFilenames, OutputType}; -use rustc::session::config::{PpMode, PpSourceMode}; -use rustc::session::search_paths::PathKind; -use rustc::session::Session; -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_utils::codegen_backend::CodegenBackend; -use rustc_codegen_utils::link::filename_for_metadata; -use rustc_data_structures::sync::{par_iter, Lrc, Once, ParallelIterator, WorkerLocal}; +use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::sync::{par_iter, Lrc, OnceCell, ParallelIterator, WorkerLocal}; use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; -use rustc_errors::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::{CrateType, Input, OutputFilenames, OutputType, PpMode, PpSourceMode}; +use rustc_session::lint; +use rustc_session::output::{filename_for_input, filename_for_metadata}; +use rustc_session::search_paths::PathKind; +use rustc_session::Session; use rustc_span::symbol::Symbol; -use rustc_span::FileName; +use rustc_span::{FileName, RealFileName}; use rustc_trait_selection::traits; use rustc_typeck as typeck; @@ -103,13 +101,15 @@ pub fn configure_and_expand( krate: ast::Crate, crate_name: &str, ) -> Result<(ast::Crate, BoxedResolver)> { + log::trace!("configure_and_expand"); // Currently, we ignore the name resolution data structures for the purposes of dependency // tracking. Instead we will run name resolution and include its output in the hash of each // item, much like we do for macro expansion. In other words, the hash reflects not just // its contents but the results of name resolution on those contents. Hopefully we'll push // this back at some point. let crate_name = crate_name.to_string(); - let (result, resolver) = BoxedResolver::new(static move || { + 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( @@ -126,11 +126,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)) @@ -170,10 +170,10 @@ pub fn register_plugins<'a>( sess.init_features(features); let crate_types = util::collect_crate_types(sess, &krate.attrs); - sess.crate_types.set(crate_types); + sess.init_crate_types(crate_types); let disambiguator = util::compute_crate_disambiguator(sess); - sess.crate_disambiguator.set(disambiguator); + sess.crate_disambiguator.set(disambiguator).expect("not yet initialized"); rustc_incremental::prepare_session_directory(sess, &crate_name, disambiguator); if sess.opts.incremental.is_some() { @@ -210,14 +210,7 @@ pub fn register_plugins<'a>( Ok((krate, Lrc::new(lint_store))) } -fn configure_and_expand_inner<'a>( - sess: &'a Session, - lint_store: &'a LintStore, - mut krate: ast::Crate, - crate_name: &str, - resolver_arenas: &'a ResolverArenas<'a>, - metadata_loader: &'a MetadataLoaderDyn, -) -> Result<(ast::Crate, Resolver<'a>)> { +fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) { sess.time("pre_AST_expansion_lint_checks", || { rustc_lint::check_ast_crate( sess, @@ -228,6 +221,18 @@ fn configure_and_expand_inner<'a>( rustc_lint::BuiltinCombinedPreExpansionLintPass::new(), ); }); +} + +fn configure_and_expand_inner<'a>( + sess: &'a Session, + lint_store: &'a LintStore, + mut krate: ast::Crate, + crate_name: &str, + resolver_arenas: &'a ResolverArenas<'a>, + metadata_loader: &'a MetadataLoaderDyn, +) -> Result<(ast::Crate, Resolver<'a>)> { + log::trace!("configure_and_expand_inner"); + pre_expansion_lint(sess, lint_store, &krate); let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas); rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition()); @@ -241,7 +246,7 @@ fn configure_and_expand_inner<'a>( alt_std_name, ); if let Some(name) = name { - sess.parse_sess.injected_crate_name.set(name); + sess.parse_sess.injected_crate_name.set(name).expect("not yet initialized"); } krate }); @@ -285,13 +290,15 @@ fn configure_and_expand_inner<'a>( let features = sess.features_untracked(); let cfg = rustc_expand::expand::ExpansionConfig { features: Some(&features), - recursion_limit: *sess.recursion_limit.get(), + recursion_limit: sess.recursion_limit(), trace_mac: sess.opts.debugging_opts.trace_macros, should_test: sess.opts.test, + span_debug: sess.opts.debugging_opts.span_debug, ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string()) }; - let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver); + let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k); + let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver, Some(&extern_mod_loaded)); // Expand macros now! let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate)); @@ -302,20 +309,34 @@ fn configure_and_expand_inner<'a>( ecx.check_unused_macros(); }); - let mut missing_fragment_specifiers: Vec<_> = - ecx.parse_sess.missing_fragment_specifiers.borrow().iter().cloned().collect(); - missing_fragment_specifiers.sort(); + let mut missing_fragment_specifiers: Vec<_> = ecx + .parse_sess + .missing_fragment_specifiers + .borrow() + .iter() + .map(|(span, node_id)| (*span, *node_id)) + .collect(); + missing_fragment_specifiers.sort_unstable_by_key(|(span, _)| *span); + + let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); - for span in missing_fragment_specifiers { + for (span, node_id) in missing_fragment_specifiers { let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER; let msg = "missing fragment specifier"; - resolver.lint_buffer().buffer_lint(lint, ast::CRATE_NODE_ID, span, msg); + resolver.lint_buffer().buffer_lint(lint, node_id, span, msg); } if cfg!(windows) { env::set_var("PATH", &old_path); } - krate - }); + + if recursion_limit_hit { + // If we hit a recursion limit, exit early to avoid later passes getting overwhelmed + // with a large AST + Err(ErrorReported) + } else { + Ok(krate) + } + })?; sess.time("maybe_building_test_harness", || { rustc_builtin_macros::test_harness::inject( @@ -338,6 +359,7 @@ fn configure_and_expand_inner<'a>( should_loop |= true; } if should_loop { + log::debug!("replacing bodies with loop {{}}"); util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate); } @@ -345,8 +367,8 @@ fn configure_and_expand_inner<'a>( rustc_ast_passes::ast_validation::check_crate(sess, &krate, &mut resolver.lint_buffer()) }); - let crate_types = sess.crate_types.borrow(); - let is_proc_macro_crate = crate_types.contains(&config::CrateType::ProcMacro); + let crate_types = sess.crate_types(); + let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); // For backwards compatibility, we don't try to run proc macro injection // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being @@ -422,12 +444,16 @@ pub fn lower_to_hir<'res, 'tcx>( resolver: &'res mut Resolver<'_>, dep_graph: &'res DepGraph, krate: &'res ast::Crate, - arena: &'tcx Arena<'tcx>, + arena: &'tcx rustc_ast_lowering::Arena<'tcx>, ) -> Crate<'tcx> { + // We're constructing the HIR here; we don't care what we will + // read, since we haven't even constructed the *input* to + // incr. comp. yet. + dep_graph.assert_ignored(); + // Lower AST to HIR. let hir_crate = rustc_ast_lowering::lower_crate( sess, - &dep_graph, &krate, resolver, rustc_parse::nt_to_tokenstream, @@ -471,13 +497,8 @@ fn generated_output_paths( // If the filename has been overridden using `-o`, it will not be modified // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. OutputType::Exe if !exact_name => { - for crate_type in sess.crate_types.borrow().iter() { - let p = ::rustc_codegen_utils::link::filename_for_input( - sess, - *crate_type, - crate_name, - outputs, - ); + for crate_type in sess.crate_types().iter() { + let p = filename_for_input(sess, *crate_type, crate_name, outputs); out_filenames.push(p); } } @@ -528,6 +549,22 @@ fn escape_dep_filename(filename: &FileName) -> String { filename.to_string().replace(" ", "\\ ") } +// Makefile comments only need escaping newlines and `\`. +// The result can be unescaped by anything that can unescape `escape_default` and friends. +fn escape_dep_env(symbol: Symbol) -> String { + let s = symbol.as_str(); + let mut escaped = String::with_capacity(s.len()); + for c in s.chars() { + match c { + '\n' => escaped.push_str(r"\n"), + '\r' => escaped.push_str(r"\r"), + '\\' => escaped.push_str(r"\\"), + _ => escaped.push(c), + } + } + escaped +} + fn write_out_deps( sess: &Session, boxed_resolver: &Steal>>, @@ -557,13 +594,16 @@ fn write_out_deps( for cnum in resolver.cstore().crates_untracked() { let source = resolver.cstore().crate_source_untracked(cnum); if let Some((path, _)) = source.dylib { - files.push(escape_dep_filename(&FileName::Real(path))); + let file_name = FileName::Real(RealFileName::Named(path)); + files.push(escape_dep_filename(&file_name)); } if let Some((path, _)) = source.rlib { - files.push(escape_dep_filename(&FileName::Real(path))); + let file_name = FileName::Real(RealFileName::Named(path)); + files.push(escape_dep_filename(&file_name)); } if let Some((path, _)) = source.rmeta { - files.push(escape_dep_filename(&FileName::Real(path))); + let file_name = FileName::Real(RealFileName::Named(path)); + files.push(escape_dep_filename(&file_name)); } } }); @@ -580,6 +620,25 @@ fn write_out_deps( for path in files { writeln!(file, "{}:", path)?; } + + // Emit special comments with information about accessed environment variables. + let env_depinfo = sess.parse_sess.env_depinfo.borrow(); + if !env_depinfo.is_empty() { + let mut envs: Vec<_> = env_depinfo + .iter() + .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) + .collect(); + envs.sort_unstable(); + writeln!(file)?; + for (k, v) in envs { + write!(file, "# env-dep:{}", k)?; + if let Some(v) = v { + write!(file, "={}", v)?; + } + writeln!(file)?; + } + } + Ok(()) })(); @@ -664,7 +723,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); @@ -677,7 +736,7 @@ pub fn default_provide(providers: &mut ty::query::Providers<'_>) { rustc_ty::provide(providers); rustc_metadata::provide(providers); rustc_lint::provide(providers); - rustc_codegen_utils::provide(providers); + rustc_symbol_mangling::provide(providers); rustc_codegen_ssa::provide(providers); } @@ -693,11 +752,11 @@ impl<'tcx> QueryContext<'tcx> { where F: FnOnce(TyCtxt<'tcx>) -> R, { - ty::tls::enter_global(self.0, |tcx| f(tcx)) + ty::tls::enter_global(self.0, f) } pub fn print_stats(&mut self) { - self.enter(|tcx| ty::query::print_stats(tcx)) + self.enter(ty::query::print_stats) } } @@ -709,11 +768,14 @@ pub fn create_global_ctxt<'tcx>( mut resolver_outputs: ResolverOutputs, outputs: OutputFilenames, crate_name: &str, - global_ctxt: &'tcx Once>, + global_ctxt: &'tcx OnceCell>, arena: &'tcx WorkerLocal>, ) -> QueryContext<'tcx> { let sess = &compiler.session(); - let defs: &'tcx Definitions = arena.alloc(mem::take(&mut resolver_outputs.definitions)); + let defs: &'tcx Definitions = arena.alloc(mem::replace( + &mut resolver_outputs.definitions, + Definitions::new(crate_name, sess.local_crate_disambiguator()), + )); let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess); @@ -731,7 +793,7 @@ pub fn create_global_ctxt<'tcx>( } let gcx = sess.time("setup_global_ctxt", || { - global_ctxt.init_locking(|| { + global_ctxt.get_or_init(|| { TyCtxt::create_global_ctxt( sess, lint_store, @@ -762,7 +824,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; @@ -799,7 +861,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()); }); }); }, @@ -824,13 +886,13 @@ 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); + + if tcx.hir().body_const_context(def_id).is_some() { + tcx.ensure().mir_drops_elaborated_and_const_checked(def_id); + } } }); @@ -897,8 +959,7 @@ fn encode_and_write_metadata( let metadata_kind = tcx .sess - .crate_types - .borrow() + .crate_types() .iter() .map(|ty| match *ty { CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => MetadataKind::None, 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 426d97cc09b92..4a41c3e97cafc 100644 --- a/src/librustc_interface/queries.rs +++ b/src/librustc_interface/queries.rs @@ -1,20 +1,21 @@ use crate::interface::{Compiler, Result}; use crate::passes::{self, BoxedResolver, QueryContext}; -use rustc::arena::Arena; -use rustc::dep_graph::DepGraph; -use rustc::session::config::{OutputFilenames, OutputType}; -use rustc::session::Session; -use rustc::ty::steal::Steal; -use rustc::ty::{GlobalCtxt, ResolverOutputs}; -use rustc::util::common::ErrorReported; use rustc_ast::{self, ast}; -use rustc_codegen_utils::codegen_backend::CodegenBackend; -use rustc_data_structures::sync::{Lrc, Once, WorkerLocal}; +use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; +use rustc_errors::ErrorReported; use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::Crate; use rustc_incremental::DepGraphFuture; use rustc_lint::LintStore; +use rustc_middle::arena::Arena; +use rustc_middle::dep_graph::DepGraph; +use rustc_middle::ty::steal::Steal; +use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt}; +use rustc_session::config::{OutputFilenames, OutputType}; +use rustc_session::{output::find_crate_name, Session}; +use rustc_span::symbol::sym; use std::any::Any; use std::cell::{Ref, RefCell, RefMut}; use std::mem; @@ -64,9 +65,10 @@ impl Default for Query { pub struct Queries<'tcx> { compiler: &'tcx Compiler, - gcx: Once>, + gcx: OnceCell>, arena: WorkerLocal>, + hir_arena: WorkerLocal>, dep_graph_future: Query>, parse: Query, @@ -84,8 +86,9 @@ impl<'tcx> Queries<'tcx> { pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> { Queries { compiler, - gcx: Once::new(), + gcx: OnceCell::new(), arena: WorkerLocal::new(|_| Arena::default()), + hir_arena: WorkerLocal::new(|_| rustc_ast_lowering::Arena::default()), dep_graph_future: Default::default(), parse: Default::default(), crate_name: Default::default(), @@ -134,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, ); @@ -157,11 +160,7 @@ impl<'tcx> Queries<'tcx> { None => { let parse_result = self.parse()?; let krate = parse_result.peek(); - rustc_codegen_utils::link::find_crate_name( - Some(self.session()), - &krate.attrs, - &self.compiler.input, - ) + find_crate_name(Some(self.session()), &krate.attrs, &self.compiler.input) } }) }) @@ -170,6 +169,7 @@ impl<'tcx> Queries<'tcx> { pub fn expansion( &self, ) -> Result<&Query<(ast::Crate, Steal>>, Lrc)>> { + log::trace!("expansion"); self.expansion.compute(|| { let crate_name = self.crate_name()?.peek().clone(); let (krate, lint_store) = self.register_plugins()?.take(); @@ -221,10 +221,10 @@ impl<'tcx> Queries<'tcx> { resolver, &*self.dep_graph()?.peek(), &krate, - &self.arena, + &self.hir_arena, )) })?; - let hir = self.arena.alloc(hir); + let hir = self.hir_arena.alloc(hir); Ok((hir, Steal::new(BoxedResolver::to_resolver_outputs(resolver)))) }) } @@ -277,11 +277,58 @@ impl<'tcx> Queries<'tcx> { // Don't do code generation if there were any errors self.session().compile_status()?; + // Hook for compile-fail tests. + Self::check_for_rustc_errors_attr(tcx); + Ok(passes::start_codegen(&***self.codegen_backend(), tcx, &*outputs.peek())) }) }) } + /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used + /// to write compile-fail tests that actually test that compilation succeeds without reporting + /// an error. + fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { + let def_id = match tcx.entry_fn(LOCAL_CRATE) { + Some((def_id, _)) => def_id, + _ => return, + }; + + let attrs = &*tcx.get_attrs(def_id.to_def_id()); + let attrs = attrs.iter().filter(|attr| attr.check_name(sym::rustc_error)); + for attr in attrs { + match attr.meta_item_list() { + // Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`. + Some(list) + if list.iter().any(|list_item| { + matches!( + list_item.ident().map(|i| i.name), + Some(sym::delay_span_bug_from_inside_query) + ) + }) => + { + tcx.ensure().trigger_delay_span_bug(def_id); + } + + // Bare `#[rustc_error]`. + None => { + tcx.sess.span_fatal( + tcx.def_span(def_id), + "fatal error triggered by #[rustc_error]", + ); + } + + // Some other attribute. + Some(_) => { + tcx.sess.span_warn( + tcx.def_span(def_id), + "unexpected annotation used with `#[rustc_error(...)]!", + ); + } + } + } + } + pub fn linker(&'tcx self) -> Result { let dep_graph = self.dep_graph()?; let prepare_outputs = self.prepare_outputs()?; diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index 1b80cf4e3dbc2..d861b444c8816 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -1,20 +1,23 @@ -extern crate getopts; - use crate::interface::parse_cfgspecs; -use rustc::lint::Level; -use rustc::middle::cstore; -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::search_paths::SearchPath; -use rustc::session::{build_session, Session}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; +use rustc_session::config::Strip; +use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; +use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; +use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; +use rustc_session::config::{ + Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, +}; +use rustc_session::lint::Level; +use rustc_session::search_paths::SearchPath; +use rustc_session::utils::NativeLibKind; +use rustc_session::{build_session, getopts, DiagnosticOutput, Session}; use rustc_span::edition::{Edition, DEFAULT_EDITION}; use rustc_span::symbol::sym; -use rustc_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; @@ -30,7 +33,14 @@ fn build_session_options_and_crate_config(matches: getopts::Matches) -> (Options fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { let registry = registry::Registry::new(&[]); let (sessopts, cfg) = build_session_options_and_crate_config(matches); - let sess = build_session(sessopts, None, registry); + let sess = build_session( + sessopts, + None, + registry, + DiagnosticOutput::Default, + Default::default(), + None, + ); (sess, cfg) } @@ -298,30 +308,30 @@ fn test_native_libs_tracking_hash_different_values() { // Reference v1.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("b"), None, Some(cstore::NativeFramework)), - (String::from("c"), None, Some(cstore::NativeUnknown)), + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), None, NativeLibKind::Framework), + (String::from("c"), None, NativeLibKind::Unspecified), ]; // Change label v2.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("X"), None, Some(cstore::NativeFramework)), - (String::from("c"), None, Some(cstore::NativeUnknown)), + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("X"), None, NativeLibKind::Framework), + (String::from("c"), None, NativeLibKind::Unspecified), ]; // Change kind v3.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("b"), None, Some(cstore::NativeStatic)), - (String::from("c"), None, Some(cstore::NativeUnknown)), + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), None, NativeLibKind::StaticBundle), + (String::from("c"), None, NativeLibKind::Unspecified), ]; // Change new-name v4.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("b"), Some(String::from("X")), Some(cstore::NativeFramework)), - (String::from("c"), None, Some(cstore::NativeUnknown)), + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), Some(String::from("X")), NativeLibKind::Framework), + (String::from("c"), None, NativeLibKind::Unspecified), ]; assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); @@ -343,21 +353,21 @@ fn test_native_libs_tracking_hash_different_order() { // Reference v1.libs = vec![ - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("b"), None, Some(cstore::NativeFramework)), - (String::from("c"), None, Some(cstore::NativeUnknown)), + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), None, NativeLibKind::Framework), + (String::from("c"), None, NativeLibKind::Unspecified), ]; v2.libs = vec![ - (String::from("b"), None, Some(cstore::NativeFramework)), - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("c"), None, Some(cstore::NativeUnknown)), + (String::from("b"), None, NativeLibKind::Framework), + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("c"), None, NativeLibKind::Unspecified), ]; v3.libs = vec![ - (String::from("c"), None, Some(cstore::NativeUnknown)), - (String::from("a"), None, Some(cstore::NativeStatic)), - (String::from("b"), None, Some(cstore::NativeFramework)), + (String::from("c"), None, NativeLibKind::Unspecified), + (String::from("a"), None, NativeLibKind::StaticBundle), + (String::from("b"), None, NativeLibKind::Framework), ]; assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); @@ -375,141 +385,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 +452,140 @@ 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!(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_debug, true); + untracked!(span_free_formats, true); + untracked!(strip, Strip::None); + untracked!(terminal_width, Some(80)); + untracked!(threads, 99); + untracked!(time, true); + untracked!(time_llvm_passes, true); + untracked!(time_passes, true); + untracked!(trace_macros, true); + untracked!(ui_testing, true); + untracked!(unpretty, Some("expanded".to_string())); + untracked!(unstable_options, true); + untracked!(validate_mir, true); + untracked!(verbose, true); + + macro_rules! tracked { + ($name: ident, $non_default_value: expr) => { + opts = reference.clone(); + opts.debugging_opts.$name = $non_default_value; + assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + }; + } + + // Make sure that changing a [TRACKED] option changes the hash. + // This list is in alphabetical order. + tracked!(allow_features, Some(vec![String::from("lang_items")])); + tracked!(always_encode_mir, true); + tracked!(asm_comments, true); + tracked!(binary_dep_depinfo, true); + tracked!(chalk, true); + tracked!(codegen_backend, Some("abc".to_string())); + tracked!(control_flow_guard, CFGuard::Checks); + tracked!(crate_attr, vec!["abc".to_string()]); + tracked!(debug_macros, true); + tracked!(dep_info_omit_d_target, true); + tracked!(dual_proc_macros, true); + tracked!(fewer_names, true); + tracked!(force_overflow_checks, Some(true)); + tracked!(force_unstable_if_unmarked, true); + tracked!(fuel, Some(("abc".to_string(), 99))); + tracked!(human_readable_cgu_names, true); + tracked!(inline_in_all_cgus, Some(true)); + tracked!(insert_sideeffect, true); + tracked!(instrument_coverage, true); + tracked!(instrument_mcount, true); + tracked!(link_only, true); + tracked!(merge_functions, Some(MergeFunctions::Disabled)); + tracked!(mir_emit_retag, true); + tracked!(mir_opt_level, 3); + tracked!(mutable_noalias, true); + tracked!(new_llvm_pass_manager, true); + tracked!(no_codegen, true); + tracked!(no_generate_arange_section, true); + tracked!(no_link, true); + tracked!(no_profiler_runtime, true); + tracked!(osx_rpath_install_name, true); + tracked!(panic_abort_tests, true); + tracked!(plt, Some(true)); + tracked!(print_fuel, Some("abc".to_string())); + tracked!(profile, true); + tracked!(profile_emit, Some(PathBuf::from("abc"))); + tracked!(relro_level, Some(RelroLevel::Full)); + tracked!(report_delayed_bugs, true); + tracked!(run_dsymutil, false); + tracked!(sanitizer, SanitizerSet::ADDRESS); + tracked!(sanitizer_memory_track_origins, 2); + tracked!(sanitizer_recover, SanitizerSet::ADDRESS); + tracked!(saturating_float_casts, Some(true)); + tracked!(share_generics, Some(true)); + tracked!(show_span, Some(String::from("abc"))); + tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1)); + tracked!(symbol_mangling_version, SymbolManglingVersion::V0); + tracked!(teach, true); + tracked!(thinlto, Some(true)); + tracked!(tls_model, Some(TlsModel::GeneralDynamic)); + tracked!(treat_err_as_bug, Some(1)); + tracked!(unleash_the_miri_inside_of_you, true); + tracked!(use_ctors_section, Some(true)); + tracked!(verify_llvm_ir, true); } #[test] diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs index df05bd7c5117d..924908e572487 100644 --- a/src/librustc_interface/util.rs +++ b/src/librustc_interface/util.rs @@ -1,12 +1,10 @@ use log::info; -use rustc::lint; -use rustc::ty; use rustc_ast::ast::{AttrVec, BlockCheckMode}; use rustc_ast::mut_visit::{visit_clobber, MutVisitor, *}; use rustc_ast::ptr::P; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_ast::{self, ast}; -use rustc_codegen_utils::codegen_backend::CodegenBackend; +use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[cfg(parallel_compiler)] @@ -15,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::{BuiltinLintDiagnostics, LintBuffer}; +use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; use rustc_session::parse::CrateConfig; use rustc_session::CrateDisambiguator; -use rustc_session::{config, early_error, filesearch, 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; use rustc_span::symbol::{sym, Symbol}; use smallvec::SmallVec; use std::env; @@ -42,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() { + if sess.crt_static(None) { cfg.insert((tf, Some(Symbol::intern("crt-static")))); } } @@ -62,35 +65,26 @@ pub fn create_session( input_path: Option, 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( +) -> (Lrc, Lrc>) { + let mut sess = session::build_session( 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) + (Lrc::new(sess), Lrc::new(codegen_backend)) } -// 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 @@ -208,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); }); @@ -270,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 { @@ -415,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().contains(&CrateType::Executable); hasher.write(if is_exe { b"exe" } else { b"lib" }); CrateDisambiguator::from(hasher.finish::()) @@ -463,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) { @@ -496,7 +487,7 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec Vec 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, } } @@ -720,6 +713,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { kind: ast::ExprKind::Block(P(b), None), span: rustc_span::DUMMY_SP, attrs: AttrVec::new(), + tokens: None, }); ast::Stmt { @@ -735,6 +729,7 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { id: self.resolver.next_node_id(), span: rustc_span::DUMMY_SP, attrs: AttrVec::new(), + tokens: None, }); let loop_stmt = ast::Stmt { diff --git a/src/librustc_lexer/src/lib.rs b/src/librustc_lexer/src/lib.rs index 25334461a113b..200e7acf80235 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::TryFrom; /// Parsed token. /// It doesn't contain information about data that has been parsed, @@ -132,9 +142,24 @@ 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 { n_hashes: u16, err: Option }, /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a" - RawByteStr { n_hashes: usize, started: bool, terminated: bool }, + RawByteStr { n_hashes: u16, err: Option }, +} + +/// Error produced validating a raw string. Represents cases like: +/// - `r##~"abcde"##`: `InvalidStarter` +/// - `r###"abcde"##`: `NoTerminator { expected: 3, found: 2, possible_terminator_offset: Some(11)` +/// - Too many `#`s (>65535): `TooManyDelimiters` +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum RawStrError { + /// Non `#` characters exist between `r` and `"` eg. `r#~"..` + InvalidStarter { bad_char: char }, + /// The string was never terminated. `possible_terminator_offset` is the number of characters after `r` or `br` where they + /// may have intended to terminate it. + NoTerminator { expected: usize, found: usize, possible_terminator_offset: Option }, + /// More than 65535 `#`s exist. + TooManyDelimiters { found: usize }, } /// Base of numeric literal encoding according to its prefix. @@ -151,16 +176,27 @@ pub enum Base { } /// `rustc` allows files to have a shebang, e.g. "#!/usr/bin/rustrun", -/// but shebang isn't a part of rust syntax, so this function -/// skips the line if it starts with a shebang ("#!"). -/// Line won't be skipped if it represents a valid Rust syntax -/// (e.g. "#![deny(missing_docs)]"). +/// but shebang isn't a part of rust syntax. pub fn strip_shebang(input: &str) -> Option { - debug_assert!(!input.is_empty()); - if !input.starts_with("#!") || input.starts_with("#![") { - return None; + // Shebang must start with `#!` literally, without any preceding whitespace. + if input.starts_with("#!") { + let input_tail = &input[2..]; + // Shebang must have something non-whitespace after `#!` on the first line. + let first_line_tail = input_tail.lines().next()?; + if first_line_tail.contains(|c| !is_whitespace(c)) { + // Ok, this is a shebang but if the next non-whitespace token is `[` or maybe + // a doc comment (due to `TokenKind::(Line,Block)Comment` ambiguity at lexer level), + // then it may be valid Rust code, so consider it Rust code. + let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok| + !matches!(tok, TokenKind::Whitespace | TokenKind::LineComment | TokenKind::BlockComment { .. }) + ); + if next_non_whitespace_token != Some(TokenKind::OpenBracket) { + // No other choice than to consider this a shebang. + return Some(2 + first_line_tail.len()); + } + } } - Some(input.find('\n').unwrap_or(input.len())) + None } /// Parses the first token from the provided input string. @@ -209,7 +245,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 +294,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 (n_hashes, err) = self.raw_double_quoted_string(1); let suffix_start = self.len_consumed(); - if terminated { + if err.is_none() { self.eat_literal_suffix(); } - let kind = RawStr { n_hashes, started, terminated }; + let kind = RawStr { n_hashes, err }; Literal { kind, suffix_start } } _ => self.ident(), @@ -293,12 +329,12 @@ impl Cursor<'_> { } ('r', '"') | ('r', '#') => { self.bump(); - let (n_hashes, started, terminated) = self.raw_double_quoted_string(); + let (n_hashes, err) = self.raw_double_quoted_string(2); let suffix_start = self.len_consumed(); - if terminated { + if err.is_none() { self.eat_literal_suffix(); } - let kind = RawByteStr { n_hashes, started, terminated }; + let kind = RawByteStr { n_hashes, err }; Literal { kind, suffix_start } } _ => self.ident(), @@ -527,10 +563,10 @@ impl Cursor<'_> { if self.first() == '\'' { self.bump(); let kind = Char { terminated: true }; - return Literal { kind, suffix_start: self.len_consumed() }; + Literal { kind, suffix_start: self.len_consumed() } + } else { + Lifetime { starts_with_number } } - - return Lifetime { starts_with_number }; } fn single_quoted_string(&mut self) -> bool { @@ -594,29 +630,51 @@ 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 `n_hashes` and an error if encountered. + fn raw_double_quoted_string(&mut self, prefix_len: usize) -> (u16, Option) { + // Wrap the actual function to handle the error with too many hashes. + // This way, it eats the whole raw string. + let (n_hashes, err) = self.raw_string_unvalidated(prefix_len); + // Only up to 65535 `#`s are allowed in raw strings + match u16::try_from(n_hashes) { + Ok(num) => (num, err), + // We lie about the number of hashes here :P + Err(_) => (0, Some(RawStrError::TooManyDelimiters { found: n_hashes })), + } + } + + fn raw_string_unvalidated(&mut self, prefix_len: usize) -> (usize, Option) { debug_assert!(self.prev() == 'r'); - let mut started: bool = false; - let mut finished: bool = false; + let start_pos = self.len_consumed(); + let mut possible_terminator_offset = None; + let mut max_hashes = 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('"') => (), + c => { + let c = c.unwrap_or(EOF_CHAR); + return (n_start_hashes, Some(RawStrError::InvalidStarter { bad_char: c })); + } } // Skip the string contents and on each '#' character met, check if this is // a raw string termination. - while !finished { + loop { self.eat_while(|c| c != '"'); if self.is_eof() { - return (n_hashes, started, finished); + return ( + n_start_hashes, + Some(RawStrError::NoTerminator { + expected: n_start_hashes, + found: max_hashes, + possible_terminator_offset, + }), + ); } // Eat closing double quote. @@ -624,7 +682,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 `RawStr { n_hashes: 3 }` + // followed by a `#` token. + let mut hashes_left = n_start_hashes; let is_closing_hash = |c| { if c == '#' && hashes_left != 0 { hashes_left -= 1; @@ -633,10 +694,18 @@ 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 (n_start_hashes, None); + } else if n_end_hashes > max_hashes { + // Keep track of possible terminators to give a hint about + // where there might be a missing terminator + possible_terminator_offset = + Some(self.len_consumed() - start_pos - n_end_hashes + prefix_len); + max_hashes = n_end_hashes; + } } - - (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..e6acc26ec2f34 --- /dev/null +++ b/src/librustc_lexer/src/tests.rs @@ -0,0 +1,137 @@ +#[cfg(test)] +mod tests { + use crate::*; + + fn check_raw_str(s: &str, expected_hashes: u16, expected_err: Option) { + let s = &format!("r{}", s); + let mut cursor = Cursor::new(s); + cursor.bump(); + let (n_hashes, err) = cursor.raw_double_quoted_string(0); + assert_eq!(n_hashes, expected_hashes); + assert_eq!(err, expected_err); + } + + #[test] + fn test_naked_raw_str() { + check_raw_str(r#""abc""#, 0, None); + } + + #[test] + fn test_raw_no_start() { + check_raw_str(r##""abc"#"##, 0, None); + } + + #[test] + fn test_too_many_terminators() { + // this error is handled in the parser later + check_raw_str(r###"#"abc"##"###, 1, None); + } + + #[test] + fn test_unterminated() { + check_raw_str( + r#"#"abc"#, + 1, + Some(RawStrError::NoTerminator { + expected: 1, + found: 0, + possible_terminator_offset: None, + }), + ); + check_raw_str( + r###"##"abc"#"###, + 2, + Some(RawStrError::NoTerminator { + expected: 2, + found: 1, + possible_terminator_offset: Some(7), + }), + ); + // We're looking for "# not just any # + check_raw_str( + r###"##"abc#"###, + 2, + Some(RawStrError::NoTerminator { + expected: 2, + found: 0, + possible_terminator_offset: None, + }), + ) + } + + #[test] + fn test_invalid_start() { + check_raw_str(r##"#~"abc"#"##, 1, Some(RawStrError::InvalidStarter { bad_char: '~' })); + } + + #[test] + fn test_unterminated_no_pound() { + // https://github.com/rust-lang/rust/issues/70677 + check_raw_str( + r#"""#, + 0, + Some(RawStrError::NoTerminator { + expected: 0, + found: 0, + possible_terminator_offset: None, + }), + ); + } + + #[test] + fn test_valid_shebang() { + // https://github.com/rust-lang/rust/issues/70528 + let input = "#!/usr/bin/rustrun\nlet x = 5;"; + assert_eq!(strip_shebang(input), Some(18)); + } + + #[test] + fn test_invalid_shebang_valid_rust_syntax() { + // https://github.com/rust-lang/rust/issues/70528 + let input = "#! [bad_attribute]"; + assert_eq!(strip_shebang(input), None); + } + + #[test] + fn test_shebang_second_line() { + // Because shebangs are interpreted by the kernel, they must be on the first line + let input = "\n#!/bin/bash"; + assert_eq!(strip_shebang(input), None); + } + + #[test] + fn test_shebang_space() { + let input = "#! /bin/bash"; + assert_eq!(strip_shebang(input), Some(input.len())); + } + + #[test] + fn test_shebang_empty_shebang() { + let input = "#! \n[attribute(foo)]"; + assert_eq!(strip_shebang(input), None); + } + + #[test] + fn test_invalid_shebang_comment() { + let input = "#!//bin/ami/a/comment\n["; + assert_eq!(strip_shebang(input), None) + } + + #[test] + fn test_invalid_shebang_another_comment() { + let input = "#!/*bin/ami/a/comment*/\n[attribute"; + assert_eq!(strip_shebang(input), None) + } + + #[test] + fn test_shebang_valid_rust_after() { + let input = "#!/*bin/ami/a/comment*/\npub fn main() {}"; + assert_eq!(strip_shebang(input), Some(23)) + } + + #[test] + fn test_shebang_followed_by_attrib() { + let input = "#!/bin/rust-scripts\n#![allow_unused(true)]"; + assert_eq!(strip_shebang(input), Some(19)); + } +} diff --git a/src/librustc_lexer/src/unescape.rs b/src/librustc_lexer/src/unescape.rs index c51bf735fa727..697d25fdb585b 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 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..5b282c4203400 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; @@ -24,7 +24,7 @@ declare_lint_pass!( impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIntoIter { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'tcx>) { // We only care about method call expressions. - if let hir::ExprKind::MethodCall(call, span, args) = &expr.kind { + if let hir::ExprKind::MethodCall(call, span, args, _) = &expr.kind { if call.ident.name != sym::into_iter { return; } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index ab8a607072123..e746396e4c684 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,36 +21,37 @@ //! `late_lint_methods!` invocation in `lib.rs`. use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc::hir::map::Map; -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}; 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_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType}; +use rustc_feature::{GateIssue, Stability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{GenericParamKind, PatKind}; -use rustc_hir::{HirIdSet, Node}; +use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind}; +use rustc_hir::{HirId, HirIdSet, Node}; +use rustc_middle::lint::LintDiagnosticBuilder; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::FutureIncompatibleInfo; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{kw, sym, 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}; -use log::debug; +use log::{debug, trace}; use std::fmt::Write; -// hardwired lints from librustc +// hardwired lints from librustc_middle pub use rustc_session::lint::builtin::*; declare_lint! { @@ -77,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( @@ -105,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() + }); + } } } } @@ -270,7 +272,7 @@ impl EarlyLintPass for UnsafeCode { }) } - _ => return, + _ => {} } } @@ -350,6 +352,7 @@ impl MissingDoc { id: Option, attrs: &[ast::Attribute], sp: Span, + article: &'static str, desc: &'static str, ) { // If we're building a test harness, then warning about @@ -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 {}", 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() + }, + ); } } } @@ -399,14 +406,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'_, '_>, krate: &hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, None, &krate.item.attrs, krate.item.span, "crate"); + self.check_missing_docs_attrs(cx, None, &krate.item.attrs, krate.item.span, "the", "crate"); for macro_def in krate.exported_macros { let has_doc = macro_def.attrs.iter().any(|a| has_doc(a)); if !has_doc { cx.struct_span_lint( MISSING_DOCS, - cx.tcx.sess.source_map().def_span(macro_def.span), + cx.tcx.sess.source_map().guess_head_span(macro_def.span), |lint| lint.build("missing documentation for macro").emit(), ); } @@ -414,12 +421,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { } fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item<'_>) { - let desc = match it.kind { - hir::ItemKind::Fn(..) => "a function", - hir::ItemKind::Mod(..) => "a module", - hir::ItemKind::Enum(..) => "an enum", - hir::ItemKind::Struct(..) => "a struct", - hir::ItemKind::Union(..) => "a union", + match it.kind { hir::ItemKind::Trait(.., trait_item_refs) => { // Issue #11592: traits are always considered exported, even when private. if let hir::VisibilityKind::Inherited = it.vis.node { @@ -429,33 +431,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { } return; } - "a trait" } - hir::ItemKind::TyAlias(..) => "a type alias", hir::ItemKind::Impl { of_trait: Some(ref trait_ref), items, .. } => { // If the trait is private, add the impl items to `private_traits` so they don't get // reported for missing docs. let real_trait = trait_ref.path.res.def_id(); - if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(real_trait) { - match cx.tcx.hir().find(hir_id) { - Some(Node::Item(item)) => { - if let hir::VisibilityKind::Inherited = item.vis.node { - for impl_item_ref in items { - self.private_traits.insert(impl_item_ref.id.hir_id); - } + if let Some(def_id) = real_trait.as_local() { + let hir_id = cx.tcx.hir().as_local_hir_id(def_id); + if let Some(Node::Item(item)) = cx.tcx.hir().find(hir_id) { + if let hir::VisibilityKind::Inherited = item.vis.node { + for impl_item_ref in items { + self.private_traits.insert(impl_item_ref.id.hir_id); } } - _ => {} } } return; } - hir::ItemKind::Const(..) => "a constant", - hir::ItemKind::Static(..) => "a static", + + hir::ItemKind::TyAlias(..) + | hir::ItemKind::Fn(..) + | hir::ItemKind::Mod(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..) + | hir::ItemKind::Const(..) + | hir::ItemKind::Static(..) => {} + _ => return, }; - self.check_missing_docs_attrs(cx, Some(it.hir_id), &it.attrs, it.span, desc); + let def_id = cx.tcx.hir().local_def_id(it.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + + self.check_missing_docs_attrs(cx, Some(it.hir_id), &it.attrs, it.span, article, desc); } fn check_trait_item(&mut self, cx: &LateContext<'_, '_>, trait_item: &hir::TraitItem<'_>) { @@ -463,17 +472,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { return; } - let desc = match trait_item.kind { - hir::TraitItemKind::Const(..) => "an associated constant", - hir::TraitItemKind::Fn(..) => "a trait method", - hir::TraitItemKind::Type(..) => "an associated type", - }; + let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); self.check_missing_docs_attrs( cx, Some(trait_item.hir_id), &trait_item.attrs, trait_item.span, + article, desc, ); } @@ -484,29 +491,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc { return; } - let desc = match impl_item.kind { - hir::ImplItemKind::Const(..) => "an associated constant", - hir::ImplItemKind::Method(..) => "a method", - hir::ImplItemKind::TyAlias(_) => "an associated type", - hir::ImplItemKind::OpaqueTy(_) => "an associated `impl Trait` type", - }; + let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); self.check_missing_docs_attrs( cx, Some(impl_item.hir_id), &impl_item.attrs, impl_item.span, + article, desc, ); } fn check_struct_field(&mut self, cx: &LateContext<'_, '_>, sf: &hir::StructField<'_>) { if !sf.is_positional() { - self.check_missing_docs_attrs(cx, Some(sf.hir_id), &sf.attrs, sf.span, "a struct field") + self.check_missing_docs_attrs( + cx, + Some(sf.hir_id), + &sf.attrs, + sf.span, + "a", + "struct field", + ) } } fn check_variant(&mut self, cx: &LateContext<'_, '_>, v: &hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, Some(v.id), &v.attrs, v.span, "a variant"); + self.check_missing_docs_attrs(cx, Some(v.id), &v.attrs, v.span, "a", "variant"); } } @@ -551,7 +562,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingCopyImplementations { return; } let param_env = ty::ParamEnv::empty(); - if ty.is_copy_modulo_regions(cx.tcx, param_env, item.span) { + if ty.is_copy_modulo_regions(cx.tcx.at(item.span), param_env) { return; } if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() { @@ -599,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)); } } }); @@ -639,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(); + }) } } } - _ => (), } } } @@ -881,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>( @@ -975,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 { @@ -1071,7 +1072,7 @@ impl TypeAliasBounds { err: &'a mut DiagnosticBuilder<'db>, } impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> { - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None @@ -1101,6 +1102,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeAliasBounds { hir::ItemKind::TyAlias(ref ty, ref generics) => (&*ty, generics), _ => return, }; + if let hir::TyKind::OpaqueDef(..) = ty.kind { + // Bounds are respected for `type X = impl Trait` + return; + } let mut suggested_changing_assoc_types = false; // There must not be a where clause if !type_alias_generics.where_clause.predicates.is_empty() { @@ -1166,7 +1171,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); @@ -1200,14 +1205,14 @@ 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::PredicateKind::*; if cx.tcx.features().trivial_bounds { let def_id = cx.tcx.hir().local_def_id(item.hir_id); let predicates = cx.tcx.predicates_of(def_id); for &(predicate, span) in predicates.predicates { - let predicate_kind_name = match predicate { + let predicate_kind_name = match predicate.kind() { Trait(..) => "Trait", TypeOutlives(..) | RegionOutlives(..) => "Lifetime", @@ -1220,7 +1225,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| { @@ -1354,7 +1360,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, } @@ -1362,7 +1368,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 } } } @@ -1372,7 +1378,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; } @@ -1385,7 +1391,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; } } @@ -1427,7 +1433,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 => { @@ -1481,7 +1487,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); } } @@ -1495,8 +1501,8 @@ impl ExplicitOutlivesRequirements { ) -> Vec> { inferred_outlives .iter() - .filter_map(|(pred, _)| match pred { - ty::Predicate::RegionOutlives(outlives) => { + .filter_map(|(pred, _)| match pred.kind() { + ty::PredicateKind::RegionOutlives(outlives) => { let outlives = outlives.skip_binder(); match outlives.0 { ty::ReEarlyBound(ebr) if ebr.index == index => Some(outlives.1), @@ -1514,8 +1520,8 @@ impl ExplicitOutlivesRequirements { ) -> Vec> { inferred_outlives .iter() - .filter_map(|(pred, _)| match pred { - ty::Predicate::TypeOutlives(outlives) => { + .filter_map(|(pred, _)| match pred.kind() { + ty::PredicateKind::TypeOutlives(outlives) => { let outlives = outlives.skip_binder(); outlives.0.is_param(index).then_some(outlives.1) } @@ -1531,7 +1537,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 { .. } => { @@ -1551,7 +1558,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() @@ -1634,7 +1641,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); @@ -1815,13 +1822,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(); }) }); } @@ -1888,7 +1903,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { } } } - } else if let hir::ExprKind::MethodCall(_, _, ref args) = expr.kind { + } else if let hir::ExprKind::MethodCall(_, _, ref args, _) = expr.kind { // Find problematic calls to `MaybeUninit::assume_init`. let def_id = cx.tables.type_dependent_def_id(expr.hir_id)?; if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) { @@ -1919,7 +1934,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)), @@ -2038,3 +2053,224 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue { } } } + +declare_lint! { + pub CLASHING_EXTERN_DECL, + Warn, + "detects when an extern fn has been declared with the same name but different types" +} + +pub struct ClashingExternDecl { + seen_decls: FxHashMap, +} + +/// Differentiate between whether the name for an extern decl came from the link_name attribute or +/// just from declaration itself. This is important because we don't want to report clashes on +/// symbol name if they don't actually clash because one or the other links against a symbol with a +/// different name. +enum SymbolName { + /// The name of the symbol + the span of the annotation which introduced the link name. + Link(Symbol, Span), + /// No link name, so just the name of the symbol. + Normal(Symbol), +} + +impl SymbolName { + fn get_name(&self) -> Symbol { + match self { + SymbolName::Link(s, _) | SymbolName::Normal(s) => *s, + } + } +} + +impl ClashingExternDecl { + crate fn new() -> Self { + ClashingExternDecl { seen_decls: FxHashMap::default() } + } + /// Insert a new foreign item into the seen set. If a symbol with the same name already exists + /// for the item, return its HirId without updating the set. + fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option { + let hid = fi.hir_id; + + let name = + &tcx.codegen_fn_attrs(tcx.hir().local_def_id(hid)).link_name.unwrap_or(fi.ident.name); + + if self.seen_decls.contains_key(name) { + // Avoid updating the map with the new entry when we do find a collision. We want to + // make sure we're always pointing to the first definition as the previous declaration. + // This lets us avoid emitting "knock-on" diagnostics. + Some(*self.seen_decls.get(name).unwrap()) + } else { + self.seen_decls.insert(*name, hid) + } + } + + /// Get the name of the symbol that's linked against for a given extern declaration. That is, + /// the name specified in a #[link_name = ...] attribute if one was specified, else, just the + /// symbol's name. + fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> SymbolName { + let did = tcx.hir().local_def_id(fi.hir_id); + if let Some((overridden_link_name, overridden_link_name_span)) = + tcx.codegen_fn_attrs(did).link_name.map(|overridden_link_name| { + // FIXME: Instead of searching through the attributes again to get span + // information, we could have codegen_fn_attrs also give span information back for + // where the attribute was defined. However, until this is found to be a + // bottleneck, this does just fine. + ( + overridden_link_name, + tcx.get_attrs(did.to_def_id()) + .iter() + .find(|at| at.check_name(sym::link_name)) + .unwrap() + .span, + ) + }) + { + SymbolName::Link(overridden_link_name, overridden_link_name_span) + } else { + SymbolName::Normal(fi.ident.name) + } + } + + /// Checks whether two types are structurally the same enough that the declarations shouldn't + /// clash. We need this so we don't emit a lint when two modules both declare an extern struct, + /// with the same members (as the declarations shouldn't clash). + fn structurally_same_type<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>, + ) -> bool { + let tcx = cx.tcx; + if a == b || rustc_middle::ty::TyS::same_type(a, b) { + // All nominally-same types are structurally same, too. + true + } else { + // Do a full, depth-first comparison between the two. + use rustc_middle::ty::TyKind::*; + let a_kind = &a.kind; + let b_kind = &b.kind; + + match (a_kind, b_kind) { + (Adt(..), Adt(..)) => { + // Adts are pretty straightforward: just compare the layouts. + use rustc_target::abi::LayoutOf; + let a_layout = cx.layout_of(a).unwrap().layout; + let b_layout = cx.layout_of(b).unwrap().layout; + a_layout == b_layout + } + (Array(a_ty, a_const), Array(b_ty, b_const)) => { + // For arrays, we also check the constness of the type. + a_const.val == b_const.val + && Self::structurally_same_type(cx, a_const.ty, b_const.ty) + && Self::structurally_same_type(cx, a_ty, b_ty) + } + (Slice(a_ty), Slice(b_ty)) => Self::structurally_same_type(cx, a_ty, b_ty), + (RawPtr(a_tymut), RawPtr(b_tymut)) => { + a_tymut.mutbl == a_tymut.mutbl + && Self::structurally_same_type(cx, &a_tymut.ty, &b_tymut.ty) + } + (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { + // For structural sameness, we don't need the region to be same. + a_mut == b_mut && Self::structurally_same_type(cx, a_ty, b_ty) + } + (FnDef(..), FnDef(..)) => { + // As we don't compare regions, skip_binder is fine. + let a_poly_sig = a.fn_sig(tcx); + let b_poly_sig = b.fn_sig(tcx); + + let a_sig = a_poly_sig.skip_binder(); + let b_sig = b_poly_sig.skip_binder(); + + (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) + == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) + && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { + Self::structurally_same_type(cx, a, b) + }) + && Self::structurally_same_type(cx, a_sig.output(), b_sig.output()) + } + (Tuple(a_substs), Tuple(b_substs)) => { + a_substs.types().eq_by(b_substs.types(), |a_ty, b_ty| { + Self::structurally_same_type(cx, a_ty, b_ty) + }) + } + // For these, it's not quite as easy to define structural-sameness quite so easily. + // For the purposes of this lint, take the conservative approach and mark them as + // not structurally same. + (Dynamic(..), Dynamic(..)) + | (Error(..), Error(..)) + | (Closure(..), Closure(..)) + | (Generator(..), Generator(..)) + | (GeneratorWitness(..), GeneratorWitness(..)) + | (Projection(..), Projection(..)) + | (Opaque(..), Opaque(..)) => false, + // These definitely should have been caught above. + (Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(), + _ => false, + } + } + } +} + +impl_lint_pass!(ClashingExternDecl => [CLASHING_EXTERN_DECL]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ClashingExternDecl { + fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, this_fi: &hir::ForeignItem<'_>) { + trace!("ClashingExternDecl: check_foreign_item: {:?}", this_fi); + if let ForeignItemKind::Fn(..) = this_fi.kind { + let tcx = *&cx.tcx; + if let Some(existing_hid) = self.insert(tcx, this_fi) { + let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid)); + let this_decl_ty = tcx.type_of(tcx.hir().local_def_id(this_fi.hir_id)); + debug!( + "ClashingExternDecl: Comparing existing {:?}: {:?} to this {:?}: {:?}", + existing_hid, existing_decl_ty, this_fi.hir_id, this_decl_ty + ); + // Check that the declarations match. + if !Self::structurally_same_type(cx, existing_decl_ty, this_decl_ty) { + let orig_fi = tcx.hir().expect_foreign_item(existing_hid); + let orig = Self::name_of_extern_decl(tcx, orig_fi); + + // We want to ensure that we use spans for both decls that include where the + // name was defined, whether that was from the link_name attribute or not. + let get_relevant_span = + |fi: &hir::ForeignItem<'_>| match Self::name_of_extern_decl(tcx, fi) { + SymbolName::Normal(_) => fi.span, + SymbolName::Link(_, annot_span) => fi.span.to(annot_span), + }; + // Finally, emit the diagnostic. + tcx.struct_span_lint_hir( + CLASHING_EXTERN_DECL, + this_fi.hir_id, + get_relevant_span(this_fi), + |lint| { + let mut expected_str = DiagnosticStyledString::new(); + expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false); + let mut found_str = DiagnosticStyledString::new(); + found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true); + + lint.build(&format!( + "`{}` redeclare{} with a different signature", + this_fi.ident.name, + if orig.get_name() == this_fi.ident.name { + "d".to_string() + } else { + format!("s `{}`", orig.get_name()) + } + )) + .span_label( + get_relevant_span(orig_fi), + &format!("`{}` previously declared here", orig.get_name()), + ) + .span_label( + get_relevant_span(this_fi), + "this signature doesn't match the previous declaration", + ) + .note_expected_found(&"", expected_str, &"", found_str) + .emit() + }, + ); + } + } + } + } +} diff --git a/src/librustc_lint/context.rs b/src/librustc_lint/context.rs index 5b7b73b48ec5b..e5d3227d5afd4 100644 --- a/src/librustc_lint/context.rs +++ b/src/librustc_lint/context.rs @@ -18,13 +18,6 @@ use self::TargetLint::*; use crate::levels::LintLevelsBuilder; use crate::passes::{EarlyLintPassObject, LateLintPassObject}; -use rustc::hir::map::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc::lint::add_elided_lifetime_in_path_suggestion; -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; @@ -32,10 +25,17 @@ use rustc_data_structures::sync; use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId}; -use rustc_session::lint::BuiltinLintDiagnostics; +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; @@ -566,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"); } @@ -788,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()); @@ -812,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 a5da960d8881c..d891466611ad3 100644 --- a/src/librustc_lint/early.rs +++ b/src/librustc_lint/early.rs @@ -18,8 +18,9 @@ use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; use rustc_ast::ast; use rustc_ast::visit as ast_visit; -use rustc_session::lint::{LintBuffer, LintPass}; +use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; +use rustc_span::symbol::Ident; use rustc_span::Span; use log::debug; @@ -37,13 +38,7 @@ struct EarlyContextAndPass<'a, T: EarlyLintPass> { impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { fn check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { - let rustc_session::lint::BufferedEarlyLint { - span, - msg, - node_id: _, - lint_id, - diagnostic, - } = early_lint; + let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; self.context.lookup_with_diagnostics( lint_id.lint, Some(span), @@ -60,7 +55,8 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { where F: FnOnce(&mut Self), { - let push = self.context.builder.push(attrs, &self.context.lint_store); + let is_crate_node = id == ast::CRATE_NODE_ID; + let push = self.context.builder.push(attrs, &self.context.lint_store, is_crate_node); self.check_id(id); self.enter_attrs(attrs); f(self); @@ -110,6 +106,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); @@ -160,7 +161,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); } @@ -326,11 +327,9 @@ pub fn check_ast_crate( lint_buffer: Option, builtin_lints: T, ) { - let mut passes: Vec<_> = if pre_expansion { - lint_store.pre_expansion_passes.iter().map(|p| (p)()).collect() - } else { - lint_store.early_passes.iter().map(|p| (p)()).collect() - }; + let passes = + if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; + let mut passes: Vec<_> = passes.iter().map(|p| (p)()).collect(); let mut buffered = lint_buffer.unwrap_or_default(); if !sess.opts.debugging_opts.no_interleave_lints { diff --git a/src/librustc_lint/internal.rs b/src/librustc_lint/internal.rs index db109aef6eb8f..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, @@ -140,7 +140,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TyTyKind { } } TyKind::Rptr(_, MutTy { ty: inner_ty, mutbl: Mutability::Not }) => { - if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner_def_id()) { + if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) { if cx.tcx.impl_trait_ref(impl_did).is_some() { return; } @@ -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..f875e2750a5c5 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,22 +9,27 @@ 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_session::lint::{builtin, Level, Lint}; +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, LintId}; use rustc_session::parse::feature_err; use rustc_session::Session; -use rustc_span::source_map::MultiSpan; use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{source_map::MultiSpan, Span, DUMMY_SP}; 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); let mut builder = LintLevelMapBuilder { levels, tcx, store }; let krate = tcx.hir().krate(); - let push = builder.levels.push(&krate.item.attrs, &store); + let push = builder.levels.push(&krate.item.attrs, &store, true); builder.levels.register_id(hir::CRATE_HIR_ID); for macro_def in krate.exported_macros { builder.levels.register_id(macro_def.hir_id); @@ -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> { @@ -80,11 +80,13 @@ impl<'s> LintLevelsBuilder<'s> { let level = cmp::min(level, self.sets.lint_cap); let lint_flag_val = Symbol::intern(lint_name); + let ids = match store.find_lints(&lint_name) { Ok(ids) => ids, Err(_) => continue, // errors handled in check_lint_name_cmdline above }; for id in ids { + self.check_gated_lint(id, DUMMY_SP); let src = LintSource::CommandLine(lint_flag_val); specs.insert(id, (level, src)); } @@ -103,11 +105,16 @@ 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 { + pub fn push( + &mut self, + attrs: &[ast::Attribute], + store: &LintStore, + is_crate_node: bool, + ) -> BuilderPush { let mut specs = FxHashMap::default(); let sess = self.sess; let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); @@ -212,8 +219,9 @@ impl<'s> LintLevelsBuilder<'s> { match store.check_lint_name(&name.as_str(), tool_name) { CheckLintNameResult::Ok(ids) => { let src = LintSource::Node(name, li.span(), reason); - for id in ids { - specs.insert(*id, (level, src)); + for &id in ids { + self.check_gated_lint(id, attr.span); + specs.insert(id, (level, src)); } } @@ -330,6 +338,40 @@ impl<'s> LintLevelsBuilder<'s> { } } + if !is_crate_node { + for (id, &(level, ref src)) in specs.iter() { + if !id.lint.crate_level_only { + continue; + } + + let (lint_attr_name, lint_attr_span) = match *src { + LintSource::Node(name, span, _) => (name, span), + _ => continue, + }; + + let lint = builtin::UNUSED_ATTRIBUTES; + let (lint_level, lint_src) = + self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess); + struct_lint_level( + self.sess, + lint, + lint_level, + lint_src, + Some(lint_attr_span.into()), + |lint| { + let mut db = lint.build(&format!( + "{}({}) is ignored unless specified at crate level", + level.as_str(), + lint_attr_name + )); + db.emit(); + }, + ); + // don't set a separate error for every lint in the group + break; + } + } + for (id, &(level, ref src)) in specs.iter() { if level == Level::Forbid { continue; @@ -383,11 +425,31 @@ impl<'s> LintLevelsBuilder<'s> { BuilderPush { prev, changed: prev != self.cur } } + /// Checks if the lint is gated on a feature that is not enabled. + fn check_gated_lint(&self, lint_id: LintId, span: Span) { + if let Some(feature) = lint_id.lint.feature_gate { + if !self.sess.features_untracked().enabled(feature) { + feature_err( + &self.sess.parse_sess, + feature, + span, + &format!("the `{}` lint is unstable", lint_id.lint.name_lower()), + ) + .emit(); + } + } + } + /// Called after `push` when the scope of a set of attributes are exited. pub fn pop(&mut self, push: BuilderPush) { 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 +458,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) } @@ -426,7 +488,8 @@ impl LintLevelMapBuilder<'_, '_> { where F: FnOnce(&mut Self), { - let push = self.levels.push(attrs, self.store); + let is_crate_hir = id == hir::CRATE_HIR_ID; + let push = self.levels.push(attrs, self.store, is_crate_hir); if push.changed { self.levels.register_id(id); } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 825ac04bc0920..b39abe7b411bb 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 //! @@ -30,12 +30,14 @@ #![feature(bool_to_option)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] +#![feature(iter_order_by)] #![feature(never_type)] #![feature(nll)] +#![feature(or_patterns)] #![recursion_limit = "256"] #[macro_use] -extern crate rustc; +extern crate rustc_middle; #[macro_use] extern crate rustc_session; @@ -53,15 +55,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 +92,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 +108,7 @@ macro_rules! early_lint_passes { $args, [ UnusedParens: UnusedParens, + UnusedBraces: UnusedBraces, UnusedImportBraces: UnusedImportBraces, UnsafeCode: UnsafeCode, AnonymousParameters: AnonymousParameters, @@ -150,6 +155,7 @@ macro_rules! late_lint_passes { // and change this to a module lint pass MissingDebugImplementations: MissingDebugImplementations::default(), ArrayIntoIter: ArrayIntoIter, + ClashingExternDecl: ClashingExternDecl::new(), ] ); }; @@ -161,7 +167,8 @@ macro_rules! late_lint_mod_passes { $args, [ HardwiredLints: HardwiredLints, - ImproperCTypes: ImproperCTypes, + ImproperCTypesDeclarations: ImproperCTypesDeclarations, + ImproperCTypesDefinitions: ImproperCTypesDefinitions, VariantSizeDifferences: VariantSizeDifferences, BoxPointers: BoxPointers, PathStatements: PathStatements, @@ -275,6 +282,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { UNUSED_FEATURES, UNUSED_LABELS, UNUSED_PARENS, + UNUSED_BRACES, REDUNDANT_SEMICOLONS ); @@ -296,6 +304,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..064b0255397ce 100644 --- a/src/librustc_lint/non_ascii_idents.rs +++ b/src/librustc_lint/non_ascii_idents.rs @@ -1,22 +1,164 @@ 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, Allow, - "detects non-ASCII identifiers" + "detects non-ASCII identifiers", + crate_level_only } declare_lint! { pub UNCOMMON_CODEPOINTS, Warn, - "detects uncommon Unicode codepoints in identifiers" + "detects uncommon Unicode codepoints in identifiers", + crate_level_only } -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", + crate_level_only +} + +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 37fefe680d7d2..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; @@ -343,7 +343,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonSnakeCase { } fn check_trait_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(_, hir::TraitMethod::Required(pnames)) = item.kind { + if let hir::TraitItemKind::Fn(_, hir::TraitFn::Required(pnames)) = item.kind { self.check_snake_case(cx, "trait method", &item.ident); for param_name in pnames { self.check_snake_case(cx, "variable", param_name); 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 b5c0296e297e0..cfafb86fbedb1 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -1,26 +1,25 @@ #![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; use rustc_errors::Applicability; 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, TypeFoldable}; use rustc_span::source_map; use rustc_span::symbol::sym; -use rustc_span::Span; +use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::{Integer, LayoutOf, TagEncoding, 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 +42,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 } } } @@ -108,23 +107,23 @@ fn lint_overflowing_range_endpoint<'a, 'tcx>( // warnings are consistent between 32- and 64-bit platforms. fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) { match int_ty { - ast::IntTy::Isize => (i64::min_value() as i128, i64::max_value() as i128), - ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128), - ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128), - ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128), - ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128), - ast::IntTy::I128 => (i128::min_value() as i128, i128::max_value()), + ast::IntTy::Isize => (i64::MIN as i128, i64::MAX as i128), + ast::IntTy::I8 => (i8::MIN as i64 as i128, i8::MAX as i128), + ast::IntTy::I16 => (i16::MIN as i64 as i128, i16::MAX as i128), + ast::IntTy::I32 => (i32::MIN as i64 as i128, i32::MAX as i128), + ast::IntTy::I64 => (i64::MIN as i128, i64::MAX as i128), + ast::IntTy::I128 => (i128::MIN as i128, i128::MAX), } } fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) { match uint_ty { - ast::UintTy::Usize => (u64::min_value() as u128, u64::max_value() as u128), - ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128), - ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128), - ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128), - ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128), - ast::UintTy::U128 => (u128::min_value(), u128::max_value()), + ast::UintTy::Usize => (u64::MIN as u128, u64::MAX as u128), + ast::UintTy::U8 => (u8::MIN as u128, u8::MAX as u128), + ast::UintTy::U16 => (u16::MIN as u128, u16::MAX as u128), + ast::UintTy::U32 => (u32::MIN as u128, u32::MAX as u128), + ast::UintTy::U64 => (u64::MIN as u128, u64::MAX as u128), + ast::UintTy::U128 => (u128::MIN, u128::MAX), } } @@ -134,7 +133,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 +149,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 +243,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 @@ -273,7 +272,6 @@ fn lint_int_literal<'a, 'tcx>( cx.sess() .source_map() .span_to_snippet(lit.span) - .ok() .expect("must get snippet from literal"), t.name_str(), min, @@ -338,7 +336,6 @@ fn lint_uint_literal<'a, 'tcx>( cx.sess() .source_map() .span_to_snippet(lit.span) - .ok() .expect("must get snippet from literal"), t.name_str(), min, @@ -358,8 +355,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!(), @@ -399,8 +395,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) => { @@ -457,8 +453,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!(), @@ -500,20 +498,30 @@ declare_lint! { "proper use of libc types in foreign modules" } -declare_lint_pass!(ImproperCTypes => [IMPROPER_CTYPES]); +declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]); + +declare_lint! { + IMPROPER_CTYPES_DEFINITIONS, + Warn, + "proper use of libc types in foreign item definitions" +} + +declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS]); + +enum ImproperCTypesMode { + Declarations, + Definitions, +} struct ImproperCTypesVisitor<'a, 'tcx> { cx: &'a LateContext<'a, 'tcx>, + mode: ImproperCTypesMode, } enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), - FfiUnsafe { ty: Ty<'tcx>, reason: &'static str, help: Option<&'static str> }, -} - -fn is_zst<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, ty: Ty<'tcx>) -> bool { - tcx.layout_of(tcx.param_env(did).and(ty)).map(|layout| layout.is_zst()).unwrap_or(false) + FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option }, } fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { @@ -524,7 +532,7 @@ fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { for field in field_def.all_fields() { let field_ty = tcx.normalize_erasing_regions(ParamEnv::reveal_all(), field.ty(tcx, substs)); - if is_zst(tcx, field.did, field_ty) { + if field_ty.is_zst(tcx, field.did) { continue; } @@ -603,6 +611,66 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } + /// Checks if the given field's type is "ffi-safe". + fn check_field_type_for_ffi( + &self, + cache: &mut FxHashSet>, + field: &ty::FieldDef, + substs: SubstsRef<'tcx>, + ) -> FfiResult<'tcx> { + let field_ty = field.ty(self.cx.tcx, substs); + if field_ty.has_opaque_types() { + self.check_type_for_ffi(cache, field_ty) + } else { + let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty); + self.check_type_for_ffi(cache, field_ty) + } + } + + /// Checks if the given `VariantDef`'s field types are "ffi-safe". + fn check_variant_for_ffi( + &self, + cache: &mut FxHashSet>, + ty: Ty<'tcx>, + def: &ty::AdtDef, + variant: &ty::VariantDef, + substs: SubstsRef<'tcx>, + ) -> FfiResult<'tcx> { + use FfiResult::*; + + if def.repr.transparent() { + // Can assume that only one field is not a ZST, so only check + // that field's type for FFI-safety. + if let Some(field) = variant.transparent_newtype_field(self.cx.tcx) { + self.check_field_type_for_ffi(cache, field, substs) + } else { + bug!("malformed transparent type"); + } + } else { + // We can't completely trust repr(C) markings; make sure the fields are + // actually safe. + let mut all_phantom = !variant.fields.is_empty(); + for field in &variant.fields { + match self.check_field_type_for_ffi(cache, &field, substs) { + FfiSafe => { + all_phantom = false; + } + FfiPhantom(..) if def.is_enum() => { + return FfiUnsafe { + ty, + reason: "this enum contains a PhantomData field".into(), + help: None, + }; + } + FfiPhantom(..) => {} + r => return r, + } + } + + if all_phantom { FfiPhantom(ty) } else { FfiSafe } + } + } + /// Checks if the given type is "ffi-safe" (has a stable, well-defined /// representation which can be exported to C code). fn check_type_for_ffi(&self, cache: &mut FxHashSet>, ty: Ty<'tcx>) -> FfiResult<'tcx> { @@ -624,15 +692,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiPhantom(ty); } match def.adt_kind() { - AdtKind::Struct => { + AdtKind::Struct | AdtKind::Union => { + let kind = if def.is_struct() { "struct" } else { "union" }; + if !def.repr.c() && !def.repr.transparent() { return FfiUnsafe { ty, - reason: "this struct has unspecified layout", - help: Some( + reason: format!("this {} has unspecified layout", kind), + help: Some(format!( "consider adding a `#[repr(C)]` or \ - `#[repr(transparent)]` attribute to this struct", - ), + `#[repr(transparent)]` attribute to this {}", + kind + )), }; } @@ -641,7 +712,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if is_non_exhaustive && !def.did.is_local() { return FfiUnsafe { ty, - reason: "this struct is non-exhaustive", + reason: format!("this {} is non-exhaustive", kind), help: None, }; } @@ -649,82 +720,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.non_enum_variant().fields.is_empty() { return FfiUnsafe { ty, - reason: "this struct has no fields", - help: Some("consider adding a member to this struct"), - }; - } - - // We can't completely trust repr(C) and repr(transparent) markings; - // make sure the fields are actually safe. - let mut all_phantom = true; - for field in &def.non_enum_variant().fields { - let field_ty = cx.normalize_erasing_regions( - ParamEnv::reveal_all(), - field.ty(cx, substs), - ); - // repr(transparent) types are allowed to have arbitrary ZSTs, not just - // PhantomData -- skip checking all ZST fields - if def.repr.transparent() && is_zst(cx, field.did, field_ty) { - continue; - } - let r = self.check_type_for_ffi(cache, field_ty); - match r { - FfiSafe => { - all_phantom = false; - } - FfiPhantom(..) => {} - FfiUnsafe { .. } => { - return r; - } - } - } - - if all_phantom { FfiPhantom(ty) } else { FfiSafe } - } - AdtKind::Union => { - if !def.repr.c() && !def.repr.transparent() { - return FfiUnsafe { - ty, - reason: "this union has unspecified layout", - help: Some( - "consider adding a `#[repr(C)]` or \ - `#[repr(transparent)]` attribute to this union", - ), - }; - } - - if def.non_enum_variant().fields.is_empty() { - return FfiUnsafe { - ty, - reason: "this union has no fields", - help: Some("consider adding a field to this union"), + reason: format!("this {} has no fields", kind), + help: Some(format!("consider adding a member to this {}", kind)), }; } - let mut all_phantom = true; - for field in &def.non_enum_variant().fields { - let field_ty = cx.normalize_erasing_regions( - ParamEnv::reveal_all(), - field.ty(cx, substs), - ); - // repr(transparent) types are allowed to have arbitrary ZSTs, not just - // PhantomData -- skip checking all ZST fields. - if def.repr.transparent() && is_zst(cx, field.did, field_ty) { - continue; - } - let r = self.check_type_for_ffi(cache, field_ty); - match r { - FfiSafe => { - all_phantom = false; - } - FfiPhantom(..) => {} - FfiUnsafe { .. } => { - return r; - } - } - } - - if all_phantom { FfiPhantom(ty) } else { FfiSafe } + self.check_variant_for_ffi(cache, ty, def, def.non_enum_variant(), substs) } AdtKind::Enum => { if def.variants.is_empty() { @@ -739,11 +740,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if !is_repr_nullable_ptr(cx, ty, def, substs) { return FfiUnsafe { ty, - reason: "enum has no representation hint", + reason: "enum has no representation hint".into(), help: Some( "consider adding a `#[repr(C)]`, \ `#[repr(transparent)]`, or integer `#[repr(...)]` \ - attribute to this enum", + attribute to this enum" + .into(), ), }; } @@ -752,7 +754,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.is_variant_list_non_exhaustive() && !def.did.is_local() { return FfiUnsafe { ty, - reason: "this enum is non-exhaustive", + reason: "this enum is non-exhaustive".into(), help: None, }; } @@ -763,37 +765,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if is_non_exhaustive && !variant.def_id.is_local() { return FfiUnsafe { ty, - reason: "this enum has non-exhaustive variants", + reason: "this enum has non-exhaustive variants".into(), help: None, }; } - for field in &variant.fields { - let field_ty = cx.normalize_erasing_regions( - ParamEnv::reveal_all(), - field.ty(cx, substs), - ); - // repr(transparent) types are allowed to have arbitrary ZSTs, not - // just PhantomData -- skip checking all ZST fields. - if def.repr.transparent() && is_zst(cx, field.did, field_ty) { - continue; - } - let r = self.check_type_for_ffi(cache, field_ty); - match r { - FfiSafe => {} - FfiUnsafe { .. } => { - return r; - } - FfiPhantom(..) => { - return FfiUnsafe { - ty, - reason: "this enum contains a PhantomData field", - help: None, - }; - } - } + match self.check_variant_for_ffi(cache, ty, def, variant, substs) { + FfiSafe => (), + r => return r, } } + FfiSafe } } @@ -801,13 +783,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Char => FfiUnsafe { ty, - reason: "the `char` type has no C equivalent", - help: Some("consider using `u32` or `libc::wchar_t` instead"), + reason: "the `char` type has no C equivalent".into(), + help: Some("consider using `u32` or `libc::wchar_t` instead".into()), }, ty::Int(ast::IntTy::I128) | ty::Uint(ast::UintTy::U128) => FfiUnsafe { ty, - reason: "128-bit integers don't currently have a known stable ABI", + reason: "128-bit integers don't currently have a known stable ABI".into(), help: None, }, @@ -816,26 +798,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Slice(_) => FfiUnsafe { ty, - reason: "slices have no C equivalent", - help: Some("consider using a raw pointer instead"), + reason: "slices have no C equivalent".into(), + help: Some("consider using a raw pointer instead".into()), }, ty::Dynamic(..) => { - FfiUnsafe { ty, reason: "trait objects have no C equivalent", help: None } + FfiUnsafe { ty, reason: "trait objects have no C equivalent".into(), help: None } } ty::Str => FfiUnsafe { ty, - reason: "string slices have no C equivalent", - help: Some("consider using `*const u8` and a length instead"), + reason: "string slices have no C equivalent".into(), + help: Some("consider using `*const u8` and a length instead".into()), }, ty::Tuple(..) => FfiUnsafe { ty, - reason: "tuples have unspecified layout", - help: Some("consider using a struct instead"), + reason: "tuples have unspecified layout".into(), + help: Some("consider using a struct instead".into()), }, + ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) + if { + matches!(self.mode, ImproperCTypesMode::Definitions) + && ty.is_sized(self.cx.tcx.at(DUMMY_SP), self.cx.param_env) + } => + { + FfiSafe + } + ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { self.check_type_for_ffi(cache, ty) } @@ -843,18 +834,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty), ty::FnPtr(sig) => { - match sig.abi() { - Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => { - return FfiUnsafe { - ty, - reason: "this function pointer has Rust-specific calling convention", - help: Some( - "consider using an `extern fn(...) -> ...` \ - function pointer instead", - ), - }; - } - _ => {} + if self.is_internal_abi(sig.abi()) { + return FfiUnsafe { + ty, + reason: "this function pointer has Rust-specific calling convention".into(), + help: Some( + "consider using an `extern fn(...) -> ...` \ + function pointer instead" + .into(), + ), + }; } let sig = cx.erase_late_bound_regions(&sig); @@ -881,17 +870,29 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Foreign(..) => FfiSafe, + // While opaque types are checked for earlier, if a projection in a struct field + // normalizes to an opaque type, then it will reach this branch. + ty::Opaque(..) => { + FfiUnsafe { ty, reason: "opaque types have no C equivalent".into(), help: None } + } + + // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, + // so they are currently ignored for the purposes of this lint. + ty::Param(..) | ty::Projection(..) + if matches!(self.mode, ImproperCTypesMode::Definitions) => + { + FfiSafe + } + ty::Param(..) + | ty::Projection(..) | ty::Infer(..) | ty::Bound(..) - | ty::Error + | ty::Error(_) | ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) | ty::Placeholder(..) - | ty::UnnormalizedProjection(..) - | ty::Projection(..) - | ty::Opaque(..) | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty), } } @@ -903,9 +904,20 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { note: &str, help: Option<&str>, ) { - self.cx.struct_span_lint(IMPROPER_CTYPES, sp, |lint| { - let mut diag = - lint.build(&format!("`extern` block uses type `{}`, which is not FFI-safe", ty)); + let lint = match self.mode { + ImproperCTypesMode::Declarations => IMPROPER_CTYPES, + ImproperCTypesMode::Definitions => IMPROPER_CTYPES_DEFINITIONS, + }; + + self.cx.struct_span_lint(lint, sp, |lint| { + let item_description = match self.mode { + ImproperCTypesMode::Declarations => "block", + ImproperCTypesMode::Definitions => "fn", + }; + let mut diag = lint.build(&format!( + "`extern` {} uses type `{}`, which is not FFI-safe", + item_description, ty + )); diag.span_label(sp, "not FFI-safe"); if let Some(help) = help { diag.help(help); @@ -921,24 +933,33 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { - use crate::rustc::ty::TypeFoldable; - - struct ProhibitOpaqueTypes<'tcx> { + struct ProhibitOpaqueTypes<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, ty: Option>, }; - impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'tcx> { + impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { - if let ty::Opaque(..) = ty.kind { - self.ty = Some(ty); - true - } else { - ty.super_visit_with(self) + match ty.kind { + ty::Opaque(..) => { + self.ty = Some(ty); + true + } + // Consider opaque types within projections FFI-safe if they do not normalize + // to more opaque types. + ty::Projection(..) => { + let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty); + + // If `ty` is a opaque type directly then `super_visit_with` won't invoke + // this function again. + if ty.has_opaque_types() { self.visit_ty(ty) } else { false } + } + _ => ty.super_visit_with(self), } } } - let mut visitor = ProhibitOpaqueTypes { ty: None }; + let mut visitor = ProhibitOpaqueTypes { cx: self.cx, ty: None }; ty.visit_with(&mut visitor); if let Some(ty) = visitor.ty { self.emit_ffi_unsafe_type_lint(ty, sp, "opaque types have no C equivalent", None); @@ -948,7 +969,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>, is_static: bool) { + fn check_type_for_ffi_and_report_errors( + &mut self, + sp: Span, + ty: Ty<'tcx>, + is_static: bool, + is_return_type: bool, + ) { // We have to check for opaque types before `normalize_erasing_regions`, // which will replace opaque types with their underlying concrete type. if self.check_for_opaque_ty(sp, ty) { @@ -958,22 +985,32 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // it is only OK to use this function because extern fns cannot have // any generic types right now: - let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); - // C doesn't really support passing arrays by value. - // The only way to pass an array by value is through a struct. - // So we first test that the top level isn't an array, - // and then recursively check the types inside. + let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty); + + // C doesn't really support passing arrays by value - the only way to pass an array by value + // is through a struct. So, first test that the top level isn't an array, and then + // recursively check the types inside. if !is_static && self.check_for_array_ty(sp, ty) { return; } + // Don't report FFI errors for unit return types. This check exists here, and not in + // `check_foreign_fn` (where it would make more sense) so that normalization has definitely + // happened. + if is_return_type && ty.is_unit() { + return; + } + match self.check_type_for_ffi(&mut FxHashSet::default(), ty) { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { self.emit_ffi_unsafe_type_lint(ty, sp, "composed only of `PhantomData`", None); } + // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic + // argument, which after substitution, is `()`, then this branch can be hit. + FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => return, FfiResult::FfiUnsafe { ty, reason, help } => { - self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); + self.emit_ffi_unsafe_type_lint(ty, sp, &reason, help.as_deref()); } } } @@ -984,31 +1021,36 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let sig = self.cx.tcx.erase_late_bound_regions(&sig); for (input_ty, input_hir) in sig.inputs().iter().zip(decl.inputs) { - self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty, false); + self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty, false, false); } if let hir::FnRetTy::Return(ref ret_hir) = decl.output { let ret_ty = sig.output(); - if !ret_ty.is_unit() { - self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false); - } + self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false, true); } } fn check_foreign_static(&mut self, id: hir::HirId, span: Span) { let def_id = self.cx.tcx.hir().local_def_id(id); let ty = self.cx.tcx.type_of(def_id); - self.check_type_for_ffi_and_report_errors(span, ty, true); + self.check_type_for_ffi_and_report_errors(span, ty, true, false); + } + + fn is_internal_abi(&self, abi: Abi) -> bool { + if let Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { + true + } else { + false + } } } -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes { +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypesDeclarations { fn check_foreign_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::ForeignItem<'_>) { - let mut vis = ImproperCTypesVisitor { cx }; + let mut vis = ImproperCTypesVisitor { cx, mode: ImproperCTypesMode::Declarations }; let abi = cx.tcx.hir().get_foreign_abi(it.hir_id); - if let Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { - // Don't worry about types in internal ABIs. - } else { + + if !vis.is_internal_abi(abi) { match it.kind { hir::ForeignItemKind::Fn(ref decl, _, _) => { vis.check_foreign_fn(it.hir_id, decl); @@ -1022,6 +1064,31 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes { } } +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypesDefinitions { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: hir::intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + _: &'tcx hir::Body<'_>, + _: Span, + hir_id: hir::HirId, + ) { + use hir::intravisit::FnKind; + + let abi = match kind { + FnKind::ItemFn(_, _, header, ..) => header.abi, + FnKind::Method(_, sig, ..) => sig.header.abi, + _ => return, + }; + + let mut vis = ImproperCTypesVisitor { cx, mode: ImproperCTypesMode::Definitions }; + if !vis.is_internal_abi(abi) { + vis.check_foreign_fn(hir_id, decl); + } + } +} + declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { @@ -1032,22 +1099,21 @@ 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(_)) => return, - Err(err @ ty::layout::LayoutError::SizeOverflow(_)) => { - bug!("failed to get layout for `{}`: {}", t, err); - } + Err( + ty::layout::LayoutError::Unknown(_) | ty::layout::LayoutError::SizeOverflow(_), + ) => return, }; let (variants, tag) = match layout.variants { - layout::Variants::Multiple { - discr_kind: layout::DiscriminantKind::Tag, - ref discr, + Variants::Multiple { + tag_encoding: TagEncoding::Direct, + ref tag, ref variants, .. - } => (variants, discr), + } => (variants, tag), _ => return, }; - let discr_size = tag.value.size(&cx.tcx).bytes(); + let tag_size = tag.value.size(&cx.tcx).bytes(); debug!( "enum `{}` is {} bytes large with layout:\n{:#?}", @@ -1061,8 +1127,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { .iter() .zip(variants) .map(|(variant, variant_layout)| { - // Subtract the size of the enum discriminant. - let bytes = variant_layout.size.bytes().saturating_sub(discr_size); + // Subtract the size of the enum tag. + let bytes = variant_layout.size.bytes().saturating_sub(tag_size); debug!("- variant `{}` is {} bytes large", variant.ident, bytes); bytes diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index d0bbc5ac26d89..8196b37391b21 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, @@ -124,7 +124,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { descr_post: &str, plural_len: usize, ) -> bool { - if ty.is_unit() || cx.tcx.is_ty_uninhabited_from(cx.tcx.parent_module(expr.hir_id), ty) + if ty.is_unit() + || cx.tcx.is_ty_uninhabited_from( + cx.tcx.parent_module(expr.hir_id).to_def_id(), + ty, + cx.param_env, + ) { return true; } @@ -141,7 +146,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { ty::Opaque(def, _) => { let mut has_emitted = false; for (predicate, _) in cx.tcx.predicates_of(def).predicates { - if let ty::Predicate::Trait(ref poly_trait_predicate, _) = predicate { + if let ty::PredicateKind::Trait(ref poly_trait_predicate, _) = + predicate.kind() + { let trait_ref = poly_trait_predicate.skip_binder().trait_ref; let def_id = trait_ref.def_id; let descr_pre = @@ -280,12 +287,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; } } @@ -313,115 +317,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 { @@ -429,7 +453,7 @@ impl UnusedParens { true } } - ')' => { + ')' | '}' => { if ate_right_paren { false } else { @@ -455,61 +479,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; @@ -522,14 +539,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) { @@ -538,7 +695,7 @@ impl EarlyLintPass for UnusedParens { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) // The other cases do not contain sub-patterns. - | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => return, + | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {}, // These are list-like patterns; parens can always be removed. TupleStruct(_, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { self.check_unused_parens_pat(cx, p, false, false); @@ -554,22 +711,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) { @@ -585,6 +736,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) { @@ -593,21 +754,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 9b4f03b3fb64b..ada48bc147e47 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 @@ -68,6 +78,7 @@ fn main() { "arm", "aarch64", "amdgpu", + "avr", "mips", "powerpc", "systemz", @@ -151,6 +162,7 @@ fn main() { if env::var_os("LLVM_NDEBUG").is_some() { cfg.define("NDEBUG", None); + cfg.debug(false); } build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm")); diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index eca1808de3f90..f54ed9b92029e 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -15,6 +15,7 @@ pub struct RustString { /// Appending to a Rust string -- used by RawRustStringOstream. #[no_mangle] +#[cfg_attr(not(bootstrap), allow(improper_ctypes_definitions))] pub unsafe extern "C" fn LLVMRustStringWriteImpl( sr: &RustString, ptr: *const c_char, @@ -76,6 +77,14 @@ pub fn initialize_available_targets() { LLVMInitializeAMDGPUAsmPrinter, LLVMInitializeAMDGPUAsmParser ); + init_target!( + llvm_component = "avr", + LLVMInitializeAVRTargetInfo, + LLVMInitializeAVRTarget, + LLVMInitializeAVRTargetMC, + LLVMInitializeAVRAsmPrinter, + LLVMInitializeAVRAsmParser + ); init_target!( llvm_component = "mips", LLVMInitializeMipsTargetInfo, 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 56b7be2f7e2d5..c17d5311e8fe6 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) @@ -197,7 +199,7 @@ impl Parse for Group { struct QueryModifiers { /// The description of the query. - desc: Option<(Option, Punctuated)>, + desc: (Option, Punctuated), /// Use this type for the in-memory cache. storage: Option, @@ -293,6 +295,9 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers { } } } + let desc = desc.unwrap_or_else(|| { + panic!("no description provided for query `{}`", query.name); + }); QueryModifiers { load_cached, storage, @@ -317,7 +322,7 @@ fn add_query_description_impl( let key = &query.key.0; // Find out if we should cache the query on disk - let cache = modifiers.cache.as_ref().map(|(args, expr)| { + let cache = if let Some((args, expr)) = modifiers.cache.as_ref() { let try_load_from_disk = if let Some((tcx, id, block)) = modifiers.load_cached.as_ref() { // Use custom code to load the query from disk quote! { @@ -356,12 +361,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 @@ -369,36 +376,32 @@ fn add_query_description_impl( #try_load_from_disk } - }); - - if cache.is_none() && modifiers.load_cached.is_some() { - panic!("load_cached modifier on query `{}` without a cache modifier", name); - } + } else { + if modifiers.load_cached.is_some() { + panic!("load_cached modifier on query `{}` without a cache modifier", name); + } + quote! {} + }; + + let (tcx, desc) = modifiers.desc; + let tcx = tcx.as_ref().map(|t| quote! { #t }).unwrap_or(quote! { _ }); + + let desc = quote! { + #[allow(unused_variables)] + fn describe( + #tcx: TyCtxt<'tcx>, + #key: #arg, + ) -> Cow<'static, str> { + format!(#desc).into() + } + }; - let desc = modifiers.desc.as_ref().map(|(tcx, desc)| { - let tcx = tcx.as_ref().map(|t| quote! { #t }).unwrap_or(quote! { _ }); - quote! { - #[allow(unused_variables)] - fn describe( - #tcx: TyCtxt<'_>, - #key: #arg, - ) -> Cow<'static, str> { - format!(#desc).into() - } + impls.extend(quote! { + impl<'tcx> QueryDescription> for queries::#name<'tcx> { + #desc + #cache } }); - - if desc.is_some() || cache.is_some() { - let cache = cache.unwrap_or(quote! {}); - let desc = desc.unwrap_or(quote! {}); - - impls.extend(quote! { - impl<'tcx> QueryDescription<'tcx> for queries::#name<'tcx> { - #desc - #cache - } - }); - } } pub fn rustc_queries(input: TokenStream) -> TokenStream { @@ -429,15 +432,15 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { }); try_load_from_on_disk_cache_stream.extend(quote! { - DepKind::#name => { - if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY { + ::rustc_middle::dep_graph::DepKind::#name => { + if <#arg as DepNodeParams>>::can_reconstruct_query_key() { debug_assert!($tcx.dep_graph .node_color($dep_node) .map(|c| c.is_green()) .unwrap_or(false)); - let key = <#arg as DepNodeParams>::recover($tcx, $dep_node).unwrap(); - if queries::#name::cache_on_disk($tcx, key, None) { + let key = <#arg as DepNodeParams>>::recover($tcx, $dep_node).unwrap(); + if queries::#name::cache_on_disk($tcx, &key, None) { let _ = $tcx.#name(key); } } @@ -486,10 +489,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! { - DepKind::#name => { - if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY { - if let Some(key) = <#arg as DepNodeParams>::recover($tcx, $dep_node) { - $tcx.force_query::>( + ::rustc_middle::dep_graph::DepKind::#name => { + if <#arg as DepNodeParams>>::can_reconstruct_query_key() { + if let Some(key) = <#arg as DepNodeParams>>::recover($tcx, $dep_node) { + force_query::, _>( + $tcx, key, DUMMY_SP, *$dep_node @@ -509,7 +513,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { } dep_node_force_stream.extend(quote! { - 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 a8e308c5c9e87..7bbe7567d2924 100644 --- a/src/librustc_metadata/Cargo.toml +++ b/src/librustc_metadata/Cargo.toml @@ -11,21 +11,24 @@ 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" } +rustc_serialize = { path = "../librustc_serialize" } stable_deref_trait = "1.0.0" rustc_ast = { path = "../librustc_ast" } rustc_expand = { path = "../librustc_expand" } rustc_span = { path = "../librustc_span" } +rustc_session = { path = "../librustc_session" } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["errhandlingapi", "libloaderapi"] } diff --git a/src/librustc_metadata/build.rs b/src/librustc_metadata/build.rs 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 18b4c9ad5044c..2c80c846681a6 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -3,22 +3,26 @@ 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::session::config; -use rustc::session::search_paths::PathKind; -use rustc::session::{CrateDisambiguator, Session}; -use rustc::ty::TyCtxt; -use rustc_ast::ast; -use rustc_ast::attr; use rustc_ast::expand::allocator::{global_allocator_spans, AllocatorKind}; +use rustc_ast::{ast, attr}; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::svh::Svh; 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::def_id::{CrateNum, LocalDefId, LOCAL_CRATE}; +use rustc_hir::definitions::Definitions; use rustc_index::vec::IndexVec; +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, ExternLocation}; +use rustc_session::lint; +use rustc_session::output::validate_crate_name; +use rustc_session::search_paths::PathKind; +use rustc_session::{CrateDisambiguator, Session}; use rustc_span::edition::Edition; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -47,6 +51,7 @@ pub struct CrateLoader<'a> { local_crate_name: Symbol, // Mutable output. cstore: CStore, + used_extern_options: FxHashSet, } pub enum LoadedMacro { @@ -99,9 +104,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()); + } }); } @@ -197,6 +208,7 @@ impl<'a> CrateLoader<'a> { allocator_kind: None, has_global_allocator: false, }, + used_extern_options: Default::default(), } } @@ -265,7 +277,7 @@ impl<'a> CrateLoader<'a> { ret = Some(cnum); } }); - return ret; + ret } fn verify_no_symbol_conflicts(&self, span: Span, root: &CrateRoot<'_>) { @@ -437,6 +449,17 @@ impl<'a> CrateLoader<'a> { dep_kind: DepKind, dep: Option<(&'b CratePaths, &'b CrateDep)>, ) -> CrateNum { + if dep.is_none() { + self.used_extern_options.insert(name); + } + if !name.as_str().is_ascii() { + self.sess + .struct_span_err( + span, + &format!("cannot load a crate with a non-ascii name `{}`", name,), + ) + .emit(); + } self.maybe_resolve_crate(name, span, dep_kind, dep).unwrap_or_else(|err| err.report()) } @@ -583,7 +606,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 +630,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().iter().any(|ct| *ct != CrateType::Rlib); if !any_non_rlib { info!("panic runtime injection skipped, only generating rlib"); return; @@ -684,7 +706,11 @@ 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.instrument_coverage + || 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"); @@ -725,8 +751,8 @@ impl<'a> CrateLoader<'a> { // At this point we've determined that we need an allocator. Let's see // 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, + let all_rlib = self.sess.crate_types().iter().all(|ct| match *ct { + CrateType::Rlib => true, _ => false, }); if all_rlib { @@ -830,6 +856,30 @@ impl<'a> CrateLoader<'a> { }); } + fn report_unused_deps(&mut self, krate: &ast::Crate) { + // Make a point span rather than covering the whole file + let span = krate.span.shrink_to_lo(); + // Complain about anything left over + for (name, entry) in self.sess.opts.externs.iter() { + if let ExternLocation::FoundInLibrarySearchDirectories = entry.location { + // Don't worry about pathless `--extern foo` sysroot references + continue; + } + if !self.used_extern_options.contains(&Symbol::intern(name)) { + self.sess.parse_sess.buffer_lint( + lint::builtin::UNUSED_CRATE_DEPENDENCIES, + span, + ast::CRATE_NODE_ID, + &format!( + "external crate `{}` unused in `{}`: remove the dependency or add `use {} as _;`", + name, + self.local_crate_name, + name), + ); + } + } + } + pub fn postprocess(&mut self, krate: &ast::Crate) { self.inject_profiler_runtime(); self.inject_allocator_crate(krate); @@ -838,12 +888,15 @@ impl<'a> CrateLoader<'a> { if log_enabled!(log::Level::Info) { dump_crates(&self.cstore); } + + self.report_unused_deps(krate); } pub fn process_extern_crate( &mut self, item: &ast::Item, definitions: &Definitions, + def_id: LocalDefId, ) -> CrateNum { match item.kind { ast::ItemKind::ExternCrate(orig_name) => { @@ -853,11 +906,7 @@ impl<'a> CrateLoader<'a> { ); let name = match orig_name { Some(orig_name) => { - crate::validate_crate_name( - Some(self.sess), - &orig_name.as_str(), - Some(item.span), - ); + validate_crate_name(Some(self.sess), &orig_name.as_str(), Some(item.span)); orig_name } None => item.ident.name, @@ -870,12 +919,11 @@ impl<'a> CrateLoader<'a> { let cnum = self.resolve_crate(name, item.span, dep_kind, None); - let def_id = definitions.opt_local_def_id(item.id).unwrap(); - let path_len = definitions.def_path(def_id.index).data.len(); + let path_len = definitions.def_path(def_id).data.len(); self.update_extern_crate( cnum, ExternCrate { - src: ExternCrateSource::Extern(def_id), + src: ExternCrateSource::Extern(def_id.to_def_id()), span: item.span, path_len, dependency_of: LOCAL_CRATE, @@ -896,7 +944,7 @@ impl<'a> CrateLoader<'a> { src: ExternCrateSource::Path, span, // to have the least priority in `update_extern_crate` - path_len: usize::max_value(), + path_len: usize::MAX, dependency_of: LOCAL_CRATE, }, ); diff --git a/src/librustc_metadata/dependency_format.rs b/src/librustc_metadata/dependency_format.rs index 9e71839dbfde6..aa5fafcc614b6 100644 --- a/src/librustc_metadata/dependency_format.rs +++ b/src/librustc_metadata/dependency_format.rs @@ -53,19 +53,18 @@ 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::session::config; -use rustc::ty::TyCtxt; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::CrateNum; +use rustc_middle::middle::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; +use rustc_middle::middle::cstore::{self, DepKind}; +use rustc_middle::middle::dependency_format::{Dependencies, DependencyList, Linkage}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::CrateType; use rustc_target::spec::PanicStrategy; crate fn calculate(tcx: TyCtxt<'_>) -> Dependencies { tcx.sess - .crate_types - .borrow() + .crate_types() .iter() .map(|&ty| { let linkage = calculate_type(tcx, ty); @@ -75,7 +74,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,27 +89,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() => { + 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 { @@ -127,9 +124,9 @@ 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 - && sess.crt_static() + if ty == CrateType::Staticlib + || (ty == CrateType::Executable + && sess.crt_static(Some(ty)) && !sess.target.target.options.crt_static_allows_dylibs) { for &cnum in tcx.crates().iter() { diff --git a/src/librustc_metadata/dynamic_lib.rs b/src/librustc_metadata/dynamic_lib.rs index f04d0239d4923..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, @@ -94,14 +82,12 @@ mod dl { let result = f(); let last_error = libc::dlerror() as *const _; - let ret = if ptr::null() == last_error { + if ptr::null() == last_error { Ok(result) } else { let s = CStr::from_ptr(last_error).to_bytes(); Err(str::from_utf8(s).unwrap().to_owned()) - }; - - ret + } } } @@ -126,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; @@ -141,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 fc988ec15cee9..8675197656a48 100644 --- a/src/librustc_metadata/foreign_modules.rs +++ b/src/librustc_metadata/foreign_modules.rs @@ -1,12 +1,12 @@ -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() }; tcx.hir().krate().visit_all_item_likes(&mut collector); - return collector.modules; + collector.modules } struct Collector<'tcx> { @@ -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 d4cc3c32616ac..76e39a476c6d8 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(min_specialization)] #![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; @@ -29,37 +29,3 @@ mod rmeta; pub mod creader; pub mod dynamic_lib; pub mod locator; - -pub fn validate_crate_name( - sess: Option<&rustc::session::Session>, - s: &str, - sp: Option, -) { - let mut err_count = 0; - { - let mut say = |s: &str| { - match (sp, sess) { - (_, None) => bug!("{}", s), - (Some(sp), Some(sess)) => sess.span_err(sp, s), - (None, Some(sess)) => sess.err(s), - } - err_count += 1; - }; - if s.is_empty() { - say("crate name must not be empty"); - } - for c in s.chars() { - if c.is_alphanumeric() { - continue; - } - if c == '_' { - continue; - } - say(&format!("invalid character `{}` in crate name: `{}`", c, s)); - } - } - - if err_count > 0 { - sess.unwrap().abort_if_errors(); - } -} diff --git a/src/librustc_metadata/link_args.rs b/src/librustc_metadata/link_args.rs index 13668b2423fdd..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; @@ -16,7 +16,7 @@ crate fn collect(tcx: TyCtxt<'_>) -> Vec { } } - return collector.args; + collector.args } struct Collector { diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index efa259d0c4e0d..1bdac1039b55a 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::session::filesearch::{FileDoesntMatch, FileMatches, FileSearch}; -use rustc::session::search_paths::PathKind; -use rustc::session::{config, CrateDisambiguator, Session}; 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::{CrateDisambiguator, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_target::spec::{Target, TargetTriple}; @@ -327,7 +328,7 @@ impl<'a> CrateLocator<'a> { .into_iter() .filter_map(|entry| entry.files()) .flatten() - .map(|location| PathBuf::from(location)) + .map(PathBuf::from) .collect() } else { // SVH being specified means this is a transitive dependency, @@ -487,6 +488,8 @@ impl<'a> CrateLocator<'a> { && self.triple != TargetTriple::from_triple(config::host_triple()) { err.note(&format!("the `{}` target may not be installed", self.triple)); + } else if self.crate_name == sym::profiler_builtins { + err.note(&"the compiler may have been built without the profiler runtime"); } err.span_label(self.span, "can't find crate"); err @@ -543,8 +546,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 +559,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 +672,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().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 @@ -949,7 +950,7 @@ fn get_metadata_section( let start = Instant::now(); let ret = get_metadata_section_imp(target, flavor, filename, loader); info!("reading {:?} => {:?}", filename.file_name().unwrap(), start.elapsed()); - return ret; + ret } /// A trivial wrapper for `Mmap` that implements `StableDeref`. diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs index 2fa9cb099dd51..fc4235a3eda09 100644 --- a/src/librustc_metadata/native_libs.rs +++ b/src/librustc_metadata/native_libs.rs @@ -1,24 +1,25 @@ -use rustc::middle::cstore::{self, NativeLibrary}; -use rustc::session::parse::feature_err; -use rustc::session::Session; -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::NativeLib; +use rustc_middle::ty::TyCtxt; +use rustc_session::parse::feature_err; +use rustc_session::utils::NativeLibKind; +use rustc_session::Session; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_target::spec::abi::Abi; -crate fn collect(tcx: TyCtxt<'_>) -> Vec { +crate fn collect(tcx: TyCtxt<'_>) -> Vec { let mut collector = Collector { tcx, libs: Vec::new() }; tcx.hir().krate().visit_all_item_likes(&mut collector); collector.process_command_line(); - return collector.libs; + collector.libs } -crate fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { +crate fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { match lib.cfg { Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None), None => true, @@ -27,7 +28,7 @@ crate fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { struct Collector<'tcx> { tcx: TyCtxt<'tcx>, - libs: Vec, + libs: Vec, } impl ItemLikeVisitor<'tcx> for Collector<'tcx> { @@ -47,11 +48,11 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { Some(item) => item, None => continue, }; - let mut lib = NativeLibrary { + let mut lib = NativeLib { name: None, - kind: cstore::NativeUnknown, + kind: NativeLibKind::Unspecified, 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; @@ -64,11 +65,11 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { None => continue, // skip like historical compilers }; lib.kind = match &*kind.as_str() { - "static" => cstore::NativeStatic, - "static-nobundle" => cstore::NativeStaticNobundle, - "dylib" => cstore::NativeUnknown, - "framework" => cstore::NativeFramework, - "raw-dylib" => cstore::NativeRawDylib, + "static" => NativeLibKind::StaticBundle, + "static-nobundle" => NativeLibKind::StaticNoBundle, + "dylib" => NativeLibKind::Dylib, + "framework" => NativeLibKind::Framework, + "raw-dylib" => NativeLibKind::RawDylib, k => { struct_span_err!( self.tcx.sess, @@ -80,7 +81,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { .span_label(item.span(), "unknown kind") .span_label(m.span, "") .emit(); - cstore::NativeUnknown + NativeLibKind::Unspecified } }; } else if item.check_name(sym::name) { @@ -134,7 +135,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> { } impl Collector<'tcx> { - fn register_native_lib(&mut self, span: Option, lib: NativeLibrary) { + fn register_native_lib(&mut self, span: Option, lib: NativeLib) { if lib.name.as_ref().map(|&s| s == kw::Invalid).unwrap_or(false) { match span { Some(span) => { @@ -154,7 +155,7 @@ impl Collector<'tcx> { return; } let is_osx = self.tcx.sess.target.target.options.is_like_osx; - if lib.kind == cstore::NativeFramework && !is_osx { + if lib.kind == NativeLibKind::Framework && !is_osx { let msg = "native frameworks are only available on macOS targets"; match span { Some(span) => struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(), @@ -162,10 +163,15 @@ 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 { + if lib.kind == NativeLibKind::StaticNoBundle && !self.tcx.features().static_nobundle { feature_err( &self.tcx.sess.parse_sess, sym::static_nobundle, @@ -174,7 +180,7 @@ impl Collector<'tcx> { ) .emit(); } - if lib.kind == cstore::NativeRawDylib && !self.tcx.features().raw_dylib { + if lib.kind == NativeLibKind::RawDylib && !self.tcx.features().raw_dylib { feature_err( &self.tcx.sess.parse_sess, sym::raw_dylib, @@ -235,8 +241,8 @@ impl Collector<'tcx> { .drain_filter(|lib| { if let Some(lib_name) = lib.name { if lib_name.as_str() == *name { - if let Some(k) = kind { - lib.kind = k; + if kind != NativeLibKind::Unspecified { + lib.kind = kind; } if let &Some(ref new_name) = new_name { lib.name = Some(Symbol::intern(new_name)); @@ -250,9 +256,9 @@ impl Collector<'tcx> { if existing.is_empty() { // Add if not found let new_name = new_name.as_ref().map(|s| &**s); // &Option -> Option<&str> - let lib = NativeLibrary { + let lib = NativeLib { name: Some(Symbol::intern(new_name.unwrap_or(name))), - kind: if let Some(k) = kind { k } else { cstore::NativeUnknown }, + kind, cfg: None, foreign_module: None, wasm_import_module: None, diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs index 7126f86c326c6..25e57aa77acd0 100644 --- a/src/librustc_metadata/rmeta/decoder.rs +++ b/src/librustc_metadata/rmeta/decoder.rs @@ -4,45 +4,44 @@ use crate::creader::CrateMetadataRef; use crate::rmeta::table::{FixedSizeEncoding, Table}; use crate::rmeta::*; -use rustc::dep_graph::{self, DepNode, DepNodeIndex}; -use rustc::hir::exports::Export; -use rustc::hir::map::definitions::DefPathTable; -use rustc::hir::map::{DefKey, DefPath, DefPathData, DefPathHash}; -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::session::Session; -use rustc::ty::codec::TyDecoder; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::util::common::record_time; +use rustc_ast::ast; +use rustc_attr as attr; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::{AtomicCell, Lock, LockGuard, Lrc, Once}; +use rustc_data_structures::sync::{AtomicCell, Lock, LockGuard, Lrc, OnceCell}; +use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; +use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, ProcMacroDerive}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::DefPathTable; +use rustc_hir::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, NativeLib}; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; +use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; +use rustc_middle::mir::{self, interpret, Body, Promoted}; +use rustc_middle::ty::codec::TyDecoder; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::util::common::record_time; +use rustc_serialize::{opaque, Decodable, Decoder, SpecializedDecoder, UseSpecializedDecodable}; +use rustc_session::Session; +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; +use proc_macro::bridge::client::ProcMacro; use std::io; use std::mem; use std::num::NonZeroUsize; -use std::u32; - -use log::debug; -use proc_macro::bridge::client::ProcMacro; -use rustc_ast::ast::{self, Ident}; -use rustc_attr as attr; -use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; -use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, ProcMacroDerive}; -use rustc_serialize::{opaque, Decodable, Decoder, SpecializedDecoder}; -use rustc_span::source_map::{self, respan, Spanned}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{self, hygiene::MacroKind, BytePos, Pos, Span, DUMMY_SP}; +use std::path::Path; pub use cstore_impl::{provide, provide_extern}; @@ -80,7 +79,7 @@ crate struct CrateMetadata { /// Proc macro descriptions for this crate, if it's a proc macro crate. raw_proc_macros: Option<&'static [ProcMacro]>, /// Source maps for code from the crate. - source_map_import_info: Once>, + source_map_import_info: OnceCell>, /// Used for decoding interpret::AllocIds in a cached & thread-safe manner. alloc_decoding_state: AllocDecodingState, /// The `DepNodeIndex` of the `DepNode` representing this upstream crate. @@ -219,7 +218,7 @@ impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadataRef<'a>, TyCtxt<'tcx>) { } } -impl<'a, 'tcx, T: Decodable> Lazy { +impl<'a, 'tcx, T: Decodable> Lazy { fn decode>(self, metadata: M) -> T { let mut dcx = metadata.decoder(self.position.get()); dcx.lazy_state = LazyState::NodeStart(self.position); @@ -227,7 +226,7 @@ impl<'a, 'tcx, T: Decodable> Lazy { } } -impl<'a: 'x, 'tcx: 'x, 'x, T: Decodable> Lazy<[T]> { +impl<'a: 'x, 'tcx: 'x, 'x, T: Decodable> Lazy<[T], usize> { fn decode>( self, metadata: M, @@ -295,15 +294,36 @@ impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { let key = ty::CReaderCacheKey { cnum: self.cdata().cnum, pos: shorthand }; - if let Some(&ty) = tcx.rcache.borrow().get(&key) { + if let Some(&ty) = tcx.ty_rcache.borrow().get(&key) { return Ok(ty); } let ty = or_insert_with(self)?; - tcx.rcache.borrow_mut().insert(key, ty); + tcx.ty_rcache.borrow_mut().insert(key, ty); Ok(ty) } + fn cached_predicate_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> Result, Self::Error> + where + F: FnOnce(&mut Self) -> Result, Self::Error>, + { + let tcx = self.tcx(); + + let key = ty::CReaderCacheKey { cnum: self.cdata().cnum, pos: shorthand }; + + if let Some(&pred) = tcx.pred_rcache.borrow().get(&key) { + return Ok(pred); + } + + let pred = or_insert_with(self)?; + tcx.pred_rcache.borrow_mut().insert(key, pred); + Ok(pred) + } + fn with_position(&mut self, pos: usize, f: F) -> R where F: FnOnce(&mut Self) -> R, @@ -322,20 +342,20 @@ impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { } } -impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> { +impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result, Self::Error> { self.read_lazy_with_meta(()) } } -impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> { +impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result, Self::Error> { let len = self.read_usize()?; if len == 0 { Ok(Lazy::empty()) } else { self.read_lazy_with_meta(len) } } } -impl<'a, 'tcx, I: Idx, T> SpecializedDecoder>> for DecodeContext<'a, 'tcx> +impl<'a, 'tcx, I: Idx, T> SpecializedDecoder, usize>> for DecodeContext<'a, 'tcx> where Option: FixedSizeEncoding, { @@ -365,7 +385,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { #[inline] fn specialized_decode(&mut self) -> Result { - self.specialized_decode().map(|i| LocalDefId::from_def_id(i)) + Ok(DefId::decode(self)?.expect_local()) } } @@ -387,7 +407,7 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { return Ok(DUMMY_SP); } - debug_assert_eq!(tag, TAG_VALID_SPAN); + debug_assert!(tag == TAG_VALID_SPAN_LOCAL || tag == TAG_VALID_SPAN_FOREIGN); let lo = BytePos::decode(self)?; let len = BytePos::decode(self)?; @@ -399,7 +419,68 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { bug!("Cannot decode Span without Session.") }; - let imported_source_files = self.cdata().imported_source_files(&sess.source_map()); + // There are two possibilities here: + // 1. This is a 'local span', which is located inside a `SourceFile` + // that came from this crate. In this case, we use the source map data + // encoded in this crate. This branch should be taken nearly all of the time. + // 2. This is a 'foreign span', which is located inside a `SourceFile` + // that came from a *different* crate (some crate upstream of the one + // whose metadata we're looking at). For example, consider this dependency graph: + // + // A -> B -> C + // + // Suppose that we're currently compiling crate A, and start deserializing + // metadata from crate B. When we deserialize a Span from crate B's metadata, + // there are two posibilites: + // + // 1. The span references a file from crate B. This makes it a 'local' span, + // which means that we can use crate B's serialized source map information. + // 2. The span references a file from crate C. This makes it a 'foreign' span, + // which means we need to use Crate *C* (not crate B) to determine the source + // map information. We only record source map information for a file in the + // crate that 'owns' it, so deserializing a Span may require us to look at + // a transitive dependency. + // + // When we encode a foreign span, we adjust its 'lo' and 'high' values + // to be based on the *foreign* crate (e.g. crate C), not the crate + // we are writing metadata for (e.g. crate B). This allows us to + // treat the 'local' and 'foreign' cases almost identically during deserialization: + // 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) + } else { + // FIXME: We don't decode dependencies of proc-macros. + // Remove this once #69976 is merged + if self.cdata().root.is_proc_macro_crate() { + debug!( + "SpecializedDecoder::specialized_decode: skipping span for proc-macro crate {:?}", + self.cdata().cnum + ); + // Decode `CrateNum` as u32 - using `CrateNum::decode` will ICE + // since we don't have `cnum_map` populated. + // This advances the decoder position so that we can continue + // to read metadata. + let _ = u32::decode(self)?; + return Ok(DUMMY_SP); + } + // tag is TAG_VALID_SPAN_FOREIGN, checked by `debug_assert` above + let cnum = CrateNum::decode(self)?; + debug!( + "SpecializedDecoder::specialized_decode: loading source files from cnum {:?}", + cnum + ); + + // Decoding 'foreign' spans should be rare enough that it's + // not worth it to maintain a per-CrateNum cache for `last_source_file_index`. + // We just set it to 0, to ensure that we don't try to access something out + // of bounds for our initial 'guess' + self.last_source_file_index = 0; + + let foreign_data = self.cdata().cstore.get_crate_data(cnum); + foreign_data.imported_source_files(sess) + }; + let source_file = { // Optimize for the case that most spans within a translated item // originate from the same source_file. @@ -413,16 +494,32 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { .binary_search_by_key(&lo, |source_file| source_file.original_start_pos) .unwrap_or_else(|index| index - 1); - self.last_source_file_index = index; + // Don't try to cache the index for foreign spans, + // as this would require a map from CrateNums to indices + if tag == TAG_VALID_SPAN_LOCAL { + self.last_source_file_index = index; + } &imported_source_files[index] } }; // Make sure our binary search above is correct. - debug_assert!(lo >= source_file.original_start_pos && lo <= source_file.original_end_pos); + debug_assert!( + lo >= source_file.original_start_pos && lo <= source_file.original_end_pos, + "Bad binary search: lo={:?} source_file.original_start_pos={:?} source_file.original_end_pos={:?}", + lo, + source_file.original_start_pos, + source_file.original_end_pos + ); // Make sure we correctly filtered out invalid spans during encoding - debug_assert!(hi >= source_file.original_start_pos && hi <= source_file.original_end_pos); + debug_assert!( + hi >= source_file.original_start_pos && hi <= source_file.original_end_pos, + "Bad binary search: hi={:?} source_file.original_start_pos={:?} source_file.original_end_pos={:?}", + hi, + source_file.original_start_pos, + source_file.original_end_pos + ); let lo = (lo + source_file.translated_source_file.start_pos) - source_file.original_start_pos; @@ -433,22 +530,15 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { } } -impl SpecializedDecoder for DecodeContext<'_, '_> { - fn specialized_decode(&mut self) -> Result { - // FIXME(jseyfried): intercrate hygiene - - Ok(Ident::with_dummy_span(Symbol::decode(self)?)) - } -} - impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { Fingerprint::decode_opaque(&mut self.opaque) } } -impl<'a, 'tcx, T: Decodable> SpecializedDecoder> - for DecodeContext<'a, 'tcx> +impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> +where + mir::ClearCrossCrate: UseSpecializedDecodable, { #[inline] fn specialized_decode(&mut self) -> Result, Self::Error> { @@ -494,8 +584,9 @@ impl MetadataBlob { } impl EntryKind { - fn def_kind(&self) -> Option { - Some(match *self { + fn def_kind(&self) -> DefKind { + match *self { + EntryKind::AnonConst(..) => DefKind::AnonConst, EntryKind::Const(..) => DefKind::Const, EntryKind::AssocConst(..) => DefKind::AssocConst, EntryKind::ImmStatic @@ -505,13 +596,12 @@ impl EntryKind { EntryKind::Struct(_, _) => DefKind::Struct, EntryKind::Union(_, _) => DefKind::Union, EntryKind::Fn(_) | EntryKind::ForeignFn(_) => DefKind::Fn, - EntryKind::Method(_) => DefKind::AssocFn, + EntryKind::AssocFn(_) => DefKind::AssocFn, EntryKind::Type => DefKind::TyAlias, EntryKind::TypeParam => DefKind::TyParam, EntryKind::ConstParam => DefKind::ConstParam, EntryKind::OpaqueTy => DefKind::OpaqueTy, EntryKind::AssocType(_) => DefKind::AssocTy, - EntryKind::AssocOpaqueTy(_) => DefKind::AssocOpaqueTy, EntryKind::Mod(_) => DefKind::Mod, EntryKind::Variant(_) => DefKind::Variant, EntryKind::Trait(_) => DefKind::Trait, @@ -519,14 +609,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, + } } } @@ -565,7 +654,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 { @@ -587,28 +676,40 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { &self.raw_proc_macros.unwrap()[pos] } - fn item_name(&self, item_index: DefIndex) -> Symbol { + fn item_ident(&self, item_index: DefIndex, sess: &Session) -> Ident { if !self.is_proc_macro(item_index) { - self.def_key(item_index) + let name = self + .def_key(item_index) .disambiguated_data .data .get_opt_name() - .expect("no name in item_name") + .expect("no name in item_ident"); + let span = self + .root + .tables + .ident_span + .get(self, item_index) + .map(|data| data.decode((self, sess))) + .unwrap_or_else(|| panic!("Missing ident span for {:?} ({:?})", name, item_index)); + Ident::new(name, span) } else { - Symbol::intern(self.raw_proc_macro(item_index).name()) + Ident::new( + Symbol::intern(self.raw_proc_macro(item_index).name()), + self.get_span(item_index, sess), + ) } } - 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 { @@ -651,6 +752,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { data.paren_sugar, data.has_auto_impl, data.is_marker, + data.specialization_kind, self.def_path_table.def_path_hash(item_id), ) } @@ -660,6 +762,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { false, false, false, + ty::trait_def::TraitSpecializationKind::None, self.def_path_table.def_path_hash(item_id), ), _ => bug!("def-index does not refer to trait or trait alias"), @@ -672,6 +775,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { kind: &EntryKind, index: DefIndex, parent_did: DefId, + sess: &Session, ) -> ty::VariantDef { let data = match kind { EntryKind::Variant(data) | EntryKind::Struct(data, _) | EntryKind::Union(data, _) => { @@ -693,19 +797,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ty::VariantDef::new( tcx, - Ident::with_dummy_span(self.item_name(index)), + self.item_ident(index, sess), variant_did, ctor_did, data.discr, self.root - .per_def + .tables .children .get(self, index) .unwrap_or(Lazy::empty()) .decode(self) .map(|index| ty::FieldDef { did: self.local_def_id(index), - ident: Ident::with_dummy_span(self.item_name(index)), + ident: self.item_ident(index, sess), vis: self.get_visibility(index), }) .collect(), @@ -729,15 +833,15 @@ 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()) .decode(self) - .map(|index| self.get_variant(tcx, &self.kind(index), index, did)) + .map(|index| self.get_variant(tcx, &self.kind(index), index, did, tcx.sess)) .collect() } else { - std::iter::once(self.get_variant(tcx, &kind, item_id, did)).collect() + std::iter::once(self.get_variant(tcx, &kind, item_id, did, tcx.sess)).collect() }; tcx.alloc_adt_def(did, adt_kind, variants, repr) @@ -748,7 +852,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( @@ -757,7 +861,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))) @@ -769,31 +873,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)) @@ -803,7 +907,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), } } @@ -831,11 +935,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)) @@ -857,8 +961,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 { @@ -867,7 +971,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. @@ -901,7 +1005,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; @@ -921,25 +1025,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: Ident::with_dummy_span(self.item_name(child_index)), - 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; } @@ -950,10 +1053,9 @@ 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), Some(name)) = - (self.def_kind(child_index), def_key.disambiguated_data.data.get_opt_name()) - { - let ident = Ident::with_dummy_span(name); + 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); let res = Res::Def(kind, def_id); @@ -1012,83 +1114,76 @@ 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::AnonConst(qualif, _) + | EntryKind::Const(qualif, _) + | EntryKind::AssocConst( + AssocContainer::ImplDefault + | AssocContainer::ImplFinal + | AssocContainer::TraitWithDefault, + qualif, + _, + ) => qualif, + _ => bug!("mir_const_qualif: unexpected kind"), } } - fn get_associated_item(&self, id: DefIndex) -> ty::AssocItem { + fn get_associated_item(&self, id: DefIndex, sess: &Session) -> ty::AssocItem { let def_key = self.def_key(id); let parent = self.local_def_id(def_key.parent.unwrap()); - let name = def_key.disambiguated_data.data.get_opt_name().unwrap(); + let ident = self.item_ident(id, sess); let (kind, container, has_self) = match self.kind(id) { EntryKind::AssocConst(container, _, _) => (ty::AssocKind::Const, container, false), - EntryKind::Method(data) => { + 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), _ => bug!("cannot get associated-item of `{:?}`", def_key), }; ty::AssocItem { - ident: Ident::with_dummy_span(name), + ident, kind, vis: self.get_visibility(id), 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 { @@ -1112,7 +1207,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 @@ -1123,25 +1218,23 @@ 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()) .decode(self) - .map(|index| respan(self.get_span(index, sess), self.item_name(index))) + .map(|index| respan(self.get_span(index, sess), self.item_ident(index, sess).name)) .collect() } @@ -1152,7 +1245,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()) @@ -1207,7 +1300,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - fn get_native_libraries(&self, sess: &Session) -> Vec { + fn get_native_libraries(&self, sess: &Session) -> Vec { if self.root.is_proc_macro_crate() { // Proc macro crates do not have any *target* native libraries. vec![] @@ -1246,31 +1339,33 @@ 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::Method(data) => data.decode(self).fn_data.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))) } } fn get_rendered_const(&self, id: DefIndex) -> String { match self.kind(id) { - EntryKind::Const(_, data) | EntryKind::AssocConst(_, _, data) => data.decode(self).0, + EntryKind::AnonConst(_, data) + | EntryKind::Const(_, data) + | EntryKind::AssocConst(_, _, data) => data.decode(self).0, _ => bug!(), } } @@ -1286,7 +1381,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // don't serialize constness for tuple variant and tuple struct constructors. fn is_const_fn_raw(&self, id: DefIndex) -> bool { let constness = match self.kind(id) { - EntryKind::Method(data) => data.decode(self).fn_data.constness, + EntryKind::AssocFn(data) => data.decode(self).fn_data.constness, EntryKind::Fn(data) => data.decode(self).constness, // Some intrinsics can be const fn. While we could recompute this (at least until we // stop having hardcoded whitelists and move to stability attributes), it seems cleaner @@ -1301,7 +1396,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { fn asyncness(&self, id: DefIndex) -> hir::IsAsync { match self.kind(id) { EntryKind::Fn(data) => data.decode(self).asyncness, - EntryKind::Method(data) => data.decode(self).fn_data.asyncness, + EntryKind::AssocFn(data) => data.decode(self).fn_data.asyncness, EntryKind::ForeignFn(data) => data.decode(self).asyncness, _ => bug!("asyncness: expected function kind"), } @@ -1332,7 +1427,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] @@ -1376,11 +1471,53 @@ 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] { - self.cdata.source_map_import_info.init_locking(|| { + 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(old_name) = name { + if let rustc_span::RealFileName::Named(one_path) = old_name { + if let Ok(rest) = one_path.strip_prefix(virtual_dir) { + let virtual_name = one_path.clone(); + let new_path = real_dir.join(rest); + debug!( + "try_to_translate_virtual_to_real: `{}` -> `{}`", + virtual_name.display(), + new_path.display(), + ); + let new_name = rustc_span::RealFileName::Devirtualized { + local_path: new_path, + virtual_name, + }; + *old_name = new_name; + } + } + } + } + } + }; + + self.cdata.source_map_import_info.get_or_init(|| { let external_source_map = self.root.source_map.decode(self); external_source_map @@ -1388,7 +1525,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, @@ -1401,6 +1538,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 @@ -1421,17 +1565,19 @@ 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, - self.cnum.as_u32(), src_hash, name_hash, source_length, + self.cnum, lines, multibyte_chars, non_narrow_chars, normalized_pos, + start_pos, + end_pos, ); debug!( "CrateMetaData::imported_source_files alloc \ @@ -1485,7 +1631,7 @@ impl CrateMetadata { def_path_table, trait_impls, raw_proc_macros, - source_map_import_info: Once::new(), + source_map_import_info: OnceCell::new(), alloc_decoding_state, dep_node_index: AtomicCell::new(DepNodeIndex::INVALID), cnum, diff --git a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs index d87e24eeed173..1b168bf01178c 100644 --- a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs +++ b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs @@ -4,29 +4,29 @@ use crate::link_args; use crate::native_libs; use crate::rmeta::{self, encoder}; -use rustc::hir::exports::Export; -use rustc::hir::map::definitions::DefPathTable; -use rustc::hir::map::{DefKey, DefPath, DefPathHash}; -use rustc::middle::cstore::{CrateSource, CrateStore, EncodedMetadata, NativeLibraryKind}; -use rustc::middle::exported_symbols::ExportedSymbol; -use rustc::middle::stability::DeprecationEntry; -use rustc::session::{CrateDisambiguator, Session}; -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; use rustc_data_structures::svh::Svh; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::DefPathTable; +use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; +use rustc_middle::hir::exports::Export; +use rustc_middle::middle::cstore::{CrateSource, CrateStore, EncodedMetadata}; +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::utils::NativeLibKind; +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 +37,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 +89,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; @@ -110,7 +106,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, |child| result.push(child.res.def_id()), tcx.sess); tcx.arena.alloc_slice(&result) } - associated_item => { cdata.get_associated_item(def_id.index) } + associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) } impl_trait_ref => { cdata.get_impl_trait(def_id.index, tcx) } impl_polarity => { cdata.get_impl_polarity(def_id.index) } coerce_unsized_info => { @@ -118,8 +114,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 +135,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) } - // FIXME(#38501) We've skipped a `read` on the `hir_owner_items` of + 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) } @@ -170,14 +168,14 @@ provide! { <'tcx> tcx, def_id, other, cdata, .iter() .filter_map(|&(exported_symbol, export_level)| { if let ExportedSymbol::NonGeneric(def_id) = exported_symbol { - return Some((def_id, export_level)) + Some((def_id, export_level)) } else { None } }) .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 +217,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 +237,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 +247,13 @@ 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) => { + Some(NativeLibKind::Dylib | NativeLibKind::RawDylib | NativeLibKind::Unspecified) => { true } _ => false, }, is_statically_included_foreign_item: |tcx, id| match tcx.native_library_kind(id) { - Some(NativeLibraryKind::NativeStatic) - | Some(NativeLibraryKind::NativeStaticNobundle) => true, + Some(NativeLibKind::StaticBundle | NativeLibKind::StaticNoBundle) => true, _ => false, }, native_library_kind: |tcx, id| { @@ -365,7 +362,7 @@ pub fn provide(providers: &mut Providers<'_>) { } } - tcx.arena.alloc(visible_parent_map) + visible_parent_map }, dependency_formats: |tcx, cnum| { @@ -425,7 +422,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 +430,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, @@ -442,8 +439,8 @@ impl CStore { ) } - pub fn associated_item_cloned_untracked(&self, def: DefId) -> ty::AssocItem { - self.get_crate_data(def.krate).get_associated_item(def.index) + pub fn associated_item_cloned_untracked(&self, def: DefId, sess: &Session) -> ty::AssocItem { + self.get_crate_data(def.krate).get_associated_item(def.index, sess) } pub fn crate_source_untracked(&self, cnum: CrateNum) -> CrateSource { diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs index 8b589f70651e4..cdc8b5e90a642 100644 --- a/src/librustc_metadata/rmeta/encoder.rs +++ b/src/librustc_metadata/rmeta/encoder.rs @@ -1,49 +1,47 @@ use crate::rmeta::table::FixedSizeEncoding; use crate::rmeta::*; -use rustc::hir::map::definitions::DefPathTable; -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 log::{debug, trace}; +use rustc_ast::ast; +use rustc_ast::attr; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stable_hasher::StableHasher; +use rustc_data_structures::sync::{join, Lrc}; +use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::DefPathTable; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::itemlikevisit::{ItemLikeVisitor, ParItemLikeVisitor}; +use rustc_hir::lang_items; use rustc_hir::{AnonConst, GenericParamKind}; use rustc_index::vec::Idx; - -use rustc::session::config::{self, CrateType}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stable_hasher::StableHasher; -use rustc_data_structures::sync::Lrc; -use rustc_serialize::{opaque, Encodable, Encoder, SpecializedEncoder}; - -use log::{debug, trace}; -use rustc_ast::ast; -use rustc_ast::attr; +use rustc_middle::hir::map::Map; +use rustc_middle::middle::cstore::{EncodedMetadata, ForeignModule, LinkagePreference, NativeLib}; +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, UseSpecializedEncodable}; +use rustc_session::config::CrateType; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, FileName, SourceFile, Span}; +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; - -use rustc_hir as hir; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; struct EncodeContext<'tcx> { opaque: opaque::Encoder, tcx: TyCtxt<'tcx>, - per_def: PerDefTableBuilders<'tcx>, + tables: TableBuilders<'tcx>, lazy_state: LazyState, type_shorthands: FxHashMap, usize>, @@ -67,6 +65,7 @@ macro_rules! encoder_methods { impl<'tcx> Encoder for EncodeContext<'tcx> { type Error = ::Error; + #[inline] fn emit_unit(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -94,13 +93,13 @@ impl<'tcx> Encoder for EncodeContext<'tcx> { } } -impl<'tcx, T> SpecializedEncoder> for EncodeContext<'tcx> { +impl<'tcx, T> SpecializedEncoder> for EncodeContext<'tcx> { fn specialized_encode(&mut self, lazy: &Lazy) -> Result<(), Self::Error> { self.emit_lazy_distance(*lazy) } } -impl<'tcx, T> SpecializedEncoder> for EncodeContext<'tcx> { +impl<'tcx, T> SpecializedEncoder> for EncodeContext<'tcx> { fn specialized_encode(&mut self, lazy: &Lazy<[T]>) -> Result<(), Self::Error> { self.emit_usize(lazy.meta)?; if lazy.meta == 0 { @@ -110,7 +109,7 @@ impl<'tcx, T> SpecializedEncoder> for EncodeContext<'tcx> { } } -impl<'tcx, I: Idx, T> SpecializedEncoder>> for EncodeContext<'tcx> +impl<'tcx, I: Idx, T> SpecializedEncoder, usize>> for EncodeContext<'tcx> where Option: FixedSizeEncoding, { @@ -167,32 +166,61 @@ impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { return TAG_INVALID_SPAN.encode(self); } - // HACK(eddyb) there's no way to indicate which crate a Span is coming - // from right now, so decoding would fail to find the SourceFile if - // it's not local to the crate the Span is found in. - if self.source_file_cache.is_imported() { - return TAG_INVALID_SPAN.encode(self); - } + // There are two possible cases here: + // 1. This span comes from a 'foreign' crate - e.g. some crate upstream of the + // crate we are writing metadata for. When the metadata for *this* crate gets + // deserialized, the deserializer will need to know which crate it originally came + // from. We use `TAG_VALID_SPAN_FOREIGN` to indicate that a `CrateNum` should + // be deserialized after the rest of the span data, which tells the deserializer + // which crate contains the source map information. + // 2. This span comes from our own crate. No special hamdling is needed - we just + // write `TAG_VALID_SPAN_LOCAL` to let the deserializer know that it should use + // our own source map information. + let (tag, lo, hi) = if self.source_file_cache.is_imported() { + // To simplify deserialization, we 'rebase' this span onto the crate it originally came from + // (the crate that 'owns' the file it references. These rebased 'lo' and 'hi' values + // are relative to the source map information for the 'foreign' crate whose CrateNum + // we write into the metadata. This allows `imported_source_files` to binary + // search through the 'foreign' crate's source map information, using the + // deserialized 'lo' and 'hi' values directly. + // + // All of this logic ensures that the final result of deserialization is a 'normal' + // Span that can be used without any additional trouble. + let external_start_pos = { + // Introduce a new scope so that we drop the 'lock()' temporary + match &*self.source_file_cache.external_src.lock() { + ExternalSource::Foreign { original_start_pos, .. } => *original_start_pos, + src => panic!("Unexpected external source {:?}", src), + } + }; + let lo = (span.lo - self.source_file_cache.start_pos) + external_start_pos; + let hi = (span.hi - self.source_file_cache.start_pos) + external_start_pos; + + (TAG_VALID_SPAN_FOREIGN, lo, hi) + } else { + (TAG_VALID_SPAN_LOCAL, span.lo, span.hi) + }; - TAG_VALID_SPAN.encode(self)?; - span.lo.encode(self)?; + tag.encode(self)?; + lo.encode(self)?; // Encode length which is usually less than span.hi and profits more // from the variable-length integer encoding that we use. - let len = span.hi - span.lo; - len.encode(self) + let len = hi - lo; + len.encode(self)?; + + if tag == TAG_VALID_SPAN_FOREIGN { + // This needs to be two lines to avoid holding the `self.source_file_cache` + // while calling `cnum.encode(self)` + let cnum = self.source_file_cache.cnum; + cnum.encode(self)?; + } + Ok(()) // Don't encode the expansion context. } } -impl SpecializedEncoder for EncodeContext<'tcx> { - fn specialized_encode(&mut self, ident: &Ident) -> Result<(), Self::Error> { - // FIXME(jseyfried): intercrate hygiene - ident.name.encode(self) - } -} - impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { #[inline] fn specialized_encode(&mut self, def_id: &LocalDefId) -> Result<(), Self::Error> { @@ -200,12 +228,28 @@ impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { } } -impl<'tcx> SpecializedEncoder> for EncodeContext<'tcx> { - fn specialized_encode(&mut self, ty: &Ty<'tcx>) -> Result<(), Self::Error> { +impl<'a, 'b, 'tcx> SpecializedEncoder<&'a ty::TyS<'b>> for EncodeContext<'tcx> +where + &'a ty::TyS<'b>: UseSpecializedEncodable, +{ + fn specialized_encode(&mut self, ty: &&'a ty::TyS<'b>) -> Result<(), Self::Error> { + debug_assert!(self.tcx.lift(ty).is_some()); + let ty = unsafe { std::mem::transmute::<&&'a ty::TyS<'b>, &&'tcx ty::TyS<'tcx>>(ty) }; ty_codec::encode_with_shorthand(self, ty, |ecx| &mut ecx.type_shorthands) } } +impl<'b, 'tcx> SpecializedEncoder> for EncodeContext<'tcx> { + fn specialized_encode(&mut self, predicate: &ty::Predicate<'b>) -> Result<(), Self::Error> { + debug_assert!(self.tcx.lift(predicate).is_some()); + let predicate = + unsafe { std::mem::transmute::<&ty::Predicate<'b>, &ty::Predicate<'tcx>>(predicate) }; + ty_codec::encode_with_shorthand(self, predicate, |encoder| { + &mut encoder.predicate_shorthands + }) + } +} + impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { use std::collections::hash_map::Entry; @@ -223,22 +267,16 @@ impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { } } -impl<'tcx> SpecializedEncoder<&'tcx [(ty::Predicate<'tcx>, Span)]> for EncodeContext<'tcx> { - fn specialized_encode( - &mut self, - predicates: &&'tcx [(ty::Predicate<'tcx>, Span)], - ) -> Result<(), Self::Error> { - ty_codec::encode_spanned_predicates(self, predicates, |ecx| &mut ecx.predicate_shorthands) - } -} - impl<'tcx> SpecializedEncoder for EncodeContext<'tcx> { fn specialized_encode(&mut self, f: &Fingerprint) -> Result<(), Self::Error> { f.encode_opaque(&mut self.opaque) } } -impl<'tcx, T: Encodable> SpecializedEncoder> for EncodeContext<'tcx> { +impl<'tcx, T> SpecializedEncoder> for EncodeContext<'tcx> +where + mir::ClearCrossCrate: UseSpecializedEncodable, +{ fn specialized_encode(&mut self, _: &mir::ClearCrossCrate) -> Result<(), Self::Error> { Ok(()) } @@ -368,6 +406,7 @@ impl<'tcx> EncodeContext<'tcx> { // any relative paths are potentially relative to a // wrong directory. FileName::Real(ref name) => { + let name = name.stable_name(); let mut adapted = (**source_file).clone(); adapted.name = Path::new(&working_dir).join(name).into(); adapted.name_hash = { @@ -388,7 +427,7 @@ impl<'tcx> EncodeContext<'tcx> { } fn encode_crate_root(&mut self) -> Lazy> { - let is_proc_macro = self.tcx.sess.crate_types.borrow().contains(&CrateType::ProcMacro); + let is_proc_macro = self.tcx.sess.crate_types().contains(&CrateType::ProcMacro); let mut i = self.position(); @@ -434,12 +473,6 @@ impl<'tcx> EncodeContext<'tcx> { let impls = self.encode_impls(); let impl_bytes = self.position() - i; - // Encode exported symbols info. - i = self.position(); - let exported_symbols = self.tcx.exported_symbols(LOCAL_CRATE); - let exported_symbols = self.encode_exported_symbols(&exported_symbols); - let exported_symbols_bytes = self.position() - i; - let tcx = self.tcx; // Encode the items. @@ -472,14 +505,21 @@ 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(); let proc_macro_data = self.encode_proc_macros(); let proc_macro_data_bytes = self.position() - i; + // Encode exported symbols info. This is prefetched in `encode_metadata` so we encode + // this last to give the prefetching as much time as possible to complete. + i = self.position(); + let exported_symbols = self.tcx.exported_symbols(LOCAL_CRATE); + let exported_symbols = self.encode_exported_symbols(&exported_symbols); + let exported_symbols_bytes = self.position() - i; + let attrs = tcx.hir().krate_attrs(); let has_default_lib_allocator = attr::contains_name(&attrs, sym::default_lib_allocator); @@ -528,7 +568,7 @@ impl<'tcx> EncodeContext<'tcx> { impls, exported_symbols, interpret_alloc_index, - per_def, + tables, }); let total_bytes = self.position(); @@ -553,7 +593,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); } @@ -565,17 +605,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); @@ -586,25 +625,26 @@ 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 })); + self.encode_ident_span(def_id, variant.ident); self.encode_stability(def_id); self.encode_deprecation(def_id); self.encode_item_type(def_id); 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); @@ -612,13 +652,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); @@ -632,28 +671,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( @@ -669,37 +708,52 @@ impl EncodeContext<'tcx> { let data = ModData { reexports: match tcx.module_exports(def_id) { - Some(exports) => self.lazy(exports), + Some(exports) => { + let hir_map = self.tcx.hir(); + self.lazy( + exports + .iter() + .map(|export| export.map_id(|id| hir_map.as_local_hir_id(id))), + ) + } _ => Lazy::empty(), }, }; - 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 + let def_id = def_id.to_def_id(); + + record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data))); + record!(self.tables.visibility[def_id] <- ty::Visibility::from_hir(vis, id, self.tcx)); + record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); + 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); self.encode_item_type(def_id); @@ -708,10 +762,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 { @@ -720,7 +773,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 { @@ -737,31 +790,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)); } @@ -769,20 +822,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); @@ -792,10 +845,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( @@ -804,13 +859,13 @@ 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::TraitMethod::Required(ref names) => { + hir::TraitFn::Required(ref names) => { self.encode_fn_param_names(names) } - hir::TraitMethod::Provided(body) => { + hir::TraitFn::Provided(body) => { self.encode_fn_param_names_for_body(body) } }; @@ -822,23 +877,23 @@ impl EncodeContext<'tcx> { } else { bug!() }; - EntryKind::Method(self.lazy(MethodData { + 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 => { @@ -846,17 +901,18 @@ impl EncodeContext<'tcx> { self.encode_item_type(def_id); } } - 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); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); - self.encode_optimized_mir(def_id); - self.encode_promoted_mir(def_id); + + // This should be kept in sync with `PrefetchVisitor.visit_trait_item`. + self.encode_optimized_mir(def_id.expect_local()); + self.encode_promoted_mir(def_id.expect_local()); } fn metadata_output_only(&self) -> bool { @@ -868,7 +924,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); @@ -880,7 +936,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); @@ -893,8 +949,8 @@ impl EncodeContext<'tcx> { bug!() } } - ty::AssocKind::Method => { - let fn_data = if let hir::ImplItemKind::Method(ref sig, body) = ast_item.kind { + ty::AssocKind::Fn => { + let fn_data = if let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind { FnData { asyncness: sig.header.asyncness, constness: sig.header.constness, @@ -903,32 +959,35 @@ impl EncodeContext<'tcx> { } else { bug!() }; - EntryKind::Method(self.lazy(MethodData { + 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); self.encode_explicit_predicates(def_id); self.encode_inferred_outlives(def_id); + + // The following part should be kept in sync with `PrefetchVisitor.visit_impl_item`. + let mir = match ast_item.kind { hir::ImplItemKind::Const(..) => true, - hir::ImplItemKind::Method(ref sig, _) => { + hir::ImplItemKind::Fn(ref sig, _) => { let generics = self.tcx.generics_of(def_id); let needs_inline = (generics.requires_monomorphization(self.tcx) || tcx.codegen_fn_attrs(def_id).requests_inline()) @@ -937,15 +996,15 @@ impl EncodeContext<'tcx> { let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; needs_inline || is_const_fn || always_encode_mir } - hir::ImplItemKind::OpaqueTy(..) | hir::ImplItemKind::TyAlias(..) => false, + 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 { @@ -955,21 +1014,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)); } } @@ -978,7 +1037,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 })); @@ -988,27 +1047,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) } @@ -1018,7 +1080,9 @@ impl EncodeContext<'tcx> { debug!("EncodeContext::encode_info_for_item({:?})", def_id); - record!(self.per_def.kind[def_id] <- match item.kind { + self.encode_ident_span(def_id, item.ident); + + 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) => { @@ -1053,7 +1117,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 { @@ -1077,12 +1141,13 @@ impl EncodeContext<'tcx> { let polarity = self.tcx.impl_polarity(def_id); let parent = if let Some(trait_ref) = trait_ref { let trait_def = self.tcx.trait_def(trait_ref.def_id); - trait_def.ancestors(self.tcx, def_id).nth(1).and_then(|node| { - match node { - specialization_graph::Node::Impl(parent) => Some(parent), - _ => None, - } - }) + trait_def.ancestors(self.tcx, def_id).ok() + .and_then(|mut an| an.nth(1).and_then(|node| { + match node { + specialization_graph::Node::Impl(parent) => Some(parent), + _ => None, + } + })) } else { None }; @@ -1114,6 +1179,7 @@ impl EncodeContext<'tcx> { paren_sugar: trait_def.paren_sugar, has_auto_impl: self.tcx.trait_is_auto(def_id), is_marker: trait_def.is_marker, + specialization_kind: trait_def.specialization_kind, }; EntryKind::Trait(self.lazy(data)) @@ -1122,26 +1188,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 @@ -1150,7 +1216,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 @@ -1175,11 +1241,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); @@ -1215,6 +1281,8 @@ impl EncodeContext<'tcx> { _ => {} } + // The following part should be kept in sync with `PrefetchVisitor.visit_item`. + let mir = match item.kind { hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => true, hir::ItemKind::Fn(ref sig, ..) => { @@ -1228,40 +1296,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) @@ -1271,37 +1340,37 @@ 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(def_id, self.tcx)); + 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::AnonConst(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); } - fn encode_native_libraries(&mut self) -> Lazy<[NativeLibrary]> { + fn encode_native_libraries(&mut self) -> Lazy<[NativeLib]> { let used_libraries = self.tcx.native_libraries(LOCAL_CRATE); self.lazy(used_libraries.iter().cloned()) } @@ -1312,10 +1381,10 @@ impl EncodeContext<'tcx> { } fn encode_proc_macros(&mut self) -> Option> { - let is_proc_macro = self.tcx.sess.crate_types.borrow().contains(&CrateType::ProcMacro); + let is_proc_macro = self.tcx.sess.crate_types().contains(&CrateType::ProcMacro); if is_proc_macro { let tcx = self.tcx; - Some(self.lazy(tcx.hir().krate().proc_macros.iter().map(|p| p.owner))) + Some(self.lazy(tcx.hir().krate().proc_macros.iter().map(|p| p.owner.local_def_index))) } else { None } @@ -1356,7 +1425,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()) @@ -1403,8 +1472,8 @@ impl EncodeContext<'tcx> { .into_iter() .map(|(trait_def_id, mut impls)| { // Bring everything into deterministic order for hashing - impls.sort_by_cached_key(|&def_index| { - tcx.hir().definitions().def_path_hash(def_index) + impls.sort_by_cached_key(|&index| { + tcx.hir().definitions().def_path_hash(LocalDefId { local_def_index: index }) }); TraitImpls { @@ -1445,7 +1514,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 { @@ -1463,7 +1532,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, @@ -1480,16 +1549,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); @@ -1519,14 +1589,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); @@ -1538,13 +1608,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); } } } @@ -1556,28 +1623,33 @@ 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.tables.ident_span[def_id] <- ident.span); + } + /// In some cases, along with the item itself, we also /// encode some sub-items. Usually we want some info from the item /// so it's easier to do that here then to wait until we would encounter @@ -1599,40 +1671,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); } } @@ -1649,8 +1721,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); } } } @@ -1662,6 +1734,70 @@ impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'tcx> { } } +/// Used to prefetch queries which will be needed later by metadata encoding. +/// Only a subset of the queries are actually prefetched to keep this code smaller. +struct PrefetchVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + mir_keys: &'tcx FxHashSet, +} + +impl<'tcx> PrefetchVisitor<'tcx> { + fn prefetch_mir(&self, def_id: LocalDefId) { + if self.mir_keys.contains(&def_id) { + self.tcx.ensure().optimized_mir(def_id); + self.tcx.ensure().promoted_mir(def_id); + } + } +} + +impl<'tcx, 'v> ParItemLikeVisitor<'v> for PrefetchVisitor<'tcx> { + fn visit_item(&self, item: &hir::Item<'_>) { + // This should be kept in sync with `encode_info_for_item`. + let tcx = self.tcx; + match item.kind { + hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { + self.prefetch_mir(tcx.hir().local_def_id(item.hir_id)) + } + hir::ItemKind::Fn(ref sig, ..) => { + let def_id = tcx.hir().local_def_id(item.hir_id); + let generics = tcx.generics_of(def_id.to_def_id()); + let needs_inline = generics.requires_monomorphization(tcx) + || 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) + } + } + _ => (), + } + } + + fn visit_trait_item(&self, trait_item: &'v hir::TraitItem<'v>) { + // This should be kept in sync with `encode_info_for_trait_item`. + self.prefetch_mir(self.tcx.hir().local_def_id(trait_item.hir_id)); + } + + fn visit_impl_item(&self, impl_item: &'v hir::ImplItem<'v>) { + // This should be kept in sync with `encode_info_for_impl_item`. + let tcx = self.tcx; + match impl_item.kind { + hir::ImplItemKind::Const(..) => { + self.prefetch_mir(tcx.hir().local_def_id(impl_item.hir_id)) + } + hir::ImplItemKind::Fn(ref sig, _) => { + let def_id = tcx.hir().local_def_id(impl_item.hir_id); + let generics = tcx.generics_of(def_id.to_def_id()); + let needs_inline = generics.requires_monomorphization(tcx) + || 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) + } + } + hir::ImplItemKind::TyAlias(..) => (), + } + } +} + // NOTE(eddyb) The following comment was preserved for posterity, even // though it's no longer relevant as EBML (which uses nested & tagged // "documents") was replaced with a scheme that can't go out of bounds. @@ -1686,35 +1822,64 @@ impl<'tcx, 'v> ItemLikeVisitor<'v> for ImplVisitor<'tcx> { // generated regardless of trailing bytes that end up in it. pub(super) fn encode_metadata(tcx: TyCtxt<'_>) -> EncodedMetadata { + // Since encoding metadata is not in a query, and nothing is cached, + // there's no need to do dep-graph tracking for any of it. + tcx.dep_graph.assert_ignored(); + + join( + || encode_metadata_impl(tcx), + || { + if tcx.sess.threads() == 1 { + return; + } + // Prefetch some queries used by metadata encoding. + // This is not necessary for correctness, but is only done for performance reasons. + // It can be removed if it turns out to cause trouble or be detrimental to performance. + join( + || { + if !tcx.sess.opts.output_types.should_codegen() { + // We won't emit MIR, so don't prefetch it. + return; + } + tcx.hir().krate().par_visit_all_item_likes(&PrefetchVisitor { + tcx, + mir_keys: tcx.mir_keys(LOCAL_CRATE), + }); + }, + || tcx.exported_symbols(LOCAL_CRATE), + ); + }, + ) + .0 +} + +fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { let mut encoder = opaque::Encoder::new(vec![]); encoder.emit_raw_bytes(METADATA_HEADER); // Will be filled with the root position after encoding everything. encoder.emit_raw_bytes(&[0, 0, 0, 0]); - // Since encoding metadata is not in a query, and nothing is cached, - // there's no need to do dep-graph tracking for any of it. - let (root, mut result) = tcx.dep_graph.with_ignore(move || { - let mut ecx = EncodeContext { - opaque: encoder, - tcx, - per_def: Default::default(), - lazy_state: LazyState::NoNode, - type_shorthands: Default::default(), - predicate_shorthands: Default::default(), - source_file_cache: tcx.sess.source_map().files()[0].clone(), - interpret_allocs: Default::default(), - interpret_allocs_inverse: Default::default(), - }; - - // Encode the rustc version string in a predictable location. - rustc_version().encode(&mut ecx).unwrap(); - - // Encode all the entries and extra information in the crate, - // culminating in the `CrateRoot` which points to all of it. - let root = ecx.encode_crate_root(); - (root, ecx.opaque.into_inner()) - }); + let mut ecx = EncodeContext { + opaque: encoder, + tcx, + tables: Default::default(), + lazy_state: LazyState::NoNode, + type_shorthands: Default::default(), + predicate_shorthands: Default::default(), + source_file_cache: tcx.sess.source_map().files()[0].clone(), + interpret_allocs: Default::default(), + interpret_allocs_inverse: Default::default(), + }; + + // Encode the rustc version string in a predictable location. + rustc_version().encode(&mut ecx).unwrap(); + + // Encode all the entries and extra information in the crate, + // culminating in the `CrateRoot` which points to all of it. + let root = ecx.encode_crate_root(); + + let mut result = ecx.opaque.into_inner(); // Encode the root position. let header = METADATA_HEADER.len(); diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs index 152bb257fa22c..0edea63f922d6 100644 --- a/src/librustc_metadata/rmeta/mod.rs +++ b/src/librustc_metadata/rmeta/mod.rs @@ -1,15 +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::session::config::SymbolManglingVersion; -use rustc::session::CrateDisambiguator; -use rustc::ty::{self, ReprOptions, Ty}; use rustc_ast::ast::{self, MacroDef}; use rustc_attr as attr; use rustc_data_structures::svh::Svh; @@ -17,8 +8,16 @@ 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, NativeLib}; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; +use rustc_middle::mir; +use rustc_middle::ty::{self, ReprOptions, Ty}; use rustc_serialize::opaque::Encoder; +use rustc_session::config::SymbolManglingVersion; +use rustc_session::CrateDisambiguator; use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{self, Span}; @@ -191,19 +190,20 @@ crate struct CrateRoot<'tcx> { lang_items: Lazy<[(DefIndex, usize)]>, lang_items_missing: Lazy<[lang_items::LangItem]>, diagnostic_items: Lazy<[(Symbol, DefIndex)]>, - native_libraries: Lazy<[NativeLibrary]>, + native_libraries: Lazy<[NativeLib]>, foreign_modules: Lazy<[ForeignModule]>, source_map: Lazy<[rustc_span::SourceFile]>, - def_path_table: Lazy, + def_path_table: Lazy, impls: Lazy<[TraitImpls]>, - exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportLevel)]), 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>, + exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportLevel)]), + compiler_builtins: bool, needs_allocator: bool, needs_panic_runtime: bool, @@ -215,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, @@ -228,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)),+ } } @@ -251,10 +251,11 @@ macro_rules! define_per_def_tables { } } -define_per_def_tables! { +define_tables! { kind: Table>, visibility: Table>, span: Table>, + ident_span: Table>, attributes: Table>, children: Table>, stability: Table>, @@ -274,12 +275,13 @@ 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)] enum EntryKind { + AnonConst(mir::ConstQualifs, Lazy), Const(mir::ConstQualifs, Lazy), ImmStatic, MutStatic, @@ -305,9 +307,8 @@ enum EntryKind { Generator(hir::GeneratorKind), Trait(Lazy), Impl(Lazy), - Method(Lazy), + AssocFn(Lazy), AssocType(AssocContainer), - AssocOpaqueTy(AssocContainer), AssocConst(AssocContainer, mir::ConstQualifs, Lazy), TraitAlias, } @@ -326,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)] @@ -343,6 +344,7 @@ struct TraitData { paren_sugar: bool, has_auto_impl: bool, is_marker: bool, + specialization_kind: ty::trait_def::TraitSpecializationKind, } #[derive(RustcEncodable, RustcDecodable)] @@ -392,7 +394,7 @@ impl AssocContainer { } #[derive(RustcEncodable, RustcDecodable)] -struct MethodData { +struct AssocFnData { fn_data: FnData, container: AssocContainer, has_self: bool, @@ -404,5 +406,6 @@ struct GeneratorData<'tcx> { } // Tags used for encoding Spans: -const TAG_VALID_SPAN: u8 = 0; -const TAG_INVALID_SPAN: u8 = 1; +const TAG_VALID_SPAN_LOCAL: u8 = 0; +const TAG_VALID_SPAN_FOREIGN: u8 = 1; +const TAG_INVALID_SPAN: u8 = 2; diff --git a/src/librustc_metadata/rmeta/table.rs b/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..21d0b102a4a66 --- /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] +rustc_arena = { path = "../librustc_arena" } +bitflags = "1.2.1" +scoped-tls = "1.0" +log = { version = "0.4", features = ["release_max_level_info", "std"] } +rustc-rayon-core = "0.3.0" +polonius-engine = "0.12.0" +rustc_apfloat = { path = "../librustc_apfloat" } +rustc_attr = { path = "../librustc_attr" } +rustc_feature = { path = "../librustc_feature" } +rustc_hir = { path = "../librustc_hir" } +rustc_target = { path = "../librustc_target" } +rustc_macros = { path = "../librustc_macros" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_query_system = { path = "../librustc_query_system" } +rustc_errors = { path = "../librustc_errors" } +rustc_index = { path = "../librustc_index" } +rustc_serialize = { path = "../librustc_serialize" } +rustc_ast = { path = "../librustc_ast" } +rustc_span = { path = "../librustc_span" } +byteorder = { version = "1.3" } +chalk-ir = "0.11.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..4f1889aeb162a --- /dev/null +++ b/src/librustc_middle/arena.rs @@ -0,0 +1,123 @@ +/// This declares a list of types which can be allocated by `Arena`. +/// +/// The `few` modifier will cause allocation to use the shared arena and recording the destructor. +/// This is faster and more memory efficient if there's only a few allocations of the type. +/// Leaving `few` out will cause the type to get its own dedicated `TypedArena` which is +/// faster and more memory efficient if there is lots of allocations. +/// +/// Specifying the `decode` modifier will add decode impls for `&T` and `&[T]` where `T` is the type +/// listed. These impls will appear in the implement_ty_decoder! macro. +#[macro_export] +macro_rules! arena_types { + ($macro:path, $args:tt, $tcx:lifetime) => ( + $macro!($args, [ + [] layouts: rustc_target::abi::Layout, rustc_target::abi::Layout; + // AdtDef are interned and compared by address + [] adt_def: rustc_middle::ty::AdtDef, rustc_middle::ty::AdtDef; + [decode] tables: rustc_middle::ty::TypeckTables<$tcx>, rustc_middle::ty::TypeckTables<'_x>; + [] const_allocs: rustc_middle::mir::interpret::Allocation, rustc_middle::mir::interpret::Allocation; + // Required for the incremental on-disk cache + [few, decode] mir_keys: rustc_hir::def_id::DefIdSet, rustc_hir::def_id::DefIdSet; + [] region_scope_tree: rustc_middle::middle::region::ScopeTree, rustc_middle::middle::region::ScopeTree; + [] dropck_outlives: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + rustc_middle::traits::query::DropckOutlivesResult<'tcx> + > + >, + rustc_middle::infer::canonical::Canonical<'_x, + rustc_middle::infer::canonical::QueryResponse<'_y, + rustc_middle::traits::query::DropckOutlivesResult<'_z> + > + >; + [] normalize_projection_ty: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + rustc_middle::traits::query::NormalizationResult<'tcx> + > + >, + rustc_middle::infer::canonical::Canonical<'_x, + rustc_middle::infer::canonical::QueryResponse<'_y, + rustc_middle::traits::query::NormalizationResult<'_z> + > + >; + [] implied_outlives_bounds: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, + Vec> + > + >, + rustc_middle::infer::canonical::Canonical<'_x, + rustc_middle::infer::canonical::QueryResponse<'_y, + Vec> + > + >; + [] type_op_subtype: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, ()> + >, + rustc_middle::infer::canonical::Canonical<'_x, + rustc_middle::infer::canonical::QueryResponse<'_y, ()> + >; + [] type_op_normalize_poly_fn_sig: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::PolyFnSig<'tcx>> + >, + rustc_middle::infer::canonical::Canonical<'_x, + rustc_middle::infer::canonical::QueryResponse<'_y, rustc_middle::ty::PolyFnSig<'_z>> + >; + [] type_op_normalize_fn_sig: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::FnSig<'tcx>> + >, + rustc_middle::infer::canonical::Canonical<'_x, + rustc_middle::infer::canonical::QueryResponse<'_y, rustc_middle::ty::FnSig<'_z>> + >; + [] type_op_normalize_predicate: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Predicate<'tcx>> + >, + rustc_middle::infer::canonical::Canonical<'_x, + rustc_middle::infer::canonical::QueryResponse<'_y, rustc_middle::ty::Predicate<'_z>> + >; + [] type_op_normalize_ty: + rustc_middle::infer::canonical::Canonical<'tcx, + rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Ty<'tcx>> + >, + rustc_middle::infer::canonical::Canonical<'_x, + rustc_middle::infer::canonical::QueryResponse<'_y, &'_z rustc_middle::ty::TyS<'_w>> + >; + [few] all_traits: Vec, Vec; + [few] privacy_access_levels: rustc_middle::middle::privacy::AccessLevels, rustc_middle::middle::privacy::AccessLevels; + [few] foreign_module: rustc_middle::middle::cstore::ForeignModule, rustc_middle::middle::cstore::ForeignModule; + [few] foreign_modules: Vec, Vec; + [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap, rustc_data_structures::fx::FxIndexMap; + [] object_safety_violations: rustc_middle::traits::ObjectSafetyViolation, rustc_middle::traits::ObjectSafetyViolation; + [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<$tcx>, rustc_middle::mir::mono::CodegenUnit<'_x>; + [] attribute: rustc_ast::ast::Attribute, rustc_ast::ast::Attribute; + [] name_set: rustc_data_structures::fx::FxHashSet, rustc_data_structures::fx::FxHashSet; + [] hir_id_set: rustc_hir::HirIdSet, rustc_hir::HirIdSet; + + // Interned types + [] tys: rustc_middle::ty::TyS<$tcx>, rustc_middle::ty::TyS<'_x>; + [] predicates: rustc_middle::ty::PredicateInner<$tcx>, rustc_middle::ty::PredicateInner<'_x>; + + // HIR query types + [few] indexed_hir: rustc_middle::hir::map::IndexedHir<$tcx>, rustc_middle::hir::map::IndexedHir<'_x>; + [few] hir_definitions: rustc_hir::definitions::Definitions, rustc_hir::definitions::Definitions; + [] hir_owner: rustc_middle::hir::Owner<$tcx>, rustc_middle::hir::Owner<'_x>; + [] hir_owner_nodes: rustc_middle::hir::OwnerNodes<$tcx>, rustc_middle::hir::OwnerNodes<'_x>; + + // Note that this deliberately duplicates items in the `rustc_hir::arena`, + // since we need to allocate this type on both the `rustc_hir` arena + // (during lowering) and the `librustc_middle` arena (for decoding MIR) + [decode] asm_template: rustc_ast::ast::InlineAsmTemplatePiece, rustc_ast::ast::InlineAsmTemplatePiece; + + // This is used to decode the &'tcx [Span] for InlineAsm's line_spans. + [decode] span: rustc_span::Span, rustc_span::Span; + [decode] used_trait_imports: rustc_data_structures::fx::FxHashSet, rustc_data_structures::fx::FxHashSet; + ], $tcx); + ) +} + +arena_types!(rustc_arena::declare_arena, [], 'tcx); diff --git a/src/librustc/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_middle/dep_graph/dep_node.rs b/src/librustc_middle/dep_graph/dep_node.rs new file mode 100644 index 0000000000000..b14f17dee6060 --- /dev/null +++ b/src/librustc_middle/dep_graph/dep_node.rs @@ -0,0 +1,406 @@ +//! This module defines the `DepNode` type which the compiler uses to represent +//! nodes in the dependency graph. A `DepNode` consists of a `DepKind` (which +//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc) +//! and a `Fingerprint`, a 128 bit hash value the exact meaning of which +//! depends on the node's `DepKind`. Together, the kind and the fingerprint +//! fully identify a dependency node, even across multiple compilation sessions. +//! In other words, the value of the fingerprint does not depend on anything +//! that is specific to a given compilation session, like an unpredictable +//! interning key (e.g., NodeId, DefId, Symbol) or the numeric value of a +//! pointer. The concept behind this could be compared to how git commit hashes +//! uniquely identify a given commit and has a few advantages: +//! +//! * A `DepNode` can simply be serialized to disk and loaded in another session +//! without the need to do any "rebasing (like we have to do for Spans and +//! NodeIds) or "retracing" like we had to do for `DefId` in earlier +//! implementations of the dependency graph. +//! * A `Fingerprint` is just a bunch of bits, which allows `DepNode` to +//! implement `Copy`, `Sync`, `Send`, `Freeze`, etc. +//! * Since we just have a bit pattern, `DepNode` can be mapped from disk into +//! memory without any post-processing (e.g., "abomination-style" pointer +//! reconstruction). +//! * Because a `DepNode` is self-contained, we can instantiate `DepNodes` that +//! refer to things that do not exist anymore. In previous implementations +//! `DepNode` contained a `DefId`. A `DepNode` referring to something that +//! had been removed between the previous and the current compilation session +//! could not be instantiated because the current compilation session +//! contained no `DefId` for thing that had been removed. +//! +//! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro +//! defines the `DepKind` enum and a corresponding `DepConstructor` enum. The +//! `DepConstructor` enum links a `DepKind` to the parameters that are needed at +//! runtime in order to construct a valid `DepNode` fingerprint. +//! +//! Because the macro sees what parameters a given `DepKind` requires, it can +//! "infer" some properties for each kind of `DepNode`: +//! +//! * Whether a `DepNode` of a given kind has any parameters at all. Some +//! `DepNode`s could represent global concepts with only one value. +//! * Whether it is possible, in principle, to reconstruct a query key from a +//! given `DepNode`. Many `DepKind`s only require a single `DefId` parameter, +//! in which case it is possible to map the node's fingerprint back to the +//! `DefId` it was computed from. In other cases, too much information gets +//! lost during fingerprint computation. +//! +//! The `DepConstructor` enum, together with `DepNode::new()` ensures that only +//! valid `DepNode` instances can be constructed. For example, the API does not +//! allow for constructing parameterless `DepNode`s with anything other +//! than a zeroed out fingerprint. More generally speaking, it relieves the +//! user of the `DepNode` API of having to know how to compute the expected +//! fingerprint for a given set of node parameters. + +use crate::mir::interpret::{GlobalId, LitToConstInput}; +use crate::traits; +use crate::traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, +}; +use crate::ty::subst::{GenericArg, SubstsRef}; +use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::definitions::DefPathHash; +use rustc_hir::HirId; +use rustc_span::symbol::Symbol; +use std::hash::Hash; + +pub use rustc_query_system::dep_graph::{DepContext, DepNodeParams}; + +// erase!() just makes tokens go away. It's used to specify which macro argument +// is repeated (i.e., which sub-expression of the macro we are in) but don't need +// to actually use any of the arguments. +macro_rules! erase { + ($x:tt) => {{}}; +} + +macro_rules! is_anon_attr { + (anon) => { + true + }; + ($attr:ident) => { + false + }; +} + +macro_rules! is_eval_always_attr { + (eval_always) => { + true + }; + ($attr:ident) => { + false + }; +} + +macro_rules! contains_anon_attr { + ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_anon_attr!($attr) | )* false}); +} + +macro_rules! contains_eval_always_attr { + ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_eval_always_attr!($attr) | )* false}); +} + +macro_rules! define_dep_nodes { + (<$tcx:tt> + $( + [$($attrs:tt)*] + $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* + ,)* + ) => ( + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, + RustcEncodable, RustcDecodable)] + #[allow(non_camel_case_types)] + pub enum DepKind { + $($variant),* + } + + impl DepKind { + #[allow(unreachable_code)] + pub fn can_reconstruct_query_key<$tcx>(&self) -> bool { + match *self { + $( + DepKind :: $variant => { + if contains_anon_attr!($($attrs)*) { + return false; + } + + // tuple args + $({ + return <$tuple_arg_ty as DepNodeParams>> + ::can_reconstruct_query_key(); + })* + + true + } + )* + } + } + + pub fn is_anon(&self) -> bool { + match *self { + $( + DepKind :: $variant => { contains_anon_attr!($($attrs)*) } + )* + } + } + + pub fn is_eval_always(&self) -> bool { + match *self { + $( + DepKind :: $variant => { contains_eval_always_attr!($($attrs)*) } + )* + } + } + + #[allow(unreachable_code)] + pub fn has_params(&self) -> bool { + match *self { + $( + DepKind :: $variant => { + // tuple args + $({ + erase!($tuple_arg_ty); + return true; + })* + + false + } + )* + } + } + } + + pub struct DepConstructor; + + #[allow(non_camel_case_types)] + impl DepConstructor { + $( + #[inline(always)] + #[allow(unreachable_code, non_snake_case)] + pub fn $variant(_tcx: TyCtxt<'_>, $(arg: $tuple_arg_ty)*) -> DepNode { + // tuple args + $({ + erase!($tuple_arg_ty); + return DepNode::construct(_tcx, DepKind::$variant, &arg) + })* + + return DepNode::construct(_tcx, DepKind::$variant, &()) + } + )* + } + + pub type DepNode = rustc_query_system::dep_graph::DepNode; + + pub trait DepNodeExt: Sized { + /// Construct a DepNode from the given DepKind and DefPathHash. This + /// method will assert that the given DepKind actually requires a + /// single DefId/DefPathHash parameter. + fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self; + + /// Extracts the DefId corresponding to this DepNode. This will work + /// if two conditions are met: + /// + /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and + /// 2. the item that the DefPath refers to exists in the current tcx. + /// + /// Condition (1) is determined by the DepKind variant of the + /// DepNode. Condition (2) might not be fulfilled if a DepNode + /// refers to something from the previous compilation session that + /// has been removed. + fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option; + + /// Used in testing + fn from_label_string(label: &str, def_path_hash: DefPathHash) + -> Result; + + /// Used in testing + fn has_label_string(label: &str) -> bool; + } + + impl DepNodeExt for DepNode { + /// Construct a DepNode from the given DepKind and DefPathHash. This + /// method will assert that the given DepKind actually requires a + /// single DefId/DefPathHash parameter. + fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode { + debug_assert!(kind.can_reconstruct_query_key() && kind.has_params()); + DepNode { + kind, + hash: def_path_hash.0, + } + } + + /// Extracts the DefId corresponding to this DepNode. This will work + /// if two conditions are met: + /// + /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and + /// 2. the item that the DefPath refers to exists in the current tcx. + /// + /// Condition (1) is determined by the DepKind variant of the + /// DepNode. Condition (2) might not be fulfilled if a DepNode + /// refers to something from the previous compilation session that + /// has been removed. + fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option { + if self.kind.can_reconstruct_query_key() { + let def_path_hash = DefPathHash(self.hash); + tcx.def_path_hash_to_def_id.as_ref()?.get(&def_path_hash).cloned() + } else { + None + } + } + + /// Used in testing + fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result { + let kind = match label { + $( + stringify!($variant) => DepKind::$variant, + )* + _ => return Err(()), + }; + + if !kind.can_reconstruct_query_key() { + return Err(()); + } + + if kind.has_params() { + Ok(DepNode::from_def_path_hash(def_path_hash, kind)) + } else { + Ok(DepNode::new_no_params(kind)) + } + } + + /// Used in testing + fn has_label_string(label: &str) -> bool { + match label { + $( + stringify!($variant) => true, + )* + _ => false, + } + } + } + + /// Contains variant => str representations for constructing + /// DepNode groups for tests. + #[allow(dead_code, non_upper_case_globals)] + pub mod label_strs { + $( + pub const $variant: &str = stringify!($variant); + )* + } + ); +} + +rustc_dep_node_append!([define_dep_nodes!][ <'tcx> + // We use this for most things when incr. comp. is turned off. + [] Null, + + // Represents metadata from an extern crate. + [eval_always] CrateMetadata(CrateNum), + + [anon] TraitSelect, + + [] CompileCodegenUnit(Symbol), +]); + +impl<'tcx> DepNodeParams> for DefId { + #[inline] + fn can_reconstruct_query_key() -> bool { + true + } + + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + tcx.def_path_hash(*self).0 + } + + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + tcx.def_path_str(*self) + } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + dep_node.extract_def_id(tcx) + } +} + +impl<'tcx> DepNodeParams> for LocalDefId { + #[inline] + fn can_reconstruct_query_key() -> bool { + true + } + + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + self.to_def_id().to_fingerprint(tcx) + } + + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + self.to_def_id().to_debug_str(tcx) + } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + dep_node.extract_def_id(tcx).map(|id| id.expect_local()) + } +} + +impl<'tcx> DepNodeParams> for CrateNum { + #[inline] + fn can_reconstruct_query_key() -> bool { + true + } + + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + let def_id = DefId { krate: *self, index: CRATE_DEF_INDEX }; + tcx.def_path_hash(def_id).0 + } + + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + tcx.crate_name(*self).to_string() + } + + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + dep_node.extract_def_id(tcx).map(|id| id.krate) + } +} + +impl<'tcx> DepNodeParams> for (DefId, DefId) { + #[inline] + fn can_reconstruct_query_key() -> bool { + false + } + + // We actually would not need to specialize the implementation of this + // method but it's faster to combine the hashes than to instantiate a full + // hashing context and stable-hashing state. + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + let (def_id_0, def_id_1) = *self; + + let def_path_hash_0 = tcx.def_path_hash(def_id_0); + let def_path_hash_1 = tcx.def_path_hash(def_id_1); + + def_path_hash_0.0.combine(def_path_hash_1.0) + } + + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + let (def_id_0, def_id_1) = *self; + + format!("({}, {})", tcx.def_path_debug_str(def_id_0), tcx.def_path_debug_str(def_id_1)) + } +} + +impl<'tcx> DepNodeParams> for HirId { + #[inline] + fn can_reconstruct_query_key() -> bool { + false + } + + // We actually would not need to specialize the implementation of this + // method but it's faster to combine the hashes than to instantiate a full + // hashing context and stable-hashing state. + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + let HirId { owner, local_id } = *self; + + let def_path_hash = tcx.def_path_hash(owner.to_def_id()); + let local_id = Fingerprint::from_smaller_hash(local_id.as_u32().into()); + + def_path_hash.0.combine(local_id) + } +} diff --git a/src/librustc_middle/dep_graph/mod.rs b/src/librustc_middle/dep_graph/mod.rs 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 80% rename from src/librustc/hir/exports.rs rename to src/librustc_middle/hir/exports.rs index 4c144a54d6345..af48c9e94ff82 100644 --- a/src/librustc/hir/exports.rs +++ b/src/librustc_middle/hir/exports.rs @@ -1,21 +1,22 @@ use crate::ty; -use rustc_ast::ast; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; -use rustc_hir::def_id::DefIdMap; +use rustc_hir::def_id::LocalDefId; use rustc_macros::HashStable; +use rustc_span::symbol::Ident; use rustc_span::Span; use std::fmt::Debug; /// This is the replacement export map. It maps a module to all of the exports /// within. -pub type ExportMap = DefIdMap>>; +pub type ExportMap = FxHashMap>>; #[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 89% rename from src/librustc/hir/map/collector.rs rename to src/librustc_middle/hir/map/collector.rs index e8233c0446d1f..dce06a5f7eeec 100644 --- a/src/librustc/hir/map/collector.rs +++ b/src/librustc_middle/hir/map/collector.rs @@ -1,7 +1,6 @@ use crate::arena::Arena; -use crate::hir::map::definitions::{self, DefPathHash}; use crate::hir::map::{Entry, HirOwnerData, Map}; -use crate::hir::{HirItem, HirOwner, HirOwnerItems}; +use crate::hir::{Owner, OwnerNodes, ParentedNode}; use crate::ich::StableHashingContext; use crate::middle::cstore::CrateStore; use rustc_data_structures::fingerprint::Fingerprint; @@ -10,7 +9,8 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::svh::Svh; use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_INDEX; -use rustc_hir::def_id::{CrateNum, DefIndex, LOCAL_CRATE}; +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}; @@ -30,12 +30,12 @@ pub(super) struct NodeCollector<'a, 'hir> { /// Source map source_map: &'a SourceMap, - map: IndexVec>, + map: IndexVec>, /// The parent of this node parent_node: hir::HirId, - current_dep_node_owner: DefIndex, + current_dep_node_owner: LocalDefId, definitions: &'a definitions::Definitions, @@ -98,7 +98,8 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { definitions: &'a definitions::Definitions, mut hcx: StableHashingContext<'a>, ) -> NodeCollector<'a, 'hir> { - let root_mod_def_path_hash = definitions.def_path_hash(CRATE_DEF_INDEX); + let root_mod_def_path_hash = + definitions.def_path_hash(LocalDefId { local_def_index: CRATE_DEF_INDEX }); let mut hir_body_nodes = Vec::new(); @@ -116,6 +117,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { body_ids: _, modules: _, proc_macros: _, + trait_map: _, } = *krate; hash_body(&mut hcx, root_mod_def_path_hash, item, &mut hir_body_nodes) @@ -126,7 +128,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { krate, source_map: sess.source_map(), parent_node: hir::CRATE_HIR_ID, - current_dep_node_owner: CRATE_DEF_INDEX, + current_dep_node_owner: LocalDefId { local_def_index: CRATE_DEF_INDEX }, definitions, hcx, hir_body_nodes, @@ -148,7 +150,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { crate_disambiguator: CrateDisambiguator, cstore: &dyn CrateStore, commandline_args_hash: u64, - ) -> (IndexVec>, Svh) { + ) -> (IndexVec>, Svh) { // Insert bodies into the map for (id, body) in self.krate.bodies.iter() { let bodies = &mut self.map[id.hir_id.owner].with_bodies.as_mut().unwrap().bodies; @@ -175,7 +177,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { .source_map .files() .iter() - .filter(|source_file| CrateNum::from_u32(source_file.crate_of_origin) == LOCAL_CRATE) + .filter(|source_file| source_file.cnum == LOCAL_CRATE) .map(|source_file| source_file.name_hash) .collect(); @@ -202,30 +204,30 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { let data = &mut self.map[id.owner]; if data.with_bodies.is_none() { - data.with_bodies = Some(arena.alloc(HirOwnerItems { + data.with_bodies = Some(arena.alloc(OwnerNodes { hash, - items: IndexVec::new(), + nodes: IndexVec::new(), bodies: FxHashMap::default(), })); } - let items = data.with_bodies.as_mut().unwrap(); + let nodes = data.with_bodies.as_mut().unwrap(); if i == 0 { // Overwrite the dummy hash with the real HIR owner hash. - items.hash = hash; + nodes.hash = hash; // FIXME: feature(impl_trait_in_bindings) broken and trigger this assert //assert!(data.signature.is_none()); data.signature = - Some(self.arena.alloc(HirOwner { parent: entry.parent, node: entry.node })); + Some(self.arena.alloc(Owner { parent: entry.parent, node: entry.node })); } else { assert_eq!(entry.parent.owner, id.owner); insert_vec_map( - &mut items.items, + &mut nodes.nodes, id.local_id, - HirItem { parent: entry.parent.local_id, node: entry.node }, + ParentedNode { parent: entry.parent.local_id, node: entry.node }, ); } } @@ -240,32 +242,22 @@ 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); - if hir_id.owner != self.current_dep_node_owner { - let node_str = match self.definitions.opt_def_index(node_id) { - Some(def_index) => self.definitions.def_path(def_index).to_string_no_crate(), + let node_str = match self.definitions.opt_hir_id_to_local_def_id(hir_id) { + Some(def_id) => self.definitions.def_path(def_id).to_string_no_crate(), 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, ) } } @@ -285,7 +277,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { F: FnOnce(&mut Self, Fingerprint), >( &mut self, - dep_node_owner: DefIndex, + dep_node_owner: LocalDefId, item_like: &T, f: F, ) { @@ -341,7 +333,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { debug!("visit_item: {:?}", i); debug_assert_eq!( i.hir_id.owner, - self.definitions.opt_def_index(self.definitions.hir_to_node_id(i.hir_id)).unwrap() + self.definitions.opt_hir_id_to_local_def_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); @@ -373,7 +365,7 @@ 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_def_index(self.definitions.hir_to_node_id(ti.hir_id)).unwrap() + self.definitions.opt_hir_id_to_local_def_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); @@ -387,7 +379,7 @@ 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_def_index(self.definitions.hir_to_node_id(ii.hir_id)).unwrap() + self.definitions.opt_hir_id_to_local_def_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); @@ -506,10 +498,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_macro_def(&mut self, macro_def: &'hir MacroDef<'hir>) { - let node_id = self.definitions.hir_to_node_id(macro_def.hir_id); - let def_index = self.definitions.opt_def_index(node_id).unwrap(); - - self.with_dep_node_owner(def_index, macro_def, |this, hash| { + self.with_dep_node_owner(macro_def.hir_id.owner, macro_def, |this, hash| { this.insert_with_hash( macro_def.span, macro_def.hir_id, diff --git a/src/librustc_middle/hir/map/mod.rs b/src/librustc_middle/hir/map/mod.rs new file mode 100644 index 0000000000000..3a4fc581f5f26 --- /dev/null +++ b/src/librustc_middle/hir/map/mod.rs @@ -0,0 +1,1065 @@ +use self::collector::NodeCollector; + +use crate::hir::{Owner, OwnerNodes}; +use crate::ty::query::Providers; +use crate::ty::TyCtxt; +use rustc_ast::ast::{self}; +use rustc_data_structures::svh::Svh; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::{DefKey, DefPath, Definitions}; +use rustc_hir::intravisit; +use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::*; +use rustc_index::vec::IndexVec; +use rustc_span::hygiene::MacroKind; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::{kw, Symbol}; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; + +pub mod blocks; +mod collector; + +/// Represents an entry and its parent `HirId`. +#[derive(Copy, Clone, Debug)] +pub struct Entry<'hir> { + parent: HirId, + node: Node<'hir>, +} + +impl<'hir> Entry<'hir> { + fn parent_node(self) -> Option { + match self.node { + Node::Crate(_) | Node::MacroDef(_) => None, + _ => Some(self.parent), + } + } +} + +fn fn_decl<'hir>(node: Node<'hir>) -> Option<&'hir FnDecl<'hir>> { + match node { + Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) + | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(&sig.decl), + Node::Expr(Expr { kind: ExprKind::Closure(_, fn_decl, ..), .. }) => Some(fn_decl), + _ => None, + } +} + +fn fn_sig<'hir>(node: Node<'hir>) -> Option<&'hir FnSig<'hir>> { + match &node { + Node::Item(Item { kind: ItemKind::Fn(sig, _, _), .. }) + | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, _), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(sig, _), .. }) => Some(sig), + _ => None, + } +} + +pub fn associated_body<'hir>(node: Node<'hir>) -> Option { + match node { + Node::Item(Item { + kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body), + .. + }) + | Node::TraitItem(TraitItem { + kind: + TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)), + .. + }) + | Node::ImplItem(ImplItem { + kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body), + .. + }) + | Node::Expr(Expr { kind: ExprKind::Closure(.., body, _, _), .. }) => Some(*body), + + Node::AnonConst(constant) => Some(constant.body), + + _ => None, + } +} + +fn is_body_owner<'hir>(node: Node<'hir>, hir_id: HirId) -> bool { + match associated_body(node) { + Some(b) => b.hir_id == hir_id, + None => false, + } +} + +pub(super) struct HirOwnerData<'hir> { + pub(super) signature: Option<&'hir Owner<'hir>>, + pub(super) with_bodies: Option<&'hir mut OwnerNodes<'hir>>, +} + +pub struct IndexedHir<'hir> { + /// The SVH of the local crate. + pub crate_hash: Svh, + + pub(super) map: IndexVec>, +} + +#[derive(Copy, Clone)] +pub struct Map<'hir> { + pub(super) tcx: TyCtxt<'hir>, +} + +/// An iterator that walks up the ancestor tree of a given `HirId`. +/// Constructed using `tcx.hir().parent_iter(hir_id)`. +pub struct ParentHirIterator<'map, 'hir> { + current_id: HirId, + map: &'map Map<'hir>, +} + +impl<'hir> Iterator for ParentHirIterator<'_, 'hir> { + type Item = (HirId, Node<'hir>); + + fn next(&mut self) -> Option { + if self.current_id == CRATE_HIR_ID { + return None; + } + loop { + // There are nodes that do not have entries, so we need to skip them. + let parent_id = self.map.get_parent_node(self.current_id); + + if parent_id == self.current_id { + self.current_id = CRATE_HIR_ID; + return None; + } + + self.current_id = parent_id; + if let Some(entry) = self.map.find_entry(parent_id) { + return Some((parent_id, entry.node)); + } + // If this `HirId` doesn't have an `Entry`, skip it and look for its `parent_id`. + } + } +} + +impl<'hir> Map<'hir> { + pub fn krate(&self) -> &'hir Crate<'hir> { + self.tcx.hir_crate(LOCAL_CRATE) + } + + #[inline] + pub fn definitions(&self) -> &'hir Definitions { + &self.tcx.definitions + } + + pub fn def_key(&self, def_id: LocalDefId) -> DefKey { + self.tcx.definitions.def_key(def_id) + } + + pub fn def_path_from_hir_id(&self, id: HirId) -> Option { + self.opt_local_def_id(id).map(|def_id| self.def_path(def_id)) + } + + pub fn def_path(&self, def_id: LocalDefId) -> DefPath { + self.tcx.definitions.def_path(def_id) + } + + #[inline] + pub fn local_def_id(&self, hir_id: HirId) -> LocalDefId { + self.opt_local_def_id(hir_id).unwrap_or_else(|| { + bug!( + "local_def_id: no entry for `{:?}`, which has a map of `{:?}`", + hir_id, + self.find_entry(hir_id) + ) + }) + } + + #[inline] + pub fn opt_local_def_id(&self, hir_id: HirId) -> Option { + self.tcx.definitions.opt_hir_id_to_local_def_id(hir_id) + } + + #[inline] + pub fn as_local_hir_id(&self, def_id: LocalDefId) -> HirId { + self.tcx.definitions.as_local_hir_id(def_id) + } + + #[inline] + pub fn local_def_id_to_hir_id(&self, def_id: LocalDefId) -> HirId { + self.tcx.definitions.local_def_id_to_hir_id(def_id) + } + + #[inline] + pub fn opt_local_def_id_to_hir_id(&self, def_id: LocalDefId) -> Option { + self.tcx.definitions.opt_local_def_id_to_hir_id(def_id) + } + + pub fn def_kind(&self, local_def_id: LocalDefId) -> DefKind { + // FIXME(eddyb) support `find` on the crate root. + if local_def_id.to_def_id().index == CRATE_DEF_INDEX { + return DefKind::Mod; + } + + let hir_id = self.local_def_id_to_hir_id(local_def_id); + match self.get(hir_id) { + Node::Item(item) => match item.kind { + ItemKind::Static(..) => DefKind::Static, + ItemKind::Const(..) => DefKind::Const, + ItemKind::Fn(..) => DefKind::Fn, + ItemKind::Mod(..) => DefKind::Mod, + ItemKind::OpaqueTy(..) => DefKind::OpaqueTy, + ItemKind::TyAlias(..) => DefKind::TyAlias, + ItemKind::Enum(..) => DefKind::Enum, + ItemKind::Struct(..) => DefKind::Struct, + ItemKind::Union(..) => DefKind::Union, + ItemKind::Trait(..) => DefKind::Trait, + ItemKind::TraitAlias(..) => DefKind::TraitAlias, + ItemKind::ExternCrate(_) => DefKind::ExternCrate, + ItemKind::Use(..) => DefKind::Use, + ItemKind::ForeignMod(..) => DefKind::ForeignMod, + ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, + ItemKind::Impl { .. } => DefKind::Impl, + }, + Node::ForeignItem(item) => match item.kind { + ForeignItemKind::Fn(..) => DefKind::Fn, + ForeignItemKind::Static(..) => DefKind::Static, + ForeignItemKind::Type => DefKind::ForeignTy, + }, + Node::TraitItem(item) => match item.kind { + TraitItemKind::Const(..) => DefKind::AssocConst, + TraitItemKind::Fn(..) => DefKind::AssocFn, + TraitItemKind::Type(..) => DefKind::AssocTy, + }, + Node::ImplItem(item) => match item.kind { + ImplItemKind::Const(..) => DefKind::AssocConst, + ImplItemKind::Fn(..) => DefKind::AssocFn, + ImplItemKind::TyAlias(..) => DefKind::AssocTy, + }, + Node::Variant(_) => DefKind::Variant, + Node::Ctor(variant_data) => { + // FIXME(eddyb) is this even possible, if we have a `Node::Ctor`? + assert_ne!(variant_data.ctor_hir_id(), None); + + let ctor_of = match self.find(self.get_parent_node(hir_id)) { + Some(Node::Item(..)) => def::CtorOf::Struct, + Some(Node::Variant(..)) => def::CtorOf::Variant, + _ => unreachable!(), + }; + DefKind::Ctor(ctor_of, def::CtorKind::from_hir(variant_data)) + } + Node::AnonConst(_) => DefKind::AnonConst, + Node::Field(_) => DefKind::Field, + Node::Expr(expr) => match expr.kind { + ExprKind::Closure(.., None) => DefKind::Closure, + ExprKind::Closure(.., Some(_)) => DefKind::Generator, + _ => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), + }, + Node::MacroDef(_) => DefKind::Macro(MacroKind::Bang), + Node::GenericParam(param) => match param.kind { + GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam, + GenericParamKind::Type { .. } => DefKind::TyParam, + GenericParamKind::Const { .. } => DefKind::ConstParam, + }, + Node::Stmt(_) + | Node::PathSegment(_) + | Node::Ty(_) + | Node::TraitRef(_) + | Node::Pat(_) + | Node::Binding(_) + | Node::Local(_) + | Node::Param(_) + | Node::Arm(_) + | Node::Lifetime(_) + | Node::Visibility(_) + | Node::Block(_) + | Node::Crate(_) => bug!("def_kind: unsupported node: {}", self.node_to_string(hir_id)), + } + } + + fn find_entry(&self, id: HirId) -> Option> { + if id.local_id == ItemLocalId::from_u32(0) { + let owner = self.tcx.hir_owner(id.owner); + owner.map(|owner| Entry { parent: owner.parent, node: owner.node }) + } else { + let owner = self.tcx.hir_owner_nodes(id.owner); + owner.and_then(|owner| { + let node = owner.nodes[id.local_id].as_ref(); + // FIXME(eddyb) use a single generic type insted of having both + // `Entry` and `ParentedNode`, which are effectively the same. + // Alternatively, rewrite code using `Entry` to use `ParentedNode`. + node.map(|node| Entry { + parent: HirId { owner: id.owner, local_id: node.parent }, + node: node.node, + }) + }) + } + } + + fn get_entry(&self, id: HirId) -> Entry<'hir> { + self.find_entry(id).unwrap() + } + + pub fn item(&self, id: HirId) -> &'hir Item<'hir> { + match self.find(id).unwrap() { + Node::Item(item) => item, + _ => bug!(), + } + } + + pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { + match self.find(id.hir_id).unwrap() { + Node::TraitItem(item) => item, + _ => bug!(), + } + } + + pub fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { + match self.find(id.hir_id).unwrap() { + Node::ImplItem(item) => item, + _ => bug!(), + } + } + + pub fn body(&self, id: BodyId) -> &'hir Body<'hir> { + self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies.get(&id.hir_id.local_id).unwrap() + } + + pub fn fn_decl_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> { + if let Some(node) = self.find(hir_id) { + fn_decl(node) + } else { + bug!("no node for hir_id `{}`", hir_id) + } + } + + pub fn fn_sig_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> { + if let Some(node) = self.find(hir_id) { + fn_sig(node) + } else { + bug!("no node for hir_id `{}`", hir_id) + } + } + + pub fn enclosing_body_owner(&self, hir_id: HirId) -> HirId { + for (parent, _) in self.parent_iter(hir_id) { + if let Some(body) = self.maybe_body_owned_by(parent) { + return self.body_owner(body); + } + } + + bug!("no `enclosing_body_owner` for hir_id `{}`", hir_id); + } + + /// Returns the `HirId` that corresponds to the definition of + /// which this is the body of, i.e., a `fn`, `const` or `static` + /// item (possibly associated), a closure, or a `hir::AnonConst`. + pub fn body_owner(&self, BodyId { hir_id }: BodyId) -> HirId { + let parent = self.get_parent_node(hir_id); + assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id))); + parent + } + + pub fn body_owner_def_id(&self, id: BodyId) -> LocalDefId { + self.local_def_id(self.body_owner(id)) + } + + /// Given a `HirId`, returns the `BodyId` associated with it, + /// if the node is a body owner, otherwise returns `None`. + pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option { + self.find(hir_id).map(associated_body).flatten() + } + + /// Given a body owner's id, returns the `BodyId` associated with it. + pub fn body_owned_by(&self, id: HirId) -> BodyId { + self.maybe_body_owned_by(id).unwrap_or_else(|| { + span_bug!( + self.span(id), + "body_owned_by: {} has no associated body", + self.node_to_string(id) + ); + }) + } + + /// 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_inside_const_context(&self, hir_id: HirId) -> bool { + self.body_const_context(self.local_def_id(self.enclosing_body_owner(hir_id))).is_some() + } + + /// Whether `hir_id` corresponds to a `mod` or a crate. + pub fn is_hir_id_module(&self, hir_id: HirId) -> bool { + match self.get_entry(hir_id).node { + Node::Item(Item { kind: ItemKind::Mod(_), .. }) | Node::Crate(..) => true, + _ => false, + } + } + + /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a + /// `while` or `loop` before reaching it, as block tail returns are not + /// available in them. + /// + /// ``` + /// fn foo(x: usize) -> bool { + /// if x == 1 { + /// true // If `get_return_block` gets passed the `id` corresponding + /// } else { // to this, it will return `foo`'s `HirId`. + /// false + /// } + /// } + /// ``` + /// + /// ``` + /// fn foo(x: usize) -> bool { + /// loop { + /// true // If `get_return_block` gets passed the `id` corresponding + /// } // to this, it will return `None`. + /// false + /// } + /// ``` + pub fn get_return_block(&self, id: HirId) -> Option { + let mut iter = self.parent_iter(id).peekable(); + let mut ignore_tail = false; + if let Some(entry) = self.find_entry(id) { + if let Node::Expr(Expr { kind: ExprKind::Ret(_), .. }) = entry.node { + // When dealing with `return` statements, we don't care about climbing only tail + // expressions. + ignore_tail = true; + } + } + while let Some((hir_id, node)) = iter.next() { + if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) { + match next_node { + Node::Block(Block { expr: None, .. }) => return None, + // The current node is not the tail expression of its parent. + Node::Block(Block { expr: Some(e), .. }) if hir_id != e.hir_id => return None, + _ => {} + } + } + match node { + Node::Item(_) + | Node::ForeignItem(_) + | Node::TraitItem(_) + | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }) + | Node::ImplItem(_) => return Some(hir_id), + // Ignore `return`s on the first iteration + Node::Expr(Expr { kind: ExprKind::Loop(..) | ExprKind::Ret(..), .. }) + | Node::Local(_) => { + return None; + } + _ => {} + } + } + None + } + + /// Retrieves the `HirId` for `id`'s parent item, or `id` itself if no + /// parent item is in this map. The "parent item" is the closest parent node + /// in the HIR which is recorded by the map and is an item, either an item + /// in a module, trait, or impl. + pub fn get_parent_item(&self, hir_id: HirId) -> HirId { + for (hir_id, node) in self.parent_iter(hir_id) { + match node { + Node::Crate(_) + | Node::Item(_) + | Node::ForeignItem(_) + | Node::TraitItem(_) + | Node::ImplItem(_) => return hir_id, + _ => {} + } + } + hir_id + } + + /// Returns the `HirId` of `id`'s nearest module parent, or `id` itself if no + /// module parent is in this map. + pub(super) fn get_module_parent_node(&self, hir_id: HirId) -> HirId { + for (hir_id, node) in self.parent_iter(hir_id) { + if let Node::Item(&Item { kind: ItemKind::Mod(_), .. }) = node { + return hir_id; + } + } + CRATE_HIR_ID + } + + /// When on a match arm tail expression or on a match arm, give back the enclosing `match` + /// expression. + /// + /// Used by error reporting when there's a type error in a match arm caused by the `match` + /// expression needing to be unit. + pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&'hir Expr<'hir>> { + for (_, node) in self.parent_iter(hir_id) { + match node { + Node::Item(_) + | Node::ForeignItem(_) + | Node::TraitItem(_) + | Node::ImplItem(_) + | Node::Stmt(Stmt { kind: StmtKind::Local(_), .. }) => break, + Node::Expr(expr @ Expr { kind: ExprKind::Match(..), .. }) => return Some(expr), + _ => {} + } + } + None + } + + /// Returns the nearest enclosing scope. A scope is roughly an item or block. + pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option { + for (hir_id, node) in self.parent_iter(hir_id) { + if let Node::Item(Item { + kind: + ItemKind::Fn(..) + | ItemKind::Const(..) + | ItemKind::Static(..) + | ItemKind::Mod(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::Union(..) + | ItemKind::Trait(..) + | ItemKind::Impl { .. }, + .. + }) + | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(..), .. }) + | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(..), .. }) + | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(..), .. }) + | Node::Block(_) = node + { + return Some(hir_id); + } + } + None + } + + /// Returns the defining scope for an opaque type definition. + pub fn get_defining_scope(&self, id: HirId) -> HirId { + let mut scope = id; + loop { + scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID); + if scope == CRATE_HIR_ID { + return CRATE_HIR_ID; + } + match self.get(scope) { + Node::Block(_) => {} + _ => break, + } + } + scope + } + + pub fn get_parent_did(&self, id: HirId) -> LocalDefId { + self.local_def_id(self.get_parent_item(id)) + } + + pub fn get_foreign_abi(&self, hir_id: HirId) -> Abi { + let parent = self.get_parent_item(hir_id); + if let Some(entry) = self.find_entry(parent) { + if let Entry { + node: Node::Item(Item { kind: ItemKind::ForeignMod(ref nm), .. }), .. + } = entry + { + return nm.abi; + } + } + bug!("expected foreign mod or inlined parent, found {}", self.node_to_string(parent)) + } + + pub fn expect_item(&self, id: HirId) -> &'hir Item<'hir> { + match self.find(id) { + Some(Node::Item(item)) => item, + _ => bug!("expected item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_impl_item(&self, id: HirId) -> &'hir ImplItem<'hir> { + match self.find(id) { + Some(Node::ImplItem(item)) => item, + _ => bug!("expected impl item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_trait_item(&self, id: HirId) -> &'hir TraitItem<'hir> { + match self.find(id) { + Some(Node::TraitItem(item)) => item, + _ => bug!("expected trait item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_variant_data(&self, id: HirId) -> &'hir VariantData<'hir> { + match self.find(id) { + Some( + Node::Ctor(vd) + | Node::Item(Item { kind: ItemKind::Struct(vd, _) | ItemKind::Union(vd, _), .. }), + ) => vd, + Some(Node::Variant(variant)) => &variant.data, + _ => bug!("expected struct or variant, found {}", self.node_to_string(id)), + } + } + + pub fn expect_variant(&self, id: HirId) -> &'hir Variant<'hir> { + match self.find(id) { + Some(Node::Variant(variant)) => variant, + _ => bug!("expected variant, found {}", self.node_to_string(id)), + } + } + + pub fn expect_foreign_item(&self, id: HirId) -> &'hir ForeignItem<'hir> { + match self.find(id) { + Some(Node::ForeignItem(item)) => item, + _ => bug!("expected foreign item, found {}", self.node_to_string(id)), + } + } + + pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> { + match self.find(id) { + Some(Node::Expr(expr)) => expr, + _ => bug!("expected expr, found {}", self.node_to_string(id)), + } + } + + pub fn opt_name(&self, id: HirId) -> Option { + Some(match self.get(id) { + Node::Item(i) => i.ident.name, + Node::ForeignItem(fi) => fi.ident.name, + Node::ImplItem(ii) => ii.ident.name, + Node::TraitItem(ti) => ti.ident.name, + Node::Variant(v) => v.ident.name, + Node::Field(f) => f.ident.name, + Node::Lifetime(lt) => lt.name.ident().name, + Node::GenericParam(param) => param.name.ident().name, + Node::Binding(&Pat { kind: PatKind::Binding(_, _, l, _), .. }) => l.name, + Node::Ctor(..) => self.name(self.get_parent_item(id)), + _ => return None, + }) + } + + pub fn name(&self, id: HirId) -> Symbol { + match self.opt_name(id) { + Some(name) => name, + None => bug!("no name for {}", self.node_to_string(id)), + } + } + + /// Given a node ID, gets a list of attributes associated with the AST + /// corresponding to the node-ID. + pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] { + let attrs = match self.find_entry(id).map(|entry| entry.node) { + Some(Node::Param(a)) => Some(&a.attrs[..]), + Some(Node::Local(l)) => Some(&l.attrs[..]), + Some(Node::Item(i)) => Some(&i.attrs[..]), + Some(Node::ForeignItem(fi)) => Some(&fi.attrs[..]), + Some(Node::TraitItem(ref ti)) => Some(&ti.attrs[..]), + Some(Node::ImplItem(ref ii)) => Some(&ii.attrs[..]), + Some(Node::Variant(ref v)) => Some(&v.attrs[..]), + Some(Node::Field(ref f)) => Some(&f.attrs[..]), + Some(Node::Expr(ref e)) => Some(&*e.attrs), + Some(Node::Stmt(ref s)) => Some(s.kind.attrs()), + Some(Node::Arm(ref a)) => Some(&*a.attrs), + Some(Node::GenericParam(param)) => Some(¶m.attrs[..]), + // Unit/tuple structs/variants take the attributes straight from + // the struct/variant definition. + Some(Node::Ctor(..)) => return self.attrs(self.get_parent_item(id)), + Some(Node::Crate(item)) => Some(&item.attrs[..]), + _ => None, + }; + attrs.unwrap_or(&[]) + } + + pub fn span(&self, hir_id: HirId) -> Span { + match self.find_entry(hir_id).map(|entry| entry.node) { + Some(Node::Param(param)) => param.span, + Some(Node::Item(item)) => item.span, + Some(Node::ForeignItem(foreign_item)) => foreign_item.span, + Some(Node::TraitItem(trait_method)) => trait_method.span, + Some(Node::ImplItem(impl_item)) => impl_item.span, + Some(Node::Variant(variant)) => variant.span, + Some(Node::Field(field)) => field.span, + Some(Node::AnonConst(constant)) => self.body(constant.body).value.span, + Some(Node::Expr(expr)) => expr.span, + Some(Node::Stmt(stmt)) => stmt.span, + Some(Node::PathSegment(seg)) => seg.ident.span, + Some(Node::Ty(ty)) => ty.span, + Some(Node::TraitRef(tr)) => tr.path.span, + Some(Node::Binding(pat)) => pat.span, + Some(Node::Pat(pat)) => pat.span, + Some(Node::Arm(arm)) => arm.span, + Some(Node::Block(block)) => block.span, + Some(Node::Ctor(..)) => match self.find(self.get_parent_node(hir_id)) { + Some(Node::Item(item)) => item.span, + Some(Node::Variant(variant)) => variant.span, + _ => unreachable!(), + }, + Some(Node::Lifetime(lifetime)) => lifetime.span, + Some(Node::GenericParam(param)) => param.span, + Some(Node::Visibility(&Spanned { + node: VisibilityKind::Restricted { ref path, .. }, + .. + })) => path.span, + Some(Node::Visibility(v)) => bug!("unexpected Visibility {:?}", v), + Some(Node::Local(local)) => local.span, + Some(Node::MacroDef(macro_def)) => macro_def.span, + Some(Node::Crate(item)) => item.span, + None => bug!("hir::map::Map::span: id not in map: {:?}", hir_id), + } + } + + pub fn span_if_local(&self, id: DefId) -> Option { + id.as_local().map(|id| self.span(self.as_local_hir_id(id))) + } + + pub fn res_span(&self, res: Res) -> Option { + match res { + Res::Err => None, + Res::Local(id) => Some(self.span(id)), + res => self.span_if_local(res.opt_def_id()?), + } + } + + /// Get a representation of this `id` for debugging purposes. + /// NOTE: Do NOT use this in diagnostics! + pub fn node_to_string(&self, id: HirId) -> String { + hir_id_to_string(self, id) + } +} + +impl<'hir> intravisit::Map<'hir> for Map<'hir> { + fn find(&self, hir_id: HirId) -> Option> { + self.find(hir_id) + } + + fn body(&self, id: BodyId) -> &'hir Body<'hir> { + self.body(id) + } + + fn item(&self, id: HirId) -> &'hir Item<'hir> { + self.item(id) + } + + fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> { + self.trait_item(id) + } + + fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> { + self.impl_item(id) + } +} + +trait Named { + fn name(&self) -> Symbol; +} + +impl Named for Spanned { + fn name(&self) -> Symbol { + self.node.name() + } +} + +impl Named for Item<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for ForeignItem<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for Variant<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for StructField<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for TraitItem<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} +impl Named for ImplItem<'_> { + fn name(&self) -> Symbol { + self.ident.name + } +} + +pub(super) fn index_hir<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> &'tcx IndexedHir<'tcx> { + assert_eq!(cnum, LOCAL_CRATE); + + let _prof_timer = tcx.sess.prof.generic_activity("build_hir_map"); + + let (map, crate_hash) = { + let hcx = tcx.create_stable_hashing_context(); + + let mut collector = + NodeCollector::root(tcx.sess, &**tcx.arena, tcx.untracked_crate, &tcx.definitions, hcx); + intravisit::walk_crate(&mut collector, tcx.untracked_crate); + + let crate_disambiguator = tcx.sess.local_crate_disambiguator(); + let cmdline_args = tcx.sess.opts.dep_tracking_hash(); + collector.finalize_and_compute_crate_hash(crate_disambiguator, &*tcx.cstore, cmdline_args) + }; + + tcx.arena.alloc(IndexedHir { crate_hash, map }) +} + +fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String { + let id_str = format!(" (hir_id={})", id); + + let path_str = || { + // This functionality is used for debugging, try to use `TyCtxt` to get + // the user-friendly path, otherwise fall back to stringifying `DefPath`. + crate::ty::tls::with_opt(|tcx| { + if let Some(tcx) = tcx { + let def_id = map.local_def_id(id); + tcx.def_path_str(def_id.to_def_id()) + } else if let Some(path) = map.def_path_from_hir_id(id) { + path.data + .into_iter() + .map(|elem| elem.data.to_string()) + .collect::>() + .join("::") + } else { + String::from("") + } + }) + }; + + let span_str = || map.tcx.sess.source_map().span_to_snippet(map.span(id)).unwrap_or_default(); + let node_str = |prefix| format!("{} {}{}", prefix, span_str(), id_str); + + match map.find(id) { + Some(Node::Item(item)) => { + let item_str = match item.kind { + ItemKind::ExternCrate(..) => "extern crate", + ItemKind::Use(..) => "use", + ItemKind::Static(..) => "static", + ItemKind::Const(..) => "const", + ItemKind::Fn(..) => "fn", + ItemKind::Mod(..) => "mod", + ItemKind::ForeignMod(..) => "foreign mod", + ItemKind::GlobalAsm(..) => "global asm", + ItemKind::TyAlias(..) => "ty", + ItemKind::OpaqueTy(..) => "opaque type", + ItemKind::Enum(..) => "enum", + ItemKind::Struct(..) => "struct", + ItemKind::Union(..) => "union", + ItemKind::Trait(..) => "trait", + ItemKind::TraitAlias(..) => "trait alias", + ItemKind::Impl { .. } => "impl", + }; + format!("{} {}{}", item_str, path_str(), id_str) + } + Some(Node::ForeignItem(_)) => format!("foreign item {}{}", path_str(), id_str), + Some(Node::ImplItem(ii)) => match ii.kind { + ImplItemKind::Const(..) => { + format!("assoc const {} in {}{}", ii.ident, path_str(), id_str) + } + ImplItemKind::Fn(..) => format!("method {} in {}{}", ii.ident, path_str(), id_str), + ImplItemKind::TyAlias(_) => { + format!("assoc type {} in {}{}", ii.ident, path_str(), id_str) + } + }, + Some(Node::TraitItem(ti)) => { + let kind = match ti.kind { + TraitItemKind::Const(..) => "assoc constant", + TraitItemKind::Fn(..) => "trait method", + TraitItemKind::Type(..) => "assoc type", + }; + + format!("{} {} in {}{}", kind, ti.ident, path_str(), id_str) + } + Some(Node::Variant(ref variant)) => { + format!("variant {} in {}{}", variant.ident, path_str(), id_str) + } + Some(Node::Field(ref field)) => { + format!("field {} in {}{}", field.ident, path_str(), id_str) + } + Some(Node::AnonConst(_)) => node_str("const"), + Some(Node::Expr(_)) => node_str("expr"), + Some(Node::Stmt(_)) => node_str("stmt"), + Some(Node::PathSegment(_)) => node_str("path segment"), + Some(Node::Ty(_)) => node_str("type"), + Some(Node::TraitRef(_)) => node_str("trait ref"), + Some(Node::Binding(_)) => node_str("local"), + Some(Node::Pat(_)) => node_str("pat"), + Some(Node::Param(_)) => node_str("param"), + Some(Node::Arm(_)) => node_str("arm"), + Some(Node::Block(_)) => node_str("block"), + Some(Node::Local(_)) => node_str("local"), + Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str), + Some(Node::Lifetime(_)) => node_str("lifetime"), + Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str), + Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str), + Some(Node::MacroDef(_)) => format!("macro {}{}", path_str(), id_str), + Some(Node::Crate(..)) => String::from("root_crate"), + None => format!("unknown node{}", id_str), + } +} + +pub fn provide(providers: &mut Providers<'_>) { + providers.def_kind = |tcx, def_id| tcx.hir().def_kind(def_id.expect_local()); +} diff --git a/src/librustc_middle/hir/mod.rs b/src/librustc_middle/hir/mod.rs 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 85% rename from src/librustc/ich/hcx.rs rename to src/librustc_middle/ich/hcx.rs index c15d54745a105..f5b0b73c49de1 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc_middle/ich/hcx.rs @@ -1,8 +1,5 @@ -use crate::hir::map::definitions::Definitions; -use crate::hir::map::DefPathHash; -use crate::ich::{self, CachingSourceMapView}; +use crate::ich; use crate::middle::cstore::CrateStore; -use crate::session::Session; use crate::ty::{fast_reject, TyCtxt}; use rustc_ast::ast; @@ -10,10 +7,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; 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, DefIndex}; +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; @@ -68,13 +67,15 @@ impl<'a> StableHashingContext<'a> { /// Don't use it for anything else or you'll run the risk of /// leaking data out of the tracking system. #[inline] - pub fn new( + fn new_with_or_without_spans( sess: &'a Session, krate: &'a hir::Crate<'a>, definitions: &'a Definitions, cstore: &'a dyn CrateStore, + always_ignore_spans: bool, ) -> Self { - let hash_spans_initial = !sess.opts.debugging_opts.incremental_ignore_spans; + let hash_spans_initial = + !always_ignore_spans && !sess.opts.debugging_opts.incremental_ignore_spans; StableHashingContext { sess, @@ -89,6 +90,33 @@ impl<'a> StableHashingContext<'a> { } } + #[inline] + pub fn new( + sess: &'a Session, + krate: &'a hir::Crate<'a>, + definitions: &'a Definitions, + cstore: &'a dyn CrateStore, + ) -> Self { + Self::new_with_or_without_spans( + sess, + krate, + definitions, + cstore, + /*always_ignore_spans=*/ false, + ) + } + + #[inline] + pub fn ignore_spans( + sess: &'a Session, + krate: &'a hir::Crate<'a>, + definitions: &'a Definitions, + cstore: &'a dyn CrateStore, + ) -> Self { + let always_ignore_spans = true; + Self::new_with_or_without_spans(sess, krate, definitions, cstore, always_ignore_spans) + } + #[inline] pub fn sess(&self) -> &'a Session { self.sess @@ -124,21 +152,16 @@ impl<'a> StableHashingContext<'a> { #[inline] pub fn def_path_hash(&self, def_id: DefId) -> DefPathHash { - if def_id.is_local() { - self.definitions.def_path_hash(def_id.index) + if let Some(def_id) = def_id.as_local() { + self.local_def_path_hash(def_id) } else { self.cstore.def_path_hash(def_id) } } #[inline] - pub fn local_def_path_hash(&self, def_index: DefIndex) -> DefPathHash { - self.definitions.def_path_hash(def_index) - } - - #[inline] - pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId { - self.definitions.node_to_hir_id(node_id) + pub fn local_def_path_hash(&self, def_id: LocalDefId) -> DefPathHash { + self.definitions.def_path_hash(def_id) } #[inline] @@ -195,8 +218,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 87% rename from src/librustc/ich/impls_hir.rs rename to src/librustc_middle/ich/impls_hir.rs index 06bfd782b59ce..78b9167ddd967 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, DefIndex, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::definitions::DefPathHash; use smallvec::SmallVec; use std::mem; @@ -116,8 +117,8 @@ impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { } #[inline] - fn local_def_path_hash(&self, def_index: DefIndex) -> DefPathHash { - self.local_def_path_hash(def_index) + fn local_def_path_hash(&self, def_id: LocalDefId) -> DefPathHash { + self.local_def_path_hash(def_id) } } @@ -197,21 +198,6 @@ impl<'a> ToStableHashKey> for hir::BodyId { } } -impl<'a> HashStable> for hir::def_id::DefIndex { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - hcx.local_def_path_hash(*self).hash_stable(hcx, hasher); - } -} - -impl<'a> ToStableHashKey> for hir::def_id::DefIndex { - type KeyType = DefPathHash; - - #[inline] - fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> DefPathHash { - hcx.local_def_path_hash(*self) - } -} - impl<'a> HashStable> for hir::TraitCandidate { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { @@ -224,16 +210,15 @@ impl<'a> HashStable> for hir::TraitCandidate { } impl<'a> ToStableHashKey> for hir::TraitCandidate { - type KeyType = (DefPathHash, SmallVec<[(DefPathHash, hir::ItemLocalId); 1]>); + type KeyType = (DefPathHash, SmallVec<[DefPathHash; 1]>); fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Self::KeyType { let hir::TraitCandidate { def_id, import_ids } = self; - let import_keys = import_ids - .iter() - .map(|hir_id| (hcx.local_def_path_hash(hir_id.owner), hir_id.local_id)) - .collect(); - (hcx.def_path_hash(*def_id), import_keys) + ( + hcx.def_path_hash(*def_id), + import_ids.iter().map(|def_id| hcx.local_def_path_hash(*def_id)).collect(), + ) } } diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc_middle/ich/impls_syntax.rs similarity index 95% rename from src/librustc/ich/impls_syntax.rs rename to src/librustc_middle/ich/impls_syntax.rs index daff8a0f1825e..300aac19e51b0 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc_middle/ich/impls_syntax.rs @@ -5,7 +5,6 @@ use crate::ich::StableHashingContext; use rustc_ast::ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; use rustc_span::SourceFile; use smallvec::SmallVec; @@ -59,10 +58,10 @@ impl<'a> HashStable> for SourceFile { name_hash, name_was_remapped, unmapped_path: _, - crate_of_origin, + cnum, // Do not hash the source as it is not encoded src: _, - src_hash, + ref src_hash, external_src: _, start_pos, end_pos: _, @@ -75,9 +74,6 @@ impl<'a> HashStable> for SourceFile { (name_hash as u64).hash_stable(hcx, hasher); name_was_remapped.hash_stable(hcx, hasher); - DefId { krate: CrateNum::from_u32(crate_of_origin), index: CRATE_DEF_INDEX } - .hash_stable(hcx, hasher); - src_hash.hash_stable(hcx, hasher); // We only hash the relative position within this source_file @@ -101,6 +97,8 @@ impl<'a> HashStable> for SourceFile { for &char_pos in normalized_pos.iter() { stable_normalized_pos(char_pos, start_pos).hash_stable(hcx, hasher); } + + cnum.hash_stable(hcx, hasher); } } diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc_middle/ich/impls_ty.rs similarity index 94% rename from src/librustc/ich/impls_ty.rs rename to src/librustc_middle/ich/impls_ty.rs index 844250f51a099..ef6247881c0be 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; @@ -86,15 +87,9 @@ impl<'a> HashStable> for ty::RegionKind { index.hash_stable(hcx, hasher); name.hash_stable(hcx, hasher); } - ty::ReScope(scope) => { - scope.hash_stable(hcx, hasher); - } ty::ReFree(ref free_region) => { free_region.hash_stable(hcx, hasher); } - ty::ReClosureBound(vid) => { - vid.hash_stable(hcx, hasher); - } ty::ReVar(..) | ty::RePlaceholder(..) => { bug!("StableHasher: unexpected region {:?}", *self) } @@ -138,8 +133,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..82dff60a26dcb --- /dev/null +++ b/src/librustc_middle/lib.rs @@ -0,0 +1,97 @@ +//! 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(extern_types)] +#![feature(nll)] +#![feature(option_expect_none)] +#![feature(or_patterns)] +#![feature(range_is_empty)] +#![feature(min_specialization)] +#![feature(track_caller)] +#![feature(trusted_len)] +#![feature(stmt_expr_attributes)] +#![feature(test)] +#![feature(in_band_lifetimes)] +#![feature(crate_visibility_modifier)] +#![feature(associated_type_bounds)] +#![feature(rustc_attrs)] +#![feature(hash_raw_entry)] +#![feature(int_error_matching)] +#![recursion_limit = "512"] + +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate scoped_tls; +#[macro_use] +extern crate rustc_macros; +#[macro_use] +extern crate rustc_data_structures; +#[macro_use] +extern crate log; +#[macro_use] +extern crate smallvec; + +#[cfg(test)] +mod tests; + +#[macro_use] +mod macros; + +#[macro_use] +pub mod query; + +#[macro_use] +pub mod arena; +pub mod dep_graph; +pub mod hir; +pub mod ich; +pub mod infer; +pub mod lint; +pub mod middle; +pub mod mir; +pub mod traits; +pub mod ty; + +pub mod util { + pub mod bug; + pub mod common; +} + +// Allows macros to refer to this crate as `::rustc_middle` +extern crate self as rustc_middle; diff --git a/src/librustc_middle/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 91% rename from src/librustc/macros.rs rename to src/librustc_middle/macros.rs index 88ddd96eec8f5..a5482b7bdcfeb 100644 --- a/src/librustc/macros.rs +++ b/src/librustc_middle/macros.rs @@ -1,16 +1,20 @@ #[macro_export] macro_rules! bug { - () => ( bug!("impossible case reached") ); - ($($message:tt)*) => ({ - $crate::util::bug::bug_fmt(file!(), line!(), format_args!($($message)*)) - }) + () => ( $crate::bug!("impossible case reached") ); + ($msg:expr) => ({ $crate::util::bug::bug_fmt(::std::format_args!($msg)) }); + ($msg:expr,) => ({ $crate::bug!($msg) }); + ($fmt:expr, $($arg:tt)+) => ({ + $crate::util::bug::bug_fmt(::std::format_args!($fmt, $($arg)+)) + }); } #[macro_export] macro_rules! span_bug { - ($span:expr, $($message:tt)*) => ({ - $crate::util::bug::span_bug_fmt(file!(), line!(), $span, format_args!($($message)*)) - }) + ($span:expr, $msg:expr) => ({ $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg)) }); + ($span:expr, $msg:expr,) => ({ $crate::span_bug!($span, $msg) }); + ($span:expr, $fmt:expr, $($arg:tt)+) => ({ + $crate::util::bug::span_bug_fmt($span, ::std::format_args!($fmt, $($arg)+)) + }); } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc/middle/codegen_fn_attrs.rs b/src/librustc_middle/middle/codegen_fn_attrs.rs similarity index 88% rename from src/librustc/middle/codegen_fn_attrs.rs rename to src/librustc_middle/middle/codegen_fn_attrs.rs index 61b25cc486446..d2749f8529bed 100644 --- a/src/librustc/middle/codegen_fn_attrs.rs +++ b/src/librustc_middle/middle/codegen_fn_attrs.rs @@ -1,5 +1,6 @@ use crate::mir::mono::Linkage; use rustc_attr::{InlineAttr, OptimizeAttr}; +use rustc_session::config::SanitizerSet; use rustc_span::symbol::Symbol; #[derive(Clone, RustcEncodable, RustcDecodable, HashStable)] @@ -30,6 +31,9 @@ pub struct CodegenFnAttrs { /// The `#[link_section = "..."]` attribute, or what executable section this /// should be placed in. pub link_section: Option, + /// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which + /// instrumentation should be disabled inside the annotated function. + pub no_sanitize: SanitizerSet, } bitflags! { @@ -69,14 +73,12 @@ bitflags! { const FFI_RETURNS_TWICE = 1 << 10; /// `#[track_caller]`: allow access to the caller location const TRACK_CALLER = 1 << 11; - /// `#[no_sanitize(address)]`: disables address sanitizer instrumentation - const NO_SANITIZE_ADDRESS = 1 << 12; - /// `#[no_sanitize(memory)]`: disables memory sanitizer instrumentation - const NO_SANITIZE_MEMORY = 1 << 13; - /// `#[no_sanitize(thread)]`: disables thread sanitizer instrumentation - 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 << 12; + /// #[ffi_const]: applies clang's `const` attribute to a foreign function + /// declaration. + const FFI_CONST = 1 << 13; } } @@ -92,6 +94,7 @@ impl CodegenFnAttrs { target_features: vec![], linkage: None, link_section: None, + no_sanitize: SanitizerSet::empty(), } } @@ -114,7 +117,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 93% rename from src/librustc/middle/cstore.rs rename to src/librustc_middle/middle/cstore.rs index 42d56c654627a..97e877df96663 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc_middle/middle/cstore.rs @@ -2,10 +2,6 @@ //! are *mostly* used as a part of that interface, but these should //! probably get a better home if someone can find one. -use crate::hir::map as hir_map; -use crate::hir::map::definitions::{DefKey, DefPathTable}; -use crate::session::search_paths::PathKind; -use crate::session::CrateDisambiguator; use crate::ty::TyCtxt; use rustc_ast::ast; @@ -13,16 +9,18 @@ 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; +use rustc_session::utils::NativeLibKind; +use rustc_session::CrateDisambiguator; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_target::spec::Target; + use std::any::Any; use std::path::{Path, PathBuf}; -pub use self::NativeLibraryKind::*; -pub use rustc_session::utils::NativeLibraryKind; - // lonely orphan structs and enums looking for a better home /// Where a crate came from on the local filesystem. One of these three options @@ -40,18 +38,8 @@ impl CrateSource { } } -#[derive( - RustcEncodable, - RustcDecodable, - Copy, - Clone, - Ord, - PartialOrd, - Eq, - PartialEq, - Debug, - HashStable -)] +#[derive(RustcEncodable, RustcDecodable, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] +#[derive(HashStable)] pub enum DepKind { /// A dependency that is only used for its macros. MacrosOnly, @@ -99,8 +87,8 @@ pub enum LinkagePreference { } #[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] -pub struct NativeLibrary { - pub kind: NativeLibraryKind, +pub struct NativeLib { + pub kind: NativeLibKind, pub name: Option, pub cfg: Option, pub foreign_module: Option, @@ -197,8 +185,8 @@ pub trait CrateStore { // resolve fn def_key(&self, def: DefId) -> DefKey; - fn def_path(&self, def: DefId) -> hir_map::DefPath; - fn def_path_hash(&self, def: DefId) -> hir_map::DefPathHash; + fn def_path(&self, def: DefId) -> DefPath; + fn def_path_hash(&self, def: DefId) -> DefPathHash; fn def_path_table(&self, cnum: CrateNum) -> &DefPathTable; // "queries" used in resolve that aren't tracked for incremental compilation 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 6ece51fe86674..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 crate::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 89% rename from src/librustc/middle/limits.rs rename to src/librustc_middle/middle/limits.rs index 22e4f5ea22261..85198482bd380 100644 --- a/src/librustc/middle/limits.rs +++ b/src/librustc_middle/middle/limits.rs @@ -5,13 +5,13 @@ //! this via an attribute on the crate like `#![recursion_limit="22"]`. This pass //! just peeks and looks for that attribute. -use crate::session::Session; -use core::num::IntErrorKind; -use rustc::bug; +use crate::bug; use rustc_ast::ast; +use rustc_data_structures::sync::OnceCell; +use rustc_session::{Limit, Session}; use rustc_span::symbol::{sym, Symbol}; -use rustc_data_structures::sync::Once; +use std::num::IntErrorKind; pub fn update_limits(sess: &Session, krate: &ast::Crate) { update_limit(sess, krate, &sess.recursion_limit, sym::recursion_limit, 128); @@ -22,7 +22,7 @@ pub fn update_limits(sess: &Session, krate: &ast::Crate) { fn update_limit( sess: &Session, krate: &ast::Crate, - limit: &Once, + limit: &OnceCell, name: Symbol, default: usize, ) { @@ -34,7 +34,7 @@ fn update_limit( if let Some(s) = attr.value_str() { match s.as_str().parse() { Ok(n) => { - limit.set(n); + limit.set(Limit::new(n)).unwrap(); return; } Err(e) => { @@ -62,5 +62,5 @@ fn update_limit( } } } - limit.set(default); + limit.set(Limit::new(default)).unwrap(); } 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/middle/region.rs b/src/librustc_middle/middle/region.rs new file mode 100644 index 0000000000000..943a065a8b5e8 --- /dev/null +++ b/src/librustc_middle/middle/region.rs @@ -0,0 +1,490 @@ +//! This file declares the `ScopeTree` type, which describes +//! the parent links in the region hierarchy. +//! +//! For more information about how MIR-based region-checking works, +//! see the [rustc dev guide]. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html + +use crate::ich::{NodeIdHashingMode, StableHashingContext}; +use crate::ty::TyCtxt; +use rustc_hir as hir; +use rustc_hir::Node; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_macros::HashStable; +use rustc_span::{Span, DUMMY_SP}; + +use std::fmt; + +/// Represents a statically-describable scope that can be used to +/// bound the lifetime/region for values. +/// +/// `Node(node_id)`: Any AST node that has any scope at all has the +/// `Node(node_id)` scope. Other variants represent special cases not +/// immediately derivable from the abstract syntax tree structure. +/// +/// `DestructionScope(node_id)` represents the scope of destructors +/// implicitly-attached to `node_id` that run immediately after the +/// expression for `node_id` itself. Not every AST node carries a +/// `DestructionScope`, but those that are `terminating_scopes` do; +/// see discussion with `ScopeTree`. +/// +/// `Remainder { block, statement_index }` represents +/// the scope of user code running immediately after the initializer +/// expression for the indexed statement, until the end of the block. +/// +/// So: the following code can be broken down into the scopes beneath: +/// +/// ```text +/// let a = f().g( 'b: { let x = d(); let y = d(); x.h(y) } ) ; +/// +/// +-+ (D12.) +/// +-+ (D11.) +/// +---------+ (R10.) +/// +-+ (D9.) +/// +----------+ (M8.) +/// +----------------------+ (R7.) +/// +-+ (D6.) +/// +----------+ (M5.) +/// +-----------------------------------+ (M4.) +/// +--------------------------------------------------+ (M3.) +/// +--+ (M2.) +/// +-----------------------------------------------------------+ (M1.) +/// +/// (M1.): Node scope of the whole `let a = ...;` statement. +/// (M2.): Node scope of the `f()` expression. +/// (M3.): Node scope of the `f().g(..)` expression. +/// (M4.): Node scope of the block labeled `'b:`. +/// (M5.): Node scope of the `let x = d();` statement +/// (D6.): DestructionScope for temporaries created during M5. +/// (R7.): Remainder scope for block `'b:`, stmt 0 (let x = ...). +/// (M8.): Node scope of the `let y = d();` statement. +/// (D9.): DestructionScope for temporaries created during M8. +/// (R10.): Remainder scope for block `'b:`, stmt 1 (let y = ...). +/// (D11.): DestructionScope for temporaries and bindings from block `'b:`. +/// (D12.): DestructionScope for temporaries created during M1 (e.g., f()). +/// ``` +/// +/// Note that while the above picture shows the destruction scopes +/// as following their corresponding node scopes, in the internal +/// data structures of the compiler the destruction scopes are +/// represented as enclosing parents. This is sound because we use the +/// enclosing parent relationship just to ensure that referenced +/// values live long enough; phrased another way, the starting point +/// of each range is not really the important thing in the above +/// picture, but rather the ending point. +// +// FIXME(pnkfelix): this currently derives `PartialOrd` and `Ord` to +// placate the same deriving in `ty::FreeRegion`, but we may want to +// actually attach a more meaningful ordering to scopes than the one +// generated via deriving here. +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] +pub struct Scope { + pub id: hir::ItemLocalId, + pub data: ScopeData, +} + +impl fmt::Debug for Scope { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.data { + ScopeData::Node => write!(fmt, "Node({:?})", self.id), + ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.id), + ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id), + ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id), + ScopeData::Remainder(fsi) => write!( + fmt, + "Remainder {{ block: {:?}, first_statement_index: {}}}", + self.id, + fsi.as_u32(), + ), + } + } +} + +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug, Copy, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] +pub enum ScopeData { + Node, + + /// Scope of the call-site for a function or closure + /// (outlives the arguments as well as the body). + CallSite, + + /// Scope of arguments passed to a function or closure + /// (they outlive its body). + Arguments, + + /// Scope of destructors for temporaries of node-id. + Destruction, + + /// Scope following a `let id = expr;` binding in a block. + Remainder(FirstStatementIndex), +} + +rustc_index::newtype_index! { + /// Represents a subscope of `block` for a binding that is introduced + /// by `block.stmts[first_statement_index]`. Such subscopes represent + /// a suffix of the block. Note that each subscope does not include + /// the initializer expression, if any, for the statement indexed by + /// `first_statement_index`. + /// + /// For example, given `{ let (a, b) = EXPR_1; let c = EXPR_2; ... }`: + /// + /// * The subscope with `first_statement_index == 0` is scope of both + /// `a` and `b`; it does not include EXPR_1, but does include + /// everything after that first `let`. (If you want a scope that + /// includes EXPR_1 as well, then do not use `Scope::Remainder`, + /// but instead another `Scope` that encompasses the whole block, + /// e.g., `Scope::Node`. + /// + /// * The subscope with `first_statement_index == 1` is scope of `c`, + /// and thus does not include EXPR_2, but covers the `...`. + pub struct FirstStatementIndex { + derive [HashStable] + } +} + +// compilation error if size of `ScopeData` is not the same as a `u32` +static_assert_size!(ScopeData, 4); + +impl Scope { + /// Returns a item-local ID associated with this scope. + /// + /// N.B., likely to be replaced as API is refined; e.g., pnkfelix + /// anticipates `fn entry_node_id` and `fn each_exit_node_id`. + pub fn item_local_id(&self) -> hir::ItemLocalId { + self.id + } + + pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option { + scope_tree + .root_body + .map(|hir_id| hir::HirId { owner: hir_id.owner, local_id: self.item_local_id() }) + } + + /// Returns the span of this `Scope`. Note that in general the + /// returned span may not correspond to the span of any `NodeId` in + /// the AST. + pub fn span(&self, tcx: TyCtxt<'_>, scope_tree: &ScopeTree) -> Span { + let hir_id = match self.hir_id(scope_tree) { + Some(hir_id) => hir_id, + None => return DUMMY_SP, + }; + let span = tcx.hir().span(hir_id); + if let ScopeData::Remainder(first_statement_index) = self.data { + if let Node::Block(ref blk) = tcx.hir().get(hir_id) { + // Want span for scope starting after the + // indexed statement and ending at end of + // `blk`; reuse span of `blk` and shift `lo` + // forward to end of indexed statement. + // + // (This is the special case alluded to in the + // doc-comment for this method) + + let stmt_span = blk.stmts[first_statement_index.index()].span; + + // To avoid issues with macro-generated spans, the span + // of the statement must be nested in that of the block. + if span.lo() <= stmt_span.lo() && stmt_span.lo() <= span.hi() { + return Span::new(stmt_span.lo(), span.hi(), span.ctxt()); + } + } + } + span + } +} + +pub type ScopeDepth = u32; + +/// The region scope tree encodes information about region relationships. +#[derive(Default, Debug)] +pub struct ScopeTree { + /// If not empty, this body is the root of this region hierarchy. + pub root_body: Option, + + /// The parent of the root body owner, if the latter is an + /// an associated const or method, as impls/traits can also + /// have lifetime parameters free in this body. + pub root_parent: Option, + + /// Maps from a scope ID to the enclosing scope id; + /// this is usually corresponding to the lexical nesting, though + /// in the case of closures the parent scope is the innermost + /// conditional expression or repeating block. (Note that the + /// enclosing scope ID for the block associated with a closure is + /// the closure itself.) + pub parent_map: FxHashMap, + + /// Maps from a variable or binding ID to the block in which that + /// variable is declared. + var_map: FxHashMap, + + /// Maps from a `NodeId` to the associated destruction scope (if any). + destruction_scopes: FxHashMap, + + /// `rvalue_scopes` includes entries for those expressions whose + /// cleanup scope is larger than the default. The map goes from the + /// expression ID to the cleanup scope id. For rvalues not present in + /// this table, the appropriate cleanup scope is the innermost + /// enclosing statement, conditional expression, or repeating + /// block (see `terminating_scopes`). + /// In constants, None is used to indicate that certain expressions + /// escape into 'static and should have no local cleanup scope. + rvalue_scopes: FxHashMap>, + + /// Encodes the hierarchy of fn bodies. Every fn body (including + /// closures) forms its own distinct region hierarchy, rooted in + /// the block that is the fn body. This map points from the ID of + /// that root block to the ID of the root block for the enclosing + /// fn, if any. Thus the map structures the fn bodies into a + /// hierarchy based on their lexical mapping. This is used to + /// handle the relationships between regions in a fn and in a + /// closure defined by that fn. See the "Modeling closures" + /// section of the README in infer::region_constraints for + /// more details. + closure_tree: FxHashMap, + + /// If there are any `yield` nested within a scope, this map + /// stores the `Span` of the last one and its index in the + /// postorder of the Visitor traversal on the HIR. + /// + /// HIR Visitor postorder indexes might seem like a peculiar + /// thing to care about. but it turns out that HIR bindings + /// and the temporary results of HIR expressions are never + /// storage-live at the end of HIR nodes with postorder indexes + /// lower than theirs, and therefore don't need to be suspended + /// at yield-points at these indexes. + /// + /// For an example, suppose we have some code such as: + /// ```rust,ignore (example) + /// foo(f(), yield y, bar(g())) + /// ``` + /// + /// With the HIR tree (calls numbered for expository purposes) + /// ``` + /// Call#0(foo, [Call#1(f), Yield(y), Call#2(bar, Call#3(g))]) + /// ``` + /// + /// Obviously, the result of `f()` was created before the yield + /// (and therefore needs to be kept valid over the yield) while + /// the result of `g()` occurs after the yield (and therefore + /// doesn't). If we want to infer that, we can look at the + /// postorder traversal: + /// ```plain,ignore + /// `foo` `f` Call#1 `y` Yield `bar` `g` Call#3 Call#2 Call#0 + /// ``` + /// + /// In which we can easily see that `Call#1` occurs before the yield, + /// and `Call#3` after it. + /// + /// To see that this method works, consider: + /// + /// Let `D` be our binding/temporary and `U` be our other HIR node, with + /// `HIR-postorder(U) < HIR-postorder(D)` (in our example, U would be + /// the yield and D would be one of the calls). Let's show that + /// `D` is storage-dead at `U`. + /// + /// Remember that storage-live/storage-dead refers to the state of + /// the *storage*, and does not consider moves/drop flags. + /// + /// Then: + /// 1. From the ordering guarantee of HIR visitors (see + /// `rustc_hir::intravisit`), `D` does not dominate `U`. + /// 2. Therefore, `D` is *potentially* storage-dead at `U` (because + /// we might visit `U` without ever getting to `D`). + /// 3. However, we guarantee that at each HIR point, each + /// binding/temporary is always either always storage-live + /// or always storage-dead. This is what is being guaranteed + /// by `terminating_scopes` including all blocks where the + /// count of executions is not guaranteed. + /// 4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`, + /// QED. + /// + /// This property ought to not on (3) in an essential way -- it + /// is probably still correct even if we have "unrestricted" terminating + /// scopes. However, why use the complicated proof when a simple one + /// works? + /// + /// A subtle thing: `box` expressions, such as `box (&x, yield 2, &y)`. It + /// might seem that a `box` expression creates a `Box` temporary + /// when it *starts* executing, at `HIR-preorder(BOX-EXPR)`. That might + /// be true in the MIR desugaring, but it is not important in the semantics. + /// + /// The reason is that semantically, until the `box` expression returns, + /// the values are still owned by their containing expressions. So + /// we'll see that `&x`. + pub yield_in_scope: FxHashMap, + + /// The number of visit_expr and visit_pat calls done in the body. + /// Used to sanity check visit_expr/visit_pat call count when + /// calculating generator interiors. + pub body_expr_count: FxHashMap, +} + +#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)] +pub struct YieldData { + /// The `Span` of the yield. + pub span: Span, + /// The number of expressions and patterns appearing before the `yield` in the body plus one. + pub expr_and_pat_count: usize, + pub source: hir::YieldSource, +} + +impl ScopeTree { + pub fn record_scope_parent(&mut self, child: Scope, parent: Option<(Scope, ScopeDepth)>) { + debug!("{:?}.parent = {:?}", child, parent); + + if let Some(p) = parent { + let prev = self.parent_map.insert(child, p); + assert!(prev.is_none()); + } + + // Record the destruction scopes for later so we can query them. + if let ScopeData::Destruction = child.data { + self.destruction_scopes.insert(child.item_local_id(), child); + } + } + + pub fn opt_destruction_scope(&self, n: hir::ItemLocalId) -> Option { + self.destruction_scopes.get(&n).cloned() + } + + /// Records that `sub_closure` is defined within `sup_closure`. These IDs + /// should be the ID of the block that is the fn body, which is + /// also the root of the region hierarchy for that fn. + pub fn record_closure_parent( + &mut self, + sub_closure: hir::ItemLocalId, + sup_closure: hir::ItemLocalId, + ) { + debug!( + "record_closure_parent(sub_closure={:?}, sup_closure={:?})", + sub_closure, sup_closure + ); + assert!(sub_closure != sup_closure); + let previous = self.closure_tree.insert(sub_closure, sup_closure); + assert!(previous.is_none()); + } + + pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { + debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime); + assert!(var != lifetime.item_local_id()); + self.var_map.insert(var, lifetime); + } + + pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option) { + debug!("record_rvalue_scope(sub={:?}, sup={:?})", var, lifetime); + if let Some(lifetime) = lifetime { + assert!(var != lifetime.item_local_id()); + } + self.rvalue_scopes.insert(var, lifetime); + } + + /// Returns the narrowest scope that encloses `id`, if any. + pub fn opt_encl_scope(&self, id: Scope) -> Option { + self.parent_map.get(&id).cloned().map(|(p, _)| p) + } + + /// Returns the lifetime of the local variable `var_id` + pub fn var_scope(&self, var_id: hir::ItemLocalId) -> Scope { + self.var_map + .get(&var_id) + .cloned() + .unwrap_or_else(|| bug!("no enclosing scope for id {:?}", var_id)) + } + + /// Returns the scope when the temp created by `expr_id` will be cleaned up. + pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> Option { + // Check for a designated rvalue scope. + if let Some(&s) = self.rvalue_scopes.get(&expr_id) { + debug!("temporary_scope({:?}) = {:?} [custom]", expr_id, s); + return s; + } + + // Otherwise, locate the innermost terminating scope + // if there's one. Static items, for instance, won't + // have an enclosing scope, hence no scope will be + // returned. + let mut id = Scope { id: expr_id, data: ScopeData::Node }; + + while let Some(&(p, _)) = self.parent_map.get(&id) { + match p.data { + ScopeData::Destruction => { + debug!("temporary_scope({:?}) = {:?} [enclosing]", expr_id, id); + return Some(id); + } + _ => id = p, + } + } + + debug!("temporary_scope({:?}) = None", expr_id); + None + } + + /// Returns `true` if `subscope` is equal to or is lexically nested inside `superscope`, and + /// `false` otherwise. + pub fn is_subscope_of(&self, subscope: Scope, superscope: Scope) -> bool { + let mut s = subscope; + debug!("is_subscope_of({:?}, {:?})", subscope, superscope); + while superscope != s { + match self.opt_encl_scope(s) { + None => { + debug!("is_subscope_of({:?}, {:?}, s={:?})=false", subscope, superscope, s); + return false; + } + Some(scope) => s = scope, + } + } + + debug!("is_subscope_of({:?}, {:?})=true", subscope, superscope); + + true + } + + /// Checks whether the given scope contains a `yield`. If so, + /// returns `Some((span, expr_count))` with the span of a yield we found and + /// the number of expressions and patterns appearing before the `yield` in the body + 1. + /// If there a are multiple yields in a scope, the one with the highest number is returned. + pub fn yield_in_scope(&self, scope: Scope) -> Option { + self.yield_in_scope.get(&scope).cloned() + } + + /// Gives the number of expressions visited in a body. + /// Used to sanity check visit_expr call count when + /// calculating generator interiors. + pub fn body_expr_count(&self, body_id: hir::BodyId) -> Option { + self.body_expr_count.get(&body_id).copied() + } +} + +impl<'a> HashStable> for ScopeTree { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let ScopeTree { + root_body, + root_parent, + ref body_expr_count, + ref parent_map, + ref var_map, + ref destruction_scopes, + ref rvalue_scopes, + ref closure_tree, + ref yield_in_scope, + } = *self; + + hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { + root_body.hash_stable(hcx, hasher); + root_parent.hash_stable(hcx, hasher); + }); + + body_expr_count.hash_stable(hcx, hasher); + parent_map.hash_stable(hcx, hasher); + var_map.hash_stable(hcx, hasher); + destruction_scopes.hash_stable(hcx, hasher); + rvalue_scopes.hash_stable(hcx, hasher); + closure_tree.hash_stable(hcx, hasher); + yield_in_scope.hash_stable(hcx, hasher); + } +} diff --git a/src/librustc/middle/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 048e24ce3aa89..54c05bca3bd2b 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc_middle/middle/stability.rs @@ -3,7 +3,6 @@ pub use self::StabilityLevel::*; -use crate::session::{DiagnosticMessageId, Session}; use crate::ty::{self, TyCtxt}; use rustc_ast::ast::CRATE_NODE_ID; use rustc_attr::{self as attr, ConstStability, Deprecation, RustcDeprecation, Stability}; @@ -17,6 +16,7 @@ use rustc_hir::{self, HirId}; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; use rustc_session::lint::{BuiltinLintDiagnostics, Lint, LintBuffer}; use rustc_session::parse::feature_err_issue; +use rustc_session::{DiagnosticMessageId, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{MultiSpan, Span}; @@ -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 84% rename from src/librustc/mir/interpret/allocation.rs rename to src/librustc_middle/mir/interpret/allocation.rs index 546ba586d30f9..96195db0bacd2 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc_middle/mir/interpret/allocation.rs @@ -1,32 +1,21 @@ //! 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`. -#[derive( - Clone, - Debug, - Eq, - PartialEq, - PartialOrd, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - HashStable -)] +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, + UninitBytesAccess, +}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] pub struct Allocation { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer. @@ -37,10 +26,11 @@ 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. + /// (`Align` guarantees that this is a power of two.) pub align: Align, /// `true` if the allocation is mutable. /// Also used by codegen to determine if a static should be put into mutable memory, @@ -99,11 +89,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, @@ -116,11 +106,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, @@ -150,7 +139,7 @@ impl Allocation<(), ()> { }) .collect(), ), - undef_mask: self.undef_mask, + init_mask: self.init_mask, align: self.align, mutability: self.mutability, extra, @@ -161,7 +150,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 @@ -172,9 +161,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. @@ -192,12 +181,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 {}", @@ -205,7 +189,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 @@ -303,18 +287,17 @@ 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. &self.get_bytes(cx, ptr, size_with_null)?[..size] } // This includes the case where `offset` is out-of-bounds to begin with. - None => throw_unsup!(UnterminatedCString(ptr.erase_tag())), + None => throw_ub!(UnterminatedCString(ptr.erase_tag())), }) } @@ -352,7 +335,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 { @@ -376,15 +359,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(); @@ -393,16 +376,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. @@ -413,7 +393,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) } @@ -430,19 +410,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, }; @@ -451,11 +431,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(()) @@ -469,7 +446,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) @@ -533,15 +510,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. @@ -566,21 +543,33 @@ impl<'tcx, Tag: Copy, Extra> Allocation { } /// Undefined bytes. -impl<'tcx, Tag, Extra> Allocation { - /// Checks that a range of bytes is defined. If not, returns the `ReadUndefBytes` - /// error which will report the first byte which is undefined. - #[inline] +impl<'tcx, Tag: Copy, Extra> Allocation { + /// Checks whether the given range is entirely defined. + /// + /// Returns `Ok(())` if it's defined. Otherwise returns the range of byte + /// indexes of the first contiguous undefined access. + fn is_defined(&self, ptr: Pointer, size: Size) -> Result<(), Range> { + self.init_mask.is_range_initialized(ptr.offset, ptr.offset + size) // `Size` addition + } + + /// Checks that a range of bytes is defined. If not, returns the `InvalidUndefBytes` + /// error which will report the first range of bytes which is undefined. fn check_defined(&self, ptr: Pointer, size: Size) -> InterpResult<'tcx> { - self.undef_mask - .is_range_defined(ptr.offset, ptr.offset + size) - .or_else(|idx| throw_unsup!(ReadUndefBytes(idx))) + self.is_defined(ptr, size).or_else(|idx_range| { + throw_ub!(InvalidUninitBytes(Some(Box::new(UninitBytesAccess { + access_ptr: ptr.erase_tag(), + access_size: size, + uninit_ptr: Pointer::new(ptr.alloc_id, idx_range.start), + uninit_size: idx_range.end - idx_range.start, // `Size` subtraction + })))) + }) } 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); } } @@ -619,13 +608,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); @@ -650,9 +639,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; @@ -665,7 +654,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, @@ -730,10 +719,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, ) })); @@ -757,48 +746,44 @@ 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. -#[derive( - Clone, - Debug, - Eq, - PartialEq, - PartialOrd, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - HashStable -)] -pub struct UndefMask { +/// is initialized. If it is `false` the byte is uninitialized. +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] +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 a range of byte + /// indexes for the first contiguous span of the uninitialized access. #[inline] - pub fn is_range_defined(&self, start: Size, end: Size) -> Result<(), Size> { + pub fn is_range_initialized(&self, start: Size, end: Size) -> Result<(), Range> { if end > self.len { - return Err(self.len); + return Err(self.len..end); } // FIXME(oli-obk): optimize this for allocations larger than a block. - let idx = (start.bytes()..end.bytes()).map(|i| Size::from_bytes(i)).find(|&i| !self.get(i)); + let idx = (start.bytes()..end.bytes()).map(Size::from_bytes).find(|&i| !self.get(i)); match idx { - Some(idx) => Err(idx), + Some(idx) => { + let undef_end = (idx.bytes()..end.bytes()) + .map(Size::from_bytes) + .find(|&i| self.get(i)) + .unwrap_or(end); + Err(idx..undef_end) + } None => Ok(()), } } @@ -880,27 +865,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..abbbbf7fbd6a4 --- /dev/null +++ b/src/librustc_middle/mir/interpret/error.rs @@ -0,0 +1,641 @@ +use super::{AllocId, Pointer, RawConst, Scalar}; + +use crate::mir::interpret::ConstValue; +use crate::ty::layout::LayoutError; +use crate::ty::query::TyCtxtAt; +use crate::ty::{self, layout, tls, FnSig, Ty}; + +use rustc_data_structures::sync::Lock; +use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::definitions::DefPathData; +use rustc_macros::HashStable; +use rustc_session::CtfeBacktrace; +use rustc_span::{def_id::DefId, Pos, Span}; +use rustc_target::abi::{Align, Size}; +use std::{any::Any, backtrace::Backtrace, fmt, mem}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)] +pub enum ErrorHandled { + /// Already reported an error for this evaluation, and the compilation is + /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`. + Reported(ErrorReported), + /// Already emitted a lint for this evaluation. + Linted, + /// Don't emit an error, the evaluation failed because the MIR was generic + /// and the substs didn't fully monomorphize it. + TooGeneric, +} + +CloneTypeFoldableImpls! { + ErrorHandled, +} + +pub type ConstEvalRawResult<'tcx> = Result, ErrorHandled>; +pub type ConstEvalResult<'tcx> = Result, ErrorHandled>; + +#[derive(Debug)] +pub struct ConstEvalErr<'tcx> { + pub span: Span, + pub error: crate::mir::interpret::InterpError<'tcx>, + pub stacktrace: Vec>, +} + +#[derive(Debug)] +pub struct FrameInfo<'tcx> { + pub instance: ty::Instance<'tcx>, + pub span: Span, + pub lint_root: Option, +} + +impl<'tcx> fmt::Display for FrameInfo<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + if tcx.def_key(self.instance.def_id()).disambiguated_data.data + == DefPathData::ClosureExpr + { + write!(f, "inside closure")?; + } else { + write!(f, "inside `{}`", self.instance)?; + } + if !self.span.is_dummy() { + let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo()); + write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?; + } + Ok(()) + }) + } +} + +impl<'tcx> ConstEvalErr<'tcx> { + pub fn struct_error( + &self, + tcx: TyCtxtAt<'tcx>, + message: &str, + emit: impl FnOnce(DiagnosticBuilder<'_>), + ) -> ErrorHandled { + self.struct_generic(tcx, message, emit, None) + } + + pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled { + self.struct_error(tcx, message, |mut e| e.emit()) + } + + pub fn report_as_lint( + &self, + tcx: TyCtxtAt<'tcx>, + message: &str, + lint_root: hir::HirId, + span: Option, + ) -> ErrorHandled { + self.struct_generic( + tcx, + message, + |mut lint: DiagnosticBuilder<'_>| { + // Apply the span. + if let Some(span) = span { + let primary_spans = lint.span.primary_spans().to_vec(); + // point at the actual error as the primary span + lint.replace_span_with(span); + // point to the `const` statement as a secondary span + // they don't have any label + for sp in primary_spans { + if sp != span { + lint.span_label(sp, ""); + } + } + } + lint.emit(); + }, + Some(lint_root), + ) + } + + /// Create a diagnostic for this const eval error. + /// + /// Sets the message passed in via `message` and adds span labels with detailed error + /// information before handing control back to `emit` to do any final processing. + /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit` + /// function to dispose of the diagnostic properly. + /// + /// If `lint_root.is_some()` report it as a lint, else report it as a hard error. + /// (Except that for some errors, we ignore all that -- see `must_error` below.) + fn struct_generic( + &self, + tcx: TyCtxtAt<'tcx>, + message: &str, + emit: impl FnOnce(DiagnosticBuilder<'_>), + lint_root: Option, + ) -> ErrorHandled { + let must_error = match self.error { + err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { + return ErrorHandled::TooGeneric; + } + err_inval!(TypeckError(error_reported)) => { + return ErrorHandled::Reported(error_reported); + } + // We must *always* hard error on these, even if the caller wants just a lint. + err_inval!(Layout(LayoutError::SizeOverflow(_))) => true, + _ => false, + }; + trace!("reporting const eval failure at {:?}", self.span); + + let err_msg = match &self.error { + InterpError::MachineStop(msg) => { + // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`). + // Should be turned into a string by now. + msg.downcast_ref::().expect("invalid MachineStop payload").clone() + } + err => err.to_string(), + }; + + let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option| { + if let Some(span_msg) = span_msg { + err.span_label(self.span, span_msg); + } + // Add spans for the stacktrace. Don't print a single-line backtrace though. + if self.stacktrace.len() > 1 { + for frame_info in &self.stacktrace { + err.span_label(frame_info.span, frame_info.to_string()); + } + } + // Let the caller finish the job. + emit(err) + }; + + if must_error { + // The `message` makes little sense here, this is a more serious error than the + // caller thinks anyway. + // See . + finish(struct_error(tcx, &err_msg), None); + ErrorHandled::Reported(ErrorReported) + } else { + // Regular case. + if let Some(lint_root) = lint_root { + // Report as lint. + let hir_id = self + .stacktrace + .iter() + .rev() + .find_map(|frame| frame.lint_root) + .unwrap_or(lint_root); + tcx.struct_span_lint_hir( + rustc_session::lint::builtin::CONST_ERR, + hir_id, + tcx.span, + |lint| finish(lint.build(message), Some(err_msg)), + ); + ErrorHandled::Linted + } else { + // Report as hard error. + finish(struct_error(tcx, message), Some(err_msg)); + ErrorHandled::Reported(ErrorReported) + } + } + } +} + +pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> { + struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg) +} + +/// Packages the kind of error we got from the const code interpreter +/// up with a Rust-level backtrace of where the error occurred. +/// Thsese should always be constructed by calling `.into()` on +/// a `InterpError`. In `librustc_mir::interpret`, we have `throw_err_*` +/// macros for this. +#[derive(Debug)] +pub struct InterpErrorInfo<'tcx> { + pub kind: InterpError<'tcx>, + backtrace: Option>, +} + +impl fmt::Display for InterpErrorInfo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.kind) + } +} + +impl InterpErrorInfo<'_> { + pub fn print_backtrace(&self) { + if let Some(backtrace) = self.backtrace.as_ref() { + print_backtrace(backtrace); + } + } +} + +fn print_backtrace(backtrace: &Backtrace) { + eprintln!("\n\nAn error occurred in miri:\n{}", backtrace); +} + +impl From for InterpErrorInfo<'_> { + fn from(err: ErrorHandled) -> Self { + match err { + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { + err_inval!(ReferencedConstant) + } + ErrorHandled::TooGeneric => err_inval!(TooGeneric), + } + .into() + } +} + +impl<'tcx> From> for InterpErrorInfo<'tcx> { + fn from(kind: InterpError<'tcx>) -> Self { + let capture_backtrace = tls::with_context_opt(|ctxt| { + if let Some(ctxt) = ctxt { + *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace) + } else { + CtfeBacktrace::Disabled + } + }); + + let backtrace = match capture_backtrace { + CtfeBacktrace::Disabled => None, + CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())), + CtfeBacktrace::Immediate => { + // Print it now. + let backtrace = Backtrace::force_capture(); + print_backtrace(&backtrace); + None + } + }; + + InterpErrorInfo { kind, backtrace } + } +} + +/// Error information for when the program we executed turned out not to actually be a valid +/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp +/// where we work on generic code or execution does not have all information available. +pub enum InvalidProgramInfo<'tcx> { + /// Resolution can fail if we are in a too generic context. + TooGeneric, + /// Cannot compute this constant because it depends on another one + /// which already produced an error. + ReferencedConstant, + /// Abort in case type errors are reached. + TypeckError(ErrorReported), + /// An error occurred during layout computation. + Layout(layout::LayoutError<'tcx>), + /// An invalid transmute happened. + TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), +} + +impl fmt::Display for InvalidProgramInfo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InvalidProgramInfo::*; + match self { + TooGeneric => write!(f, "encountered overly generic constant"), + ReferencedConstant => write!(f, "referenced constant has errors"), + TypeckError(ErrorReported) => { + write!(f, "encountered constants with type errors, stopping evaluation") + } + Layout(ref err) => write!(f, "{}", err), + TransmuteSizeDiff(from_ty, to_ty) => write!( + f, + "transmuting `{}` to `{}` is not possible, because these types do not have the same size", + from_ty, to_ty + ), + } + } +} + +/// Details of why a pointer had to be in-bounds. +#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)] +pub enum CheckInAllocMsg { + MemoryAccessTest, + NullPointerTest, + PointerArithmeticTest, + InboundsTest, +} + +impl fmt::Display for CheckInAllocMsg { + /// When this is printed as an error the context looks like this + /// "{test name} failed: pointer must be in-bounds at offset..." + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match *self { + CheckInAllocMsg::MemoryAccessTest => "memory access", + CheckInAllocMsg::NullPointerTest => "NULL pointer test", + CheckInAllocMsg::PointerArithmeticTest => "pointer arithmetic", + CheckInAllocMsg::InboundsTest => "inbounds test", + } + ) + } +} + +/// Details of an access to uninitialized bytes where it is not allowed. +#[derive(Debug)] +pub struct UninitBytesAccess { + /// Location of the original memory access. + pub access_ptr: Pointer, + /// Size of the original memory access. + pub access_size: Size, + /// Location of the first uninitialized byte that was accessed. + pub uninit_ptr: Pointer, + /// Number of consecutive uninitialized bytes that were accessed. + pub uninit_size: Size, +} + +/// Error information for when the program caused Undefined Behavior. +pub enum UndefinedBehaviorInfo<'tcx> { + /// Free-form case. Only for errors that are never caught! + Ub(String), + /// Unreachable code was executed. + Unreachable, + /// A slice/array index projection went out-of-bounds. + BoundsCheckFailed { + len: u64, + index: u64, + }, + /// Something was divided by 0 (x / 0). + DivisionByZero, + /// Something was "remainded" by 0 (x % 0). + RemainderByZero, + /// Overflowing inbounds pointer arithmetic. + PointerArithOverflow, + /// Invalid metadata in a wide pointer (using `str` to avoid allocations). + InvalidMeta(&'static str), + /// Invalid drop function in vtable. + InvalidDropFn(FnSig<'tcx>), + /// Reading a C string that does not end within its allocation. + UnterminatedCString(Pointer), + /// Dereferencing a dangling pointer after it got freed. + PointerUseAfterFree(AllocId), + /// Used a pointer outside the bounds it is valid for. + PointerOutOfBounds { + ptr: Pointer, + msg: CheckInAllocMsg, + allocation_size: Size, + }, + /// Using an integer as a pointer in the wrong way. + DanglingIntPointer(u64, CheckInAllocMsg), + /// Used a pointer with bad alignment. + AlignmentCheckFailed { + required: Align, + has: Align, + }, + /// Writing to read-only memory. + WriteToReadOnly(AllocId), + // Trying to access the data behind a function pointer. + DerefFunctionPointer(AllocId), + /// The value validity check found a problem. + /// Should only be thrown by `validity.rs` and always point out which part of the value + /// is the problem. + ValidationFailure(String), + /// Using a non-boolean `u8` as bool. + InvalidBool(u8), + /// Using a non-character `u32` as character. + InvalidChar(u32), + /// The tag of an enum does not encode an actual discriminant. + InvalidTag(Scalar), + /// Using a pointer-not-to-a-function as function pointer. + InvalidFunctionPointer(Pointer), + /// Using a string that is not valid UTF-8, + InvalidStr(std::str::Utf8Error), + /// Using uninitialized data where it is not allowed. + InvalidUninitBytes(Option>), + /// Working with a local that is not currently live. + DeadLocal, + /// Data size is not equal to target size. + ScalarSizeMismatch { + target_size: u64, + data_size: u64, + }, +} + +impl fmt::Display for UndefinedBehaviorInfo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use UndefinedBehaviorInfo::*; + match self { + Ub(msg) => write!(f, "{}", msg), + Unreachable => write!(f, "entering unreachable code"), + BoundsCheckFailed { ref len, ref index } => { + write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index) + } + DivisionByZero => write!(f, "dividing by zero"), + RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), + PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), + InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), + InvalidDropFn(sig) => write!( + f, + "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type", + sig + ), + UnterminatedCString(p) => write!( + f, + "reading a null-terminated string starting at {} with no null found before end of allocation", + p, + ), + PointerUseAfterFree(a) => { + write!(f, "pointer to {} was dereferenced after this allocation got freed", a) + } + PointerOutOfBounds { ptr, msg, allocation_size } => write!( + f, + "{} failed: pointer must be in-bounds at offset {}, \ + but is outside bounds of {} which has size {}", + msg, + ptr.offset.bytes(), + ptr.alloc_id, + allocation_size.bytes() + ), + DanglingIntPointer(_, CheckInAllocMsg::NullPointerTest) => { + write!(f, "NULL pointer is not allowed for this operation") + } + DanglingIntPointer(i, msg) => { + write!(f, "{} failed: 0x{:x} is not a valid pointer", msg, i) + } + AlignmentCheckFailed { required, has } => write!( + f, + "accessing memory with alignment {}, but alignment {} is required", + has.bytes(), + required.bytes() + ), + WriteToReadOnly(a) => write!(f, "writing to {} which is read-only", a), + DerefFunctionPointer(a) => write!(f, "accessing {} which contains a function", a), + ValidationFailure(ref err) => write!(f, "type validation failed: {}", err), + InvalidBool(b) => { + write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b) + } + InvalidChar(c) => { + write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c) + } + InvalidTag(val) => write!(f, "enum value has invalid tag: {}", val), + InvalidFunctionPointer(p) => { + write!(f, "using {} as function pointer but it does not point to a function", p) + } + InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err), + InvalidUninitBytes(Some(access)) => write!( + f, + "reading {} byte{} of memory starting at {}, \ + but {} byte{} {} uninitialized starting at {}, \ + and this operation requires initialized memory", + access.access_size.bytes(), + pluralize!(access.access_size.bytes()), + access.access_ptr, + access.uninit_size.bytes(), + pluralize!(access.uninit_size.bytes()), + if access.uninit_size.bytes() != 1 { "are" } else { "is" }, + access.uninit_ptr, + ), + InvalidUninitBytes(None) => write!( + f, + "using uninitialized data, but this operation requires initialized memory" + ), + DeadLocal => write!(f, "accessing a dead local variable"), + ScalarSizeMismatch { target_size, data_size } => write!( + f, + "scalar size mismatch: expected {} bytes but got {} bytes instead", + target_size, data_size + ), + } + } +} + +/// Error information for when the program did something that might (or might not) be correct +/// to do according to the Rust spec, but due to limitations in the interpreter, the +/// operation could not be carried out. These limitations can differ between CTFE and the +/// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses. +pub enum UnsupportedOpInfo { + /// Free-form case. Only for errors that are never caught! + Unsupported(String), + /// Accessing an unsupported foreign static. + ReadForeignStatic(DefId), + /// Could not find MIR for a function. + NoMirFor(DefId), + /// Encountered a pointer where we needed raw bytes. + ReadPointerAsBytes, + // + // The variants below are only reachable from CTFE/const prop, miri will never emit them. + // + /// Encountered raw bytes where we needed a pointer. + ReadBytesAsPointer, + /// Accessing thread local statics + ThreadLocalStatic(DefId), +} + +impl fmt::Display for UnsupportedOpInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use UnsupportedOpInfo::*; + match self { + Unsupported(ref msg) => write!(f, "{}", msg), + ReadForeignStatic(did) => { + write!(f, "cannot read from foreign (extern) static ({:?})", did) + } + NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did), + ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",), + ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"), + ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did), + } + } +} + +/// Error information for when the program exhausted the resources granted to it +/// by the interpreter. +pub enum ResourceExhaustionInfo { + /// The stack grew too big. + StackFrameLimitReached, + /// The program ran for too long. + /// + /// The exact limit is set by the `const_eval_limit` attribute. + StepLimitReached, +} + +impl fmt::Display for ResourceExhaustionInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use ResourceExhaustionInfo::*; + match self { + StackFrameLimitReached => { + write!(f, "reached the configured maximum number of stack frames") + } + StepLimitReached => { + write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)") + } + } + } +} + +/// A trait to work around not having trait object upcasting. +pub trait AsAny: Any { + fn as_any(&self) -> &dyn Any; +} +impl AsAny for T { + #[inline(always)] + fn as_any(&self) -> &dyn Any { + self + } +} + +/// A trait for machine-specific errors (or other "machine stop" conditions). +pub trait MachineStopType: AsAny + fmt::Display + Send {} +impl MachineStopType for String {} + +impl dyn MachineStopType { + #[inline(always)] + pub fn downcast_ref(&self) -> Option<&T> { + self.as_any().downcast_ref() + } +} + +#[cfg(target_arch = "x86_64")] +static_assert_size!(InterpError<'_>, 40); + +pub enum InterpError<'tcx> { + /// The program caused undefined behavior. + UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), + /// The program did something the interpreter does not support (some of these *might* be UB + /// but the interpreter is not sure). + Unsupported(UnsupportedOpInfo), + /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). + InvalidProgram(InvalidProgramInfo<'tcx>), + /// The program exhausted the interpreter's resources (stack/heap too big, + /// execution takes too long, ...). + ResourceExhaustion(ResourceExhaustionInfo), + /// Stop execution for a machine-controlled reason. This is never raised by + /// the core engine itself. + MachineStop(Box), +} + +pub type InterpResult<'tcx, T = ()> = Result>; + +impl fmt::Display for InterpError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InterpError::*; + match *self { + Unsupported(ref msg) => write!(f, "{}", msg), + InvalidProgram(ref msg) => write!(f, "{}", msg), + UndefinedBehavior(ref msg) => write!(f, "{}", msg), + ResourceExhaustion(ref msg) => write!(f, "{}", msg), + MachineStop(ref msg) => write!(f, "{}", msg), + } + } +} + +// Forward `Debug` to `Display`, so it does not look awful. +impl fmt::Debug for InterpError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl InterpError<'_> { + /// Some errors allocate to be created as they contain free-form strings. + /// And sometimes we want to be sure that did not happen as it is a + /// waste of resources. + pub fn allocates(&self) -> bool { + match self { + // Zero-sized boxes do not allocate. + InterpError::MachineStop(b) => mem::size_of_val::(&**b) > 0, + InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_)) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::InvalidUninitBytes(Some(_))) => { + true + } + _ => false, + } + } +} diff --git a/src/librustc_middle/mir/interpret/mod.rs b/src/librustc_middle/mir/interpret/mod.rs new file mode 100644 index 0000000000000..5e57b60894a58 --- /dev/null +++ b/src/librustc_middle/mir/interpret/mod.rs @@ -0,0 +1,610 @@ +//! An interpreter for MIR used in CTFE and by miri. + +#[macro_export] +macro_rules! err_unsup { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::Unsupported( + $crate::mir::interpret::UnsupportedOpInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_unsup_format { + ($($tt:tt)*) => { err_unsup!(Unsupported(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! err_inval { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::InvalidProgram( + $crate::mir::interpret::InvalidProgramInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_ub { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::UndefinedBehavior( + $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_ub_format { + ($($tt:tt)*) => { err_ub!(Ub(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! err_exhaust { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::ResourceExhaustion( + $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* + ) + }; +} + +#[macro_export] +macro_rules! err_machine_stop { + ($($tt:tt)*) => { + $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) + }; +} + +// In the `throw_*` macros, avoid `return` to make them work with `try {}`. +#[macro_export] +macro_rules! throw_unsup { + ($($tt:tt)*) => { Err::(err_unsup!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_unsup_format { + ($($tt:tt)*) => { throw_unsup!(Unsupported(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! throw_inval { + ($($tt:tt)*) => { Err::(err_inval!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_ub { + ($($tt:tt)*) => { Err::(err_ub!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_ub_format { + ($($tt:tt)*) => { throw_ub!(Ub(format!($($tt)*))) }; +} + +#[macro_export] +macro_rules! throw_exhaust { + ($($tt:tt)*) => { Err::(err_exhaust!($($tt)*))? }; +} + +#[macro_export] +macro_rules! throw_machine_stop { + ($($tt:tt)*) => { Err::(err_machine_stop!($($tt)*))? }; +} + +mod allocation; +mod error; +mod pointer; +mod queries; +mod value; + +use std::convert::TryFrom; +use std::fmt; +use std::io; +use std::num::NonZeroU32; +use std::sync::atomic::{AtomicU32, Ordering}; + +use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::{HashMapExt, Lock}; +use rustc_data_structures::tiny_list::TinyList; +use rustc_hir::def_id::DefId; +use rustc_macros::HashStable; +use rustc_serialize::{Decodable, Encodable, Encoder}; +use rustc_target::abi::{Endian, Size}; + +use crate::mir; +use crate::ty::codec::TyDecoder; +use crate::ty::subst::GenericArgKind; +use crate::ty::{self, Instance, Ty, TyCtxt}; + +pub use self::error::{ + struct_error, CheckInAllocMsg, ConstEvalErr, ConstEvalRawResult, ConstEvalResult, ErrorHandled, + FrameInfo, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, MachineStopType, + ResourceExhaustionInfo, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo, +}; + +pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUninit}; + +pub use self::allocation::{Allocation, AllocationExtra, InitMask, Relocations}; + +pub use self::pointer::{Pointer, PointerArithmetic}; + +/// Uniquely identifies one of the following: +/// - A constant +/// - A static +/// - A const fn where all arguments (if any) are zero-sized types +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable, Lift)] +pub struct GlobalId<'tcx> { + /// For a constant or static, the `Instance` of the item itself. + /// For a promoted global, the `Instance` of the function they belong to. + pub instance: ty::Instance<'tcx>, + + /// The index for promoted globals within their function's `mir::Body`. + pub promoted: Option, +} + +/// Input argument for `tcx.lit_to_const`. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable)] +pub struct LitToConstInput<'tcx> { + /// The absolute value of the resultant constant. + pub lit: &'tcx LitKind, + /// The type of the constant. + pub ty: Ty<'tcx>, + /// If the constant is negative. + pub neg: bool, +} + +/// Error type for `tcx.lit_to_const`. +#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] +pub enum LitToConstError { + /// The literal's inferred type did not match the expected `ty` in the input. + /// This is used for graceful error handling (`delay_span_bug`) in + /// type checking (`Const::from_anon_const`). + TypeError, + UnparseableFloat, + Reported, +} + +#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct AllocId(pub u64); + +// We want the `Debug` output to be readable as it is used by `derive(Debug)` for +// all the Miri types. +impl fmt::Debug for AllocId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { write!(f, "a{}", self.0) } else { write!(f, "alloc{}", self.0) } + } +} + +impl fmt::Display for AllocId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl rustc_serialize::UseSpecializedEncodable for AllocId {} +impl rustc_serialize::UseSpecializedDecodable for AllocId {} + +#[derive(RustcDecodable, RustcEncodable)] +enum AllocDiscriminant { + Alloc, + Fn, + Static, +} + +pub fn specialized_encode_alloc_id<'tcx, E: Encoder>( + encoder: &mut E, + tcx: TyCtxt<'tcx>, + alloc_id: AllocId, +) -> Result<(), E::Error> { + match tcx.global_alloc(alloc_id) { + GlobalAlloc::Memory(alloc) => { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); + AllocDiscriminant::Alloc.encode(encoder)?; + alloc.encode(encoder)?; + } + GlobalAlloc::Function(fn_instance) => { + trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + AllocDiscriminant::Fn.encode(encoder)?; + fn_instance.encode(encoder)?; + } + GlobalAlloc::Static(did) => { + assert!(!tcx.is_thread_local_static(did)); + // References to statics doesn't need to know about their allocations, + // just about its `DefId`. + AllocDiscriminant::Static.encode(encoder)?; + did.encode(encoder)?; + } + } + Ok(()) +} + +// Used to avoid infinite recursion when decoding cyclic allocations. +type DecodingSessionId = NonZeroU32; + +#[derive(Clone)] +enum State { + Empty, + InProgressNonAlloc(TinyList), + InProgress(TinyList, AllocId), + Done(AllocId), +} + +pub struct AllocDecodingState { + // For each `AllocId`, we keep track of which decoding state it's currently in. + decoding_state: Vec>, + // The offsets of each allocation in the data stream. + data_offsets: Vec, +} + +impl AllocDecodingState { + pub fn new_decoding_session(&self) -> AllocDecodingSession<'_> { + static DECODER_SESSION_ID: AtomicU32 = AtomicU32::new(0); + let counter = DECODER_SESSION_ID.fetch_add(1, Ordering::SeqCst); + + // Make sure this is never zero. + let session_id = DecodingSessionId::new((counter & 0x7FFFFFFF) + 1).unwrap(); + + AllocDecodingSession { state: self, session_id } + } + + pub fn new(data_offsets: Vec) -> Self { + let decoding_state = vec![Lock::new(State::Empty); data_offsets.len()]; + + Self { decoding_state, data_offsets } + } +} + +#[derive(Copy, Clone)] +pub struct AllocDecodingSession<'s> { + state: &'s AllocDecodingState, + session_id: DecodingSessionId, +} + +impl<'s> AllocDecodingSession<'s> { + /// Decodes an `AllocId` in a thread-safe way. + pub fn decode_alloc_id(&self, decoder: &mut D) -> Result + where + D: TyDecoder<'tcx>, + { + // Read the index of the allocation. + let idx = usize::try_from(decoder.read_u32()?).unwrap(); + let pos = usize::try_from(self.state.data_offsets[idx]).unwrap(); + + // Decode the `AllocDiscriminant` now so that we know if we have to reserve an + // `AllocId`. + let (alloc_kind, pos) = decoder.with_position(pos, |decoder| { + let alloc_kind = AllocDiscriminant::decode(decoder)?; + Ok((alloc_kind, decoder.position())) + })?; + + // Check the decoding state to see if it's already decoded or if we should + // decode it here. + let alloc_id = { + let mut entry = self.state.decoding_state[idx].lock(); + + match *entry { + State::Done(alloc_id) => { + return Ok(alloc_id); + } + ref mut entry @ State::Empty => { + // We are allowed to decode. + match alloc_kind { + AllocDiscriminant::Alloc => { + // If this is an allocation, we need to reserve an + // `AllocId` so we can decode cyclic graphs. + let alloc_id = decoder.tcx().reserve_alloc_id(); + *entry = + State::InProgress(TinyList::new_single(self.session_id), alloc_id); + Some(alloc_id) + } + AllocDiscriminant::Fn | AllocDiscriminant::Static => { + // Fns and statics cannot be cyclic, and their `AllocId` + // is determined later by interning. + *entry = + State::InProgressNonAlloc(TinyList::new_single(self.session_id)); + None + } + } + } + State::InProgressNonAlloc(ref mut sessions) => { + if sessions.contains(&self.session_id) { + bug!("this should be unreachable"); + } else { + // Start decoding concurrently. + sessions.insert(self.session_id); + None + } + } + State::InProgress(ref mut sessions, alloc_id) => { + if sessions.contains(&self.session_id) { + // Don't recurse. + return Ok(alloc_id); + } else { + // Start decoding concurrently. + sessions.insert(self.session_id); + Some(alloc_id) + } + } + } + }; + + // Now decode the actual data. + let alloc_id = decoder.with_position(pos, |decoder| { + match alloc_kind { + AllocDiscriminant::Alloc => { + let alloc = <&'tcx Allocation as Decodable>::decode(decoder)?; + // We already have a reserved `AllocId`. + let alloc_id = alloc_id.unwrap(); + trace!("decoded alloc {:?}: {:#?}", alloc_id, alloc); + decoder.tcx().set_alloc_id_same_memory(alloc_id, alloc); + Ok(alloc_id) + } + AllocDiscriminant::Fn => { + assert!(alloc_id.is_none()); + trace!("creating fn alloc ID"); + let instance = ty::Instance::decode(decoder)?; + trace!("decoded fn alloc instance: {:?}", instance); + let alloc_id = decoder.tcx().create_fn_alloc(instance); + Ok(alloc_id) + } + AllocDiscriminant::Static => { + assert!(alloc_id.is_none()); + trace!("creating extern static alloc ID"); + let did = DefId::decode(decoder)?; + trace!("decoded static def-ID: {:?}", did); + let alloc_id = decoder.tcx().create_static_alloc(did); + Ok(alloc_id) + } + } + })?; + + self.state.decoding_state[idx].with_lock(|entry| { + *entry = State::Done(alloc_id); + }); + + Ok(alloc_id) + } +} + +/// An allocation in the global (tcx-managed) memory can be either a function pointer, +/// a static, or a "real" allocation with some data in it. +#[derive(Debug, Clone, Eq, PartialEq, Hash, RustcDecodable, RustcEncodable, HashStable)] +pub enum GlobalAlloc<'tcx> { + /// The alloc ID is used as a function pointer. + Function(Instance<'tcx>), + /// The alloc ID points to a "lazy" static variable that did not get computed (yet). + /// This is also used to break the cycle in recursive statics. + Static(DefId), + /// The alloc ID points to memory. + Memory(&'tcx Allocation), +} + +impl GlobalAlloc<'tcx> { + /// Panics if the `GlobalAlloc` does not refer to an `GlobalAlloc::Memory` + #[track_caller] + #[inline] + pub fn unwrap_memory(&self) -> &'tcx Allocation { + match *self { + GlobalAlloc::Memory(mem) => mem, + _ => bug!("expected memory, got {:?}", self), + } + } + + /// Panics if the `GlobalAlloc` is not `GlobalAlloc::Function` + #[track_caller] + #[inline] + pub fn unwrap_fn(&self) -> Instance<'tcx> { + match *self { + GlobalAlloc::Function(instance) => instance, + _ => bug!("expected function, got {:?}", self), + } + } +} + +crate struct AllocMap<'tcx> { + /// Maps `AllocId`s to their corresponding allocations. + alloc_map: FxHashMap>, + + /// Used to ensure that statics and functions only get one associated `AllocId`. + /// Should never contain a `GlobalAlloc::Memory`! + // + // FIXME: Should we just have two separate dedup maps for statics and functions each? + dedup: FxHashMap, AllocId>, + + /// The `AllocId` to assign to the next requested ID. + /// Always incremented; never gets smaller. + next_id: AllocId, +} + +impl<'tcx> AllocMap<'tcx> { + crate fn new() -> Self { + AllocMap { alloc_map: Default::default(), dedup: Default::default(), next_id: AllocId(0) } + } + fn reserve(&mut self) -> AllocId { + let next = self.next_id; + self.next_id.0 = self.next_id.0.checked_add(1).expect( + "You overflowed a u64 by incrementing by 1... \ + You've just earned yourself a free drink if we ever meet. \ + Seriously, how did you do that?!", + ); + next + } +} + +impl<'tcx> TyCtxt<'tcx> { + /// Obtains a new allocation ID that can be referenced but does not + /// yet have an allocation backing it. + /// + /// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such + /// an `AllocId` from a query. + pub fn reserve_alloc_id(&self) -> AllocId { + self.alloc_map.lock().reserve() + } + + /// Reserves a new ID *if* this allocation has not been dedup-reserved before. + /// Should only be used for function pointers and statics, we don't want + /// to dedup IDs for "real" memory! + fn reserve_and_set_dedup(&self, alloc: GlobalAlloc<'tcx>) -> AllocId { + let mut alloc_map = self.alloc_map.lock(); + match alloc { + GlobalAlloc::Function(..) | GlobalAlloc::Static(..) => {} + GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"), + } + if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) { + return alloc_id; + } + let id = alloc_map.reserve(); + debug!("creating alloc {:?} with id {}", alloc, id); + alloc_map.alloc_map.insert(id, alloc.clone()); + alloc_map.dedup.insert(alloc, id); + id + } + + /// Generates an `AllocId` for a static or return a cached one in case this function has been + /// called on the same static before. + pub fn create_static_alloc(&self, static_id: DefId) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::Static(static_id)) + } + + /// Generates an `AllocId` for a function. Depending on the function type, + /// this might get deduplicated or assigned a new ID each time. + pub fn create_fn_alloc(&self, instance: Instance<'tcx>) -> AllocId { + // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated + // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be + // duplicated across crates. + // We thus generate a new `AllocId` for every mention of a function. This means that + // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true. + // However, formatting code relies on function identity (see #58320), so we only do + // this for generic functions. Lifetime parameters are ignored. + let is_generic = instance.substs.into_iter().any(|kind| match kind.unpack() { + GenericArgKind::Lifetime(_) => false, + _ => true, + }); + if is_generic { + // Get a fresh ID. + let mut alloc_map = self.alloc_map.lock(); + let id = alloc_map.reserve(); + alloc_map.alloc_map.insert(id, GlobalAlloc::Function(instance)); + id + } else { + // Deduplicate. + self.reserve_and_set_dedup(GlobalAlloc::Function(instance)) + } + } + + /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical + /// `Allocation` with a different `AllocId`. + /// Statics with identical content will still point to the same `Allocation`, i.e., + /// their data will be deduplicated through `Allocation` interning -- but they + /// are different places in memory and as such need different IDs. + pub fn create_memory_alloc(&self, mem: &'tcx Allocation) -> AllocId { + let id = self.reserve_alloc_id(); + self.set_alloc_id_memory(id, mem); + id + } + + /// Returns `None` in case the `AllocId` is dangling. An `InterpretCx` can still have a + /// local `Allocation` for that `AllocId`, but having such an `AllocId` in a constant is + /// illegal and will likely ICE. + /// This function exists to allow const eval to detect the difference between evaluation- + /// local dangling pointers and allocations in constants/statics. + #[inline] + pub fn get_global_alloc(&self, id: AllocId) -> Option> { + self.alloc_map.lock().alloc_map.get(&id).cloned() + } + + #[inline] + #[track_caller] + /// Panics in case the `AllocId` is dangling. Since that is impossible for `AllocId`s in + /// constants (as all constants must pass interning and validation that check for dangling + /// ids), this function is frequently used throughout rustc, but should not be used within + /// the miri engine. + pub fn global_alloc(&self, id: AllocId) -> GlobalAlloc<'tcx> { + match self.get_global_alloc(id) { + Some(alloc) => alloc, + None => bug!("could not find allocation for {}", id), + } + } + + /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to + /// call this function twice, even with the same `Allocation` will ICE the compiler. + pub fn set_alloc_id_memory(&self, id: AllocId, mem: &'tcx Allocation) { + if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) { + bug!("tried to set allocation ID {}, but it was already existing as {:#?}", id, old); + } + } + + /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called + /// twice for the same `(AllocId, Allocation)` pair. + fn set_alloc_id_same_memory(&self, id: AllocId, mem: &'tcx Allocation) { + self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem)); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Methods to access integers in the target endianness +//////////////////////////////////////////////////////////////////////////////// + +#[inline] +pub fn write_target_uint( + endianness: Endian, + mut target: &mut [u8], + data: u128, +) -> Result<(), io::Error> { + let len = target.len(); + match endianness { + Endian::Little => target.write_uint128::(data, len), + Endian::Big => target.write_uint128::(data, len), + } +} + +#[inline] +pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result { + match endianness { + Endian::Little => source.read_uint128::(source.len()), + Endian::Big => source.read_uint128::(source.len()), + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Methods to facilitate working with signed integers stored in a u128 +//////////////////////////////////////////////////////////////////////////////// + +/// Truncates `value` to `size` bits and then sign-extend it to 128 bits +/// (i.e., if it is negative, fill with 1's on the left). +#[inline] +pub fn sign_extend(value: u128, size: Size) -> u128 { + let size = size.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + // Sign-extend it. + let shift = 128 - size; + // Shift the unsigned value to the left, then shift back to the right as signed + // (essentially fills with FF on the left). + (((value << shift) as i128) >> shift) as u128 +} + +/// Truncates `value` to `size` bits. +#[inline] +pub fn truncate(value: u128, size: Size) -> u128 { + let size = size.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + let shift = 128 - size; + // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). + (value << shift) >> shift +} + +/// Computes the unsigned absolute value without wrapping or panicking. +#[inline] +pub fn uabs(value: i64) -> u64 { + // The only tricky part here is if value == i64::MIN. In that case, + // wrapping_abs() returns i64::MIN == -2^63. Casting this value to a u64 + // gives 2^63, the correct value. + value.wrapping_abs() as u64 +} diff --git a/src/librustc_middle/mir/interpret/pointer.rs b/src/librustc_middle/mir/interpret/pointer.rs new file mode 100644 index 0000000000000..ccad4f0a135a1 --- /dev/null +++ b/src/librustc_middle/mir/interpret/pointer.rs @@ -0,0 +1,208 @@ +use super::{uabs, 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_min(&self) -> i64 { + let max_isize_plus_1 = 1i128 << (self.pointer_size().bits() - 1); + i64::try_from(-max_isize_plus_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) { + // We do not need to check if i fits in a machine usize. If it doesn't, + // either the wrapping_add will wrap or res will not fit in a pointer. + let res = val.overflowing_add(i); + self.truncate_to_ptr(res) + } + + #[inline] + fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) { + // We need to make sure that i fits in a machine isize. + let n = uabs(i); + if i >= 0 { + let (val, over) = self.overflowing_offset(val, n); + (val, over || i > self.machine_isize_max()) + } else { + let res = val.overflowing_sub(n); + let (val, over) = self.truncate_to_ptr(res); + (val, over || i < self.machine_isize_min()) + } + } + + #[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 78% rename from src/librustc/mir/interpret/value.rs rename to src/librustc_middle/mir/interpret/value.rs index 1e630c96dd409..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}; @@ -23,23 +23,12 @@ pub struct RawConst<'tcx> { /// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for /// array length computations, enum discriminants and the pattern matching logic. -#[derive( - Copy, - Clone, - Debug, - Eq, - PartialEq, - PartialOrd, - Ord, - RustcEncodable, - RustcDecodable, - Hash, - HashStable -)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] +#[derive(HashStable)] 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` @@ -98,19 +87,9 @@ impl<'tcx> ConstValue<'tcx> { /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes /// of a simple value or a pointer into another `Allocation` -#[derive( - Clone, - Copy, - Eq, - PartialEq, - Ord, - PartialOrd, - RustcEncodable, - RustcDecodable, - Hash, - HashStable -)] -pub enum Scalar { +#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] +#[derive(HashStable)] +pub enum Scalar { /// The raw bytes of a simple value. Raw { /// The first `size` bytes of `data` are the value. @@ -122,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), @@ -146,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), } } } @@ -177,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, @@ -210,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 } } @@ -219,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] @@ -302,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] @@ -397,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) } @@ -415,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) } @@ -455,10 +440,11 @@ impl<'tcx, Tag> Scalar { } pub fn to_bool(self) -> InterpResult<'tcx, bool> { - match self { - Scalar::Raw { data: 0, size: 1 } => Ok(false), - Scalar::Raw { data: 1, size: 1 } => Ok(true), - _ => throw_unsup!(InvalidBool), + let val = self.to_u8()?; + match val { + 0 => Ok(false), + 1 => Ok(true), + _ => throw_ub!(InvalidBool(val)), } } @@ -466,7 +452,7 @@ impl<'tcx, Tag> Scalar { let val = self.to_u32()?; match ::std::char::from_u32(val) { Some(c) => Ok(c), - None => throw_unsup!(InvalidChar(val as u128)), + None => throw_ub!(InvalidChar(val)), } } @@ -478,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] @@ -510,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())) } } @@ -556,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_unsup!(ReadUndefBytes(Size::ZERO)), + ScalarMaybeUninit::Scalar(scalar) => Ok(scalar), + ScalarMaybeUninit::Uninit => throw_ub!(InvalidUninitBytes(None)), } } @@ -691,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..d89c35d313ca3 --- /dev/null +++ b/src/librustc_middle/mir/mod.rs @@ -0,0 +1,2938 @@ +//! 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::abi; +use rustc_target::asm::InlineAsmRegOrRegClass; +use std::borrow::Cow; +use std::fmt::{self, Debug, Display, Formatter, Write}; +use std::ops::{Index, IndexMut}; +use std::slice; +use std::{iter, mem, option}; + +use self::predecessors::{PredecessorCache, Predecessors}; +pub use self::query::*; + +pub mod 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, + DropElab = 3, + Optimized = 4, +} + +impl MirPhase { + /// Gets the index of the current MirPhase within the set of all `MirPhase`s. + pub fn phase_index(&self) -> usize { + *self as usize + } +} + +/// The lowered representation of a single function. +#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable, TypeFoldable)] +pub struct Body<'tcx> { + /// A list of basic blocks. References to basic block use a newtyped index type `BasicBlock` + /// that indexes into this vector. + basic_blocks: IndexVec>, + + /// Records how far through the "desugaring and optimization" process this particular + /// MIR has traversed. This is particularly useful when inlining, since in that context + /// we instantiate the promoted constants and add them to our promoted vector -- but those + /// promoted items have already been optimized, whereas ours have not. This field allows + /// us to see the difference and forego optimization on the inlined promoted items. + pub phase: MirPhase, + + /// A list of source scopes; these are referenced by statements + /// and used for debuginfo. Indexed by a `SourceScope`. + pub source_scopes: IndexVec, + + /// The yield type of the function, if it is a generator. + pub yield_ty: Option>, + + /// Generator drop glue. + pub generator_drop: Option>>, + + /// The layout of a generator. Produced by the state transformation. + pub generator_layout: Option>, + + /// If this is a generator then record the type of source expression that caused this generator + /// to be created. + pub generator_kind: Option, + + /// Declarations of locals. + /// + /// The first local is the return value pointer, followed by `arg_count` + /// locals for the function arguments, followed by any user-declared + /// variables and temporaries. + pub local_decls: LocalDecls<'tcx>, + + /// User type annotations. + pub user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, + + /// The number of arguments this function takes. + /// + /// Starting at local 1, `arg_count` locals will be provided by the caller + /// and can be assumed to be initialized. + /// + /// If this MIR was built for a constant, this will be 0. + pub arg_count: usize, + + /// Mark an argument local (which must be a tuple) as getting passed as + /// its individual components at the LLVM level. + /// + /// This is used for the "rust-call" ABI. + pub spread_arg: Option, + + /// Debug information pertaining to user variables, including captures. + pub var_debug_info: Vec>, + + /// 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, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub enum Safety { + Safe, + /// Unsafe because of a PushUnsafeBlock + BuiltinUnsafe, + /// Unsafe because of an unsafe fn + FnUnsafe, + /// Unsafe because of an `unsafe` block + ExplicitUnsafe(hir::HirId), +} + +impl<'tcx> Index for Body<'tcx> { + type Output = BasicBlockData<'tcx>; + + #[inline] + fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> { + &self.basic_blocks()[index] + } +} + +impl<'tcx> IndexMut for Body<'tcx> { + #[inline] + fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { + &mut self.basic_blocks_mut()[index] + } +} + +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable)] +pub enum ClearCrossCrate { + Clear, + Set(T), +} + +impl ClearCrossCrate { + pub fn as_ref(&self) -> ClearCrossCrate<&T> { + match self { + ClearCrossCrate::Clear => ClearCrossCrate::Clear, + ClearCrossCrate::Set(v) => ClearCrossCrate::Set(v), + } + } + + pub fn assert_crate_local(self) -> T { + match self { + ClearCrossCrate::Clear => bug!("unwrapping cross-crate data"), + ClearCrossCrate::Set(v) => v, + } + } +} + +const TAG_CLEAR_CROSS_CRATE_CLEAR: u8 = 0; +const TAG_CLEAR_CROSS_CRATE_SET: u8 = 1; + +impl rustc_serialize::UseSpecializedEncodable for ClearCrossCrate { + #[inline] + fn default_encode(&self, e: &mut E) -> Result<(), E::Error> { + match *self { + ClearCrossCrate::Clear => TAG_CLEAR_CROSS_CRATE_CLEAR.encode(e), + ClearCrossCrate::Set(ref val) => { + TAG_CLEAR_CROSS_CRATE_SET.encode(e)?; + val.encode(e) + } + } + } +} +impl rustc_serialize::UseSpecializedDecodable for ClearCrossCrate { + #[inline] + fn default_decode(d: &mut D) -> Result, D::Error> + where + D: rustc_serialize::Decoder, + { + let discr = u8::decode(d)?; + + match discr { + TAG_CLEAR_CROSS_CRATE_CLEAR => Ok(ClearCrossCrate::Clear), + TAG_CLEAR_CROSS_CRATE_SET => { + let val = T::decode(d)?; + Ok(ClearCrossCrate::Set(val)) + } + _ => unreachable!(), + } + } +} + +/// Grouped information about the source code origin of a MIR entity. +/// Intended to be inspected by diagnostics and debuginfo. +/// Most passes can work with it as a whole, within a single function. +// The unofficial Cranelift backend, at least as of #65828, needs `SourceInfo` to implement `Eq` and +// `Hash`. Please ping @bjorn3 if removing them. +#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash, HashStable)] +pub struct SourceInfo { + /// The source span for the AST pertaining to this MIR entity. + pub span: Span, + + /// The source scope, keeping track of which bindings can be + /// seen by debuginfo, active lint levels, `unsafe {...}`, etc. + pub scope: SourceScope, +} + +impl SourceInfo { + #[inline] + pub fn outermost(span: Span) -> Self { + SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Borrow kinds + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + Shared, + + /// The immediately borrowed place must be immutable, but projections from + /// it don't need to be. For example, a shallow borrow of `a.b` doesn't + /// conflict with a mutable borrow of `a.b.c`. + /// + /// This is used when lowering matches: when matching on a place we want to + /// ensure that place have the same value from the start of the match until + /// an arm is selected. This prevents this code from compiling: + /// + /// let mut x = &Some(0); + /// match *x { + /// None => (), + /// Some(_) if { x = &None; false } => (), + /// Some(_) => (), + /// } + /// + /// This can't be a shared borrow because mutably borrowing (*x as Some).0 + /// should not prevent `if let None = x { ... }`, for example, because the + /// mutating `(*x as Some).0` can't affect the discriminant of `x`. + /// We can also report errors with this kind of borrow differently. + Shallow, + + /// Data must be immutable but not aliasable. This kind of borrow + /// cannot currently be expressed by the user and is used only in + /// implicit closure bindings. It is needed when the closure is + /// borrowing or mutating a mutable referent, e.g.: + /// + /// let x: &mut isize = ...; + /// let y = || *x += 5; + /// + /// If we were to try to translate this closure into a more explicit + /// form, we'd encounter an error with the code as written: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// This is then illegal because you cannot mutate an `&mut` found + /// in an aliasable location. To solve, you'd have to translate with + /// an `&mut` borrow: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// Now the assignment to `**env.x` is legal, but creating a + /// mutable pointer to `x` is not because `x` is not mutable. We + /// could fix this by declaring `x` as `let mut x`. This is ok in + /// user code, if awkward, but extra weird for closures, since the + /// borrow is hidden. + /// + /// So we introduce a "unique imm" borrow -- the referent is + /// immutable, but not aliasable. This solves the problem. For + /// simplicity, we don't give users the way to express this + /// borrow, it's just used when translating closures. + Unique, + + /// Data is mutable and not aliasable. + Mut { + /// `true` if this borrow arose from method-call auto-ref + /// (i.e., `adjustment::Adjust::Borrow`). + allow_two_phase_borrow: bool, + }, +} + +impl BorrowKind { + pub fn allows_two_phase_borrow(&self) -> bool { + match *self { + BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false, + BorrowKind::Mut { allow_two_phase_borrow } => allow_two_phase_borrow, + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Variables and temps + +rustc_index::newtype_index! { + pub struct Local { + derive [HashStable] + DEBUG_FORMAT = "_{}", + const RETURN_PLACE = 0, + } +} + +impl Atom for Local { + fn index(self) -> usize { + Idx::index(self) + } +} + +/// Classifies locals into categories. See `Body::local_kind`. +#[derive(PartialEq, Eq, Debug, HashStable)] +pub enum LocalKind { + /// User-declared variable binding. + Var, + /// Compiler-introduced temporary. + Temp, + /// Function argument. + Arg, + /// Location of function's return value. + ReturnPointer, +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct VarBindingForm<'tcx> { + /// Is variable bound via `x`, `mut x`, `ref x`, or `ref mut x`? + pub binding_mode: ty::BindingMode, + /// If an explicit type was provided for this variable binding, + /// this holds the source Span of that type. + /// + /// NOTE: if you want to change this to a `HirId`, be wary that + /// doing so breaks incremental compilation (as of this writing), + /// while a `Span` does not cause our tests to fail. + pub opt_ty_info: Option, + /// Place of the RHS of the =, or the subject of the `match` where this + /// variable is initialized. None in the case of `let PATTERN;`. + /// Some((None, ..)) in the case of and `let [mut] x = ...` because + /// (a) the right-hand side isn't evaluated as a place expression. + /// (b) it gives a way to separate this case from the remaining cases + /// for diagnostics. + pub opt_match_place: Option<(Option>, Span)>, + /// The span of the pattern in which this variable was bound. + pub pat_span: Span, +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] +pub enum BindingForm<'tcx> { + /// This is a binding for a non-`self` binding, or a `self` that has an explicit type. + Var(VarBindingForm<'tcx>), + /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit. + ImplicitSelf(ImplicitSelfKind), + /// Reference used in a guard expression to ensure immutability. + RefForGuard, +} + +/// Represents what type of implicit self a function has, if any. +#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub enum ImplicitSelfKind { + /// Represents a `fn x(self);`. + Imm, + /// Represents a `fn x(mut self);`. + Mut, + /// Represents a `fn x(&self);`. + ImmRef, + /// Represents a `fn x(&mut self);`. + MutRef, + /// Represents when a function does not have a self argument or + /// when a function has a `self: X` argument. + None, +} + +CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, } + +mod binding_form_impl { + use crate::ich::StableHashingContext; + use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + + impl<'a, 'tcx> HashStable> for super::BindingForm<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + use super::BindingForm::*; + ::std::mem::discriminant(self).hash_stable(hcx, hasher); + + match self { + Var(binding) => binding.hash_stable(hcx, hasher), + ImplicitSelf(kind) => kind.hash_stable(hcx, hasher), + RefForGuard => (), + } + } + } +} + +/// `BlockTailInfo` is attached to the `LocalDecl` for temporaries +/// created during evaluation of expressions in a block tail +/// expression; that is, a block like `{ STMT_1; STMT_2; EXPR }`. +/// +/// It is used to improve diagnostics when such temporaries are +/// involved in borrow_check errors, e.g., explanations of where the +/// temporaries come from, when their destructors are run, and/or how +/// one might revise the code to satisfy the borrow checker's rules. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct BlockTailInfo { + /// If `true`, then the value resulting from evaluating this tail + /// expression is ignored by the block's expression context. + /// + /// Examples include `{ ...; tail };` and `let _ = { ...; tail };` + /// but not e.g., `let _x = { ...; tail };` + pub tail_result_is_ignored: bool, + + /// `Span` of the tail expression. + pub span: Span, +} + +/// A MIR local. +/// +/// This can be a binding declared by the user, a temporary inserted by the compiler, a function +/// argument, or the return place. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct LocalDecl<'tcx> { + /// Whether this is a mutable minding (i.e., `let x` or `let mut x`). + /// + /// Temporaries and the return place are always mutable. + pub mutability: Mutability, + + // FIXME(matthewjasper) Don't store in this in `Body` + pub local_info: Option>>, + + /// `true` if this is an internal local. + /// + /// These locals are not based on types in the source code and are only used + /// for a few desugarings at the moment. + /// + /// The generator transformation will sanity check the locals which are live + /// across a suspension point against the type components of the generator + /// which type checking knows are live across a suspension point. We need to + /// flag drop flags to avoid triggering this check as they are introduced + /// after typeck. + /// + /// Unsafety checking will also ignore dereferences of these locals, + /// so they can be used for raw pointers only used in a desugaring. + /// + /// This should be sound because the drop flags are fully algebraic, and + /// therefore don't affect the OIBIT or outlives properties of the + /// generator. + pub internal: bool, + + /// If this local is a temporary and `is_block_tail` is `Some`, + /// then it is a temporary created for evaluation of some + /// subexpression of some block's tail expression (with no + /// intervening statement context). + // FIXME(matthewjasper) Don't store in this in `Body` + pub is_block_tail: Option, + + /// The type of this local. + pub ty: Ty<'tcx>, + + /// If the user manually ascribed a type to this variable, + /// e.g., via `let x: T`, then we carry that type here. The MIR + /// borrow checker needs this information since it can affect + /// region inference. + // FIXME(matthewjasper) Don't store in this in `Body` + pub user_ty: Option>, + + /// The *syntactic* (i.e., not visibility) source scope the local is defined + /// in. If the local was defined in a let-statement, this + /// is *within* the let-statement, rather than outside + /// of it. + /// + /// This is needed because the visibility source scope of locals within + /// a let-statement is weird. + /// + /// The reason is that we want the local to be *within* the let-statement + /// for lint purposes, but we want the local to be *after* the let-statement + /// for names-in-scope purposes. + /// + /// That's it, if we have a let-statement like the one in this + /// function: + /// + /// ``` + /// fn foo(x: &str) { + /// #[allow(unused_mut)] + /// let mut x: u32 = { // <- one unused mut + /// let mut y: u32 = x.parse().unwrap(); + /// y + 2 + /// }; + /// drop(x); + /// } + /// ``` + /// + /// Then, from a lint point of view, the declaration of `x: u32` + /// (and `y: u32`) are within the `#[allow(unused_mut)]` scope - the + /// lint scopes are the same as the AST/HIR nesting. + /// + /// However, from a name lookup point of view, the scopes look more like + /// as if the let-statements were `match` expressions: + /// + /// ``` + /// fn foo(x: &str) { + /// match { + /// match x.parse().unwrap() { + /// y => y + 2 + /// } + /// } { + /// x => drop(x) + /// }; + /// } + /// ``` + /// + /// We care about the name-lookup scopes for debuginfo - if the + /// debuginfo instruction pointer is at the call to `x.parse()`, we + /// want `x` to refer to `x: &str`, but if it is at the call to + /// `drop(x)`, we want it to refer to `x: u32`. + /// + /// To allow both uses to work, we need to have more than a single scope + /// for a local. We have the `source_info.scope` represent the "syntactic" + /// lint scope (with a variable being under its let block) while the + /// `var_debug_info.source_info.scope` represents the "local variable" + /// scope (where the "rest" of a block is under all prior let-statements). + /// + /// The end result looks like this: + /// + /// ```text + /// ROOT SCOPE + /// │{ argument x: &str } + /// │ + /// │ │{ #[allow(unused_mut)] } // This is actually split into 2 scopes + /// │ │ // in practice because I'm lazy. + /// │ │ + /// │ │← x.source_info.scope + /// │ │← `x.parse().unwrap()` + /// │ │ + /// │ │ │← y.source_info.scope + /// │ │ + /// │ │ │{ let y: u32 } + /// │ │ │ + /// │ │ │← y.var_debug_info.source_info.scope + /// │ │ │← `y + 2` + /// │ + /// │ │{ let x: u32 } + /// │ │← x.var_debug_info.source_info.scope + /// │ │← `drop(x)` // This accesses `x: u32`. + /// ``` + pub source_info: SourceInfo, +} + +// `LocalDecl` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(LocalDecl<'_>, 56); + +/// Extra information about a some locals that's used for diagnostics. (Not +/// used for non-StaticRef temporaries, the return place, or anonymous function +/// parameters.) +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub enum LocalInfo<'tcx> { + /// A user-defined local variable or function parameter + /// + /// The `BindingForm` is solely used for local diagnostics when generating + /// warnings/errors when compiling the current crate, and therefore it need + /// not be visible across crates. + User(ClearCrossCrate>), + /// A temporary created that references the static with the given `DefId`. + StaticRef { def_id: DefId, is_thread_local: bool }, +} + +impl<'tcx> LocalDecl<'tcx> { + /// Returns `true` only if local is a binding that can itself be + /// made mutable via the addition of the `mut` keyword, namely + /// something like the occurrences of `x` in: + /// - `fn foo(x: Type) { ... }`, + /// - `let x = ...`, + /// - or `match ... { C(x) => ... }` + pub fn can_be_made_mutable(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + })))) => true, + + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( + ImplicitSelfKind::Imm, + )))) => true, + + _ => false, + } + } + + /// Returns `true` if local is definitely not a `ref ident` or + /// `ref mut ident` binding. (Such bindings cannot be made into + /// mutable bindings, but the inverse does not necessarily hold). + pub fn is_nonref_binding(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + })))) => true, + + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(_)))) => true, + + _ => false, + } + } + + /// Returns `true` if this variable is a named variable or function + /// parameter declared by the user. + #[inline] + pub fn is_user_variable(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(_)) => true, + _ => false, + } + } + + /// Returns `true` if this is a reference to a variable bound in a `match` + /// expression that is used to access said variable for the guard of the + /// match arm. + pub fn is_ref_for_guard(&self) -> bool { + match self.local_info { + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))) => true, + _ => false, + } + } + + /// Returns `Some` if this is a reference to a static item that is used to + /// access that static + pub fn is_ref_to_static(&self) -> bool { + match self.local_info { + Some(box LocalInfo::StaticRef { .. }) => true, + _ => false, + } + } + + /// Returns `Some` if this is a reference to a static item that is used to + /// access that static + pub fn is_ref_to_thread_local(&self) -> bool { + match self.local_info { + Some(box LocalInfo::StaticRef { is_thread_local, .. }) => is_thread_local, + _ => false, + } + } + + /// Returns `true` is the local is from a compiler desugaring, e.g., + /// `__next` from a `for` loop. + #[inline] + pub fn from_compiler_desugaring(&self) -> bool { + self.source_info.span.desugaring_kind().is_some() + } + + /// Creates a new `LocalDecl` for a temporary: mutable, non-internal. + #[inline] + pub fn new(ty: Ty<'tcx>, span: Span) -> Self { + Self::with_source_info(ty, SourceInfo::outermost(span)) + } + + /// Like `LocalDecl::new`, but takes a `SourceInfo` instead of a `Span`. + #[inline] + pub fn with_source_info(ty: Ty<'tcx>, source_info: SourceInfo) -> Self { + LocalDecl { + mutability: Mutability::Mut, + local_info: None, + internal: false, + is_block_tail: None, + ty, + user_ty: None, + source_info, + } + } + + /// Converts `self` into same `LocalDecl` except tagged as internal. + #[inline] + pub fn internal(mut self) -> Self { + self.internal = true; + self + } + + /// Converts `self` into same `LocalDecl` except tagged as immutable. + #[inline] + pub fn immutable(mut self) -> Self { + self.mutability = Mutability::Not; + self + } + + /// Converts `self` into same `LocalDecl` except tagged as internal temporary. + #[inline] + pub fn block_tail(mut self, info: BlockTailInfo) -> Self { + assert!(self.is_block_tail.is_none()); + self.is_block_tail = Some(info); + self + } +} + +/// Debug information pertaining to a user variable. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct VarDebugInfo<'tcx> { + pub name: Symbol, + + /// Source info of the user variable, including the scope + /// within which the variable is visible (to debuginfo) + /// (see `LocalDecl`'s `source_info` field for more details). + pub source_info: SourceInfo, + + /// Where the data for this user variable is to be found. + /// NOTE(eddyb) There's an unenforced invariant that this `Place` is + /// based on a `Local`, not a `Static`, and contains no indexing. + pub place: Place<'tcx>, +} + +/////////////////////////////////////////////////////////////////////////// +// BasicBlock + +rustc_index::newtype_index! { + pub struct BasicBlock { + derive [HashStable] + DEBUG_FORMAT = "bb{}", + const START_BLOCK = 0, + } +} + +impl BasicBlock { + pub fn start_location(self) -> Location { + Location { block: self, statement_index: 0 } + } +} + +/////////////////////////////////////////////////////////////////////////// +// BasicBlockData and Terminator + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct BasicBlockData<'tcx> { + /// List of statements in this block. + pub statements: Vec>, + + /// Terminator for this block. + /// + /// N.B., this should generally ONLY be `None` during construction. + /// Therefore, you should generally access it via the + /// `terminator()` or `terminator_mut()` methods. The only + /// exception is that certain passes, such as `simplify_cfg`, swap + /// out the terminator temporarily with `None` while they continue + /// to recurse over the set of basic blocks. + pub terminator: Option>, + + /// If true, this block lies on an unwind path. This is used + /// during codegen where distinct kinds of basic blocks may be + /// generated (particularly for MSVC cleanup). Unwind blocks must + /// only branch to other unwind blocks. + pub is_cleanup: bool, +} + +#[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. + /// This is always the same as the type of `discr`. + /// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing. + switch_ty: Ty<'tcx>, + + /// Possible values. The locations to branch to in each case + /// are found in the corresponding indices from the `targets` vector. + values: Cow<'tcx, [u128]>, + + /// Possible branch sites. The last element of this vector is used + /// for the otherwise branch, so targets.len() == values.len() + 1 + /// should hold. + // + // This invariant is quite non-obvious and also could be improved. + // One way to make this invariant is to have something like this instead: + // + // branches: Vec<(ConstInt, BasicBlock)>, + // otherwise: Option // exhaustive if None + // + // However we’ve decided to keep this as-is until we figure a case + // where some other approach seems to be strictly better than other. + targets: Vec, + }, + + /// Indicates that the landing pad is finished and unwinding should + /// continue. Emitted by `build::scope::diverge_cleanup`. + Resume, + + /// Indicates that the landing pad is finished and that the process + /// should abort. Used to prevent unwinding for foreign items. + Abort, + + /// Indicates a normal return. The return place should have + /// been filled in before this executes. This can occur multiple times + /// in different basic blocks. + Return, + + /// Indicates a terminator that can never be reached. + Unreachable, + + /// Drop the `Place`. + Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option }, + + /// Drop the `Place` and assign the new value over it. This ensures + /// that the assignment to `P` occurs *even if* the destructor for + /// place unwinds. Its semantics are best explained by the + /// elaboration: + /// + /// ``` + /// BB0 { + /// DropAndReplace(P <- V, goto BB1, unwind BB2) + /// } + /// ``` + /// + /// becomes + /// + /// ``` + /// BB0 { + /// Drop(P, goto BB1, unwind BB2) + /// } + /// BB1 { + /// // P is now uninitialized + /// P <- V + /// } + /// BB2 { + /// // P is now uninitialized -- its dtor panicked + /// P <- V + /// } + /// ``` + DropAndReplace { + place: Place<'tcx>, + value: Operand<'tcx>, + target: BasicBlock, + unwind: Option, + }, + + /// Block ends with a call of a converging function. + Call { + /// The function that’s being called. + func: Operand<'tcx>, + /// Arguments the function is called with. + /// These are owned by the callee, which is free to modify them. + /// This allows the memory occupied by "by-value" arguments to be + /// reused across function calls without duplicating the contents. + args: Vec>, + /// Destination for the return value. If some, the call is converging. + destination: Option<(Place<'tcx>, BasicBlock)>, + /// Cleanups to be done if the call unwinds. + cleanup: Option, + /// `true` if this is from a call in HIR rather than from an overloaded + /// operator. True for overloaded function call. + from_hir_call: bool, + /// This `Span` is the span of the function, without the dot and receiver + /// (e.g. `foo(a, b)` in `x.foo(a, b)` + fn_span: Span, + }, + + /// Jump to the target if the condition has the expected value, + /// otherwise panic with a message and a cleanup target. + Assert { + cond: Operand<'tcx>, + expected: bool, + msg: AssertMessage<'tcx>, + target: BasicBlock, + cleanup: Option, + }, + + /// A suspend point. + Yield { + /// The value to return. + value: Operand<'tcx>, + /// Where to resume to. + resume: BasicBlock, + /// The place to store the resume argument in. + resume_arg: Place<'tcx>, + /// Cleanup to be done if the generator is dropped at this suspend point. + drop: Option, + }, + + /// Indicates the end of the dropping of a generator. + GeneratorDrop, + + /// A block where control flow only ever takes one real path, but borrowck + /// needs to be more conservative. + FalseEdge { + /// The target normal control flow will take. + real_target: BasicBlock, + /// A block control flow could conceptually jump to, but won't in + /// practice. + imaginary_target: BasicBlock, + }, + /// A terminator for blocks that only take one path in reality, but where we + /// reserve the right to unwind in borrowck, even if it won't happen in practice. + /// This can arise in infinite loops with no function calls for example. + FalseUnwind { + /// The target normal control flow will take. + real_target: BasicBlock, + /// The imaginary cleanup block link. This particular path will never be taken + /// in practice, but in order to avoid fragility we want to always + /// consider it in borrowck. We don't want to accept programs which + /// pass borrowck only when `panic=abort` or some assertions are disabled + /// due to release vs. debug mode builds. This needs to be an `Option` because + /// of the `remove_noop_landing_pads` and `no_landing_pads` passes. + unwind: Option, + }, + + /// Block ends with an inline assembly block. This is a terminator since + /// inline assembly is allowed to diverge. + InlineAsm { + /// The template for the inline assembly, with placeholders. + template: &'tcx [InlineAsmTemplatePiece], + + /// The operands for the inline assembly, as `Operand`s or `Place`s. + operands: Vec>, + + /// Miscellaneous options for the inline assembly. + options: InlineAsmOptions, + + /// Source spans for each line of the inline assembly code. These are + /// used to map assembler errors back to the line in the source code. + line_spans: &'tcx [Span], + + /// Destination block after the inline assembly returns, unless it is + /// diverging (InlineAsmOptions::NORETURN). + destination: Option, + }, +} + +/// 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 { + def_id: DefId, + }, +} + +/// Type for MIR `Assert` terminator error messages. +pub type AssertMessage<'tcx> = AssertKind>; + +pub type Successors<'a> = + iter::Chain, slice::Iter<'a, BasicBlock>>; +pub type SuccessorsMut<'a> = + iter::Chain, slice::IterMut<'a, BasicBlock>>; + +impl<'tcx> Terminator<'tcx> { + pub fn successors(&self) -> Successors<'_> { + self.kind.successors() + } + + pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { + self.kind.successors_mut() + } + + pub fn unwind(&self) -> Option<&Option> { + self.kind.unwind() + } + + pub fn unwind_mut(&mut self) -> Option<&mut Option> { + self.kind.unwind_mut() + } +} + +impl<'tcx> TerminatorKind<'tcx> { + pub fn if_( + tcx: TyCtxt<'tcx>, + cond: Operand<'tcx>, + t: BasicBlock, + f: BasicBlock, + ) -> TerminatorKind<'tcx> { + static BOOL_SWITCH_FALSE: &[u128] = &[0]; + TerminatorKind::SwitchInt { + discr: cond, + switch_ty: tcx.types.bool, + values: From::from(BOOL_SWITCH_FALSE), + targets: vec![f, t], + } + } + + pub fn successors(&self) -> Successors<'_> { + use self::TerminatorKind::*; + match *self { + Resume + | Abort + | GeneratorDrop + | Return + | Unreachable + | Call { destination: None, cleanup: None, .. } + | InlineAsm { destination: None, .. } => None.into_iter().chain(&[]), + Goto { target: ref t } + | Call { destination: None, cleanup: Some(ref t), .. } + | Call { destination: Some((_, ref t)), cleanup: None, .. } + | Yield { resume: ref t, drop: None, .. } + | DropAndReplace { target: ref t, unwind: None, .. } + | Drop { target: ref t, unwind: None, .. } + | Assert { target: ref t, cleanup: None, .. } + | FalseUnwind { real_target: ref t, unwind: None } + | InlineAsm { destination: Some(ref t), .. } => Some(t).into_iter().chain(&[]), + Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. } + | Yield { resume: ref t, drop: Some(ref u), .. } + | DropAndReplace { target: ref t, unwind: Some(ref u), .. } + | Drop { target: ref t, unwind: Some(ref u), .. } + | Assert { target: ref t, cleanup: Some(ref u), .. } + | FalseUnwind { real_target: ref t, unwind: Some(ref u) } => { + Some(t).into_iter().chain(slice::from_ref(u)) + } + SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]), + FalseEdge { ref real_target, ref imaginary_target } => { + Some(real_target).into_iter().chain(slice::from_ref(imaginary_target)) + } + } + } + + pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { + use self::TerminatorKind::*; + match *self { + Resume + | Abort + | GeneratorDrop + | Return + | Unreachable + | Call { destination: None, cleanup: None, .. } + | InlineAsm { destination: None, .. } => None.into_iter().chain(&mut []), + Goto { target: ref mut t } + | Call { destination: None, cleanup: Some(ref mut t), .. } + | Call { destination: Some((_, ref mut t)), cleanup: None, .. } + | Yield { resume: ref mut t, drop: None, .. } + | DropAndReplace { target: ref mut t, unwind: None, .. } + | Drop { target: ref mut t, unwind: None, .. } + | Assert { target: ref mut t, cleanup: None, .. } + | FalseUnwind { real_target: ref mut t, unwind: None } + | InlineAsm { destination: Some(ref mut t), .. } => Some(t).into_iter().chain(&mut []), + Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. } + | Yield { resume: ref mut t, drop: Some(ref mut u), .. } + | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. } + | Drop { target: ref mut t, unwind: Some(ref mut u), .. } + | Assert { target: ref mut t, cleanup: Some(ref mut u), .. } + | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => { + Some(t).into_iter().chain(slice::from_mut(u)) + } + SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets[..]), + FalseEdge { ref mut real_target, ref mut imaginary_target } => { + Some(real_target).into_iter().chain(slice::from_mut(imaginary_target)) + } + } + } + + pub fn unwind(&self) -> Option<&Option> { + match *self { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop + | TerminatorKind::Yield { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::InlineAsm { .. } => None, + TerminatorKind::Call { cleanup: ref unwind, .. } + | TerminatorKind::Assert { cleanup: ref unwind, .. } + | TerminatorKind::DropAndReplace { ref unwind, .. } + | TerminatorKind::Drop { ref unwind, .. } + | TerminatorKind::FalseUnwind { ref unwind, .. } => Some(unwind), + } + } + + pub fn unwind_mut(&mut self) -> Option<&mut Option> { + match *self { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop + | TerminatorKind::Yield { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::InlineAsm { .. } => None, + TerminatorKind::Call { cleanup: ref mut unwind, .. } + | TerminatorKind::Assert { cleanup: ref mut unwind, .. } + | TerminatorKind::DropAndReplace { ref mut unwind, .. } + | TerminatorKind::Drop { ref mut unwind, .. } + | TerminatorKind::FalseUnwind { ref mut unwind, .. } => Some(unwind), + } + } +} + +impl<'tcx> 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 { place, .. } => write!(fmt, "drop({:?})", place), + DropAndReplace { place, value, .. } => { + write!(fmt, "replace({:?} <- {:?})", place, value) + } + Call { func, args, destination, .. } => { + if let Some((destination, _)) = destination { + write!(fmt, "{:?} = ", destination)?; + } + write!(fmt, "{:?}(", func)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(fmt, ", ")?; + } + write!(fmt, "{:?}", arg)?; + } + write!(fmt, ")") + } + Assert { cond, expected, msg, .. } => { + write!(fmt, "assert(")?; + if !expected { + write!(fmt, "!")?; + } + write!(fmt, "{:?}, ", cond)?; + msg.fmt_assert_args(fmt)?; + write!(fmt, ")") + } + FalseEdge { .. } => write!(fmt, "falseEdge"), + FalseUnwind { .. } => write!(fmt, "falseUnwind"), + InlineAsm { template, ref operands, options, .. } => { + write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?; + for op in operands { + write!(fmt, ", ")?; + let print_late = |&late| if late { "late" } else { "" }; + match op { + InlineAsmOperand::In { reg, value } => { + write!(fmt, "in({}) {:?}", reg, value)?; + } + InlineAsmOperand::Out { reg, late, place: Some(place) } => { + write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?; + } + InlineAsmOperand::Out { reg, late, place: None } => { + write!(fmt, "{}out({}) _", print_late(late), reg)?; + } + InlineAsmOperand::InOut { + reg, + late, + in_value, + out_place: Some(out_place), + } => { + write!( + fmt, + "in{}out({}) {:?} => {:?}", + print_late(late), + reg, + in_value, + out_place + )?; + } + InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => { + write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?; + } + InlineAsmOperand::Const { value } => { + write!(fmt, "const {:?}", value)?; + } + InlineAsmOperand::SymFn { value } => { + write!(fmt, "sym_fn {:?}", value)?; + } + InlineAsmOperand::SymStatic { def_id } => { + write!(fmt, "sym_static {:?}", def_id)?; + } + } + } + write!(fmt, ", options({:?}))", options) + } + } + } + + /// Returns the list of labels for the edges to the successor basic blocks. + pub fn fmt_successor_labels(&self) -> Vec> { + use self::TerminatorKind::*; + match *self { + Return | Resume | Abort | Unreachable | GeneratorDrop => vec![], + Goto { .. } => vec!["".into()], + SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| { + let param_env = ty::ParamEnv::empty(); + let switch_ty = tcx.lift(&switch_ty).unwrap(); + let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; + values + .iter() + .map(|&u| { + ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty) + .to_string() + .into() + }) + .chain(iter::once("otherwise".into())) + .collect() + }), + Call { destination: Some(_), cleanup: Some(_), .. } => { + vec!["return".into(), "unwind".into()] + } + Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()], + Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()], + Call { destination: None, cleanup: None, .. } => vec![], + Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()], + Yield { drop: None, .. } => vec!["resume".into()], + DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => { + vec!["return".into()] + } + DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => { + vec!["return".into(), "unwind".into()] + } + Assert { cleanup: None, .. } => vec!["".into()], + Assert { .. } => vec!["success".into(), "unwind".into()], + FalseEdge { .. } => vec!["real".into(), "imaginary".into()], + FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()], + FalseUnwind { unwind: None, .. } => vec!["real".into()], + InlineAsm { destination: Some(_), .. } => vec!["".into()], + InlineAsm { destination: None, .. } => vec![], + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Statements + +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct Statement<'tcx> { + pub source_info: SourceInfo, + pub kind: StatementKind<'tcx>, +} + +// `Statement` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(Statement<'_>, 32); + +impl Statement<'_> { + /// Changes a statement to a nop. This is both faster than deleting instructions and avoids + /// invalidating statement indices in `Location`s. + pub fn make_nop(&mut self) { + self.kind = StatementKind::Nop + } + + /// Changes a statement to a nop and returns the original statement. + pub fn replace_nop(&mut self) -> Self { + Statement { + source_info: self.source_info, + kind: mem::replace(&mut self.kind, StatementKind::Nop), + } + } +} + +#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub enum StatementKind<'tcx> { + /// Write the RHS Rvalue to the LHS Place. + Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), + + /// This represents all the reading that a pattern match may do + /// (e.g., inspecting constants and discriminant values), and the + /// kind of pattern it comes from. This is in order to adapt potential + /// error messages to these specific patterns. + /// + /// Note that this also is emitted for regular `let` bindings to ensure that locals that are + /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;` + FakeRead(FakeReadCause, Box>), + + /// Write the discriminant for a variant to the enum Place. + SetDiscriminant { place: Box>, variant_index: VariantIdx }, + + /// Start a live range for the storage of the local. + StorageLive(Local), + + /// End the current live range for the storage of the local. + StorageDead(Local), + + /// Executes a piece of inline Assembly. Stored in a Box to keep the size + /// of `StatementKind` low. + LlvmInlineAsm(Box>), + + /// Retag references in the given place, ensuring they got fresh tags. This is + /// part of the Stacked Borrows model. These statements are currently only interpreted + /// by miri and only generated when "-Z mir-emit-retag" is passed. + /// See + /// for more details. + Retag(RetagKind, Box>), + + /// Encodes a user's type ascription. These need to be preserved + /// intact so that NLL can respect them. For example: + /// + /// let a: T = y; + /// + /// The effect of this annotation is to relate the type `T_y` of the place `y` + /// to the user-given type `T`. The effect depends on the specified variance: + /// + /// - `Covariant` -- requires that `T_y <: T` + /// - `Contravariant` -- requires that `T_y :> T` + /// - `Invariant` -- requires that `T_y == T` + /// - `Bivariant` -- no effect + AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance), + + /// No-op. Useful for deleting instructions without affecting statement indices. + Nop, +} + +/// Describes what kind of retag is to be performed. +#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, HashStable)] +pub enum RetagKind { + /// The initial retag when entering a function. + FnEntry, + /// Retag preparing for a two-phase borrow. + TwoPhase, + /// Retagging raw pointers. + Raw, + /// A "normal" retag. + Default, +} + +/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists. +#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, HashStable, PartialEq)] +pub enum FakeReadCause { + /// Inject a fake read of the borrowed input at the end of each guards + /// code. + /// + /// This should ensure that you cannot change the variant for an enum while + /// you are in the midst of matching on it. + ForMatchGuard, + + /// `let x: !; match x {}` doesn't generate any read of x so we need to + /// generate a read of x to check that it is initialized and safe. + ForMatchedPlace, + + /// A fake read of the RefWithinGuard version of a bind-by-value variable + /// in a match guard to ensure that it's value hasn't change by the time + /// we create the OutsideGuard version. + ForGuardBinding, + + /// Officially, the semantics of + /// + /// `let pattern = ;` + /// + /// is that `` is evaluated into a temporary and then this temporary is + /// into the pattern. + /// + /// However, if we see the simple pattern `let var = `, we optimize this to + /// evaluate `` directly into the variable `var`. This is mostly unobservable, + /// but in some cases it can affect the borrow checker, as in #53695. + /// Therefore, we insert a "fake read" here to ensure that we get + /// appropriate errors. + ForLet, + + /// If we have an index expression like + /// + /// (*x)[1][{ x = y; 4}] + /// + /// then the first bounds check is invalidated when we evaluate the second + /// index expression. Thus we create a fake borrow of `x` across the second + /// indexer, which will cause a borrow check error. + ForIndex, +} + +#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct LlvmInlineAsm<'tcx> { + pub asm: hir::LlvmInlineAsmInner, + pub outputs: Box<[Place<'tcx>]>, + pub inputs: Box<[(Span, Operand<'tcx>)]>, +} + +impl Debug for Statement<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use self::StatementKind::*; + match self.kind { + Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv), + FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place), + Retag(ref kind, ref place) => write!( + fmt, + "Retag({}{:?})", + match kind { + RetagKind::FnEntry => "[fn entry] ", + RetagKind::TwoPhase => "[2phase] ", + RetagKind::Raw => "[raw] ", + RetagKind::Default => "", + }, + place, + ), + StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place), + StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place), + SetDiscriminant { ref place, variant_index } => { + write!(fmt, "discriminant({:?}) = {:?}", place, variant_index) + } + LlvmInlineAsm(ref asm) => { + write!(fmt, "llvm_asm!({:?} : {:?} : {:?})", asm.asm, asm.outputs, asm.inputs) + } + AscribeUserType(box (ref place, ref c_ty), ref variance) => { + write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) + } + Nop => write!(fmt, "nop"), + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Places + +/// A path to a value; something that can be evaluated without +/// changing or disturbing program state. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, HashStable)] +pub struct Place<'tcx> { + pub local: Local, + + /// projection out of a place (access a field, deref a pointer, etc) + pub projection: &'tcx List>, +} + +impl<'tcx> rustc_serialize::UseSpecializedDecodable for Place<'tcx> {} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(RustcEncodable, RustcDecodable, HashStable)] +pub enum ProjectionElem { + Deref, + Field(Field, T), + Index(V), + + /// These indices are generated by slice patterns. Easiest to explain + /// by example: + /// + /// ``` + /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, + /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, + /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, + /// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true }, + /// ``` + ConstantIndex { + /// index or -index (in Python terms), depending on from_end + offset: u32, + /// The thing being indexed must be at least this long. For arrays this + /// is always the exact length. + min_length: u32, + /// Counting backwards from end? This is always false when indexing an + /// array. + from_end: bool, + }, + + /// These indices are generated by slice patterns. + /// + /// If `from_end` is true `slice[from..slice.len() - to]`. + /// Otherwise `array[from..to]`. + Subslice { + from: u32, + to: u32, + /// Whether `to` counts from the start or end of the array/slice. + /// For `PlaceElem`s this is `true` if and only if the base is a slice. + /// For `ProjectionKind`, this can also be `true` for arrays. + from_end: bool, + }, + + /// "Downcast" to a variant of an ADT. Currently, we only introduce + /// this for ADTs with more than one variant. It may be better to + /// just introduce it always, or always for enums. + /// + /// The included Symbol is the name of the variant, used for printing MIR. + Downcast(Option, VariantIdx), +} + +impl ProjectionElem { + /// Returns `true` if the target of this projection may refer to a different region of memory + /// than the base. + fn is_indirect(&self) -> bool { + match self { + Self::Deref => true, + + Self::Field(_, _) + | Self::Index(_) + | Self::ConstantIndex { .. } + | Self::Subslice { .. } + | Self::Downcast(_, _) => false, + } + } +} + +/// Alias for projections as they appear in places, where the base is a place +/// and the index is a local. +pub type PlaceElem<'tcx> = ProjectionElem>; + +// At least on 64 bit systems, `PlaceElem` should not be larger than two pointers. +#[cfg(target_arch = "x86_64")] +static_assert_size!(PlaceElem<'_>, 16); + +/// Alias for projections as they appear in `UserTypeProjection`, where we +/// need neither the `V` parameter for `Index` nor the `T` for `Field`. +pub type ProjectionKind = ProjectionElem<(), ()>; + +rustc_index::newtype_index! { + pub struct Field { + derive [HashStable] + DEBUG_FORMAT = "field[{}]" + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PlaceRef<'tcx> { + pub local: Local, + pub projection: &'tcx [PlaceElem<'tcx>], +} + +impl<'tcx> Place<'tcx> { + // FIXME change this to a const fn by also making List::empty a const fn. + pub fn return_place() -> Place<'tcx> { + Place { local: RETURN_PLACE, projection: List::empty() } + } + + /// Returns `true` if this `Place` contains a `Deref` projection. + /// + /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the + /// same region of memory as its base. + pub fn is_indirect(&self) -> bool { + self.projection.iter().any(|elem| elem.is_indirect()) + } + + /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or + /// a single deref of a local. + // + // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? + pub fn local_or_deref_local(&self) -> Option { + match self.as_ref() { + PlaceRef { local, projection: [] } + | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), + _ => None, + } + } + + /// If this place represents a local variable like `_X` with no + /// projections, return `Some(_X)`. + pub fn as_local(&self) -> Option { + self.as_ref().as_local() + } + + pub fn as_ref(&self) -> PlaceRef<'tcx> { + PlaceRef { local: self.local, projection: &self.projection } + } +} + +impl From for Place<'_> { + fn from(local: Local) -> Self { + Place { local, projection: List::empty() } + } +} + +impl<'tcx> PlaceRef<'tcx> { + /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or + /// a single deref of a local. + // + // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? + pub fn local_or_deref_local(&self) -> Option { + match *self { + PlaceRef { local, projection: [] } + | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), + _ => None, + } + } + + /// If this place represents a local variable like `_X` with no + /// projections, return `Some(_X)`. + pub fn as_local(&self) -> Option { + match *self { + PlaceRef { local, projection: [] } => Some(local), + _ => None, + } + } +} + +impl Debug for Place<'_> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + for elem in self.projection.iter().rev() { + match elem { + ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { + write!(fmt, "(").unwrap(); + } + ProjectionElem::Deref => { + write!(fmt, "(*").unwrap(); + } + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => {} + } + } + + write!(fmt, "{:?}", self.local)?; + + for elem in self.projection.iter() { + match elem { + ProjectionElem::Downcast(Some(name), _index) => { + write!(fmt, " as {})", name)?; + } + ProjectionElem::Downcast(None, index) => { + write!(fmt, " as variant#{:?})", index)?; + } + ProjectionElem::Deref => { + write!(fmt, ")")?; + } + ProjectionElem::Field(field, ty) => { + write!(fmt, ".{:?}: {:?})", field.index(), ty)?; + } + ProjectionElem::Index(ref index) => { + write!(fmt, "[{:?}]", index)?; + } + ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => { + write!(fmt, "[{:?} of {:?}]", offset, min_length)?; + } + ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => { + write!(fmt, "[-{:?} of {:?}]", offset, min_length)?; + } + ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => { + write!(fmt, "[{:?}:]", from)?; + } + ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => { + write!(fmt, "[:-{:?}]", to)?; + } + ProjectionElem::Subslice { from, to, from_end: true } => { + write!(fmt, "[{:?}:-{:?}]", from, to)?; + } + ProjectionElem::Subslice { from, to, from_end: false } => { + write!(fmt, "[{:?}..{:?}]", from, to)?; + } + } + } + + Ok(()) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Scopes + +rustc_index::newtype_index! { + pub struct SourceScope { + derive [HashStable] + DEBUG_FORMAT = "scope[{}]", + const OUTERMOST_SOURCE_SCOPE = 0, + } +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct SourceScopeData { + pub span: Span, + pub parent_scope: Option, + + /// Crate-local information for this source scope, that can't (and + /// needn't) be tracked across crates. + pub local_data: ClearCrossCrate, +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct SourceScopeLocalData { + /// An `HirId` with lint levels equivalent to this scope's lint levels. + pub lint_root: hir::HirId, + /// The unsafe block that contains this node. + pub safety: Safety, +} + +/////////////////////////////////////////////////////////////////////////// +// Operands + +/// These are values that can appear inside an rvalue. They are intentionally +/// limited to prevent rvalues from being nested in one another. +#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub enum Operand<'tcx> { + /// Copy: The value must be available for use afterwards. + /// + /// This implies that the type of the place must be `Copy`; this is true + /// by construction during build, but also checked by the MIR type checker. + Copy(Place<'tcx>), + + /// Move: The value (including old borrows of it) will not be used again. + /// + /// Safe for values of all types (modulo future developments towards `?Move`). + /// Correct usage patterns are enforced by the borrow checker for safe code. + /// `Copy` may be converted to `Move` to enable "last-use" optimizations. + Move(Place<'tcx>), + + /// Synthesizes a constant value. + Constant(Box>), +} + +impl<'tcx> Debug for Operand<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use self::Operand::*; + match *self { + Constant(ref a) => write!(fmt, "{:?}", a), + Copy(ref place) => write!(fmt, "{:?}", place), + Move(ref place) => write!(fmt, "move {:?}", place), + } + } +} + +impl<'tcx> Operand<'tcx> { + /// Convenience helper to make a constant that refers to the fn + /// with given `DefId` and substs. Since this is used to synthesize + /// MIR, assumes `user_ty` is None. + pub fn function_handle( + tcx: TyCtxt<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + span: Span, + ) -> Self { + let ty = tcx.type_of(def_id).subst(tcx, substs); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::zero_sized(tcx, ty), + }) + } + + /// Convenience helper to make a literal-like constant from a given scalar value. + /// Since this is used to synthesize MIR, assumes `user_ty` is None. + pub fn const_from_scalar( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + val: Scalar, + span: Span, + ) -> Operand<'tcx> { + debug_assert!({ + let param_env_and_ty = ty::ParamEnv::empty().and(ty); + let type_size = tcx + .layout_of(param_env_and_ty) + .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + let scalar_size = abi::Size::from_bytes(match val { + Scalar::Raw { size, .. } => size, + _ => panic!("Invalid scalar type {:?}", val), + }); + scalar_size == type_size + }); + Operand::Constant(box Constant { + span, + user_ty: None, + literal: ty::Const::from_scalar(tcx, val, ty), + }) + } + + pub fn to_copy(&self) -> Self { + match *self { + Operand::Copy(_) | Operand::Constant(_) => self.clone(), + Operand::Move(place) => Operand::Copy(place), + } + } + + /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a + /// constant. + pub fn place(&self) -> Option> { + match self { + Operand::Copy(place) | Operand::Move(place) => Some(*place), + Operand::Constant(_) => None, + } + } +} + +/////////////////////////////////////////////////////////////////////////// +/// Rvalues + +#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)] +pub enum Rvalue<'tcx> { + /// x (either a move or copy, depending on type of x) + Use(Operand<'tcx>), + + /// [x; 32] + Repeat(Operand<'tcx>, &'tcx ty::Const<'tcx>), + + /// &x or &mut x + Ref(Region<'tcx>, BorrowKind, Place<'tcx>), + + /// Accessing a thread local static. This is inherently a runtime operation, even if llvm + /// treats it as an access to a static. This `Rvalue` yields a reference to the thread local + /// static. + ThreadLocalRef(DefId), + + /// Create a raw pointer to the given place + /// Can be generated by raw address of expressions (`&raw const x`), + /// or when casting a reference to a raw pointer. + AddressOf(Mutability, Place<'tcx>), + + /// length of a `[X]` or `[X;n]` value + Len(Place<'tcx>), + + Cast(CastKind, Operand<'tcx>, Ty<'tcx>), + + BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), + CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), + + NullaryOp(NullOp, Ty<'tcx>), + UnaryOp(UnOp, Operand<'tcx>), + + /// Read the discriminant of an ADT. + /// + /// Undefined (i.e., no effort is made to make it defined, but there’s no reason why it cannot + /// be defined to return, say, a 0) if ADT is not an enum. + Discriminant(Place<'tcx>), + + /// Creates an aggregate value, like a tuple or struct. This is + /// only needed because we want to distinguish `dest = Foo { x: + /// ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case + /// that `Foo` has a destructor. These rvalues can be optimized + /// away after type-checking and before lowering. + Aggregate(Box>, Vec>), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum CastKind { + Misc, + Pointer(PointerCast), +} + +#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum AggregateKind<'tcx> { + /// The type is of the element + Array(Ty<'tcx>), + Tuple, + + /// The second field is the variant index. It's equal to 0 for struct + /// and union expressions. The fourth field is + /// active field number and is present only for union expressions + /// -- e.g., for a union expression `SomeUnion { c: .. }`, the + /// active field index would identity the field `c` + Adt(&'tcx AdtDef, VariantIdx, SubstsRef<'tcx>, Option, Option), + + Closure(DefId, SubstsRef<'tcx>), + Generator(DefId, SubstsRef<'tcx>, hir::Movability), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum BinOp { + /// The `+` operator (addition) + Add, + /// The `-` operator (subtraction) + Sub, + /// The `*` operator (multiplication) + Mul, + /// The `/` operator (division) + Div, + /// The `%` operator (modulus) + Rem, + /// The `^` operator (bitwise xor) + BitXor, + /// The `&` operator (bitwise and) + BitAnd, + /// The `|` operator (bitwise or) + BitOr, + /// The `<<` operator (shift left) + Shl, + /// The `>>` operator (shift right) + Shr, + /// The `==` operator (equality) + Eq, + /// The `<` operator (less than) + Lt, + /// The `<=` operator (less than or equal to) + Le, + /// The `!=` operator (not equal to) + Ne, + /// The `>=` operator (greater than or equal to) + Ge, + /// The `>` operator (greater than) + Gt, + /// The `ptr.offset` operator + Offset, +} + +impl BinOp { + pub fn is_checkable(self) -> bool { + use self::BinOp::*; + match self { + Add | Sub | Mul | Shl | Shr => true, + _ => false, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum NullOp { + /// Returns the size of a value of that type + SizeOf, + /// Creates a new uninitialized box for a value of that type + Box, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum UnOp { + /// The `!` operator for logical inversion + Not, + /// The `-` operator for negation + Neg, +} + +impl<'tcx> Debug for Rvalue<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use self::Rvalue::*; + + match *self { + Use(ref place) => write!(fmt, "{:?}", place), + Repeat(ref a, ref b) => { + write!(fmt, "[{:?}; ", a)?; + pretty_print_const(b, fmt, false)?; + write!(fmt, "]") + } + Len(ref a) => write!(fmt, "Len({:?})", a), + Cast(ref kind, ref place, ref ty) => { + write!(fmt, "{:?} as {:?} ({:?})", place, ty, kind) + } + BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), + CheckedBinaryOp(ref op, ref a, ref b) => { + write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) + } + UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), + Discriminant(ref place) => write!(fmt, "discriminant({:?})", place), + NullaryOp(ref op, ref t) => write!(fmt, "{:?}({:?})", op, t), + ThreadLocalRef(did) => ty::tls::with(|tcx| { + let muta = tcx.static_mutability(did).unwrap().prefix_str(); + write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did)) + }), + Ref(region, borrow_kind, ref place) => { + let kind_str = match borrow_kind { + BorrowKind::Shared => "", + BorrowKind::Shallow => "shallow ", + BorrowKind::Mut { .. } | BorrowKind::Unique => "mut ", + }; + + // When printing regions, add trailing space if necessary. + let print_region = ty::tls::with(|tcx| { + tcx.sess.verbose() || tcx.sess.opts.debugging_opts.identify_regions + }); + let region = if print_region { + let mut region = region.to_string(); + if !region.is_empty() { + region.push(' '); + } + region + } else { + // Do not even print 'static + String::new() + }; + write!(fmt, "&{}{}{:?}", region, kind_str, place) + } + + AddressOf(mutability, ref place) => { + let kind_str = match mutability { + Mutability::Mut => "mut", + Mutability::Not => "const", + }; + + write!(fmt, "&raw {} {:?}", kind_str, place) + } + + Aggregate(ref kind, ref places) => { + let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| { + let mut tuple_fmt = fmt.debug_tuple(name); + for place in places { + tuple_fmt.field(place); + } + tuple_fmt.finish() + }; + + match **kind { + AggregateKind::Array(_) => write!(fmt, "{:?}", places), + + AggregateKind::Tuple => { + if places.is_empty() { + write!(fmt, "()") + } else { + fmt_tuple(fmt, "") + } + } + + AggregateKind::Adt(adt_def, variant, substs, _user_ty, _) => { + let variant_def = &adt_def.variants[variant]; + + let name = ty::tls::with(|tcx| { + let mut name = String::new(); + let substs = tcx.lift(&substs).expect("could not lift for printing"); + FmtPrinter::new(tcx, &mut name, Namespace::ValueNS) + .print_def_path(variant_def.def_id, substs)?; + Ok(name) + })?; + + match variant_def.ctor_kind { + CtorKind::Const => fmt.write_str(&name), + CtorKind::Fn => fmt_tuple(fmt, &name), + CtorKind::Fictive => { + let mut struct_fmt = fmt.debug_struct(&name); + for (field, place) in variant_def.fields.iter().zip(places) { + struct_fmt.field(&field.ident.as_str(), place); + } + struct_fmt.finish() + } + } + } + + AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| { + if let Some(def_id) = def_id.as_local() { + let hir_id = tcx.hir().as_local_hir_id(def_id); + let name = if tcx.sess.opts.debugging_opts.span_free_formats { + let substs = tcx.lift(&substs).unwrap(); + format!( + "[closure@{}]", + tcx.def_path_str_with_substs(def_id.to_def_id(), substs), + ) + } else { + let span = tcx.hir().span(hir_id); + format!("[closure@{}]", tcx.sess.source_map().span_to_string(span)) + }; + let mut struct_fmt = fmt.debug_struct(&name); + + if let Some(upvars) = tcx.upvars_mentioned(def_id) { + for (&var_id, place) in upvars.keys().zip(places) { + let var_name = tcx.hir().name(var_id); + struct_fmt.field(&var_name.as_str(), place); + } + } + + struct_fmt.finish() + } else { + write!(fmt, "[closure]") + } + }), + + AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| { + if let Some(def_id) = def_id.as_local() { + let hir_id = tcx.hir().as_local_hir_id(def_id); + let name = format!("[generator@{:?}]", tcx.hir().span(hir_id)); + let mut struct_fmt = fmt.debug_struct(&name); + + if let Some(upvars) = tcx.upvars_mentioned(def_id) { + for (&var_id, place) in upvars.keys().zip(places) { + let var_name = tcx.hir().name(var_id); + struct_fmt.field(&var_name.as_str(), place); + } + } + + struct_fmt.finish() + } else { + write!(fmt, "[generator]") + } + }), + } + } + } + } +} + +/////////////////////////////////////////////////////////////////////////// +/// Constants +/// +/// Two constants are equal if they are the same constant. Note that +/// this does not necessarily mean that they are "==" in Rust -- in +/// particular one must be wary of `NaN`! + +#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub struct Constant<'tcx> { + pub span: Span, + + /// Optional user-given type: for something like + /// `collect::>`, this would be present and would + /// indicate that `Vec<_>` was explicitly specified. + /// + /// Needed for NLL to impose user-given type constraints. + pub user_ty: Option, + + pub literal: &'tcx ty::Const<'tcx>, +} + +impl Constant<'tcx> { + pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option { + match self.literal.val.try_to_scalar() { + Some(Scalar::Ptr(ptr)) => match tcx.global_alloc(ptr.alloc_id) { + GlobalAlloc::Static(def_id) => { + assert!(!tcx.is_thread_local_static(def_id)); + Some(def_id) + } + _ => None, + }, + _ => None, + } + } +} + +/// A collection of projections into user types. +/// +/// They are projections because a binding can occur a part of a +/// parent pattern that has been ascribed a type. +/// +/// Its a collection because there can be multiple type ascriptions on +/// the path from the root of the pattern down to the binding itself. +/// +/// An example: +/// +/// ```rust +/// struct S<'a>((i32, &'a str), String); +/// let S((_, w): (i32, &'static str), _): S = ...; +/// // ------ ^^^^^^^^^^^^^^^^^^^ (1) +/// // --------------------------------- ^ (2) +/// ``` +/// +/// The highlights labelled `(1)` show the subpattern `(_, w)` being +/// ascribed the type `(i32, &'static str)`. +/// +/// The highlights labelled `(2)` show the whole pattern being +/// ascribed the type `S`. +/// +/// In this example, when we descend to `w`, we will have built up the +/// following two projected types: +/// +/// * base: `S`, projection: `(base.0).1` +/// * base: `(i32, &'static str)`, projection: `base.1` +/// +/// The first will lead to the constraint `w: &'1 str` (for some +/// inferred region `'1`). The second will lead to the constraint `w: +/// &'static str`. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct UserTypeProjections { + pub contents: Vec<(UserTypeProjection, Span)>, +} + +impl<'tcx> UserTypeProjections { + pub fn none() -> Self { + UserTypeProjections { contents: vec![] } + } + + pub fn is_empty(&self) -> bool { + self.contents.is_empty() + } + + pub fn from_projections(projs: impl Iterator) -> Self { + UserTypeProjections { contents: projs.collect() } + } + + pub fn projections_and_spans( + &self, + ) -> impl Iterator + ExactSizeIterator { + self.contents.iter() + } + + pub fn projections(&self) -> impl Iterator + ExactSizeIterator { + self.contents.iter().map(|&(ref user_type, _span)| user_type) + } + + pub fn push_projection(mut self, user_ty: &UserTypeProjection, span: Span) -> Self { + self.contents.push((user_ty.clone(), span)); + self + } + + fn map_projections( + mut self, + mut f: impl FnMut(UserTypeProjection) -> UserTypeProjection, + ) -> Self { + self.contents = self.contents.drain(..).map(|(proj, span)| (f(proj), span)).collect(); + self + } + + pub fn index(self) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.index()) + } + + pub fn subslice(self, from: u32, to: u32) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.subslice(from, to)) + } + + pub fn deref(self) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.deref()) + } + + pub fn leaf(self, field: Field) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.leaf(field)) + } + + pub fn variant(self, adt_def: &'tcx AdtDef, variant_index: VariantIdx, field: Field) -> Self { + self.map_projections(|pat_ty_proj| pat_ty_proj.variant(adt_def, variant_index, field)) + } +} + +/// Encodes the effect of a user-supplied type annotation on the +/// subcomponents of a pattern. The effect is determined by applying the +/// given list of proejctions to some underlying base type. Often, +/// the projection element list `projs` is empty, in which case this +/// directly encodes a type in `base`. But in the case of complex patterns with +/// subpatterns and bindings, we want to apply only a *part* of the type to a variable, +/// in which case the `projs` vector is used. +/// +/// Examples: +/// +/// * `let x: T = ...` -- here, the `projs` vector is empty. +/// +/// * `let (x, _): T = ...` -- here, the `projs` vector would contain +/// `field[0]` (aka `.0`), indicating that the type of `s` is +/// determined by finding the type of the `.0` field from `T`. +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, PartialEq)] +pub struct UserTypeProjection { + pub base: UserTypeAnnotationIndex, + pub projs: Vec, +} + +impl Copy for ProjectionKind {} + +impl UserTypeProjection { + pub(crate) fn index(mut self) -> Self { + self.projs.push(ProjectionElem::Index(())); + self + } + + pub(crate) fn subslice(mut self, from: u32, to: u32) -> Self { + self.projs.push(ProjectionElem::Subslice { from, to, from_end: true }); + self + } + + pub(crate) fn deref(mut self) -> Self { + self.projs.push(ProjectionElem::Deref); + self + } + + pub(crate) fn leaf(mut self, field: Field) -> Self { + self.projs.push(ProjectionElem::Field(field, ())); + self + } + + pub(crate) fn variant( + mut self, + adt_def: &AdtDef, + variant_index: VariantIdx, + field: Field, + ) -> Self { + self.projs.push(ProjectionElem::Downcast( + Some(adt_def.variants[variant_index].ident.name), + variant_index, + )); + self.projs.push(ProjectionElem::Field(field, ())); + self + } +} + +CloneTypeFoldableAndLiftImpls! { ProjectionKind, } + +impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection { + fn super_fold_with>(&self, folder: &mut F) -> Self { + use crate::mir::ProjectionElem::*; + + let base = self.base.fold_with(folder); + let projs: Vec<_> = self + .projs + .iter() + .map(|&elem| match elem { + Deref => Deref, + Field(f, ()) => Field(f, ()), + Index(()) => Index(()), + Downcast(symbol, variantidx) => Downcast(symbol, variantidx), + ConstantIndex { offset, min_length, from_end } => { + ConstantIndex { offset, min_length, from_end } + } + Subslice { from, to, from_end } => Subslice { from, to, from_end }, + }) + .collect(); + + UserTypeProjection { base, projs } + } + + fn super_visit_with>(&self, visitor: &mut Vs) -> bool { + self.base.visit_with(visitor) + // Note: there's nothing in `self.proj` to visit. + } +} + +rustc_index::newtype_index! { + pub struct Promoted { + derive [HashStable] + DEBUG_FORMAT = "promoted[{}]" + } +} + +impl<'tcx> Debug for Constant<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + write!(fmt, "{}", self) + } +} + +impl<'tcx> Display for Constant<'tcx> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + write!(fmt, "const ")?; + pretty_print_const(self.literal, fmt, true) + } +} + +fn pretty_print_const( + c: &ty::Const<'tcx>, + fmt: &mut Formatter<'_>, + print_types: bool, +) -> fmt::Result { + use crate::ty::print::PrettyPrinter; + ty::tls::with(|tcx| { + let literal = tcx.lift(&c).unwrap(); + let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS); + cx.print_alloc_ids = true; + cx.pretty_print_const(literal, print_types)?; + Ok(()) + }) +} + +impl<'tcx> graph::DirectedGraph for Body<'tcx> { + type Node = BasicBlock; +} + +impl<'tcx> graph::WithNumNodes for Body<'tcx> { + #[inline] + fn num_nodes(&self) -> usize { + self.basic_blocks.len() + } +} + +impl<'tcx> graph::WithStartNode for Body<'tcx> { + #[inline] + fn start_node(&self) -> Self::Node { + START_BLOCK + } +} + +impl<'tcx> graph::WithSuccessors for Body<'tcx> { + #[inline] + fn successors(&self, node: Self::Node) -> >::Iter { + self.basic_blocks[node].terminator().successors().cloned() + } +} + +impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> { + type Item = BasicBlock; + type Iter = iter::Cloned>; +} + +impl graph::GraphPredecessors<'graph> for Body<'tcx> { + type Item = BasicBlock; + type Iter = smallvec::IntoIter<[BasicBlock; 4]>; +} + +impl graph::WithPredecessors for Body<'tcx> { + #[inline] + fn predecessors(&self, node: Self::Node) -> >::Iter { + self.predecessors()[node].clone().into_iter() + } +} + +/// `Location` represents the position of the start of the statement; or, if +/// `statement_index` equals the number of statements, then the start of the +/// terminator. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, HashStable)] +pub struct Location { + /// The block that the location is within. + pub block: BasicBlock, + + pub statement_index: usize, +} + +impl fmt::Debug for Location { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{:?}[{}]", self.block, self.statement_index) + } +} + +impl Location { + pub const START: Location = Location { block: START_BLOCK, statement_index: 0 }; + + /// Returns the location immediately after this one within the enclosing block. + /// + /// Note that if this location represents a terminator, then the + /// resulting location would be out of bounds and invalid. + pub fn successor_within_block(&self) -> Location { + Location { block: self.block, statement_index: self.statement_index + 1 } + } + + /// Returns `true` if `other` is earlier in the control flow graph than `self`. + pub fn is_predecessor_of<'tcx>(&self, other: Location, body: &Body<'tcx>) -> bool { + // If we are in the same block as the other location and are an earlier statement + // then we are a predecessor of `other`. + if self.block == other.block && self.statement_index < other.statement_index { + return true; + } + + let predecessors = body.predecessors(); + + // If we're in another block, then we want to check that block is a predecessor of `other`. + let mut queue: Vec = predecessors[other.block].to_vec(); + let mut visited = FxHashSet::default(); + + while let Some(block) = queue.pop() { + // If we haven't visited this block before, then make sure we visit it's predecessors. + if visited.insert(block) { + queue.extend(predecessors[block].iter().cloned()); + } else { + continue; + } + + // If we found the block that `self` is in, then we are a predecessor of `other` (since + // we found that block by looking at the predecessors of `other`). + if self.block == block { + return true; + } + } + + false + } + + pub fn dominates(&self, other: Location, dominators: &Dominators) -> bool { + if self.block == other.block { + self.statement_index <= other.statement_index + } else { + dominators.is_dominated_by(other.block, self.block) + } + } +} + +/// Coverage data associated with each function (MIR) instrumented with coverage counters, when +/// compiled with `-Zinstrument_coverage`. The query `tcx.coverage_data(DefId)` computes these +/// values on demand (during code generation). This query is only valid after executing the MIR pass +/// `InstrumentCoverage`. +#[derive(Clone, RustcEncodable, RustcDecodable, Debug, HashStable)] +pub struct CoverageData { + /// A hash value that can be used by the consumer of the coverage profile data to detect + /// changes to the instrumented source of the associated MIR body (typically, for an + /// individual function). + pub hash: u64, + + /// The total number of coverage region counters added to the MIR `Body`. + pub num_counters: u32, +} diff --git a/src/librustc/mir/mono.rs b/src/librustc_middle/mir/mono.rs similarity index 93% rename from src/librustc/mir/mono.rs rename to src/librustc_middle/mir/mono.rs index 4f8efc1607eaa..f1c1b962ab997 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc_middle/mir/mono.rs @@ -1,14 +1,15 @@ use crate::dep_graph::{DepConstructor, DepNode, WorkProduct, WorkProductId}; -use crate::ich::{Fingerprint, NodeIdHashingMode, StableHashingContext}; -use crate::session::config::OptLevel; +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; use rustc_span::symbol::Symbol; use std::fmt; @@ -90,18 +91,18 @@ impl<'tcx> MonoItem<'tcx> { match *self { MonoItem::Fn(ref instance) => { let entry_def_id = tcx.entry_fn(LOCAL_CRATE).map(|(id, _)| id); - // If this function isn't inlined or otherwise has explicit - // linkage, then we'll be creating a globally shared version. - if self.explicit_linkage(tcx).is_some() + // If this function isn't inlined or otherwise has an extern + // indicator, then we'll be creating a globally shared version. + if tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator() || !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 }; } // At this point we don't have explicit linkage and we're an // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU + // be creating a local copy per CGU. if generate_cgu_internal_copies { return InstantiationMode::LocalCopy; } @@ -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..7508c0239397f --- /dev/null +++ b/src/librustc_middle/mir/predecessors.rs @@ -0,0 +1,80 @@ +//! Lazily compute the reverse control-flow graph for the MIR. + +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::OnceCell; +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: OnceCell, +} + +impl PredecessorCache { + #[inline] + pub(super) fn new() -> Self { + PredecessorCache { cache: OnceCell::new() } + } + + /// Invalidates the predecessor cache. + #[inline] + pub(super) fn invalidate(&mut self) { + // 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 never need to do synchronization when `invalidate` is called, we can + // simply reinitialize the `OnceCell`. + self.cache = OnceCell::new(); + } + + /// Returns the the predecessor graph for this MIR. + #[inline] + pub(super) fn compute( + &self, + basic_blocks: &IndexVec>, + ) -> &Predecessors { + self.cache.get_or_init(|| { + 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); + } + } + } + + 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..1aae97cc2a894 --- /dev/null +++ b/src/librustc_middle/mir/query.rs @@ -0,0 +1,249 @@ +//! 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 { + /// Only permitted in regular `fn`s, prohibitted in `const fn`s. + General, + /// Permitted both in `const fn`s and regular `fn`s. + GeneralAndConstFn, + /// Borrow of packed field. + /// Has to be handled as a lint for backwards compatibility. + BorrowPacked, + /// Unsafe operation in an `unsafe fn` but outside an `unsafe` block. + /// Has to be handled as a lint for backwards compatibility. + /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. + UnsafeFn, + /// Borrow of packed field in an `unsafe fn` but outside an `unsafe` block. + /// Has to be handled as a lint for backwards compatibility. + /// Should stay gated under `#![feature(unsafe_block_in_unsafe_fn)]`. + UnsafeFnBorrowPacked, +} + +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub struct UnsafetyViolation { + pub source_info: SourceInfo, + pub lint_root: hir::HirId, + 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(ReturnConstraint), + Yield, + UseAsConst, + UseAsStatic, + TypeAnnotation, + Cast, + + /// A constraint that came from checking the body of a closure. + /// + /// We try to get the category that the closure used when reporting this. + ClosureBounds, + CallArgument, + CopyBound, + SizedBound, + Assignment, + OpaqueType, + ClosureUpvar(hir::HirId), + + /// A "boring" constraint (caused by the given location) is one that + /// the user probably doesn't want to see described in diagnostics, + /// because it is kind of an artifact of the type system setup. + /// Example: `x = Foo { field: y }` technically creates + /// intermediate regions representing the "type of `Foo { field: y + /// }`", and data flows from `y` into those variables, but they + /// are not very interesting. The assignment into `x` on the other + /// hand might be. + Boring, + // Boring and applicable everywhere. + BoringNoLocation, + + /// A constraint that doesn't correspond to anything the user sees. + Internal, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(RustcEncodable, RustcDecodable, HashStable)] +pub enum ReturnConstraint { + Normal, + ClosureUpvar(hir::HirId), +} + +/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing +/// that must outlive some region. +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub enum ClosureOutlivesSubject<'tcx> { + /// Subject is a type, typically a type parameter, but could also + /// be a projection. Indicates a requirement like `T: 'a` being + /// passed to the caller, where the type here is `T`. + /// + /// The type here is guaranteed not to contain any free regions at + /// present. + Ty(Ty<'tcx>), + + /// Subject is a free region from the closure. Indicates a requirement + /// like `'a: 'b` being passed to the caller; the region here is `'a`. + Region(ty::RegionVid), +} + +/// The constituent parts of an ADT or array. +#[derive(Copy, Clone, Debug, HashStable)] +pub struct DestructuredConst<'tcx> { + pub variant: Option, + pub fields: &'tcx [&'tcx ty::Const<'tcx>], +} diff --git a/src/librustc/mir/tcx.rs b/src/librustc_middle/mir/tcx.rs similarity index 91% rename from src/librustc/mir/tcx.rs rename to src/librustc_middle/mir/tcx.rs index 13996a74acb35..efcd41e5c188d 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc_middle/mir/tcx.rs @@ -4,11 +4,10 @@ */ 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> { @@ -56,8 +55,8 @@ impl<'tcx> PlaceTy<'tcx> { /// Convenience wrapper around `projection_ty_core` for /// `PlaceElem`, where we can just use the `Ty` that is already /// stored inline on field projection elems. - pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: &PlaceElem<'tcx>) -> PlaceTy<'tcx> { - self.projection_ty_core(tcx, ty::ParamEnv::empty(), elem, |_, _, ty| ty) + pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { + self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty) } /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` @@ -124,7 +123,7 @@ impl<'tcx> Place<'tcx> { { projection .iter() - .fold(PlaceTy::from_ty(local_decls.local_decls()[local].ty), |place_ty, elem| { + .fold(PlaceTy::from_ty(local_decls.local_decls()[local].ty), |place_ty, &elem| { place_ty.projection_ty(tcx, elem) }) } @@ -149,7 +148,16 @@ impl<'tcx> Rvalue<'tcx> { { match *self { Rvalue::Use(ref operand) => operand.ty(local_decls, tcx), - Rvalue::Repeat(ref operand, count) => tcx.mk_array(operand.ty(local_decls, tcx), count), + Rvalue::Repeat(ref operand, count) => { + tcx.mk_ty(ty::Array(operand.ty(local_decls, tcx), count)) + } + Rvalue::ThreadLocalRef(did) => { + if tcx.is_mutable_static(did) { + tcx.mk_mut_ptr(tcx.type_of(did)) + } else { + tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.type_of(did)) + } + } Rvalue::Ref(reg, bk, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; tcx.mk_ref(reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() }) @@ -171,20 +179,8 @@ 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::Discriminant(ref place) => { - let ty = place.ty(local_decls, tcx).ty; - match ty.kind { - ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx), - ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), - _ => { - // This can only be `0`, for now, so `u8` will suffice. - tcx.types.u8 - } - } - } + Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx), + Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx), Rvalue::NullaryOp(NullOp::Box, t) => tcx.mk_box(t), Rvalue::NullaryOp(NullOp::SizeOf, _) => tcx.types.usize, Rvalue::Aggregate(ref ak, ref ops) => match **ak { 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_middle/mir/type_foldable.rs b/src/librustc_middle/mir/type_foldable.rs new file mode 100644 index 0000000000000..89f8f10449e2d --- /dev/null +++ b/src/librustc_middle/mir/type_foldable.rs @@ -0,0 +1,333 @@ +//! `TypeFoldable` implementations for MIR types + +use super::*; +use crate::ty; + +CloneTypeFoldableAndLiftImpls! { + BlockTailInfo, + MirPhase, + SourceInfo, + FakeReadCause, + RetagKind, + SourceScope, + SourceScopeData, + SourceScopeLocalData, + UserTypeAnnotationIndex, +} + +impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + use crate::mir::TerminatorKind::*; + + let kind = match self.kind { + Goto { target } => Goto { target }, + SwitchInt { ref discr, switch_ty, ref values, ref targets } => SwitchInt { + discr: discr.fold_with(folder), + switch_ty: switch_ty.fold_with(folder), + values: values.clone(), + targets: targets.clone(), + }, + Drop { ref place, target, unwind } => { + Drop { place: place.fold_with(folder), target, unwind } + } + DropAndReplace { ref place, ref value, target, unwind } => DropAndReplace { + place: place.fold_with(folder), + value: value.fold_with(folder), + target, + unwind, + }, + Yield { ref value, resume, ref resume_arg, drop } => Yield { + value: value.fold_with(folder), + resume, + resume_arg: resume_arg.fold_with(folder), + drop, + }, + Call { ref func, ref args, ref destination, cleanup, from_hir_call, fn_span } => { + let dest = + destination.as_ref().map(|&(ref loc, dest)| (loc.fold_with(folder), dest)); + + Call { + func: func.fold_with(folder), + args: args.fold_with(folder), + destination: dest, + cleanup, + from_hir_call, + fn_span, + } + } + Assert { ref cond, expected, ref msg, target, cleanup } => { + use AssertKind::*; + let msg = match msg { + BoundsCheck { ref len, ref index } => { + BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder) } + } + Overflow(_) + | OverflowNeg + | DivisionByZero + | RemainderByZero + | ResumedAfterReturn(_) + | ResumedAfterPanic(_) => msg.clone(), + }; + Assert { cond: cond.fold_with(folder), expected, msg, target, cleanup } + } + GeneratorDrop => GeneratorDrop, + Resume => Resume, + Abort => Abort, + Return => Return, + Unreachable => Unreachable, + FalseEdge { real_target, imaginary_target } => { + FalseEdge { real_target, imaginary_target } + } + FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind }, + InlineAsm { template, ref operands, options, line_spans, destination } => InlineAsm { + template, + operands: operands.fold_with(folder), + options, + line_spans, + destination, + }, + }; + Terminator { source_info: self.source_info, kind } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + use crate::mir::TerminatorKind::*; + + match self.kind { + SwitchInt { ref discr, switch_ty, .. } => { + discr.visit_with(visitor) || switch_ty.visit_with(visitor) + } + Drop { ref place, .. } => place.visit_with(visitor), + DropAndReplace { ref place, ref value, .. } => { + place.visit_with(visitor) || value.visit_with(visitor) + } + Yield { ref value, .. } => value.visit_with(visitor), + Call { ref func, ref args, ref destination, .. } => { + let dest = if let Some((ref loc, _)) = *destination { + loc.visit_with(visitor) + } else { + false + }; + dest || func.visit_with(visitor) || args.visit_with(visitor) + } + Assert { ref cond, ref msg, .. } => { + if cond.visit_with(visitor) { + use AssertKind::*; + match msg { + BoundsCheck { ref len, ref index } => { + len.visit_with(visitor) || index.visit_with(visitor) + } + Overflow(_) + | OverflowNeg + | DivisionByZero + | RemainderByZero + | ResumedAfterReturn(_) + | ResumedAfterPanic(_) => false, + } + } else { + false + } + } + InlineAsm { ref operands, .. } => operands.visit_with(visitor), + Goto { .. } + | Resume + | Abort + | Return + | GeneratorDrop + | Unreachable + | FalseEdge { .. } + | FalseUnwind { .. } => false, + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for GeneratorKind { + fn super_fold_with>(&self, _: &mut F) -> Self { + *self + } + + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + +impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Place { local: self.local.fold_with(folder), projection: self.projection.fold_with(folder) } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.local.visit_with(visitor) || self.projection.visit_with(visitor) + } +} + +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_place_elems(&v) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + use crate::mir::Rvalue::*; + match *self { + Use(ref op) => Use(op.fold_with(folder)), + Repeat(ref op, len) => Repeat(op.fold_with(folder), len), + ThreadLocalRef(did) => ThreadLocalRef(did.fold_with(folder)), + Ref(region, bk, ref place) => { + Ref(region.fold_with(folder), bk, place.fold_with(folder)) + } + AddressOf(mutability, ref place) => AddressOf(mutability, place.fold_with(folder)), + Len(ref place) => Len(place.fold_with(folder)), + Cast(kind, ref op, ty) => Cast(kind, op.fold_with(folder), ty.fold_with(folder)), + BinaryOp(op, ref rhs, ref lhs) => { + BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)) + } + CheckedBinaryOp(op, ref rhs, ref lhs) => { + CheckedBinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)) + } + UnaryOp(op, ref val) => UnaryOp(op, val.fold_with(folder)), + Discriminant(ref place) => Discriminant(place.fold_with(folder)), + NullaryOp(op, ty) => NullaryOp(op, ty.fold_with(folder)), + Aggregate(ref kind, ref fields) => { + let kind = box match **kind { + AggregateKind::Array(ty) => AggregateKind::Array(ty.fold_with(folder)), + AggregateKind::Tuple => AggregateKind::Tuple, + AggregateKind::Adt(def, v, substs, user_ty, n) => AggregateKind::Adt( + def, + v, + substs.fold_with(folder), + user_ty.fold_with(folder), + n, + ), + AggregateKind::Closure(id, substs) => { + AggregateKind::Closure(id, substs.fold_with(folder)) + } + AggregateKind::Generator(id, substs, movablity) => { + AggregateKind::Generator(id, substs.fold_with(folder), movablity) + } + }; + Aggregate(kind, fields.fold_with(folder)) + } + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + use crate::mir::Rvalue::*; + match *self { + Use(ref op) => op.visit_with(visitor), + Repeat(ref op, _) => op.visit_with(visitor), + ThreadLocalRef(did) => did.visit_with(visitor), + Ref(region, _, ref place) => region.visit_with(visitor) || place.visit_with(visitor), + AddressOf(_, ref place) => place.visit_with(visitor), + Len(ref place) => place.visit_with(visitor), + Cast(_, ref op, ty) => op.visit_with(visitor) || ty.visit_with(visitor), + BinaryOp(_, ref rhs, ref lhs) | CheckedBinaryOp(_, ref rhs, ref lhs) => { + rhs.visit_with(visitor) || lhs.visit_with(visitor) + } + UnaryOp(_, ref val) => val.visit_with(visitor), + Discriminant(ref place) => place.visit_with(visitor), + NullaryOp(_, ty) => ty.visit_with(visitor), + Aggregate(ref kind, ref fields) => { + (match **kind { + AggregateKind::Array(ty) => ty.visit_with(visitor), + AggregateKind::Tuple => false, + AggregateKind::Adt(_, _, substs, user_ty, _) => { + substs.visit_with(visitor) || user_ty.visit_with(visitor) + } + AggregateKind::Closure(_, substs) => substs.visit_with(visitor), + AggregateKind::Generator(_, substs, _) => substs.visit_with(visitor), + }) || fields.visit_with(visitor) + } + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + match *self { + Operand::Copy(ref place) => Operand::Copy(place.fold_with(folder)), + Operand::Move(ref place) => Operand::Move(place.fold_with(folder)), + Operand::Constant(ref c) => Operand::Constant(c.fold_with(folder)), + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match *self { + Operand::Copy(ref place) | Operand::Move(ref place) => place.visit_with(visitor), + Operand::Constant(ref c) => c.visit_with(visitor), + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + use crate::mir::ProjectionElem::*; + + match *self { + Deref => Deref, + Field(f, ty) => Field(f, ty.fold_with(folder)), + Index(v) => Index(v.fold_with(folder)), + 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 }, + } + } + + fn super_visit_with>(&self, visitor: &mut Vs) -> bool { + use crate::mir::ProjectionElem::*; + + match self { + Field(_, ty) => ty.visit_with(visitor), + Index(v) => v.visit_with(visitor), + _ => false, + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for Field { + fn super_fold_with>(&self, _: &mut F) -> Self { + *self + } + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + +impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal { + fn super_fold_with>(&self, _: &mut F) -> Self { + *self + } + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + +impl<'tcx, R: Idx, C: Idx> TypeFoldable<'tcx> for BitMatrix { + fn super_fold_with>(&self, _: &mut F) -> Self { + self.clone() + } + fn super_visit_with>(&self, _: &mut V) -> bool { + false + } +} + +impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Constant { + span: self.span, + user_ty: self.user_ty.fold_with(folder), + literal: self.literal.fold_with(folder), + } + } + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.literal.visit_with(visitor) + } +} diff --git a/src/librustc/mir/visit.rs b/src/librustc_middle/mir/visit.rs similarity index 84% rename from src/librustc/mir/visit.rs rename to src/librustc_middle/mir/visit.rs index 2aca6f684f4fe..2efc5f1dabedc 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); } @@ -117,12 +108,6 @@ macro_rules! make_mir_visitor { self.super_terminator(terminator, location); } - fn visit_terminator_kind(&mut self, - kind: & $($mutability)? TerminatorKind<'tcx>, - location: Location) { - self.super_terminator_kind(kind, location); - } - fn visit_assert_message(&mut self, msg: & $($mutability)? AssertMessage<'tcx>, location: Location) { @@ -163,13 +148,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 +232,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 +253,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 +282,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 +367,7 @@ macro_rules! make_mir_visitor { location ); } - StatementKind::InlineAsm(asm) => { + StatementKind::LlvmInlineAsm(asm) => { for output in & $($mutability)? asm.outputs[..] { self.visit_place( output, @@ -429,57 +411,67 @@ macro_rules! make_mir_visitor { let Terminator { source_info, kind } = terminator; self.visit_source_info(source_info); - self.visit_terminator_kind(kind, location); - } - - fn super_terminator_kind(&mut self, - kind: & $($mutability)? TerminatorKind<'tcx>, - source_location: Location) { match kind { TerminatorKind::Goto { .. } | TerminatorKind::Resume | TerminatorKind::Abort | - TerminatorKind::Return | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | - TerminatorKind::FalseEdges { .. } | + TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } => { } + TerminatorKind::Return => { + // `return` logically moves from the return place `_0`. Note that the place + // cannot be changed by any visitor, though. + let $($mutability)? local = RETURN_PLACE; + self.visit_local( + & $($mutability)? local, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move), + location, + ); + + assert_eq!( + local, + RETURN_PLACE, + "`MutVisitor` tried to mutate return place of `return` terminator" + ); + } + TerminatorKind::SwitchInt { discr, switch_ty, values: _, targets: _ } => { - self.visit_operand(discr, source_location); - self.visit_ty(switch_ty, TyContext::Location(source_location)); + self.visit_operand(discr, location); + self.visit_ty(switch_ty, TyContext::Location(location)); } TerminatorKind::Drop { - location, + place, target: _, unwind: _, } => { self.visit_place( - location, + place, PlaceContext::MutatingUse(MutatingUseContext::Drop), - source_location + location ); } TerminatorKind::DropAndReplace { - location, + place, value, target: _, unwind: _, } => { self.visit_place( - location, + place, PlaceContext::MutatingUse(MutatingUseContext::Drop), - source_location + location ); - self.visit_operand(value, source_location); + self.visit_operand(value, location); } TerminatorKind::Call { @@ -488,16 +480,17 @@ macro_rules! make_mir_visitor { destination, cleanup: _, from_hir_call: _, + fn_span: _ } => { - self.visit_operand(func, source_location); + self.visit_operand(func, location); for arg in args { - self.visit_operand(arg, source_location); + self.visit_operand(arg, location); } if let Some((destination, _)) = destination { self.visit_place( destination, PlaceContext::MutatingUse(MutatingUseContext::Call), - source_location + location ); } } @@ -509,8 +502,8 @@ macro_rules! make_mir_visitor { target: _, cleanup: _, } => { - self.visit_operand(cond, source_location); - self.visit_assert_message(msg, source_location); + self.visit_operand(cond, location); + self.visit_assert_message(msg, location); } TerminatorKind::Yield { @@ -519,14 +512,53 @@ macro_rules! make_mir_visitor { resume_arg, drop: _, } => { - self.visit_operand(value, source_location); + self.visit_operand(value, location); self.visit_place( resume_arg, - PlaceContext::MutatingUse(MutatingUseContext::Store), - source_location, + PlaceContext::MutatingUse(MutatingUseContext::Yield), + location, ); } + TerminatorKind::InlineAsm { + template: _, + operands, + options: _, + line_spans: _, + destination: _, + } => { + for op in operands { + match op { + InlineAsmOperand::In { value, .. } + | InlineAsmOperand::Const { value } => { + self.visit_operand(value, location); + } + InlineAsmOperand::Out { place, .. } => { + if let Some(place) = place { + self.visit_place( + place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location, + ); + } + } + InlineAsmOperand::InOut { in_value, out_place, .. } => { + self.visit_operand(in_value, location); + if let Some(out_place) = out_place { + self.visit_place( + out_place, + PlaceContext::MutatingUse(MutatingUseContext::Store), + location, + ); + } + } + InlineAsmOperand::SymFn { value } => { + self.visit_constant(value, location); + } + InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } } } @@ -558,6 +590,8 @@ macro_rules! make_mir_visitor { self.visit_operand(value, location); } + Rvalue::ThreadLocalRef(_) => {} + Rvalue::Ref(r, bk, path) => { self.visit_region(r, location); let ctx = match bk { @@ -710,13 +744,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 +761,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 +848,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 +871,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 +880,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 +890,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) = projection.get(i) { + if let Some(elem) = self.process_projection_elem(elem, location) { // This converts the borrowed projection into `Cow::Owned(_)` and returns a // clone of the projection so we can mutate and reintern later. let vec = projection.to_mut(); @@ -879,13 +913,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, @@ -900,19 +951,14 @@ macro_rules! visit_place_fns { &mut self, local: Local, proj_base: &[PlaceElem<'tcx>], - elem: &PlaceElem<'tcx>, + elem: PlaceElem<'tcx>, context: PlaceContext, location: Location, ) { self.super_projection_elem(local, proj_base, elem, context, location); } - fn super_place( - &mut self, - place: &Place<'tcx>, - context: PlaceContext, - location: Location, - ) { + fn super_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { let mut context = context; if !place.projection.is_empty() { @@ -923,12 +969,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( @@ -939,7 +982,7 @@ macro_rules! visit_place_fns { location: Location, ) { let mut cursor = projection; - while let [proj_base @ .., elem] = cursor { + while let &[ref proj_base @ .., elem] = cursor { cursor = proj_base; self.visit_projection_elem(local, cursor, elem, context, location); } @@ -949,7 +992,7 @@ macro_rules! visit_place_fns { &mut self, _local: Local, _proj_base: &[PlaceElem<'tcx>], - elem: &PlaceElem<'tcx>, + elem: PlaceElem<'tcx>, _context: PlaceContext, location: Location, ) { @@ -959,21 +1002,18 @@ macro_rules! visit_place_fns { } ProjectionElem::Index(local) => { self.visit_local( - 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 +1100,8 @@ pub enum MutatingUseContext { AsmOutput, /// Destination of a call. Call, + /// Destination of a yield. + Yield, /// Being dropped. Drop, /// Mutable borrow. @@ -1108,9 +1150,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 +1163,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 +1211,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..2f51b98085b4e --- /dev/null +++ b/src/librustc_middle/query/mod.rs @@ -0,0 +1,1442 @@ +use crate::dep_graph::SerializedDepNodeIndex; +use crate::mir::interpret::{GlobalId, LitToConstInput}; +use crate::traits; +use crate::traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, +}; +use crate::ty::query::queries; +use crate::ty::subst::{GenericArg, SubstsRef}; +use crate::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_query_system::query::QueryDescription; + +use rustc_span::symbol::Symbol; +use std::borrow::Cow; + +fn describe_as_module(def_id: 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> { + desc { |tcx| "computing type of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + + query analysis(key: CrateNum) -> Result<(), ErrorReported> { + eval_always + desc { "running analysis passes on this crate" } + } + + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to its + /// associated generics. + query generics_of(key: DefId) -> ty::Generics { + desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + load_cached(tcx, id) { + let generics: Option = tcx.queries.on_disk_cache + .try_load_query_result(tcx, id); + generics + } + } + + /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the + /// predicates (where-clauses) that must be proven true in order + /// to reference it. This is almost always the "predicates query" + /// that you want. + /// + /// `predicates_of` builds on `predicates_defined_on` -- in fact, + /// it is almost always the same as that query, except for the + /// case of traits. For traits, `predicates_of` contains + /// an additional `Self: Trait<...>` predicate that users don't + /// actually write. This reflects the fact that to invoke the + /// trait (e.g., via `Default::default`) you must supply types + /// that actually implement the trait. (However, this extra + /// predicate gets in the way of some checks, which are intended + /// to operate over only the actual where-clauses written by the + /// user.) + query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + + /// Returns the list of predicates that can be used for + /// `SelectionCandidate::ProjectionCandidate` and + /// `ProjectionTyCandidate::TraitDef`. + /// Specifically this is the bounds (equivalent to) those + /// written on the trait's type definition, or those + /// after the `impl` keyword + /// + /// type X: Bound + 'lt + /// ^^^^^^^^^^^ + /// impl Debug + Display + /// ^^^^^^^^^^^^^^^ + /// + /// `key` is the `DefId` of the associated type or opaque type. + query projection_predicates(key: DefId) -> &'tcx ty::List> { + desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) } + } + + query native_libraries(_: CrateNum) -> Lrc> { + desc { "looking up the native libraries of a linked crate" } + } + + query lint_levels(_: CrateNum) -> LintLevelMap { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "computing the lint levels for items in this crate" } + } + + query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { + eval_always + desc { |tcx| "parent module of `{}`", tcx.def_path_str(key.to_def_id()) } + } + } + + Codegen { + query is_panic_runtime(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate is_panic_runtime" } + } + } + + Codegen { + /// Set of all the `DefId`s in this crate that have MIR associated with + /// them. This includes all the body owners, but also things like struct + /// constructors. + query mir_keys(_: CrateNum) -> FxHashSet { + storage(ArenaCacheSelector<'tcx>) + desc { "getting a list of all mir_keys" } + } + + /// Maps DefId's that have an associated `mir::Body` to the result + /// of the MIR const-checking pass. This is the set of qualifs in + /// the final value of a `const`. + query mir_const_qualif(key: DefId) -> mir::ConstQualifs { + desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + + /// Fetch the MIR for a given `DefId` right after it's built - this includes + /// unreachable code. + query mir_built(key: LocalDefId) -> Steal> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "building MIR for `{}`", tcx.def_path_str(key.to_def_id()) } + } + + /// Fetch the MIR for a given `DefId` up till the point where it is + /// ready for const qualification. + /// + /// See the README for the `mir` module for details. + query mir_const(key: DefId) -> Steal> { + desc { |tcx| "processing MIR for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + no_hash + } + + query mir_drops_elaborated_and_const_checked(key: LocalDefId) -> Steal> { + storage(ArenaCacheSelector<'tcx>) + no_hash + desc { |tcx| "elaborating drops for `{}`", tcx.def_path_str(key.to_def_id()) } + } + + 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> { + desc { |tcx| "optimizing MIR for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + } + + query coverage_data(key: DefId) -> mir::CoverageData { + desc { |tcx| "retrieving coverage data from MIR for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { key.is_local() } + } + + query promoted_mir(key: DefId) -> IndexVec> { + desc { |tcx| "optimizing promoted MIR for `{}`", tcx.def_path_str(key) } + 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(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } + } + + /// Returns the predicates written explicitly by the user. + query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } + } + + /// Returns the inferred outlives predicates (e.g., for `struct + /// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`). + query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) } + } + + /// Maps from the `DefId` of a trait to the list of + /// super-predicates. This is a subset of the full list of + /// predicates. We store these in a separate map because we must + /// evaluate them even during type conversion, often before the + /// full predicates are available (note that supertraits have + /// additional acyclicity requirements). + query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing the supertraits of `{}`", tcx.def_path_str(key) } + } + + /// To avoid cycles within the predicates of a single item we compute + /// per-type-parameter predicates for resolving `T::AssocTy`. + query type_param_predicates(key: (DefId, LocalDefId)) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing the bounds for type parameter `{}`", { + let id = tcx.hir().as_local_hir_id(key.1); + tcx.hir().ty_param_name(id) + }} + } + + query trait_def(key: DefId) -> ty::TraitDef { + desc { |tcx| "computing trait definition for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + } + query adt_def(key: DefId) -> &'tcx ty::AdtDef { + desc { |tcx| "computing ADT definition for `{}`", tcx.def_path_str(key) } + } + query adt_destructor(key: DefId) -> Option { + desc { |tcx| "computing `Drop` impl for `{}`", tcx.def_path_str(key) } + } + + // The cycle error here should be reported as an error by `check_representable`. + // We consider the type as Sized in the meanwhile to avoid + // further errors (done in impl Value for AdtSizedConstraint). + // Use `cycle_delay_bug` to delay the cycle error here to be emitted later + // in case we accidentally otherwise don't emit an error. + query adt_sized_constraint( + key: DefId + ) -> AdtSizedConstraint<'tcx> { + desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) } + cycle_delay_bug + } + + query adt_dtorck_constraint( + key: DefId + ) -> Result, NoSolution> { + desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if this is a const fn, use the `is_const_fn` to know whether your crate + /// actually sees it as const fn (e.g., the const-fn-ness might be unstable and you might + /// not have the feature gate active). + /// + /// **Do not call this function manually.** It is only meant to cache the base data for the + /// `is_const_fn` function. + query is_const_fn_raw(key: DefId) -> bool { + desc { |tcx| "checking if item is const fn: `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if this is a const `impl`. **Do not call this function manually.** + /// + /// This query caches the base data for the `is_const_impl` helper function, which also + /// takes into account stability attributes (e.g., `#[rustc_const_unstable]`). + query is_const_impl_raw(key: DefId) -> bool { + desc { |tcx| "checking if item is const impl: `{}`", tcx.def_path_str(key) } + } + + query asyncness(key: DefId) -> hir::IsAsync { + desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if calls to the function may be promoted. + /// + /// This is either because the function is e.g., a tuple-struct or tuple-variant + /// constructor, or because it has the `#[rustc_promotable]` attribute. The attribute should + /// be removed in the future in favour of some form of check which figures out whether the + /// function does not inspect the bits of any of its arguments (so is essentially just a + /// constructor function). + query is_promotable_const_fn(key: DefId) -> bool { + desc { |tcx| "checking if item is promotable: `{}`", tcx.def_path_str(key) } + } + + query const_fn_is_allowed_fn_ptr(key: DefId) -> bool { + desc { |tcx| "checking if const fn allows `fn()` types: `{}`", tcx.def_path_str(key) } + } + + /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). + query is_foreign_item(key: DefId) -> bool { + desc { |tcx| "checking if `{}` is a foreign item", tcx.def_path_str(key) } + } + + /// Returns `Some(mutability)` if the node pointed to by `def_id` is a static item. + query static_mutability(def_id: DefId) -> Option { + desc { |tcx| "looking up static mutability of `{}`", tcx.def_path_str(def_id) } + } + + /// Returns `Some(generator_kind)` if the node pointed to by `def_id` is a generator. + query generator_kind(def_id: DefId) -> Option { + desc { |tcx| "looking up generator kind of `{}`", tcx.def_path_str(def_id) } + } + + /// Gets a map with the variance of every item; use `item_variance` instead. + query crate_variances(_: CrateNum) -> ty::CrateVariancesMap<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { "computing the variances for items in this crate" } + } + + /// Maps from the `DefId` of a type or region parameter to its (inferred) variance. + query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { + desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) } + } + } + + TypeChecking { + /// Maps from thee `DefId` of a type to its (inferred) outlives. + query inferred_outlives_crate(_: CrateNum) + -> ty::CratePredicatesMap<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { "computing the inferred outlives predicates for items in this crate" } + } + } + + Other { + /// Maps from an impl/trait `DefId to a list of the `DefId`s of its items. + query associated_item_def_ids(key: DefId) -> &'tcx [DefId] { + desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) } + } + + /// Maps from a trait item to the trait item "descriptor". + query associated_item(key: DefId) -> ty::AssocItem { + desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } + storage(ArenaCacheSelector<'tcx>) + } + + /// Collects the associated items defined on a trait or impl. + query associated_items(key: DefId) -> ty::AssociatedItems<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) } + } + + query impl_trait_ref(key: DefId) -> Option> { + desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(key) } + } + query impl_polarity(key: DefId) -> ty::ImplPolarity { + desc { |tcx| "computing implementation polarity of `{}`", tcx.def_path_str(key) } + } + + query issue33140_self_ty(key: DefId) -> Option> { + desc { |tcx| "computing Self type wrt issue #33140 `{}`", tcx.def_path_str(key) } + } + } + + TypeChecking { + /// Maps a `DefId` of a type to a list of its inherent impls. + /// Contains implementations of methods that are inherent to a type. + /// Methods in these implementations don't need to be exported. + query inherent_impls(key: DefId) -> &'tcx [DefId] { + desc { |tcx| "collecting inherent impls for `{}`", tcx.def_path_str(key) } + eval_always + } + } + + TypeChecking { + /// The result of unsafety-checking this `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. + /// + /// Unsafety checking is executed for each method separately, but we only want + /// to emit this error once per derive. As there are some impls with multiple + /// methods, we use a query for deduplication. + query unsafe_derive_on_repr_packed(key: LocalDefId) -> () { + desc { |tcx| "processing `{}`", tcx.def_path_str(key.to_def_id()) } + } + + /// The signature of functions. + query fn_sig(key: DefId) -> ty::PolyFnSig<'tcx> { + desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } + } + } + + Other { + query lint_mod(key: LocalDefId) -> () { + desc { |tcx| "linting {}", describe_as_module(key.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(key: DefId) + -> ty::adjustment::CoerceUnsizedInfo { + desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } + } + } + + TypeChecking { + query typeck_item_bodies(_: CrateNum) -> () { + desc { "type-checking all item bodies" } + } + + query typeck_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 FxHashSet { + desc { |tcx| "used_trait_imports `{}`", tcx.def_path_str(key.to_def_id()) } + cache_on_disk_if { true } + } + } + + TypeChecking { + query has_typeck_tables(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` has a body", tcx.def_path_str(def_id) } + } + + query coherent_trait(def_id: DefId) -> () { + desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } + } + } + + BorrowChecking { + /// Borrow-checks the function body. If this is a closure, returns + /// additional requirements that the closure's creator must verify. + query mir_borrowck(key: LocalDefId) -> 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()) + } + } + + /// Destructure a constant ADT or array into its variant index and its + /// field values. + query destructure_const( + key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>> + ) -> mir::DestructuredConst<'tcx> { + desc { "destructure constant" } + } + + query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> { + desc { "get a &core::panic::Location referring to a span" } + } + + query lit_to_const( + key: LitToConstInput<'tcx> + ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> { + desc { "converting literal to const" } + } + } + + TypeChecking { + query check_match(key: DefId) { + desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + } + + /// Performs part of the privacy check and computes "access levels". + query privacy_access_levels(_: CrateNum) -> &'tcx AccessLevels { + eval_always + desc { "privacy access levels" } + } + query check_private_in_public(_: CrateNum) -> () { + eval_always + desc { "checking for private elements in public interfaces" } + } + } + + Other { + query reachable_set(_: CrateNum) -> &'tcx HirIdSet { + desc { "reachability" } + } + + /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; + /// in the case of closures, this will be redirected to the enclosing function. + query region_scope_tree(def_id: DefId) -> &'tcx region::ScopeTree { + desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) } + } + + query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } + } + + /// The `symbol_name` query provides the symbol name for calling a + /// given instance from the local crate. In particular, it will also + /// look up the correct symbol name of instances from upstream crates. + query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName { + desc { "computing the symbol for `{}`", key } + cache_on_disk_if { true } + } + + query def_kind(def_id: DefId) -> DefKind { + desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } + } + query def_span(def_id: DefId) -> Span { + desc { |tcx| "looking up span for `{}`", tcx.def_path_str(def_id) } + // FIXME(mw): DefSpans are not really inputs since they are derived from + // HIR. But at the moment HIR hashing still contains some hacks that allow + // to make type debuginfo to be source location independent. Declaring + // DefSpan an input makes sure that changes to these are always detected + // regardless of HIR hashing. + eval_always + } + query lookup_stability(def_id: DefId) -> Option<&'tcx attr::Stability> { + desc { |tcx| "looking up stability of `{}`", tcx.def_path_str(def_id) } + } + query lookup_const_stability(def_id: DefId) -> Option<&'tcx attr::ConstStability> { + desc { |tcx| "looking up const stability of `{}`", tcx.def_path_str(def_id) } + } + query lookup_deprecation_entry(def_id: DefId) -> Option { + desc { |tcx| "checking whether `{}` is deprecated", tcx.def_path_str(def_id) } + } + query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] { + desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } + } + } + + Codegen { + query codegen_fn_attrs(def_id: DefId) -> CodegenFnAttrs { + desc { |tcx| "computing codegen attributes of `{}`", tcx.def_path_str(def_id) } + storage(ArenaCacheSelector<'tcx>) + cache_on_disk_if { true } + } + } + + Other { + query fn_arg_names(def_id: DefId) -> &'tcx [Symbol] { + desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } + } + /// Gets the rendered value of the specified constant or associated constant. + /// Used by rustdoc. + query rendered_const(def_id: DefId) -> String { + desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) } + } + query impl_parent(def_id: DefId) -> Option { + desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } + } + } + + TypeChecking { + query trait_of_item(def_id: DefId) -> Option { + desc { |tcx| "finding trait defining `{}`", tcx.def_path_str(def_id) } + } + } + + Codegen { + query is_mir_available(key: DefId) -> bool { + desc { |tcx| "checking if item has mir available: `{}`", tcx.def_path_str(key) } + } + } + + Other { + query vtable_methods(key: ty::PolyTraitRef<'tcx>) + -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { + desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) } + } + } + + Codegen { + query codegen_fulfill_obligation( + key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) + ) -> Result, ErrorReported> { + cache_on_disk_if { true } + desc { |tcx| + "checking if `{}` fulfills its obligations", + tcx.def_path_str(key.1.def_id()) + } + } + } + + TypeChecking { + query all_local_trait_impls(key: CrateNum) -> &'tcx BTreeMap> { + desc { "local trait impls" } + } + query trait_impls_of(key: DefId) -> ty::trait_def::TraitImpls { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "trait impls of `{}`", tcx.def_path_str(key) } + } + query specialization_graph_of(key: DefId) -> specialization_graph::Graph { + storage(ArenaCacheSelector<'tcx>) + desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(key) } + cache_on_disk_if { true } + } + query object_safety_violations(key: DefId) -> &'tcx [traits::ObjectSafetyViolation] { + desc { |tcx| "determine object safety of trait `{}`", tcx.def_path_str(key) } + } + + /// Gets the ParameterEnvironment for a given item; this environment + /// will be in "user-facing" mode, meaning that it is suitable for + /// type-checking etc, and it does not normalize specializable + /// associated types. This is almost always what you want, + /// unless you are doing MIR optimizations, in which case you + /// might want to use `reveal_all()` method to change modes. + query param_env(def_id: DefId) -> ty::ParamEnv<'tcx> { + desc { |tcx| "computing normalized predicates of `{}`", tcx.def_path_str(def_id) } + } + + /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`, + /// `ty.is_copy()`, etc, since that will prune the environment where possible. + query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Copy`", env.value } + } + /// Query backing `TyS::is_sized`. + query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `Sized`", env.value } + } + /// Query backing `TyS::is_freeze`. + query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is freeze", env.value } + } + /// Query backing `TyS::needs_drop`. + query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` needs drop", env.value } + } + + /// Query backing `TyS::is_structural_eq_shallow`. + /// + /// This is only correct for ADTs. Call `is_structural_eq_shallow` to handle all types + /// correctly. + query has_structural_eq_impls(ty: Ty<'tcx>) -> bool { + desc { + "computing whether `{:?}` implements `PartialStructuralEq` and `StructuralEq`", + ty + } + } + + /// A list of types where the ADT requires drop if and only if any of + /// those types require drop. If the ADT is known to always need drop + /// then `Err(AlwaysRequiresDrop)` is returned. + query adt_drop_tys(def_id: DefId) -> Result<&'tcx ty::List>, AlwaysRequiresDrop> { + desc { |tcx| "computing when `{}` needs drop", tcx.def_path_str(def_id) } + cache_on_disk_if { true } + } + + query layout_raw( + env: ty::ParamEnvAnd<'tcx, Ty<'tcx>> + ) -> Result<&'tcx rustc_target::abi::Layout, ty::layout::LayoutError<'tcx>> { + desc { "computing layout of `{}`", env.value } + } + } + + Other { + query dylib_dependency_formats(_: CrateNum) + -> &'tcx [(CrateNum, LinkagePreference)] { + desc { "dylib dependency formats of crate" } + } + + query dependency_formats(_: CrateNum) + -> Lrc + { + desc { "get the linkage format of all dependencies" } + } + } + + Codegen { + query is_compiler_builtins(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate is_compiler_builtins" } + } + query has_global_allocator(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate has_global_allocator" } + } + query has_panic_handler(_: CrateNum) -> bool { + fatal_cycle + desc { "checking if the crate has_panic_handler" } + } + query is_profiler_runtime(_: CrateNum) -> bool { + fatal_cycle + desc { "query a crate is `#![profiler_runtime]`" } + } + query panic_strategy(_: CrateNum) -> PanicStrategy { + fatal_cycle + desc { "query a crate's configured panic strategy" } + } + query is_no_builtins(_: CrateNum) -> bool { + fatal_cycle + desc { "test whether a crate has `#![no_builtins]`" } + } + query symbol_mangling_version(_: CrateNum) -> SymbolManglingVersion { + fatal_cycle + desc { "query a crate's symbol mangling version" } + } + + query extern_crate(def_id: DefId) -> Option<&'tcx ExternCrate> { + eval_always + desc { "getting crate's ExternCrateData" } + } + } + + TypeChecking { + query specializes(_: (DefId, DefId)) -> bool { + desc { "computing whether impls specialize one another" } + } + query in_scope_traits_map(_: LocalDefId) + -> Option<&'tcx FxHashMap>> { + eval_always + desc { "traits in scope at a block" } + } + } + + Other { + query module_exports(def_id: LocalDefId) -> Option<&'tcx [Export]> { + desc { |tcx| "looking up items exported by `{}`", tcx.def_path_str(def_id.to_def_id()) } + eval_always + } + } + + TypeChecking { + query impl_defaultness(def_id: DefId) -> hir::Defaultness { + desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } + } + + query check_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + } + query check_trait_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + } + query check_impl_item_well_formed(key: LocalDefId) -> () { + desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key.to_def_id()) } + } + } + + + Linking { + // The `DefId`s of all non-generic functions and statics in the given crate + // that can be reached from outside the crate. + // + // We expect this items to be available for being linked to. + // + // This query can also be called for `LOCAL_CRATE`. In this case it will + // compute which items will be reachable to other crates, taking into account + // the kind of crate that is currently compiled. Crates with only a + // C interface have fewer reachable things. + // + // Does not include external symbols that don't have a corresponding DefId, + // like the compiler-generated `main` function and so on. + query reachable_non_generics(_: CrateNum) + -> DefIdMap { + storage(ArenaCacheSelector<'tcx>) + desc { "looking up the exported symbols of a crate" } + } + query is_reachable_non_generic(def_id: DefId) -> bool { + desc { |tcx| "checking whether `{}` is an exported symbol", tcx.def_path_str(def_id) } + } + query is_unreachable_local_definition(def_id: DefId) -> bool { + desc { |tcx| + "checking whether `{}` is reachable from outside the crate", + tcx.def_path_str(def_id), + } + } + } + + Codegen { + /// The entire set of monomorphizations the local crate can safely link + /// to because they are exported from upstream crates. Do not depend on + /// this directly, as its value changes anytime a monomorphization gets + /// added or removed in any upstream crate. Instead use the narrower + /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even + /// better, `Instance::upstream_monomorphization()`. + query upstream_monomorphizations( + k: CrateNum + ) -> DefIdMap, CrateNum>> { + storage(ArenaCacheSelector<'tcx>) + desc { "collecting available upstream monomorphizations `{:?}`", k } + } + + /// Returns the set of upstream monomorphizations available for the + /// generic function identified by the given `def_id`. The query makes + /// sure to make a stable selection if the same monomorphization is + /// available in multiple upstream crates. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + query upstream_monomorphizations_for(def_id: DefId) + -> Option<&'tcx FxHashMap, CrateNum>> { + desc { |tcx| + "collecting available upstream monomorphizations for `{}`", + tcx.def_path_str(def_id), + } + } + + /// Returns the upstream crate that exports drop-glue for the given + /// type (`substs` is expected to be a single-item list containing the + /// type one wants drop-glue for). + /// + /// This is a subset of `upstream_monomorphizations_for` in order to + /// increase dep-tracking granularity. Otherwise adding or removing any + /// type with drop-glue in any upstream crate would invalidate all + /// functions calling drop-glue of an upstream type. + /// + /// You likely want to call `Instance::upstream_monomorphization()` + /// instead of invoking this query directly. + /// + /// NOTE: This query could easily be extended to also support other + /// common functions that have are large set of monomorphizations + /// (like `Clone::clone` for example). + query upstream_drop_glue_for(substs: SubstsRef<'tcx>) -> Option { + desc { "available upstream drop-glue for `{:?}`", substs } + } + } + + Other { + query foreign_modules(_: CrateNum) -> &'tcx [ForeignModule] { + desc { "looking up the foreign modules of a linked crate" } + } + + /// Identifies the entry-point (e.g., the `main` function) for a given + /// crate, returning `None` if there is no entry point (such as for library crates). + query entry_fn(_: CrateNum) -> Option<(LocalDefId, EntryFnType)> { + desc { "looking up the entry function of a crate" } + } + query plugin_registrar_fn(_: CrateNum) -> Option { + desc { "looking up the plugin registrar for a crate" } + } + query proc_macro_decls_static(_: CrateNum) -> Option { + desc { "looking up the derive registrar for a crate" } + } + query crate_disambiguator(_: CrateNum) -> CrateDisambiguator { + eval_always + desc { "looking up the disambiguator a crate" } + } + query crate_hash(_: CrateNum) -> Svh { + eval_always + desc { "looking up the hash a crate" } + } + query crate_host_hash(_: CrateNum) -> Option { + eval_always + desc { "looking up the hash of a host version of a crate" } + } + query original_crate_name(_: CrateNum) -> Symbol { + eval_always + desc { "looking up the original name a crate" } + } + query extra_filename(_: CrateNum) -> String { + eval_always + desc { "looking up the extra filename for a crate" } + } + } + + TypeChecking { + query implementations_of_trait(_: (CrateNum, DefId)) + -> &'tcx [DefId] { + desc { "looking up implementations of a trait in a crate" } + } + query all_trait_implementations(_: CrateNum) + -> &'tcx [DefId] { + desc { "looking up all (?) trait implementations" } + } + } + + Other { + query dllimport_foreign_items(_: CrateNum) + -> FxHashSet { + storage(ArenaCacheSelector<'tcx>) + desc { "dllimport_foreign_items" } + } + query is_dllimport_foreign_item(def_id: DefId) -> bool { + desc { |tcx| "is_dllimport_foreign_item({})", tcx.def_path_str(def_id) } + } + query is_statically_included_foreign_item(def_id: DefId) -> bool { + desc { |tcx| "is_statically_included_foreign_item({})", tcx.def_path_str(def_id) } + } + query native_library_kind(def_id: DefId) + -> Option { + desc { |tcx| "native_library_kind({})", tcx.def_path_str(def_id) } + } + } + + Linking { + query link_args(_: CrateNum) -> Lrc> { + eval_always + desc { "looking up link arguments for a crate" } + } + } + + BorrowChecking { + /// Lifetime resolution. See `middle::resolve_lifetimes`. + query resolve_lifetimes(_: CrateNum) -> ResolveLifetimes { + storage(ArenaCacheSelector<'tcx>) + desc { "resolving lifetimes" } + } + query named_region_map(_: LocalDefId) -> + Option<&'tcx FxHashMap> { + desc { "looking up a named region" } + } + query is_late_bound_map(_: LocalDefId) -> + Option<&'tcx FxHashSet> { + desc { "testing if a region is late bound" } + } + query object_lifetime_defaults_map(_: LocalDefId) + -> Option<&'tcx FxHashMap>> { + desc { "looking up lifetime defaults for a region" } + } + } + + TypeChecking { + query visibility(def_id: DefId) -> ty::Visibility { + desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } + } + } + + Other { + query dep_kind(_: CrateNum) -> DepKind { + eval_always + desc { "fetching what a dependency looks like" } + } + query crate_name(_: CrateNum) -> Symbol { + eval_always + desc { "fetching what a crate is named" } + } + query item_children(def_id: DefId) -> &'tcx [Export] { + desc { |tcx| "collecting child items of `{}`", tcx.def_path_str(def_id) } + } + query extern_mod_stmt_cnum(def_id: LocalDefId) -> Option { + desc { |tcx| "computing crate imported by `{}`", tcx.def_path_str(def_id.to_def_id()) } + } + + query get_lib_features(_: CrateNum) -> LibFeatures { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the lib features map" } + } + query defined_lib_features(_: CrateNum) + -> &'tcx [(Symbol, Option)] { + desc { "calculating the lib features defined in a crate" } + } + /// Returns the lang items defined in another crate by loading it from metadata. + // FIXME: It is illegal to pass a `CrateNum` other than `LOCAL_CRATE` here, just get rid + // of that argument? + query get_lang_items(_: CrateNum) -> LanguageItems { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the lang items map" } + } + + /// Returns all diagnostic items defined in all crates. + query all_diagnostic_items(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the diagnostic items map" } + } + + /// Returns the lang items defined in another crate by loading it from metadata. + query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] { + desc { "calculating the lang items defined in a crate" } + } + + /// Returns the diagnostic items defined in a crate. + query diagnostic_items(_: CrateNum) -> FxHashMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the diagnostic items map in a crate" } + } + + query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] { + desc { "calculating the missing lang items in a crate" } + } + query visible_parent_map(_: CrateNum) + -> DefIdMap { + storage(ArenaCacheSelector<'tcx>) + desc { "calculating the visible parent map" } + } + query missing_extern_crate_item(_: CrateNum) -> bool { + eval_always + desc { "seeing if we're missing an `extern crate` item for this crate" } + } + query used_crate_source(_: CrateNum) -> Lrc { + eval_always + desc { "looking at the source for a crate" } + } + query postorder_cnums(_: CrateNum) -> &'tcx [CrateNum] { + eval_always + desc { "generating a postorder list of CrateNums" } + } + + query upvars_mentioned(def_id: DefId) -> Option<&'tcx FxIndexMap> { + desc { |tcx| "collecting upvars mentioned in `{}`", tcx.def_path_str(def_id) } + eval_always + } + query maybe_unused_trait_import(def_id: LocalDefId) -> bool { + eval_always + desc { |tcx| "maybe_unused_trait_import for `{}`", tcx.def_path_str(def_id.to_def_id()) } + } + query maybe_unused_extern_crates(_: CrateNum) + -> &'tcx [(LocalDefId, Span)] { + eval_always + desc { "looking up all possibly unused extern crates" } + } + query names_imported_by_glob_use(def_id: LocalDefId) + -> &'tcx FxHashSet { + eval_always + desc { |tcx| "names_imported_by_glob_use for `{}`", tcx.def_path_str(def_id.to_def_id()) } + } + + query stability_index(_: CrateNum) -> stability::Index<'tcx> { + storage(ArenaCacheSelector<'tcx>) + eval_always + desc { "calculating the stability index for the local crate" } + } + query all_crate_nums(_: CrateNum) -> &'tcx [CrateNum] { + eval_always + desc { "fetching all foreign CrateNum instances" } + } + + /// A vector of every trait accessible in the whole crate + /// (i.e., including those from subcrates). This is used only for + /// error reporting. + query all_traits(_: CrateNum) -> &'tcx [DefId] { + desc { "fetching all foreign and local traits" } + } + } + + Linking { + /// The list of symbols exported from the given crate. + /// + /// - All names contained in `exported_symbols(cnum)` are guaranteed to + /// correspond to a publicly visible symbol in `cnum` machine code. + /// - The `exported_symbols` sets of different crates do not intersect. + query exported_symbols(_: CrateNum) + -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] { + desc { "exported_symbols" } + } + } + + Codegen { + query collect_and_partition_mono_items(_: CrateNum) + -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { + eval_always + desc { "collect_and_partition_mono_items" } + } + query is_codegened_item(def_id: DefId) -> bool { + desc { |tcx| "determining whether `{}` needs codegen", tcx.def_path_str(def_id) } + } + query codegen_unit(_: Symbol) -> &'tcx CodegenUnit<'tcx> { + desc { "codegen_unit" } + } + query 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..a49a0045812b0 --- /dev/null +++ b/src/librustc_middle/traits/chalk.rs @@ -0,0 +1,360 @@ +//! 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 rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; + +use rustc_hir::def_id::DefId; + +use smallvec::SmallVec; + +use std::cmp::Ordering; +use std::fmt; +use std::hash::{Hash, Hasher}; + +#[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 InternedConst = Box>; + type InternedConcreteConst = ConstValue<'tcx>; + type InternedGenericArg = Box>; + type InternedGoal = Box>; + type InternedGoals = Vec>; + type InternedSubstitution = Vec>; + type InternedProgramClause = Box>; + type InternedProgramClauses = Vec>; + type InternedQuantifiedWhereClauses = Vec>; + type InternedVariableKinds = Vec>; + type InternedCanonicalVarKinds = Vec>; + type DefId = DefId; + type InternedAdtId = &'tcx AdtDef; + 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_const(&self, constant: chalk_ir::ConstData) -> Self::InternedConst { + Box::new(constant) + } + + fn const_data<'a>(&self, constant: &'a Self::InternedConst) -> &'a chalk_ir::ConstData { + &constant + } + + fn const_eq( + &self, + _ty: &Self::InternedType, + c1: &Self::InternedConcreteConst, + c2: &Self::InternedConcreteConst, + ) -> bool { + c1 == c2 + } + + fn intern_generic_arg(&self, data: chalk_ir::GenericArgData) -> Self::InternedGenericArg { + Box::new(data) + } + + fn generic_arg_data<'a>( + &self, + data: &'a Self::InternedGenericArg, + ) -> &'a chalk_ir::GenericArgData { + &data + } + + fn intern_goal(&self, goal: chalk_ir::GoalData) -> Self::InternedGoal { + Box::new(goal) + } + + fn goal_data<'a>(&self, goal: &'a Self::InternedGoal) -> &'a chalk_ir::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 [chalk_ir::GenericArg] { + 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_generic_arg_kinds( + &self, + data: impl IntoIterator, E>>, + ) -> Result { + data.into_iter().collect::, _>>() + } + + fn variable_kinds_data<'a>( + &self, + parameter_kinds: &'a Self::InternedVariableKinds, + ) -> &'a [chalk_ir::VariableKind] { + 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::CanonicalVarKind] { + 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..fc37cb2504daa --- /dev/null +++ b/src/librustc_middle/traits/mod.rs @@ -0,0 +1,744 @@ +//! Trait Resolution. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +mod chalk; +pub mod query; +pub mod select; +pub mod specialization_graph; +mod structural_impls; + +use crate::infer::canonical::Canonical; +use crate::mir::interpret::ErrorHandled; +use crate::ty::subst::SubstsRef; +use crate::ty::{self, AdtKind, Ty, TyCtxt}; + +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; +use smallvec::SmallVec; + +use std::borrow::Cow; +use std::fmt; +use std::ops::Deref; +use std::rc::Rc; + +pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; + +pub type ChalkCanonicalGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>; + +pub use self::ImplSource::*; +pub use self::ObligationCauseCode::*; +pub use self::SelectionError::*; + +pub use self::chalk::{ + ChalkEnvironmentAndGoal, ChalkEnvironmentClause, RustInterner as ChalkRustInterner, +}; + +/// Depending on the stage of compilation, we want projection to be +/// more or less conservative. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] +pub enum Reveal { + /// At type-checking time, we refuse to project any associated + /// type that is marked `default`. Non-`default` ("final") types + /// are always projected. This is necessary in general for + /// soundness of specialization. However, we *could* allow + /// projections in fully-monomorphic cases. We choose not to, + /// because we prefer for `default type` to force the type + /// definition to be treated abstractly by any consumers of the + /// impl. Concretely, that means that the following example will + /// fail to compile: + /// + /// ``` + /// trait Assoc { + /// type Output; + /// } + /// + /// impl Assoc for T { + /// default type Output = bool; + /// } + /// + /// fn main() { + /// let <() as Assoc>::Output = true; + /// } + /// ``` + UserFacing, + + /// At codegen time, all monomorphic projections will succeed. + /// Also, `impl Trait` is normalized to the concrete type, + /// which has to be already collected by type-checking. + /// + /// NOTE: as `impl Trait`'s concrete type should *never* + /// be observable directly by the user, `Reveal::All` + /// should not be used by checks which may expose + /// type equality or type contents to the user. + /// There are some exceptions, e.g., around OIBITS and + /// transmute-checking, which expose some details, but + /// not the whole concrete type of the `impl Trait`. + All, +} + +/// The reason why we incurred this obligation; used for error reporting. +/// +/// As the happy path does not care about this struct, storing this on the heap +/// ends up increasing performance. +/// +/// We do not want to intern this as there are a lot of obligation causes which +/// only live for a short period of time. +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ObligationCause<'tcx> { + /// `None` for `ObligationCause::dummy`, `Some` otherwise. + data: Option>>, +} + +const DUMMY_OBLIGATION_CAUSE_DATA: ObligationCauseData<'static> = + ObligationCauseData { span: DUMMY_SP, body_id: hir::CRATE_HIR_ID, code: MiscObligation }; + +// Correctly format `ObligationCause::dummy`. +impl<'tcx> fmt::Debug for ObligationCause<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ObligationCauseData::fmt(self, f) + } +} + +impl Deref for ObligationCause<'tcx> { + type Target = ObligationCauseData<'tcx>; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.data.as_deref().unwrap_or(&DUMMY_OBLIGATION_CAUSE_DATA) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ObligationCauseData<'tcx> { + pub span: Span, + + /// The ID of the fn body that triggered this obligation. This is + /// used for region obligations to determine the precise + /// environment in which the region obligation should be evaluated + /// (in particular, closures can add new assumptions). See the + /// field `region_obligations` of the `FulfillmentContext` for more + /// information. + pub body_id: hir::HirId, + + pub code: ObligationCauseCode<'tcx>, +} + +impl<'tcx> ObligationCause<'tcx> { + #[inline] + pub fn new( + span: Span, + body_id: hir::HirId, + code: ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + ObligationCause { data: Some(Rc::new(ObligationCauseData { span, body_id, code })) } + } + + pub fn misc(span: Span, body_id: hir::HirId) -> ObligationCause<'tcx> { + ObligationCause::new(span, body_id, MiscObligation) + } + + pub fn dummy_with_span(span: Span) -> ObligationCause<'tcx> { + ObligationCause::new(span, hir::CRATE_HIR_ID, MiscObligation) + } + + #[inline(always)] + pub fn dummy() -> ObligationCause<'tcx> { + ObligationCause { data: None } + } + + pub fn make_mut(&mut self) -> &mut ObligationCauseData<'tcx> { + Rc::make_mut(self.data.get_or_insert_with(|| Rc::new(DUMMY_OBLIGATION_CAUSE_DATA))) + } + + pub fn span(&self, tcx: TyCtxt<'tcx>) -> Span { + match self.code { + ObligationCauseCode::CompareImplMethodObligation { .. } + | ObligationCauseCode::MainFunctionType + | ObligationCauseCode::StartFunctionType => { + tcx.sess.source_map().guess_head_span(self.span) + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + arm_span, + .. + }) => arm_span, + _ => self.span, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum ObligationCauseCode<'tcx> { + /// Not well classified or should be obvious from the span. + MiscObligation, + + /// A slice or array is WF only if `T: Sized`. + SliceOrArrayElem, + + /// A tuple is WF only if its middle elements are `Sized`. + TupleElem, + + /// This is the trait reference from the given projection. + ProjectionWf(ty::ProjectionTy<'tcx>), + + /// In an impl of trait `X` for type `Y`, type `Y` must + /// also implement all supertraits of `X`. + ItemObligation(DefId), + + /// Like `ItemObligation`, but with extra detail on the source of the obligation. + BindingObligation(DefId, Span), + + /// A type like `&'a T` is WF only if `T: 'a`. + ReferenceOutlivesReferent(Ty<'tcx>), + + /// A type like `Box + 'b>` is WF only if `'b: 'a`. + ObjectTypeBound(Ty<'tcx>, ty::Region<'tcx>), + + /// Obligation incurred due to an object cast. + ObjectCastObligation(/* Object type */ Ty<'tcx>), + + /// Obligation incurred due to a coercion. + Coercion { + source: Ty<'tcx>, + target: Ty<'tcx>, + }, + + /// Various cases where expressions must be `Sized` / `Copy` / etc. + /// `L = X` implies that `L` is `Sized`. + AssignmentLhsSized, + /// `(x1, .., xn)` must be `Sized`. + TupleInitializerSized, + /// `S { ... }` must be `Sized`. + StructInitializerSized, + /// Type of each variable must be `Sized`. + VariableType(hir::HirId), + /// Argument type must be `Sized`. + SizedArgumentType, + /// 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 `ImplSource` +/// indicates where the impl comes from. +/// +/// For example, the obligation may be satisfied by a specific impl (case A), +/// or it may be relative to some bound that is in scope (case B). +/// +/// ``` +/// impl Clone for Option { ... } // Impl_1 +/// impl Clone for Box { ... } // Impl_2 +/// impl Clone for i32 { ... } // Impl_3 +/// +/// fn foo(concrete: Option>, param: T, mixed: Option) { +/// // Case A: Vtable points at a specific impl. Only possible when +/// // type is concretely known. If the impl itself has bounded +/// // type parameters, Vtable will carry resolutions for those as well: +/// concrete.clone(); // Vtable(Impl_1, [Vtable(Impl_2, [Vtable(Impl_3)])]) +/// +/// // Case A: ImplSource points at a specific impl. Only possible when +/// // type is concretely known. If the impl itself has bounded +/// // type parameters, ImplSource will carry resolutions for those as well: +/// concrete.clone(); // ImplSource(Impl_1, [ImplSource(Impl_2, [ImplSource(Impl_3)])]) +/// +/// // Case B: ImplSource must be provided by caller. This applies when +/// // type is a type parameter. +/// param.clone(); // ImplSourceParam +/// +/// // Case C: A mix of cases A and B. +/// mixed.clone(); // ImplSource(Impl_1, [ImplSourceParam]) +/// } +/// ``` +/// +/// ### The type parameter `N` +/// +/// See explanation on `ImplSourceUserDefinedData`. +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub enum ImplSource<'tcx, N> { + /// ImplSource identifying a particular impl. + ImplSourceUserDefined(ImplSourceUserDefinedData<'tcx, N>), + + /// ImplSource for auto trait implementations. + /// This carries the information and nested obligations with regards + /// to an auto implementation for a trait `Trait`. The nested obligations + /// ensure the trait implementation holds for all the constituent types. + ImplSourceAutoImpl(ImplSourceAutoImplData), + + /// Successful resolution to an obligation provided by the caller + /// for some type parameter. The `Vec` represents the + /// obligations incurred from normalizing the where-clause (if + /// any). + ImplSourceParam(Vec), + + /// Virtual calls through an object. + ImplSourceObject(ImplSourceObjectData<'tcx, N>), + + /// Successful resolution for a builtin trait. + ImplSourceBuiltin(ImplSourceBuiltinData), + + /// ImplSource automatically generated for a closure. The `DefId` is the ID + /// of the closure expression. This is a `ImplSourceUserDefined` in spirit, but the + /// impl is generated by the compiler and does not appear in the source. + ImplSourceClosure(ImplSourceClosureData<'tcx, N>), + + /// Same as above, but for a function pointer type with the given signature. + ImplSourceFnPointer(ImplSourceFnPointerData<'tcx, N>), + + /// ImplSource for a builtin `DeterminantKind` trait implementation. + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData), + + /// ImplSource automatically generated for a generator. + ImplSourceGenerator(ImplSourceGeneratorData<'tcx, N>), + + /// ImplSource for a trait alias. + ImplSourceTraitAlias(ImplSourceTraitAliasData<'tcx, N>), +} + +impl<'tcx, N> ImplSource<'tcx, N> { + pub fn nested_obligations(self) -> Vec { + match self { + ImplSourceUserDefined(i) => i.nested, + ImplSourceParam(n) => n, + ImplSourceBuiltin(i) => i.nested, + ImplSourceAutoImpl(d) => d.nested, + ImplSourceClosure(c) => c.nested, + ImplSourceGenerator(c) => c.nested, + ImplSourceObject(d) => d.nested, + ImplSourceFnPointer(d) => d.nested, + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) => Vec::new(), + ImplSourceTraitAlias(d) => d.nested, + } + } + + pub fn borrow_nested_obligations(&self) -> &[N] { + match &self { + ImplSourceUserDefined(i) => &i.nested[..], + ImplSourceParam(n) => &n[..], + ImplSourceBuiltin(i) => &i.nested[..], + ImplSourceAutoImpl(d) => &d.nested[..], + ImplSourceClosure(c) => &c.nested[..], + ImplSourceGenerator(c) => &c.nested[..], + ImplSourceObject(d) => &d.nested[..], + ImplSourceFnPointer(d) => &d.nested[..], + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) => &[], + ImplSourceTraitAlias(d) => &d.nested[..], + } + } + + pub fn map(self, f: F) -> ImplSource<'tcx, M> + where + F: FnMut(N) -> M, + { + match self { + ImplSourceUserDefined(i) => ImplSourceUserDefined(ImplSourceUserDefinedData { + impl_def_id: i.impl_def_id, + substs: i.substs, + nested: i.nested.into_iter().map(f).collect(), + }), + ImplSourceParam(n) => ImplSourceParam(n.into_iter().map(f).collect()), + ImplSourceBuiltin(i) => ImplSourceBuiltin(ImplSourceBuiltinData { + nested: i.nested.into_iter().map(f).collect(), + }), + ImplSourceObject(o) => ImplSourceObject(ImplSourceObjectData { + upcast_trait_ref: o.upcast_trait_ref, + vtable_base: o.vtable_base, + nested: o.nested.into_iter().map(f).collect(), + }), + ImplSourceAutoImpl(d) => ImplSourceAutoImpl(ImplSourceAutoImplData { + trait_def_id: d.trait_def_id, + nested: d.nested.into_iter().map(f).collect(), + }), + ImplSourceClosure(c) => ImplSourceClosure(ImplSourceClosureData { + closure_def_id: c.closure_def_id, + substs: c.substs, + nested: c.nested.into_iter().map(f).collect(), + }), + ImplSourceGenerator(c) => ImplSourceGenerator(ImplSourceGeneratorData { + generator_def_id: c.generator_def_id, + substs: c.substs, + nested: c.nested.into_iter().map(f).collect(), + }), + ImplSourceFnPointer(p) => ImplSourceFnPointer(ImplSourceFnPointerData { + fn_ty: p.fn_ty, + nested: p.nested.into_iter().map(f).collect(), + }), + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) => { + ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData) + } + ImplSourceTraitAlias(d) => ImplSourceTraitAlias(ImplSourceTraitAliasData { + alias_def_id: d.alias_def_id, + substs: d.substs, + nested: d.nested.into_iter().map(f).collect(), + }), + } + } +} + +/// Identifies a particular impl in the source, along with a set of +/// substitutions from the impl's type/lifetime parameters. The +/// `nested` vector corresponds to the nested obligations attached to +/// the impl's type parameters. +/// +/// The type parameter `N` indicates the type used for "nested +/// obligations" that are required by the impl. During type-check, this +/// is `Obligation`, as one might expect. During codegen, however, this +/// is `()`, because codegen only requires a shallow resolution of an +/// impl, and nested obligations are satisfied later. +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct ImplSourceUserDefinedData<'tcx, N> { + pub impl_def_id: DefId, + pub substs: SubstsRef<'tcx>, + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct ImplSourceGeneratorData<'tcx, N> { + pub generator_def_id: DefId, + pub substs: SubstsRef<'tcx>, + /// Nested obligations. This can be non-empty if the generator + /// signature contains associated types. + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct ImplSourceClosureData<'tcx, N> { + pub closure_def_id: DefId, + pub substs: SubstsRef<'tcx>, + /// Nested obligations. This can be non-empty if the closure + /// signature contains associated types. + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct ImplSourceAutoImplData { + pub trait_def_id: DefId, + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct ImplSourceBuiltinData { + pub nested: Vec, +} + +#[derive(PartialEq, Eq, Clone, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct ImplSourceObjectData<'tcx, N> { + /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`. + pub upcast_trait_ref: ty::PolyTraitRef<'tcx>, + + /// The vtable is formed by concatenating together the method lists of + /// the base object trait and all supertraits; this is the start of + /// `upcast_trait_ref`'s methods in that vtable. + pub vtable_base: usize, + + pub nested: Vec, +} + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct ImplSourceFnPointerData<'tcx, N> { + pub fn_ty: Ty<'tcx>, + pub nested: Vec, +} + +// FIXME(@lcnr): This should be refactored and merged with other builtin vtables. +#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct ImplSourceDiscriminantKindData; + +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)] +pub struct ImplSourceTraitAliasData<'tcx, N> { + pub alias_def_id: DefId, + pub substs: SubstsRef<'tcx>, + pub nested: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum ObjectSafetyViolation { + /// `Self: Sized` declared on the trait. + SizedSelf(SmallVec<[Span; 1]>), + + /// Supertrait reference references `Self` an in illegal location + /// (e.g., `trait Foo : Bar`). + SupertraitSelf(SmallVec<[Span; 1]>), + + /// Method has something illegal. + Method(Symbol, MethodViolationCode, Span), + + /// Associated const. + AssocConst(Symbol, Span), +} + +impl ObjectSafetyViolation { + pub fn error_msg(&self) -> Cow<'static, str> { + match *self { + ObjectSafetyViolation::SizedSelf(_) => "it requires `Self: Sized`".into(), + ObjectSafetyViolation::SupertraitSelf(ref spans) => { + if spans.iter().any(|sp| *sp != DUMMY_SP) { + "it uses `Self` as a type parameter in this".into() + } else { + "it cannot use `Self` as a type parameter in a supertrait or `where`-clause" + .into() + } + } + ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { + format!("associated function `{}` has no `self` parameter", name).into() + } + ObjectSafetyViolation::Method( + name, + MethodViolationCode::ReferencesSelfInput(_), + DUMMY_SP, + ) => format!("method `{}` references the `Self` type in its parameters", name).into(), + ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfInput(_), _) => { + format!("method `{}` references the `Self` type in this parameter", name).into() + } + ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfOutput, _) => { + format!("method `{}` references the `Self` type in its return type", name).into() + } + ObjectSafetyViolation::Method( + name, + MethodViolationCode::WhereClauseReferencesSelf, + _, + ) => { + format!("method `{}` references the `Self` type in its `where` clause", name).into() + } + ObjectSafetyViolation::Method(name, MethodViolationCode::Generic, _) => { + format!("method `{}` has generic type parameters", name).into() + } + ObjectSafetyViolation::Method(name, MethodViolationCode::UndispatchableReceiver, _) => { + format!("method `{}`'s `self` parameter cannot be dispatched on", name).into() + } + ObjectSafetyViolation::AssocConst(name, DUMMY_SP) => { + format!("it contains associated `const` `{}`", name).into() + } + ObjectSafetyViolation::AssocConst(..) => "it contains this associated `const`".into(), + } + } + + pub fn solution(&self) -> Option<(String, Option<(String, Span)>)> { + Some(match *self { + ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => { + return None; + } + ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(sugg), _) => ( + format!( + "consider turning `{}` into a method by giving it a `&self` argument or \ + constraining it so it does not apply to trait objects", + name + ), + sugg.map(|(sugg, sp)| (sugg.to_string(), sp)), + ), + ObjectSafetyViolation::Method( + name, + MethodViolationCode::UndispatchableReceiver, + span, + ) => ( + format!("consider changing method `{}`'s `self` parameter to be `&self`", name), + Some(("&Self".to_string(), span)), + ), + ObjectSafetyViolation::AssocConst(name, _) + | ObjectSafetyViolation::Method(name, ..) => { + (format!("consider moving `{}` to another trait", name), None) + } + }) + } + + pub fn spans(&self) -> SmallVec<[Span; 1]> { + // When `span` comes from a separate crate, it'll be `DUMMY_SP`. Treat it as `None` so + // diagnostics use a `note` instead of a `span_label`. + match self { + ObjectSafetyViolation::SupertraitSelf(spans) + | ObjectSafetyViolation::SizedSelf(spans) => spans.clone(), + ObjectSafetyViolation::AssocConst(_, span) + | ObjectSafetyViolation::Method(_, _, span) + if *span != DUMMY_SP => + { + smallvec![*span] + } + _ => smallvec![], + } + } +} + +/// Reasons a method might not be object-safe. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum MethodViolationCode { + /// e.g., `fn foo()` + StaticMethod(Option<(&'static str, Span)>), + + /// e.g., `fn foo(&self, x: Self)` + ReferencesSelfInput(usize), + + /// e.g., `fn foo(&self) -> Self` + ReferencesSelfOutput, + + /// e.g., `fn foo(&self) where Self: Clone` + WhereClauseReferencesSelf, + + /// e.g., `fn foo()` + Generic, + + /// the method's receiver (`self` argument) can't be dispatched on + UndispatchableReceiver, +} diff --git a/src/librustc_middle/traits/query.rs b/src/librustc_middle/traits/query.rs new file mode 100644 index 0000000000000..69696ac9e93c0 --- /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_middle/traits/select.rs b/src/librustc_middle/traits/select.rs new file mode 100644 index 0000000000000..a12f5910b14b9 --- /dev/null +++ b/src/librustc_middle/traits/select.rs @@ -0,0 +1,338 @@ +//! Candidate selection. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection + +use self::EvaluationResult::*; + +use super::{SelectionError, SelectionResult}; + +use crate::dep_graph::DepNodeIndex; +use crate::ty::{self, TyCtxt}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sync::Lock; +use rustc_hir::def_id::DefId; + +#[derive(Clone, Default)] +pub struct SelectionCache<'tcx> { + pub hashmap: Lock< + FxHashMap< + ty::ParamEnvAnd<'tcx, ty::TraitRef<'tcx>>, + WithDepNode>>, + >, + >, +} + +impl<'tcx> SelectionCache<'tcx> { + /// Actually frees the underlying memory in contrast to what stdlib containers do on `clear` + pub fn clear(&self) { + *self.hashmap.borrow_mut() = Default::default(); + } +} + +/// The selection process begins by considering all impls, where +/// 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 `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 +/// candidate. If the obligation is fully known, this is guaranteed +/// by coherence. However, if the obligation contains type parameters +/// or variables, there may be multiple such impls. +/// +/// It is not a real problem if multiple matching impls exist because +/// of type variables - it just means the obligation isn't sufficiently +/// elaborated. In that case we report an ambiguity, and the caller can +/// try again after more type information has been gathered or report a +/// "type annotations needed" error. +/// +/// However, with type parameters, this can be a real problem - type +/// parameters don't unify with regular types, but they *can* unify +/// with variables from blanket impls, and (unless we know its bounds +/// will always be satisfied) picking the blanket impl will be wrong +/// for at least *some* substitutions. To make this concrete, if we have +/// +/// ```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 +/// implement `AsDebug`. +/// +/// Because where-clauses match the type exactly, multiple clauses can +/// only match if there are unresolved variables, and we can mostly just +/// report this ambiguity in that case. This is still a problem - we can't +/// *do anything* with ambiguities that involve only regions. This is issue +/// #21974. +/// +/// If a single where-clause matches and there are no inference +/// variables left, then it definitely matches and we can just select +/// it. +/// +/// In fact, we even select the where-clause when the obligation contains +/// inference variables. The can lead to inference making "leaps of logic", +/// for example in this situation: +/// +/// ```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); } +/// ``` +/// +/// Here the obligation `>` can be matched by both the blanket +/// impl and the where-clause. We select the where-clause and unify `$0=bool`, +/// so the program prints "false". However, if the where-clause is omitted, +/// the blanket impl is selected, we unify `$0=()`, and the program prints +/// "()". +/// +/// Exactly the same issues apply to projection and object candidates, except +/// that we can have both a projection candidate and a where-clause candidate +/// for the same obligation. In that case either would do (except that +/// different "leaps of logic" would occur if inference variables are +/// present), and we just pick the where-clause. This is, for example, +/// required for associated types to work in default impls, as the bounds +/// are visible both as projection bounds and as where-clauses from the +/// parameter environment. +#[derive(PartialEq, Eq, Debug, Clone, TypeFoldable)] +pub enum SelectionCandidate<'tcx> { + BuiltinCandidate { + /// `false` if there are no *further* obligations. + has_nested: bool, + }, + ParamCandidate(ty::PolyTraitRef<'tcx>), + ImplCandidate(DefId), + AutoImplCandidate(DefId), + + /// This is a trait matching with a projected type as `Self`, and + /// we found an applicable bound in the trait definition. + ProjectionCandidate, + + /// Implementation of a `Fn`-family trait by one of the anonymous types + /// generated for a `||` expression. + ClosureCandidate, + + /// Implementation of a `Generator` trait by one of the anonymous types + /// generated for a generator. + GeneratorCandidate, + + /// Implementation of a `Fn`-family trait by one of the anonymous + /// types generated for a fn pointer type (e.g., `fn(int) -> int`) + FnPointerCandidate, + + /// Builtin implementation of `DiscriminantKind`. + DiscriminantKindCandidate, + + TraitAliasCandidate(DefId), + + ObjectCandidate, + + BuiltinObjectCandidate, + + BuiltinUnsizeCandidate, +} + +/// The result of trait evaluation. The order is important +/// here as the evaluation of a list is the maximum of the +/// evaluations. +/// +/// The evaluation results are ordered: +/// - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions` +/// implies `EvaluatedToAmbig` implies `EvaluatedToUnknown` +/// - `EvaluatedToErr` implies `EvaluatedToRecur` +/// - the "union" of evaluation results is equal to their maximum - +/// all the "potential success" candidates can potentially succeed, +/// so they are noops when unioned with a definite error, and within +/// the categories it's easy to see that the unions are correct. +#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, HashStable)] +pub enum EvaluationResult { + /// Evaluation successful. + EvaluatedToOk, + /// Evaluation successful, but there were unevaluated region obligations. + EvaluatedToOkModuloRegions, + /// Evaluation is known to be ambiguous -- it *might* hold for some + /// assignment of inference variables, but it might not. + /// + /// While this has the same meaning as `EvaluatedToUnknown` -- we can't + /// know whether this obligation holds or not -- it is the result we + /// would get with an empty stack, and therefore is cacheable. + EvaluatedToAmbig, + /// Evaluation failed because of recursion involving inference + /// variables. We are somewhat imprecise there, so we don't actually + /// know the real result. + /// + /// This can't be trivially cached for the same reason as `EvaluatedToRecur`. + EvaluatedToUnknown, + /// Evaluation failed because we encountered an obligation we are already + /// trying to prove on this branch. + /// + /// We know this branch can't be a part of a minimal proof-tree for + /// the "root" of our cycle, because then we could cut out the recursion + /// and maintain a valid proof tree. However, this does not mean + /// that all the obligations on this branch do not hold -- it's possible + /// that we entered this branch "speculatively", and that there + /// might be some other way to prove this obligation that does not + /// go through this cycle -- so we can't cache this as a failure. + /// + /// For example, suppose we have this: + /// + /// ```rust,ignore (pseudo-Rust) + /// pub trait Trait { fn xyz(); } + /// // This impl is "useless", but we can still have + /// // an `impl Trait for SomeUnsizedType` somewhere. + /// impl Trait for T { fn xyz() {} } + /// + /// pub fn foo() { + /// ::xyz(); + /// } + /// ``` + /// + /// When checking `foo`, we have to prove `T: Trait`. This basically + /// translates into this: + /// + /// ```plain,ignore + /// (T: Trait + Sized →_\impl T: Trait), T: Trait ⊢ T: Trait + /// ``` + /// + /// When we try to prove it, we first go the first option, which + /// recurses. This shows us that the impl is "useless" -- it won't + /// tell us that `T: Trait` unless it already implemented `Trait` + /// by some other means. However, that does not prevent `T: Trait` + /// does not hold, because of the bound (which can indeed be satisfied + /// by `SomeUnsizedType` from another crate). + // + // FIXME: when an `EvaluatedToRecur` goes past its parent root, we + // ought to convert it to an `EvaluatedToErr`, because we know + // there definitely isn't a proof tree for that obligation. Not + // doing so is still sound -- there isn't any proof tree, so the + // branch still can't be a part of a minimal one -- but does not re-enable caching. + EvaluatedToRecur, + /// Evaluation failed. + EvaluatedToErr, +} + +impl EvaluationResult { + /// Returns `true` if this evaluation result is known to apply, even + /// considering outlives constraints. + pub fn must_apply_considering_regions(self) -> bool { + self == EvaluatedToOk + } + + /// Returns `true` if this evaluation result is known to apply, ignoring + /// outlives constraints. + pub fn must_apply_modulo_regions(self) -> bool { + self <= EvaluatedToOkModuloRegions + } + + pub fn may_apply(self) -> bool { + match self { + EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToUnknown => { + true + } + + EvaluatedToErr | EvaluatedToRecur => false, + } + } + + pub fn is_stack_dependent(self) -> bool { + match self { + EvaluatedToUnknown | EvaluatedToRecur => true, + + EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToErr => false, + } + } +} + +/// Indicates that trait evaluation caused overflow. +#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable)] +pub struct OverflowError; + +impl<'tcx> From for SelectionError<'tcx> { + fn from(OverflowError: OverflowError) -> SelectionError<'tcx> { + SelectionError::Overflow + } +} + +#[derive(Clone, Default)] +pub struct EvaluationCache<'tcx> { + pub hashmap: Lock< + FxHashMap>, WithDepNode>, + >, +} + +impl<'tcx> EvaluationCache<'tcx> { + /// Actually frees the underlying memory in contrast to what stdlib containers do on `clear` + pub fn clear(&self) { + *self.hashmap.borrow_mut() = Default::default(); + } +} + +#[derive(Clone, Eq, PartialEq)] +pub struct WithDepNode { + dep_node: DepNodeIndex, + cached_value: T, +} + +impl WithDepNode { + pub fn new(dep_node: DepNodeIndex, cached_value: T) -> Self { + WithDepNode { dep_node, cached_value } + } + + pub fn get(&self, tcx: TyCtxt<'_>) -> T { + tcx.dep_graph.read_index(self.dep_node); + self.cached_value.clone() + } +} + +#[derive(Clone, Debug)] +pub enum IntercrateAmbiguityCause { + DownstreamCrate { trait_desc: String, self_desc: Option }, + UpstreamCrateUpdate { trait_desc: String, self_desc: Option }, + ReservationImpl { message: String }, +} + +impl IntercrateAmbiguityCause { + /// Emits notes when the overlap is caused by complex intercrate ambiguities. + /// See #23980 for details. + pub fn add_intercrate_ambiguity_hint(&self, err: &mut rustc_errors::DiagnosticBuilder<'_>) { + err.note(&self.intercrate_ambiguity_hint()); + } + + pub fn intercrate_ambiguity_hint(&self) -> String { + match self { + &IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => { + let self_desc = if let &Some(ref ty) = self_desc { + format!(" for type `{}`", ty) + } else { + String::new() + }; + format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc) + } + &IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => { + let self_desc = if let &Some(ref ty) = self_desc { + format!(" for type `{}`", ty) + } else { + String::new() + }; + format!( + "upstream crates may add a new impl of trait `{}`{} \ + in future versions", + trait_desc, self_desc + ) + } + &IntercrateAmbiguityCause::ReservationImpl { ref message } => message.clone(), + } + } +} diff --git a/src/librustc_middle/traits/specialization_graph.rs b/src/librustc_middle/traits/specialization_graph.rs new file mode 100644 index 0000000000000..f4961617b81c6 --- /dev/null +++ b/src/librustc_middle/traits/specialization_graph.rs @@ -0,0 +1,246 @@ +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 { + tcx.associated_items(self.def_id()) + .filter_by_name_unhygienic(trait_item_name.name) + .find(move |impl_item| { + trait_item_kind == impl_item.kind + && tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id) + }) + .copied() + } + + pub fn def_id(&self) -> DefId { + match *self { + Node::Impl(did) => did, + Node::Trait(did) => did, + } + } +} + +#[derive(Copy, Clone)] +pub struct Ancestors<'tcx> { + trait_def_id: DefId, + specialization_graph: &'tcx Graph, + current_source: Option, +} + +impl Iterator for Ancestors<'_> { + type Item = Node; + fn next(&mut self) -> Option { + let cur = self.current_source.take(); + if let Some(Node::Impl(cur_impl)) = cur { + let parent = self.specialization_graph.parent(cur_impl); + + self.current_source = if parent == self.trait_def_id { + Some(Node::Trait(parent)) + } else { + Some(Node::Impl(parent)) + }; + } + cur + } +} + +/// Information about the most specialized definition of an associated item. +pub struct LeafDef { + /// The associated item described by this `LeafDef`. + pub item: ty::AssocItem, + + /// The node in the specialization graph containing the definition of `item`. + pub defining_node: Node, + + /// The "top-most" (ie. least specialized) specialization graph node that finalized the + /// definition of `item`. + /// + /// Example: + /// + /// ``` + /// trait Tr { + /// fn assoc(&self); + /// } + /// + /// impl Tr for T { + /// default fn assoc(&self) {} + /// } + /// + /// impl Tr for u8 {} + /// ``` + /// + /// If we start the leaf definition search at `impl Tr for u8`, that impl will be the + /// `finalizing_node`, while `defining_node` will be the generic impl. + /// + /// If the leaf definition search is started at the generic impl, `finalizing_node` will be + /// `None`, since the most specialized impl we found still allows overriding the method + /// (doesn't finalize it). + pub finalizing_node: Option, +} + +impl LeafDef { + /// Returns whether this definition is known to not be further specializable. + pub fn is_final(&self) -> bool { + self.finalizing_node.is_some() + } +} + +impl<'tcx> Ancestors<'tcx> { + /// Finds the bottom-most (ie. most specialized) definition of an associated + /// item. + pub fn leaf_def( + mut self, + tcx: TyCtxt<'tcx>, + trait_item_name: Ident, + trait_item_kind: ty::AssocKind, + ) -> Option { + let trait_def_id = self.trait_def_id; + let mut finalizing_node = None; + + self.find_map(|node| { + if let Some(item) = node.item(tcx, trait_item_name, trait_item_kind, trait_def_id) { + if finalizing_node.is_none() { + let is_specializable = item.defaultness.is_default() + || tcx.impl_defaultness(node.def_id()).is_default(); + + if !is_specializable { + finalizing_node = Some(node); + } + } + + Some(LeafDef { item, defining_node: node, finalizing_node }) + } else { + // Item not mentioned. This "finalizes" any defaulted item provided by an ancestor. + finalizing_node = Some(node); + None + } + }) + } +} + +/// Walk up the specialization ancestors of a given impl, starting with that +/// impl itself. +/// +/// Returns `Err` if an error was reported while building the specialization +/// graph. +pub fn ancestors( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + start_from_impl: DefId, +) -> Result, ErrorReported> { + let specialization_graph = tcx.specialization_graph_of(trait_def_id); + if specialization_graph.has_errored { + 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..faaa576f17903 --- /dev/null +++ b/src/librustc_middle/traits/structural_impls.rs @@ -0,0 +1,312 @@ +use crate::traits; +use crate::ty::{Lift, TyCtxt}; + +use std::fmt; +use std::rc::Rc; + +// Structural impls for the structs in `traits`. + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + super::ImplSourceUserDefined(ref v) => write!(f, "{:?}", v), + + super::ImplSourceAutoImpl(ref t) => write!(f, "{:?}", t), + + super::ImplSourceClosure(ref d) => write!(f, "{:?}", d), + + super::ImplSourceGenerator(ref d) => write!(f, "{:?}", d), + + super::ImplSourceFnPointer(ref d) => write!(f, "ImplSourceFnPointer({:?})", d), + + super::ImplSourceDiscriminantKind(ref d) => write!(f, "{:?}", d), + + super::ImplSourceObject(ref d) => write!(f, "{:?}", d), + + super::ImplSourceParam(ref n) => write!(f, "ImplSourceParam({:?})", n), + + super::ImplSourceBuiltin(ref d) => write!(f, "{:?}", d), + + super::ImplSourceTraitAlias(ref d) => write!(f, "{:?}", d), + } + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceUserDefinedData(impl_def_id={:?}, substs={:?}, nested={:?})", + self.impl_def_id, self.substs, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceGeneratorData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceGeneratorData(generator_def_id={:?}, substs={:?}, nested={:?})", + self.generator_def_id, self.substs, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceClosureData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceClosureData(closure_def_id={:?}, substs={:?}, nested={:?})", + self.closure_def_id, self.substs, self.nested + ) + } +} + +impl fmt::Debug for traits::ImplSourceBuiltinData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ImplSourceBuiltinData(nested={:?})", self.nested) + } +} + +impl fmt::Debug for traits::ImplSourceAutoImplData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceAutoImplData(trait_def_id={:?}, nested={:?})", + self.trait_def_id, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceObjectData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceObjectData(upcast={:?}, vtable_base={}, nested={:?})", + self.upcast_trait_ref, self.vtable_base, self.nested + ) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceFnPointerData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ImplSourceFnPointerData(fn_ty={:?}, nested={:?})", self.fn_ty, self.nested) + } +} + +impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitAliasData<'tcx, N> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "ImplSourceTraitAlias(alias_def_id={:?}, substs={:?}, nested={:?})", + self.alias_def_id, self.substs, self.nested + ) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Lift implementations + +impl<'a, 'tcx> Lift<'tcx> for traits::SelectionError<'a> { + type Lifted = traits::SelectionError<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + super::Unimplemented => Some(super::Unimplemented), + super::OutputTypeParameterMismatch(a, b, ref err) => { + tcx.lift(&(a, b)).and_then(|(a, b)| { + tcx.lift(err).map(|err| super::OutputTypeParameterMismatch(a, b, err)) + }) + } + super::TraitNotObjectSafe(def_id) => Some(super::TraitNotObjectSafe(def_id)), + super::ConstEvalFailure(err) => Some(super::ConstEvalFailure(err)), + super::Overflow => Some(super::Overflow), + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { + type Lifted = traits::ObligationCauseCode<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + super::ReturnNoExpression => Some(super::ReturnNoExpression), + super::MiscObligation => Some(super::MiscObligation), + super::SliceOrArrayElem => Some(super::SliceOrArrayElem), + super::TupleElem => Some(super::TupleElem), + super::ProjectionWf(proj) => tcx.lift(&proj).map(super::ProjectionWf), + super::ItemObligation(def_id) => Some(super::ItemObligation(def_id)), + super::BindingObligation(def_id, span) => Some(super::BindingObligation(def_id, span)), + super::ReferenceOutlivesReferent(ty) => { + tcx.lift(&ty).map(super::ReferenceOutlivesReferent) + } + super::ObjectTypeBound(ty, r) => { + tcx.lift(&ty).and_then(|ty| tcx.lift(&r).map(|r| super::ObjectTypeBound(ty, r))) + } + super::ObjectCastObligation(ty) => tcx.lift(&ty).map(super::ObjectCastObligation), + super::Coercion { source, target } => { + Some(super::Coercion { source: tcx.lift(&source)?, target: tcx.lift(&target)? }) + } + super::AssignmentLhsSized => Some(super::AssignmentLhsSized), + super::TupleInitializerSized => Some(super::TupleInitializerSized), + super::StructInitializerSized => Some(super::StructInitializerSized), + super::VariableType(id) => Some(super::VariableType(id)), + super::ReturnValue(id) => Some(super::ReturnValue(id)), + super::ReturnType => Some(super::ReturnType), + super::SizedArgumentType => 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::new(self.span, self.body_id, code)) + } +} + +// For codegen only. +impl<'a, 'tcx> Lift<'tcx> for traits::ImplSource<'a, ()> { + type Lifted = traits::ImplSource<'tcx, ()>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match self.clone() { + traits::ImplSourceUserDefined(traits::ImplSourceUserDefinedData { + impl_def_id, + substs, + nested, + }) => tcx.lift(&substs).map(|substs| { + traits::ImplSourceUserDefined(traits::ImplSourceUserDefinedData { + impl_def_id, + substs, + nested, + }) + }), + traits::ImplSourceAutoImpl(t) => Some(traits::ImplSourceAutoImpl(t)), + traits::ImplSourceGenerator(traits::ImplSourceGeneratorData { + generator_def_id, + substs, + nested, + }) => tcx.lift(&substs).map(|substs| { + traits::ImplSourceGenerator(traits::ImplSourceGeneratorData { + generator_def_id, + substs, + nested, + }) + }), + traits::ImplSourceClosure(traits::ImplSourceClosureData { + closure_def_id, + substs, + nested, + }) => tcx.lift(&substs).map(|substs| { + traits::ImplSourceClosure(traits::ImplSourceClosureData { + closure_def_id, + substs, + nested, + }) + }), + traits::ImplSourceFnPointer(traits::ImplSourceFnPointerData { fn_ty, nested }) => { + tcx.lift(&fn_ty).map(|fn_ty| { + traits::ImplSourceFnPointer(traits::ImplSourceFnPointerData { fn_ty, nested }) + }) + } + traits::ImplSourceDiscriminantKind(traits::ImplSourceDiscriminantKindData) => { + Some(traits::ImplSourceDiscriminantKind(traits::ImplSourceDiscriminantKindData)) + } + traits::ImplSourceParam(n) => Some(traits::ImplSourceParam(n)), + traits::ImplSourceBuiltin(n) => Some(traits::ImplSourceBuiltin(n)), + traits::ImplSourceObject(traits::ImplSourceObjectData { + upcast_trait_ref, + vtable_base, + nested, + }) => tcx.lift(&upcast_trait_ref).map(|trait_ref| { + traits::ImplSourceObject(traits::ImplSourceObjectData { + upcast_trait_ref: trait_ref, + vtable_base, + nested, + }) + }), + traits::ImplSourceTraitAlias(traits::ImplSourceTraitAliasData { + alias_def_id, + substs, + nested, + }) => tcx.lift(&substs).map(|substs| { + traits::ImplSourceTraitAlias(traits::ImplSourceTraitAliasData { + alias_def_id, + substs, + nested, + }) + }), + } + } +} diff --git a/src/librustc/ty/_match.rs b/src/librustc_middle/ty/_match.rs similarity index 92% rename from src/librustc/ty/_match.rs rename to src/librustc_middle/ty/_match.rs index 35f8eb20475c7..db9229ae3d214 100644 --- a/src/librustc/ty/_match.rs +++ b/src/librustc_middle/ty/_match.rs @@ -68,15 +68,18 @@ 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))) } - (&ty::Error, _) | (_, &ty::Error) => Ok(self.tcx().types.err), + (&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(self.tcx().ty_error()), _ => relate::super_relate_tys(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_middle/ty/codec.rs b/src/librustc_middle/ty/codec.rs new file mode 100644 index 0000000000000..67ceaca103e9f --- /dev/null +++ b/src/librustc_middle/ty/codec.rs @@ -0,0 +1,542 @@ +// This module contains some shared code for encoding and decoding various +// things from the `ty` module, and in particular implements support for +// "shorthands" which allow to have pointers back into the already encoded +// stream instead of re-encoding the same thing twice. +// +// The functionality in here is shared between persisting to crate metadata and +// persisting to incr. comp. caches. + +use crate::arena::ArenaAllocatable; +use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; +use crate::mir::{self, interpret::Allocation}; +use crate::ty::subst::SubstsRef; +use crate::ty::{self, List, Ty, TyCtxt}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; +use rustc_span::Span; +use std::convert::{TryFrom, TryInto}; +use std::hash::Hash; +use std::intrinsics; +use std::marker::DiscriminantKind; + +/// The shorthand encoding uses an enum's variant index `usize` +/// and is offset by this value so it never matches a real variant. +/// This offset is also chosen so that the first byte is never < 0x80. +pub const SHORTHAND_OFFSET: usize = 0x80; + +pub trait EncodableWithShorthand: Clone + Eq + Hash { + type Variant: Encodable; + fn variant(&self) -> &Self::Variant; +} + +#[allow(rustc::usage_of_ty_tykind)] +impl<'tcx> EncodableWithShorthand for Ty<'tcx> { + type Variant = ty::TyKind<'tcx>; + fn variant(&self) -> &Self::Variant { + &self.kind + } +} + +impl<'tcx> EncodableWithShorthand for ty::Predicate<'tcx> { + type Variant = ty::PredicateKind<'tcx>; + fn variant(&self) -> &Self::Variant { + self.kind() + } +} + +pub trait TyEncoder: Encoder { + fn position(&self) -> usize; +} + +impl TyEncoder for opaque::Encoder { + #[inline] + fn position(&self) -> usize { + self.position() + } +} + +/// Encode the given value or a previously cached shorthand. +pub fn encode_with_shorthand(encoder: &mut E, value: &T, cache: M) -> Result<(), E::Error> +where + E: TyEncoder, + M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, + T: EncodableWithShorthand, + ::Discriminant: Ord + TryFrom, +{ + let existing_shorthand = cache(encoder).get(value).cloned(); + if let Some(shorthand) = existing_shorthand { + return encoder.emit_usize(shorthand); + } + + let variant = value.variant(); + + let start = encoder.position(); + variant.encode(encoder)?; + let len = encoder.position() - start; + + // The shorthand encoding uses the same usize as the + // discriminant, with an offset so they can't conflict. + let discriminant = intrinsics::discriminant_value(variant); + assert!(discriminant < SHORTHAND_OFFSET.try_into().ok().unwrap()); + + let shorthand = start + SHORTHAND_OFFSET; + + // Get the number of bits that leb128 could fit + // in the same space as the fully encoded type. + let leb128_bits = len * 7; + + // Check that the shorthand is a not longer than the + // full encoding itself, i.e., it's an obvious win. + if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { + cache(encoder).insert(value.clone(), shorthand); + } + + Ok(()) +} + +pub trait TyDecoder<'tcx>: Decoder { + fn tcx(&self) -> TyCtxt<'tcx>; + + fn peek_byte(&self) -> u8; + + fn position(&self) -> usize; + + fn cached_ty_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> Result, Self::Error> + where + F: FnOnce(&mut Self) -> Result, Self::Error>; + + fn cached_predicate_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> Result, Self::Error> + where + F: FnOnce(&mut Self) -> Result, Self::Error>; + + fn with_position(&mut self, pos: usize, f: F) -> R + where + F: FnOnce(&mut Self) -> R; + + fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum; + + fn positioned_at_shorthand(&self) -> bool { + (self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0 + } +} + +#[inline] +pub fn decode_arena_allocable<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( + decoder: &mut D, +) -> Result<&'tcx T, D::Error> +where + D: TyDecoder<'tcx>, +{ + Ok(decoder.tcx().arena.alloc(Decodable::decode(decoder)?)) +} + +#[inline] +pub fn decode_arena_allocable_slice<'tcx, D, T: ArenaAllocatable<'tcx> + Decodable>( + decoder: &mut D, +) -> Result<&'tcx [T], D::Error> +where + D: TyDecoder<'tcx>, +{ + Ok(decoder.tcx().arena.alloc_from_iter( as Decodable>::decode(decoder)?)) +} + +#[inline] +pub fn decode_cnum(decoder: &mut D) -> Result +where + D: TyDecoder<'tcx>, +{ + let cnum = CrateNum::from_u32(u32::decode(decoder)?); + Ok(decoder.map_encoded_cnum_to_current(cnum)) +} + +#[allow(rustc::usage_of_ty_tykind)] +#[inline] +pub fn decode_ty(decoder: &mut D) -> Result, D::Error> +where + D: TyDecoder<'tcx>, +{ + // Handle shorthands first, if we have an usize > 0x80. + if decoder.positioned_at_shorthand() { + let pos = decoder.read_usize()?; + assert!(pos >= SHORTHAND_OFFSET); + let shorthand = pos - SHORTHAND_OFFSET; + + decoder.cached_ty_for_shorthand(shorthand, |decoder| { + decoder.with_position(shorthand, Ty::decode) + }) + } else { + let tcx = decoder.tcx(); + Ok(tcx.mk_ty(ty::TyKind::decode(decoder)?)) + } +} + +#[inline] +pub fn decode_predicate(decoder: &mut D) -> Result, D::Error> +where + D: TyDecoder<'tcx>, +{ + // Handle shorthands first, if we have an usize > 0x80. + if decoder.positioned_at_shorthand() { + let pos = decoder.read_usize()?; + assert!(pos >= SHORTHAND_OFFSET); + let shorthand = pos - SHORTHAND_OFFSET; + + decoder.cached_predicate_for_shorthand(shorthand, |decoder| { + decoder.with_position(shorthand, ty::Predicate::decode) + }) + } else { + let tcx = decoder.tcx(); + Ok(tcx.mk_predicate(ty::PredicateKind::decode(decoder)?)) + } +} + +#[inline] +pub fn decode_spanned_predicates( + decoder: &mut D, +) -> Result<&'tcx [(ty::Predicate<'tcx>, Span)], D::Error> +where + D: TyDecoder<'tcx>, +{ + let tcx = decoder.tcx(); + Ok(tcx.arena.alloc_from_iter( + (0..decoder.read_usize()?) + .map(|_| Decodable::decode(decoder)) + .collect::, _>>()?, + )) +} + +#[inline] +pub fn decode_substs(decoder: &mut D) -> Result, D::Error> +where + D: TyDecoder<'tcx>, +{ + let len = decoder.read_usize()?; + let tcx = decoder.tcx(); + Ok(tcx.mk_substs((0..len).map(|_| Decodable::decode(decoder)))?) +} + +#[inline] +pub fn decode_place(decoder: &mut D) -> Result, D::Error> +where + D: TyDecoder<'tcx>, +{ + let local: mir::Local = Decodable::decode(decoder)?; + let len = decoder.read_usize()?; + let projection: &'tcx List> = + decoder.tcx().mk_place_elems((0..len).map(|_| Decodable::decode(decoder)))?; + Ok(mir::Place { local, projection }) +} + +#[inline] +pub fn decode_region(decoder: &mut D) -> Result, D::Error> +where + D: TyDecoder<'tcx>, +{ + Ok(decoder.tcx().mk_region(Decodable::decode(decoder)?)) +} + +#[inline] +pub fn decode_ty_slice(decoder: &mut D) -> Result<&'tcx ty::List>, D::Error> +where + D: TyDecoder<'tcx>, +{ + let len = decoder.read_usize()?; + Ok(decoder.tcx().mk_type_list((0..len).map(|_| Decodable::decode(decoder)))?) +} + +#[inline] +pub fn decode_adt_def(decoder: &mut D) -> Result<&'tcx ty::AdtDef, D::Error> +where + D: TyDecoder<'tcx>, +{ + let def_id = DefId::decode(decoder)?; + Ok(decoder.tcx().adt_def(def_id)) +} + +#[inline] +pub fn decode_existential_predicate_slice( + decoder: &mut D, +) -> Result<&'tcx ty::List>, D::Error> +where + D: TyDecoder<'tcx>, +{ + let len = decoder.read_usize()?; + Ok(decoder.tcx().mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) +} + +#[inline] +pub fn decode_canonical_var_infos(decoder: &mut D) -> Result, D::Error> +where + D: TyDecoder<'tcx>, +{ + let len = decoder.read_usize()?; + let interned: Result, _> = + (0..len).map(|_| Decodable::decode(decoder)).collect(); + Ok(decoder.tcx().intern_canonical_var_infos(interned?.as_slice())) +} + +#[inline] +pub fn decode_const(decoder: &mut D) -> Result<&'tcx ty::Const<'tcx>, D::Error> +where + D: TyDecoder<'tcx>, +{ + Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?)) +} + +#[inline] +pub fn decode_allocation(decoder: &mut D) -> Result<&'tcx Allocation, D::Error> +where + D: TyDecoder<'tcx>, +{ + Ok(decoder.tcx().intern_const_alloc(Decodable::decode(decoder)?)) +} + +#[macro_export] +macro_rules! __impl_decoder_methods { + ($($name:ident -> $ty:ty;)*) => { + $( + #[inline] + fn $name(&mut self) -> Result<$ty, Self::Error> { + self.opaque.$name() + } + )* + } +} + +#[macro_export] +macro_rules! impl_arena_allocatable_decoder { + ([]$args:tt) => {}; + ([decode $(, $attrs:ident)*] + [[$DecoderName:ident [$($typaram:tt),*]], [$name:ident: $ty:ty, $gen_ty:ty], $tcx:lifetime]) => { + // FIXME(#36588): These impls are horribly unsound as they allow + // the caller to pick any lifetime for `'tcx`, including `'static`. + #[allow(unused_lifetimes)] + impl<'_x, '_y, '_z, '_w, '_a, $($typaram),*> SpecializedDecoder<&'_a $gen_ty> + for $DecoderName<$($typaram),*> + where &'_a $gen_ty: UseSpecializedDecodable + { + #[inline] + fn specialized_decode(&mut self) -> Result<&'_a $gen_ty, Self::Error> { + unsafe { + std::mem::transmute::< + Result<&$tcx $ty, Self::Error>, + Result<&'_a $gen_ty, Self::Error>, + >(decode_arena_allocable(self)) + } + } + } + + #[allow(unused_lifetimes)] + impl<'_x, '_y, '_z, '_w, '_a, $($typaram),*> SpecializedDecoder<&'_a [$gen_ty]> + for $DecoderName<$($typaram),*> + where &'_a [$gen_ty]: UseSpecializedDecodable + { + #[inline] + fn specialized_decode(&mut self) -> Result<&'_a [$gen_ty], Self::Error> { + unsafe { + std::mem::transmute::< + Result<&$tcx [$ty], Self::Error>, + Result<&'_a [$gen_ty], Self::Error>, + >(decode_arena_allocable_slice(self)) + } + } + } + }; + ([$ignore:ident $(, $attrs:ident)*]$args:tt) => { + impl_arena_allocatable_decoder!([$($attrs),*]$args); + }; +} + +#[macro_export] +macro_rules! impl_arena_allocatable_decoders { + ($args:tt, [$($a:tt $name:ident: $ty:ty, $gen_ty:ty;)*], $tcx:lifetime) => { + $( + impl_arena_allocatable_decoder!($a [$args, [$name: $ty, $gen_ty], $tcx]); + )* + } +} + +#[macro_export] +macro_rules! implement_ty_decoder { + ($DecoderName:ident <$($typaram:tt),*>) => { + mod __ty_decoder_impl { + use std::borrow::Cow; + use std::mem::transmute; + + use rustc_serialize::{Decoder, SpecializedDecoder, UseSpecializedDecodable}; + + use $crate::infer::canonical::CanonicalVarInfos; + use $crate::ty; + use $crate::ty::codec::*; + use $crate::ty::subst::InternalSubsts; + use rustc_hir::def_id::CrateNum; + + use rustc_span::Span; + + use super::$DecoderName; + + impl<$($typaram ),*> Decoder for $DecoderName<$($typaram),*> { + type Error = String; + + __impl_decoder_methods! { + read_nil -> (); + + read_u128 -> u128; + read_u64 -> u64; + read_u32 -> u32; + read_u16 -> u16; + read_u8 -> u8; + read_usize -> usize; + + read_i128 -> i128; + read_i64 -> i64; + read_i32 -> i32; + read_i16 -> i16; + read_i8 -> i8; + read_isize -> isize; + + read_bool -> bool; + read_f64 -> f64; + read_f32 -> f32; + read_char -> char; + read_str -> Cow<'_, str>; + } + + fn error(&mut self, err: &str) -> Self::Error { + self.opaque.error(err) + } + } + + // FIXME(#36588): These impls are horribly unsound as they allow + // the caller to pick any lifetime for `'tcx`, including `'static`. + + arena_types!(impl_arena_allocatable_decoders, [$DecoderName [$($typaram),*]], 'tcx); + + impl<$($typaram),*> SpecializedDecoder + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result { + decode_cnum(self) + } + } + + impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x ty::TyS<'_y>> + for $DecoderName<$($typaram),*> + where &'_x ty::TyS<'_y>: UseSpecializedDecodable + { + fn specialized_decode(&mut self) -> Result<&'_x ty::TyS<'_y>, Self::Error> { + unsafe { + transmute::< + Result, Self::Error>, + Result<&'_x ty::TyS<'_y>, Self::Error>, + >(decode_ty(self)) + } + } + } + + impl<'_x, $($typaram),*> SpecializedDecoder> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result, Self::Error> { + unsafe { + transmute::< + Result, Self::Error>, + Result, Self::Error>, + >(decode_predicate(self)) + } + } + } + + impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x [(ty::Predicate<'_y>, Span)]> + for $DecoderName<$($typaram),*> + where &'_x [(ty::Predicate<'_y>, Span)]: UseSpecializedDecodable { + fn specialized_decode(&mut self) + -> Result<&'_x [(ty::Predicate<'_y>, Span)], Self::Error> + { + unsafe { transmute(decode_spanned_predicates(self)) } + } + } + + impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x InternalSubsts<'_y>> + for $DecoderName<$($typaram),*> + where &'_x InternalSubsts<'_y>: UseSpecializedDecodable { + fn specialized_decode(&mut self) -> Result<&'_x InternalSubsts<'_y>, Self::Error> { + unsafe { transmute(decode_substs(self)) } + } + } + + impl<'_x, $($typaram),*> SpecializedDecoder<$crate::mir::Place<'_x>> + for $DecoderName<$($typaram),*> { + fn specialized_decode( + &mut self + ) -> Result<$crate::mir::Place<'_x>, Self::Error> { + unsafe { transmute(decode_place(self)) } + } + } + + impl<'_x, $($typaram),*> SpecializedDecoder> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result, Self::Error> { + unsafe { transmute(decode_region(self)) } + } + } + + impl<'_x, '_y, '_z, $($typaram),*> SpecializedDecoder<&'_x ty::List<&'_y ty::TyS<'_z>>> + for $DecoderName<$($typaram),*> + where &'_x ty::List<&'_y ty::TyS<'_z>>: UseSpecializedDecodable { + fn specialized_decode(&mut self) + -> Result<&'_x ty::List<&'_y ty::TyS<'_z>>, Self::Error> { + unsafe { transmute(decode_ty_slice(self)) } + } + } + + impl<'_x, $($typaram),*> SpecializedDecoder<&'_x ty::AdtDef> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) -> Result<&'_x ty::AdtDef, Self::Error> { + unsafe { transmute(decode_adt_def(self)) } + } + } + + impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x ty::List>> + for $DecoderName<$($typaram),*> + where &'_x ty::List>: UseSpecializedDecodable { + fn specialized_decode(&mut self) + -> Result<&'_x ty::List>, Self::Error> { + unsafe { transmute(decode_existential_predicate_slice(self)) } + } + } + + impl<'_x, $($typaram),*> SpecializedDecoder> + for $DecoderName<$($typaram),*> { + fn specialized_decode(&mut self) + -> Result, Self::Error> { + unsafe { transmute(decode_canonical_var_infos(self)) } + } + } + + impl<'_x, '_y, $($typaram),*> SpecializedDecoder<&'_x $crate::ty::Const<'_y>> + for $DecoderName<$($typaram),*> + where &'_x $crate::ty::Const<'_y>: UseSpecializedDecodable { + fn specialized_decode(&mut self) -> Result<&'_x ty::Const<'_y>, Self::Error> { + unsafe { transmute(decode_const(self)) } + } + } + + impl<'_x, $($typaram),*> SpecializedDecoder<&'_x $crate::mir::interpret::Allocation> + for $DecoderName<$($typaram),*> { + fn specialized_decode( + &mut self + ) -> Result<&'_x $crate::mir::interpret::Allocation, Self::Error> { + unsafe { transmute(decode_allocation(self)) } + } + } + } + }; +} diff --git a/src/librustc/ty/context.rs b/src/librustc_middle/ty/context.rs similarity index 84% rename from src/librustc/ty/context.rs rename to src/librustc_middle/ty/context.rs index 611a2dc20b410..df08e083d2cbb 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc_middle/ty/context.rs @@ -1,51 +1,30 @@ //! Type context book-keeping. 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::dep_graph::{self, DepConstructor, DepGraph}; +use crate::hir::exports::ExportMap; use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; -use crate::lint::{struct_lint_level, LintSource}; +use crate::lint::{struct_lint_level, LintDiagnosticBuilder, 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::cstore::{CrateStoreDyn, EncodedMetadata}; 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::{self, Allocation, ConstValue, Scalar}; +use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted}; use crate::traits; -use crate::traits::{Clause, Clauses, Goal, GoalKind, Goals}; -use crate::ty::free_region_map::FreeRegionMap; -use crate::ty::layout::{LayoutDetails, TargetDataLayout, VariantIdx}; -use crate::ty::query; use crate::ty::steal::Steal; -use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; -use crate::ty::subst::{GenericArgKind, UserSubsts}; -use crate::ty::CanonicalPolyFnSig; -use crate::ty::GenericParamDefKind; -use crate::ty::RegionKind; -use crate::ty::ReprOptions; +use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts}; use crate::ty::TyKind::*; -use crate::ty::{self, DefIdTree, Ty, TypeAndMut}; -use crate::ty::{AdtDef, AdtKind, Const, Region}; -use crate::ty::{BindingMode, BoundVar}; -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 crate::ty::{ + self, query, AdtDef, AdtKind, BindingMode, BoundVar, CanonicalPolyFnSig, Const, ConstVid, + DefIdTree, ExistentialPredicate, FloatVar, FloatVid, GenericParamDefKind, InferConst, InferTy, + IntVar, IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate, PredicateInner, PredicateKind, + ProjectionTy, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, + TyVid, TypeAndMut, +}; use rustc_ast::ast; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_ast::node_id::NodeMap; use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::SelfProfilerRef; @@ -54,20 +33,23 @@ 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, DefIndex, LOCAL_CRATE}; -use rustc_hir::{HirId, Node, TraitCandidate}; -use rustc_hir::{ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; +use rustc_hir::definitions::{DefPathHash, Definitions}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::lang_items::{self, PanicLocationLangItem}; +use rustc_hir::{HirId, ItemKind, ItemLocalId, ItemLocalMap, ItemLocalSet, Node, TraitCandidate}; use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable; -use rustc_session::config::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_span::{Span, DUMMY_SP}; +use rustc_target::abi::{Layout, TargetDataLayout, VariantIdx}; use rustc_target::spec::abi; use smallvec::SmallVec; @@ -96,13 +78,13 @@ pub struct CtxtInterners<'tcx> { canonical_var_infos: InternedSet<'tcx, List>, region: InternedSet<'tcx, RegionKind>, existential_predicates: InternedSet<'tcx, List>>, + predicate: InternedSet<'tcx, PredicateInner<'tcx>>, predicates: InternedSet<'tcx, List>>, - 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,13 +97,13 @@ impl<'tcx> CtxtInterners<'tcx> { region: Default::default(), existential_predicates: Default::default(), canonical_var_infos: Default::default(), + predicate: 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(), } } @@ -143,6 +125,23 @@ impl<'tcx> CtxtInterners<'tcx> { }) .0 } + + #[inline(never)] + fn intern_predicate(&self, kind: PredicateKind<'tcx>) -> &'tcx PredicateInner<'tcx> { + self.predicate + .intern(kind, |kind| { + let flags = super::flags::FlagComputation::for_predicate(&kind); + + let predicate_struct = PredicateInner { + kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + + Interned(self.arena.alloc(predicate_struct)) + }) + .0 + } } pub struct CommonTypes<'tcx> { @@ -163,9 +162,9 @@ pub struct CommonTypes<'tcx> { pub u128: Ty<'tcx>, pub f32: Ty<'tcx>, pub f64: Ty<'tcx>, + pub str_: Ty<'tcx>, pub never: Ty<'tcx>, pub self_param: Ty<'tcx>, - pub err: Ty<'tcx>, /// Dummy type used for the `Self` of a `TraitRef` created for converting /// a trait object, and which gets removed in `ExistentialTraitRef`. @@ -185,41 +184,40 @@ pub struct CommonLifetimes<'tcx> { } pub struct CommonConsts<'tcx> { - pub err: &'tcx Const<'tcx>, + pub unit: &'tcx Const<'tcx>, } pub struct LocalTableInContext<'a, V> { - local_id_root: Option, + hir_owner: Option, data: &'a ItemLocalMap, } /// Validate that the given HirId (respectively its `local_id` part) can be /// safely used as a key in the tables of a TypeckTable. For that to be /// the case, the HirId must have the same `owner` as all the other IDs in -/// this table (signified by `local_id_root`). Otherwise the HirId +/// this table (signified by `hir_owner`). Otherwise the HirId /// would be in a different frame of reference and using its `local_id` /// would result in lookup errors, or worse, in silently wrong data being /// stored/returned. fn validate_hir_id_for_typeck_tables( - local_id_root: Option, + hir_owner: Option, hir_id: hir::HirId, mut_access: bool, ) { - if let Some(local_id_root) = local_id_root { - if hir_id.owner != local_id_root.index { + if let Some(hir_owner) = hir_owner { + if hir_id.owner != hir_owner { ty::tls::with(|tcx| { bug!( - "node {} with HirId::owner {:?} cannot be placed in \ - TypeckTables with local_id_root {:?}", + "node {} with HirId::owner {:?} cannot be placed in TypeckTables with hir_owner {:?}", tcx.hir().node_to_string(hir_id), - DefId::local(hir_id.owner), - local_id_root + hir_id.owner, + hir_owner ) }); } } else { // We use "Null Object" TypeckTables in some of the analysis passes. - // These are just expected to be empty and their `local_id_root` is + // These are just expected to be empty and their `hir_owner` is // `None`. Therefore we cannot verify whether a given `HirId` would // be a valid key for the given table. Instead we make sure that // nobody tries to write to such a Null Object table. @@ -231,12 +229,12 @@ fn validate_hir_id_for_typeck_tables( impl<'a, V> LocalTableInContext<'a, V> { pub fn contains_key(&self, id: hir::HirId) -> bool { - validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id, false); self.data.contains_key(&id.local_id) } pub fn get(&self, id: hir::HirId) -> Option<&V> { - validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id, false); self.data.get(&id.local_id) } @@ -254,28 +252,28 @@ impl<'a, V> ::std::ops::Index for LocalTableInContext<'a, V> { } pub struct LocalTableInContextMut<'a, V> { - local_id_root: Option, + hir_owner: Option, data: &'a mut ItemLocalMap, } impl<'a, V> LocalTableInContextMut<'a, V> { pub fn get_mut(&mut self, id: hir::HirId) -> Option<&mut V> { - validate_hir_id_for_typeck_tables(self.local_id_root, id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, id, true); self.data.get_mut(&id.local_id) } pub fn entry(&mut self, id: hir::HirId) -> Entry<'_, hir::ItemLocalId, V> { - validate_hir_id_for_typeck_tables(self.local_id_root, id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, id, true); self.data.entry(id.local_id) } pub fn insert(&mut self, id: hir::HirId, val: V) -> Option { - validate_hir_id_for_typeck_tables(self.local_id_root, id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, id, true); self.data.insert(id.local_id, val) } pub fn remove(&mut self, id: hir::HirId) -> Option { - validate_hir_id_for_typeck_tables(self.local_id_root, id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, id, true); self.data.remove(&id.local_id) } } @@ -301,14 +299,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. @@ -317,14 +315,16 @@ 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, } #[derive(RustcEncodable, RustcDecodable, Debug)] pub struct TypeckTables<'tcx> { - /// The HirId::owner all ItemLocalIds in this table are relative to. - pub local_id_root: Option, + /// The `HirId::owner` all `ItemLocalId`s in this table are relative to. + pub hir_owner: Option, /// Resolved definitions for `::X` associated paths and /// method calls, including those of overloaded operators. @@ -387,7 +387,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 @@ -410,16 +410,11 @@ pub struct TypeckTables<'tcx> { /// This is used for warning unused imports. During type /// checking, this `Lrc` should not be cloned: it must have a ref-count /// of 1 so that we can insert things into the set mutably. - pub used_trait_imports: Lrc, + 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, - - /// Stores the free-region relationships that were deduced from - /// its where-clauses and parameter types. These are then - /// read-again by borrowck. - pub free_region_map: FreeRegionMap<'tcx>, + /// 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. @@ -429,7 +424,7 @@ pub struct TypeckTables<'tcx> { /// The upvarID contains the HIR node ID and it also contains the full path /// leading to the member of the struct or tuple that is used instead of the /// entire variable. - pub upvar_list: ty::UpvarListMap, + pub closure_captures: ty::UpvarListMap, /// Stores the type, expression, span and optional scope span of all types /// that are live across the yield of this generator (if a generator). @@ -437,9 +432,9 @@ pub struct TypeckTables<'tcx> { } impl<'tcx> TypeckTables<'tcx> { - pub fn empty(local_id_root: Option) -> TypeckTables<'tcx> { + pub fn empty(hir_owner: Option) -> TypeckTables<'tcx> { TypeckTables { - local_id_root, + hir_owner, type_dependent_defs: Default::default(), field_indices: Default::default(), user_provided_types: Default::default(), @@ -455,10 +450,9 @@ impl<'tcx> TypeckTables<'tcx> { fru_field_types: Default::default(), coercion_casts: Default::default(), used_trait_imports: Lrc::new(Default::default()), - tainted_by_errors: false, - free_region_map: Default::default(), + tainted_by_errors: None, concrete_opaque_types: Default::default(), - upvar_list: Default::default(), + closure_captures: Default::default(), generator_interior_types: Default::default(), } } @@ -476,11 +470,11 @@ impl<'tcx> TypeckTables<'tcx> { pub fn type_dependent_defs( &self, ) -> LocalTableInContext<'_, Result<(DefKind, DefId), ErrorReported>> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.type_dependent_defs } + LocalTableInContext { hir_owner: self.hir_owner, data: &self.type_dependent_defs } } pub fn type_dependent_def(&self, id: HirId) -> Option<(DefKind, DefId)> { - validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id, false); self.type_dependent_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) } @@ -491,39 +485,33 @@ impl<'tcx> TypeckTables<'tcx> { pub fn type_dependent_defs_mut( &mut self, ) -> LocalTableInContextMut<'_, Result<(DefKind, DefId), ErrorReported>> { - LocalTableInContextMut { - local_id_root: self.local_id_root, - data: &mut self.type_dependent_defs, - } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.type_dependent_defs } } pub fn field_indices(&self) -> LocalTableInContext<'_, usize> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.field_indices } + LocalTableInContext { hir_owner: self.hir_owner, data: &self.field_indices } } pub fn field_indices_mut(&mut self) -> LocalTableInContextMut<'_, usize> { - LocalTableInContextMut { local_id_root: self.local_id_root, data: &mut self.field_indices } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.field_indices } } pub fn user_provided_types(&self) -> LocalTableInContext<'_, CanonicalUserType<'tcx>> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.user_provided_types } + LocalTableInContext { hir_owner: self.hir_owner, data: &self.user_provided_types } } pub fn user_provided_types_mut( &mut self, ) -> LocalTableInContextMut<'_, CanonicalUserType<'tcx>> { - LocalTableInContextMut { - local_id_root: self.local_id_root, - data: &mut self.user_provided_types, - } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.user_provided_types } } pub fn node_types(&self) -> LocalTableInContext<'_, Ty<'tcx>> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.node_types } + LocalTableInContext { hir_owner: self.hir_owner, data: &self.node_types } } pub fn node_types_mut(&mut self) -> LocalTableInContextMut<'_, Ty<'tcx>> { - LocalTableInContextMut { local_id_root: self.local_id_root, data: &mut self.node_types } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.node_types } } pub fn node_type(&self, id: hir::HirId) -> Ty<'tcx> { @@ -533,21 +521,21 @@ impl<'tcx> TypeckTables<'tcx> { } pub fn node_type_opt(&self, id: hir::HirId) -> Option> { - validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id, false); self.node_types.get(&id.local_id).cloned() } pub fn node_substs_mut(&mut self) -> LocalTableInContextMut<'_, SubstsRef<'tcx>> { - LocalTableInContextMut { local_id_root: self.local_id_root, data: &mut self.node_substs } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.node_substs } } pub fn node_substs(&self, id: hir::HirId) -> SubstsRef<'tcx> { - validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id, false); self.node_substs.get(&id.local_id).cloned().unwrap_or_else(|| InternalSubsts::empty()) } pub fn node_substs_opt(&self, id: hir::HirId) -> Option> { - validate_hir_id_for_typeck_tables(self.local_id_root, id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, id, false); self.node_substs.get(&id.local_id).cloned() } @@ -580,17 +568,17 @@ impl<'tcx> TypeckTables<'tcx> { } pub fn adjustments(&self) -> LocalTableInContext<'_, Vec>> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.adjustments } + LocalTableInContext { hir_owner: self.hir_owner, data: &self.adjustments } } pub fn adjustments_mut( &mut self, ) -> LocalTableInContextMut<'_, Vec>> { - LocalTableInContextMut { local_id_root: self.local_id_root, data: &mut self.adjustments } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.adjustments } } pub fn expr_adjustments(&self, expr: &hir::Expr<'_>) -> &[ty::adjustment::Adjustment<'tcx>] { - validate_hir_id_for_typeck_tables(self.local_id_root, expr.hir_id, false); + validate_hir_id_for_typeck_tables(self.hir_owner, expr.hir_id, false); self.adjustments.get(&expr.hir_id.local_id).map_or(&[], |a| &a[..]) } @@ -625,66 +613,51 @@ impl<'tcx> TypeckTables<'tcx> { } pub fn pat_binding_modes(&self) -> LocalTableInContext<'_, BindingMode> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.pat_binding_modes } + LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_binding_modes } } pub fn pat_binding_modes_mut(&mut self) -> LocalTableInContextMut<'_, BindingMode> { - LocalTableInContextMut { - local_id_root: self.local_id_root, - data: &mut self.pat_binding_modes, - } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes } } pub fn pat_adjustments(&self) -> LocalTableInContext<'_, Vec>> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.pat_adjustments } + LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_adjustments } } pub fn pat_adjustments_mut(&mut self) -> LocalTableInContextMut<'_, Vec>> { - LocalTableInContextMut { - local_id_root: self.local_id_root, - data: &mut self.pat_adjustments, - } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> { self.upvar_capture_map[&upvar_id] } - pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, ast::Name)> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.closure_kind_origins } + 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)> { - LocalTableInContextMut { - local_id_root: self.local_id_root, - data: &mut self.closure_kind_origins, - } + pub fn closure_kind_origins_mut(&mut self) -> LocalTableInContextMut<'_, (Span, Symbol)> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.closure_kind_origins } } pub fn liberated_fn_sigs(&self) -> LocalTableInContext<'_, ty::FnSig<'tcx>> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.liberated_fn_sigs } + LocalTableInContext { hir_owner: self.hir_owner, data: &self.liberated_fn_sigs } } pub fn liberated_fn_sigs_mut(&mut self) -> LocalTableInContextMut<'_, ty::FnSig<'tcx>> { - LocalTableInContextMut { - local_id_root: self.local_id_root, - data: &mut self.liberated_fn_sigs, - } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.liberated_fn_sigs } } pub fn fru_field_types(&self) -> LocalTableInContext<'_, Vec>> { - LocalTableInContext { local_id_root: self.local_id_root, data: &self.fru_field_types } + LocalTableInContext { hir_owner: self.hir_owner, data: &self.fru_field_types } } pub fn fru_field_types_mut(&mut self) -> LocalTableInContextMut<'_, Vec>> { - LocalTableInContextMut { - local_id_root: self.local_id_root, - data: &mut self.fru_field_types, - } + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.fru_field_types } } pub fn is_coercion_cast(&self, hir_id: hir::HirId) -> bool { - validate_hir_id_for_typeck_tables(self.local_id_root, hir_id, true); + validate_hir_id_for_typeck_tables(self.hir_owner, hir_id, true); self.coercion_casts.contains(&hir_id.local_id) } @@ -700,7 +673,7 @@ impl<'tcx> TypeckTables<'tcx> { impl<'a, 'tcx> HashStable> for TypeckTables<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let ty::TypeckTables { - local_id_root, + hir_owner, ref type_dependent_defs, ref field_indices, ref user_provided_types, @@ -719,9 +692,8 @@ impl<'a, 'tcx> HashStable> for TypeckTables<'tcx> { ref used_trait_imports, tainted_by_errors, - ref free_region_map, ref concrete_opaque_types, - ref upvar_list, + ref closure_captures, ref generator_interior_types, } = *self; @@ -738,16 +710,12 @@ impl<'a, 'tcx> HashStable> for TypeckTables<'tcx> { hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| { let ty::UpvarId { var_path, closure_expr_id } = *up_var_id; - let local_id_root = local_id_root.expect("trying to hash invalid TypeckTables"); + assert_eq!(Some(var_path.hir_id.owner), hir_owner); - let var_owner_def_id = - DefId { krate: local_id_root.krate, index: var_path.hir_id.owner }; - let closure_def_id = - DefId { krate: local_id_root.krate, index: closure_expr_id.to_def_id().index }; ( - hcx.def_path_hash(var_owner_def_id), + hcx.local_def_path_hash(var_path.hir_id.owner), var_path.hir_id.local_id, - hcx.def_path_hash(closure_def_id), + hcx.local_def_path_hash(closure_expr_id), ) }); @@ -757,9 +725,8 @@ impl<'a, 'tcx> HashStable> for TypeckTables<'tcx> { coercion_casts.hash_stable(hcx, hasher); used_trait_imports.hash_stable(hcx, hasher); tainted_by_errors.hash_stable(hcx, hasher); - free_region_map.hash_stable(hcx, hasher); concrete_opaque_types.hash_stable(hcx, hasher); - upvar_list.hash_stable(hcx, hasher); + closure_captures.hash_stable(hcx, hasher); generator_interior_types.hash_stable(hcx, hasher); }) } @@ -855,7 +822,6 @@ impl<'tcx> CommonTypes<'tcx> { bool: mk(Bool), char: mk(Char), never: mk(Never), - err: mk(Error), isize: mk(Int(ast::IntTy::Isize)), i8: mk(Int(ast::IntTy::I8)), i16: mk(Int(ast::IntTy::I16)), @@ -870,6 +836,7 @@ impl<'tcx> CommonTypes<'tcx> { u128: mk(Uint(ast::UintTy::U128)), f32: mk(Float(ast::FloatTy::F32)), f64: mk(Float(ast::FloatTy::F64)), + str_: mk(Str), self_param: mk(ty::Param(ty::ParamTy { index: 0, name: kw::SelfUpper })), trait_object_dummy_self: mk(Infer(ty::FreshTy(0))), @@ -894,9 +861,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, }), } } @@ -963,14 +930,14 @@ pub struct GlobalCtxt<'tcx> { pub consts: CommonConsts<'tcx>, /// Resolutions of `extern crate` items produced by resolver. - extern_crate_map: NodeMap, + extern_crate_map: FxHashMap, /// Map indicating what traits are in scope for places where this /// is relevant; generated by resolve. - trait_map: FxHashMap>>, + trait_map: FxHashMap>>, /// Export map produced by name resolution. - export_map: FxHashMap>>, + export_map: ExportMap, pub(crate) untracked_crate: &'tcx hir::Crate<'tcx>, pub(crate) definitions: &'tcx Definitions, @@ -981,17 +948,18 @@ pub struct GlobalCtxt<'tcx> { pub queries: query::Queries<'tcx>, - maybe_unused_trait_imports: FxHashSet, - maybe_unused_extern_crates: Vec<(DefId, Span)>, + maybe_unused_trait_imports: FxHashSet, + maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, /// A map of glob use to a set of names it actually imports. Currently only /// used in save-analysis. - glob_map: FxHashMap>, + 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>>, + // Internal caches for metadata decoding. No need to track deps on this. + pub ty_rcache: Lock>>, + pub pred_rcache: Lock>>, /// Caches the results of trait selection. This cache is used /// for things that do not have to do with the parameters in scope. @@ -1018,30 +986,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( @@ -1051,8 +1013,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 { @@ -1064,7 +1025,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 { @@ -1075,7 +1036,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)) } @@ -1160,14 +1121,9 @@ 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); + for (hir_id, v) in krate.trait_map.iter() { 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))) - .collect(); - map.insert(hir_id.local_id, StableVec::new(v)); + map.insert(hir_id.local_id, StableVec::new(v.to_vec())); } GlobalCtxt { @@ -1183,38 +1139,17 @@ impl<'tcx> TyCtxt<'tcx> { consts: common_consts, extern_crate_map: resolutions.extern_crate_map, trait_map, - export_map: resolutions - .export_map - .into_iter() - .map(|(k, v)| { - let exports: Vec<_> = v - .into_iter() - .map(|e| e.map_id(|id| definitions.node_to_hir_id(id))) - .collect(); - (k, exports) - }) - .collect(), - maybe_unused_trait_imports: resolutions - .maybe_unused_trait_imports - .into_iter() - .map(|id| definitions.local_def_id(id)) - .collect(), - maybe_unused_extern_crates: resolutions - .maybe_unused_extern_crates - .into_iter() - .map(|(id, sp)| (definitions.local_def_id(id), sp)) - .collect(), - glob_map: resolutions - .glob_map - .into_iter() - .map(|(id, names)| (definitions.local_def_id(id), names)) - .collect(), + export_map: resolutions.export_map, + maybe_unused_trait_imports: resolutions.maybe_unused_trait_imports, + maybe_unused_extern_crates: resolutions.maybe_unused_extern_crates, + glob_map: resolutions.glob_map, extern_prelude: resolutions.extern_prelude, untracked_crate: krate, definitions, def_path_hash_to_def_id, queries: query::Queries::new(providers, extern_providers, on_disk_query_result_cache), - rcache: Default::default(), + ty_rcache: Default::default(), + pred_rcache: Default::default(), selection_cache: Default::default(), evaluation_cache: Default::default(), crate_name: Symbol::intern(crate_name), @@ -1228,6 +1163,31 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used. + #[track_caller] + pub fn ty_error(self) -> Ty<'tcx> { + self.ty_error_with_message(DUMMY_SP, "TyKind::Error constructed but no error reported") + } + + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to + /// ensure it gets used. + #[track_caller] + pub fn ty_error_with_message>(self, span: S, msg: &str) -> Ty<'tcx> { + self.sess.delay_span_bug(span, msg); + self.mk_ty(Error(super::sty::DelaySpanBugEmitted(()))) + } + + /// Like `err` but for constants. + #[track_caller] + pub fn const_error(self, ty: Ty<'tcx>) -> &'tcx Const<'tcx> { + self.sess + .delay_span_bug(DUMMY_SP, "ty::ConstKind::Error constructed but no error reported."); + self.mk_const(ty::Const { + val: ty::ConstKind::Error(super::sty::DelaySpanBugEmitted(())), + ty, + }) + } + pub fn consider_optimizing String>(&self, msg: T) -> bool { let cname = self.crate_name(LOCAL_CRATE).as_str(); self.sess.consider_optimizing(&cname, msg) @@ -1238,7 +1198,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) } @@ -1269,8 +1229,8 @@ impl<'tcx> TyCtxt<'tcx> { self.features_query(LOCAL_CRATE) } - pub fn def_key(self, id: DefId) -> hir_map::DefKey { - if id.is_local() { self.hir().def_key(id) } else { self.cstore.def_key(id) } + pub fn def_key(self, id: DefId) -> rustc_hir::definitions::DefKey { + if let Some(id) = id.as_local() { self.hir().def_key(id) } else { self.cstore.def_key(id) } } /// Converts a `DefId` into its fully expanded `DefPath` (every @@ -1278,8 +1238,12 @@ 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 { - if id.is_local() { self.hir().def_path(id) } else { self.cstore.def_path(id) } + pub fn def_path(self, id: DefId) -> rustc_hir::definitions::DefPath { + if let Some(id) = id.as_local() { + self.hir().def_path(id) + } else { + self.cstore.def_path(id) + } } /// Returns whether or not the crate with CrateNum 'cnum' @@ -1289,9 +1253,9 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn def_path_hash(self, def_id: DefId) -> hir_map::DefPathHash { - if def_id.is_local() { - self.definitions.def_path_hash(def_id.index) + pub fn def_path_hash(self, def_id: DefId) -> rustc_hir::definitions::DefPathHash { + if let Some(def_id) = def_id.as_local() { + self.definitions.def_path_hash(def_id) } else { self.cstore.def_path_hash(def_id) } @@ -1326,7 +1290,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn encode_metadata(self) -> EncodedMetadata { - let _prof_timer = self.prof.generic_activity("generate_crate_metadata"); + let _prof_timer = self.prof.verbose_generic_activity("generate_crate_metadata"); self.cstore.encode_metadata(self) } @@ -1343,6 +1307,13 @@ impl<'tcx> TyCtxt<'tcx> { StableHashingContext::new(self.sess, krate, self.definitions, &*self.cstore) } + #[inline(always)] + pub fn create_no_span_stable_hashing_context(self) -> StableHashingContext<'tcx> { + let krate = self.gcx.untracked_crate; + + StableHashingContext::ignore_spans(self.sess, krate, self.definitions, &*self.cstore) + } + // This method makes sure that we have a DepNode and a Fingerprint for // every upstream crate. It needs to be called once right after the tcx is // created. @@ -1379,7 +1350,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 @@ -1409,11 +1380,18 @@ 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()); - self.sess.crate_types.borrow().iter().any(|crate_type| { + self.sess.crate_types().iter().any(|crate_type| { match crate_type { CrateType::Executable | CrateType::Staticlib @@ -1441,25 +1419,70 @@ 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) } _ => return None, }; - return Some(FreeRegionInfo { + Some(FreeRegionInfo { def_id: suitable_region_binding_scope, boundregion: bound_region, is_impl_item, - }); + }) + } + + /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type. + pub fn return_type_impl_or_dyn_traits(&self, scope_def_id: DefId) -> Vec<&'tcx hir::Ty<'tcx>> { + let hir_id = self.hir().as_local_hir_id(scope_def_id.expect_local()); + let hir_output = match self.hir().get(hir_id) { + Node::Item(hir::Item { + kind: + ItemKind::Fn( + hir::FnSig { + decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. }, + .. + }, + .., + ), + .. + }) + | Node::ImplItem(hir::ImplItem { + kind: + hir::ImplItemKind::Fn( + hir::FnSig { + decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. }, + .. + }, + _, + ), + .. + }) + | Node::TraitItem(hir::TraitItem { + kind: + hir::TraitItemKind::Fn( + hir::FnSig { + decl: hir::FnDecl { output: hir::FnRetTy::Return(ty), .. }, + .. + }, + _, + ), + .. + }) => ty, + _ => return vec![], + }; + + let mut v = TraitObjectVisitor(vec![], self.hir()); + v.visit_ty(hir_output); + v.0 } pub fn return_type_impl_trait(&self, scope_def_id: 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 { @@ -1520,19 +1543,12 @@ 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) { - match self.def_key(def_id).disambiguated_data.data { - DefPathData::TypeNs(..) | DefPathData::ValueNs(..) | DefPathData::MacroNs(..) => { - let kind = self.def_kind(def_id).unwrap(); - (kind.article(), kind.descr(def_id)) - } - 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"), + 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"), }, - DefPathData::LifetimeNs(..) => ("a", "lifetime"), - DefPathData::Impl => ("an", "implementation"), - _ => bug!("article_and_description called on def_id {:?}", def_id), + def_kind => (def_kind.article(), def_kind.descr(def_id)), } } } @@ -1616,11 +1632,9 @@ 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_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'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>} @@ -1633,7 +1647,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; @@ -1660,7 +1674,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. @@ -1869,7 +1883,7 @@ macro_rules! sty_debug_print { let variant = match t.kind { ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Str | ty::Never => continue, - ty::Error => /* unimportant */ continue, + ty::Error(_) => /* unimportant */ continue, $(ty::$variant(..) => &mut $variant,)* }; let lt = t.flags.intersects(ty::TypeFlags::HAS_RE_INFER); @@ -1928,7 +1942,6 @@ impl<'tcx> TyCtxt<'tcx> { Bound, Param, Infer, - UnnormalizedProjection, Projection, Opaque, Foreign @@ -1979,48 +1992,44 @@ impl<'tcx> Borrow> for Interned<'tcx, TyS<'tcx>> { &self.0.kind } } - -// N.B., an `Interned>` compares and hashes as its elements. -impl<'tcx, T: PartialEq> PartialEq for Interned<'tcx, List> { - fn eq(&self, other: &Interned<'tcx, List>) -> bool { - self.0[..] == other.0[..] +// N.B., an `Interned` compares and hashes as a `PredicateKind`. +impl<'tcx> PartialEq for Interned<'tcx, PredicateInner<'tcx>> { + fn eq(&self, other: &Interned<'tcx, PredicateInner<'tcx>>) -> bool { + self.0.kind == other.0.kind } } -impl<'tcx, T: Eq> Eq for Interned<'tcx, List> {} +impl<'tcx> Eq for Interned<'tcx, PredicateInner<'tcx>> {} -impl<'tcx, T: Hash> Hash for Interned<'tcx, List> { +impl<'tcx> Hash for Interned<'tcx, PredicateInner<'tcx>> { fn hash(&self, s: &mut H) { - self.0[..].hash(s) + self.0.kind.hash(s) } } -impl<'tcx> Borrow<[Ty<'tcx>]> for Interned<'tcx, List>> { - fn borrow<'a>(&'a self) -> &'a [Ty<'tcx>] { - &self.0[..] +impl<'tcx> Borrow> for Interned<'tcx, PredicateInner<'tcx>> { + fn borrow<'a>(&'a self) -> &'a PredicateKind<'tcx> { + &self.0.kind } } -impl<'tcx> Borrow<[CanonicalVarInfo]> for Interned<'tcx, List> { - fn borrow(&self) -> &[CanonicalVarInfo] { - &self.0[..] +// N.B., an `Interned>` compares and hashes as its elements. +impl<'tcx, T: PartialEq> PartialEq for Interned<'tcx, List> { + fn eq(&self, other: &Interned<'tcx, List>) -> bool { + self.0[..] == other.0[..] } } -impl<'tcx> Borrow<[GenericArg<'tcx>]> for Interned<'tcx, InternalSubsts<'tcx>> { - fn borrow<'a>(&'a self) -> &'a [GenericArg<'tcx>] { - &self.0[..] - } -} +impl<'tcx, T: Eq> Eq for Interned<'tcx, List> {} -impl<'tcx> Borrow<[ProjectionKind]> for Interned<'tcx, List> { - fn borrow(&self) -> &[ProjectionKind] { - &self.0[..] +impl<'tcx, T: Hash> Hash for Interned<'tcx, List> { + fn hash(&self, s: &mut H) { + self.0[..].hash(s) } } -impl<'tcx> Borrow<[PlaceElem<'tcx>]> for Interned<'tcx, List>> { - fn borrow(&self) -> &[PlaceElem<'tcx>] { +impl<'tcx, T> Borrow<[T]> for Interned<'tcx, List> { + fn borrow<'a>(&'a self) -> &'a [T] { &self.0[..] } } @@ -2031,46 +2040,20 @@ 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>> -{ - fn borrow<'a>(&'a self) -> &'a [ExistentialPredicate<'tcx>] { - &self.0[..] - } -} - -impl<'tcx> Borrow<[Predicate<'tcx>]> for Interned<'tcx, List>> { - fn borrow<'a>(&'a self) -> &'a [Predicate<'tcx>] { - &self.0[..] - } -} - impl<'tcx> Borrow> for Interned<'tcx, Const<'tcx>> { fn borrow<'a>(&'a self) -> &'a Const<'tcx> { &self.0 } } -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>] { - &self.0[..] +impl<'tcx> Borrow> for Interned<'tcx, PredicateKind<'tcx>> { + fn borrow<'a>(&'a self) -> &'a PredicateKind<'tcx> { + &self.0 } } macro_rules! direct_interners { - ($($name:ident: $method:ident($ty:ty)),+) => { + ($($name:ident: $method:ident($ty:ty),)+) => { $(impl<'tcx> PartialEq for Interned<'tcx, $ty> { fn eq(&self, other: &Self) -> bool { self.0 == other.0 @@ -2095,15 +2078,10 @@ 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!( +direct_interners! { region: mk_region(RegionKind), - goal: mk_goal(GoalKind<'tcx>), - const_: mk_const(Const<'tcx>) -); + const_: mk_const(Const<'tcx>), +} macro_rules! slice_interners { ($($field:ident: $method:ident($ty:ty)),+) => ( @@ -2123,10 +2101,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> { @@ -2138,24 +2116,32 @@ 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) + /// Same a `self.mk_region(kind)`, but avoids accessing the interners if + /// `*r == kind`. + #[inline] + pub fn reuse_or_mk_region(self, r: Region<'tcx>, kind: RegionKind) -> Region<'tcx> { + if *r == kind { r } else { self.mk_region(kind) } } #[allow(rustc::usage_of_ty_tykind)] @@ -2164,6 +2150,12 @@ impl<'tcx> TyCtxt<'tcx> { self.interners.intern_ty(st) } + #[inline] + pub fn mk_predicate(&self, kind: PredicateKind<'tcx>) -> Predicate<'tcx> { + let inner = self.interners.intern_predicate(kind); + Predicate { inner } + } + pub fn mk_mach_int(self, tm: ast::IntTy) -> Ty<'tcx> { match tm { ast::IntTy::Isize => self.types.isize, @@ -2193,14 +2185,9 @@ impl<'tcx> TyCtxt<'tcx> { } } - #[inline] - pub fn mk_str(self) -> Ty<'tcx> { - self.mk_ty(Str) - } - #[inline] pub fn mk_static_str(self) -> Ty<'tcx> { - self.mk_imm_ref(self.lifetimes.re_static, self.mk_str()) + self.mk_imm_ref(self.lifetimes.re_static, self.types.str_) } #[inline] @@ -2243,6 +2230,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); @@ -2317,11 +2310,6 @@ impl<'tcx> TyCtxt<'tcx> { if self.features().never_type_fallback { self.types.never } else { self.types.unit } } - #[inline] - pub fn mk_bool(self) -> Ty<'tcx> { - self.mk_ty(Bool) - } - #[inline] pub fn mk_fn_def(self, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> { self.mk_ty(FnDef(def_id, substs)) @@ -2506,12 +2494,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( @@ -2571,12 +2558,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. @@ -2749,30 +2740,22 @@ 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| { - assert_eq!(id.krate, LOCAL_CRATE); - let id = tcx.hir().definitions().def_index_to_hir_id(id.index); + let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); tcx.stability().local_stability(id) }; providers.lookup_const_stability = |tcx, id| { - assert_eq!(id.krate, LOCAL_CRATE); - let id = tcx.hir().definitions().def_index_to_hir_id(id.index); + let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); tcx.stability().local_const_stability(id) }; providers.lookup_deprecation_entry = |tcx, id| { - assert_eq!(id.krate, LOCAL_CRATE); - let id = tcx.hir().definitions().def_index_to_hir_id(id.index); + let id = tcx.hir().local_def_id_to_hir_id(id.expect_local()); tcx.stability().local_deprecation_entry(id) }; - providers.extern_mod_stmt_cnum = |tcx, id| { - let id = tcx.hir().as_local_node_id(id).unwrap(); - tcx.extern_crate_map.get(&id).cloned() - }; + providers.extern_mod_stmt_cnum = |tcx, id| tcx.extern_crate_map.get(&id).cloned(); providers.all_crate_nums = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); tcx.arena.alloc_slice(&tcx.cstore.crates_untracked()) @@ -2783,7 +2766,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..b22727bdd7587 --- /dev/null +++ b/src/librustc_middle/ty/diagnostics.rs @@ -0,0 +1,270 @@ +//! Diagnostics related methods for `TyS`. + +use crate::ty::sty::InferTy; +use crate::ty::TyKind::*; +use crate::ty::{TyCtxt, TyS}; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; + +impl<'tcx> TyS<'tcx> { + /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive. + pub fn is_primitive_ty(&self) -> bool { + match self.kind { + Bool + | Char + | Str + | Int(_) + | Uint(_) + | Float(_) + | Infer( + InferTy::IntVar(_) + | InferTy::FloatVar(_) + | InferTy::FreshIntTy(_) + | InferTy::FreshFloatTy(_), + ) => true, + _ => false, + } + } + + /// Whether the type is succinctly representable as a type instead of just referred to with a + /// description in error messages. This is used in the main error message. + pub fn is_simple_ty(&self) -> bool { + match self.kind { + Bool + | Char + | Str + | Int(_) + | Uint(_) + | Float(_) + | Infer( + InferTy::IntVar(_) + | InferTy::FloatVar(_) + | InferTy::FreshIntTy(_) + | InferTy::FreshFloatTy(_), + ) => true, + Ref(_, x, _) | Array(x, _) | Slice(x) => x.peel_refs().is_simple_ty(), + Tuple(tys) if tys.is_empty() => true, + _ => false, + } + } + + /// Whether the type is succinctly representable as a type instead of just referred to with a + /// description in error messages. This is used in the primary span label. Beyond what + /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to + /// ADTs with no type arguments. + pub fn is_simple_text(&self) -> bool { + match self.kind { + Adt(_, substs) => substs.types().next().is_none(), + Ref(_, ty, _) => ty.is_simple_text(), + _ => self.is_simple_ty(), + } + } + + /// Whether the type can be safely suggested during error recovery. + pub fn is_suggestable(&self) -> bool { + match self.kind { + Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..) + | Projection(..) => false, + _ => true, + } + } +} + +/// Suggest restricting a type param with a new bound. +pub fn suggest_constraining_type_param( + tcx: TyCtxt<'_>, + generics: &hir::Generics<'_>, + err: &mut DiagnosticBuilder<'_>, + param_name: &str, + constraint: &str, + def_id: Option, +) -> bool { + let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + + let param = if let Some(param) = param { + param + } else { + return false; + }; + + const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound"; + let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name); + let msg_restrict_type_further = + format!("consider further restricting type parameter `{}`", param_name); + + if def_id == tcx.lang_items().sized_trait() { + // Type parameters are already `Sized` by default. + err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); + return true; + } + let mut suggest_restrict = |span| { + err.span_suggestion_verbose( + span, + MSG_RESTRICT_BOUND_FURTHER, + format!(" + {}", constraint), + Applicability::MachineApplicable, + ); + }; + + if param_name.starts_with("impl ") { + // If there's an `impl Trait` used in argument position, suggest + // restricting it: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: impl Foo) { ... } + // -------- + // | + // replace with: `impl Foo + Bar` + + suggest_restrict(param.span.shrink_to_hi()); + return true; + } + + if generics.where_clause.predicates.is_empty() + // Given `trait Base: Super` where `T: Copy`, suggest restricting in the + // `where` clause instead of `trait Base: Super`. + && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) + { + if let Some(bounds_span) = param.bounds_span() { + // If user has provided some bounds, suggest restricting them: + // + // fn foo(t: T) { ... } + // --- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: T) { ... } + // -- + // | + // replace with: `T: Bar +` + suggest_restrict(bounds_span.shrink_to_hi()); + } else { + // If user hasn't provided any bounds, suggest adding a new one: + // + // fn foo(t: T) { ... } + // - help: consider restricting this type parameter with `T: Foo` + err.span_suggestion_verbose( + param.span.shrink_to_hi(), + &msg_restrict_type, + format!(": {}", constraint), + Applicability::MachineApplicable, + ); + } + + true + } else { + // This part is a bit tricky, because using the `where` clause user can + // provide zero, one or many bounds for the same type parameter, so we + // have following cases to consider: + // + // 1) When the type parameter has been provided zero bounds + // + // Message: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - help: consider restricting this type parameter with `where X: Bar` + // + // Suggestion: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - insert: `, X: Bar` + // + // + // 2) When the type parameter has been provided one bound + // + // Message: + // fn foo(t: T) where T: Foo { ... } + // ^^^^^^ + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion: + // fn foo(t: T) where T: Foo { ... } + // ^^ + // | + // replace with: `T: Bar +` + // + // + // 3) When the type parameter has been provided many bounds + // + // Message: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - insert: `, T: Zar` + + let mut param_spans = Vec::new(); + + for predicate in generics.where_clause.predicates { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { + span, bounded_ty, .. + }) = predicate + { + if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { + if let Some(segment) = path.segments.first() { + if segment.ident.to_string() == param_name { + param_spans.push(span); + } + } + } + } + } + + match ¶m_spans[..] { + &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + _ => { + err.span_suggestion_verbose( + generics.where_clause.tail_span_for_suggestion(), + &msg_restrict_type_further, + format!(", {}: {}", param_name, constraint), + Applicability::MachineApplicable, + ); + } + } + + true + } +} + +/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. +pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>); + +impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { + type Map = rustc_hir::intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + match ty.kind { + hir::TyKind::TraitObject( + _, + hir::Lifetime { + name: + hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, + .. + }, + ) => { + self.0.push(ty); + } + hir::TyKind::OpaqueDef(item_id, _) => { + self.0.push(ty); + let item = self.1.expect_item(item_id.id); + hir::intravisit::walk_item(self, item); + } + _ => {} + } + hir::intravisit::walk_ty(self, ty); + } +} diff --git a/src/librustc_middle/ty/erase_regions.rs b/src/librustc_middle/ty/erase_regions.rs new file mode 100644 index 0000000000000..ba165925b6474 --- /dev/null +++ b/src/librustc_middle/ty/erase_regions.rs @@ -0,0 +1,68 @@ +use crate::ty::fold::{TypeFoldable, TypeFolder}; +use crate::ty::{self, Ty, TyCtxt, TypeFlags}; + +pub(super) fn provide(providers: &mut ty::query::Providers<'_>) { + *providers = ty::query::Providers { erase_regions_ty, ..*providers }; +} + +fn erase_regions_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + // N.B., use `super_fold_with` here. If we used `fold_with`, it + // could invoke the `erase_regions_ty` query recursively. + ty.super_fold_with(&mut RegionEraserVisitor { tcx }) +} + +impl<'tcx> TyCtxt<'tcx> { + /// Returns an equivalent value with all free regions removed (note + /// that late-bound regions remain, because they are important for + /// subtyping, but they are anonymized and normalized as well).. + pub fn erase_regions(self, value: &T) -> T + where + T: TypeFoldable<'tcx>, + { + // If there's nothing to erase avoid performing the query at all + if !value.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND | TypeFlags::HAS_FREE_REGIONS) { + return value.clone(); + } + + let value1 = value.fold_with(&mut RegionEraserVisitor { tcx: self }); + debug!("erase_regions({:?}) = {:?}", value, value1); + value1 + } +} + +struct RegionEraserVisitor<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl TypeFolder<'tcx> for RegionEraserVisitor<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + 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 + where + T: TypeFoldable<'tcx>, + { + let u = self.tcx.anonymize_late_bound_regions(t); + u.super_fold_with(self) + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + // because late-bound regions affect subtyping, we can't + // erase the bound/free distinction, but we can replace + // all free regions with 'erased. + // + // Note that we *CAN* replace early-bound regions -- the + // type system never "sees" those, they get substituted + // away. In codegen, they will always be erased to 'erased + // whenever a substitution occurs. + match *r { + ty::ReLateBound(..) => r, + _ => self.tcx.lifetimes.re_erased, + } + } +} diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs new file mode 100644 index 0000000000000..6113359ca93a7 --- /dev/null +++ b/src/librustc_middle/ty/error.rs @@ -0,0 +1,905 @@ +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 => { + // FIXME: account for returning some type in a trait fn impl that has + // an assoc type as a return type (#72076). + if let hir::Defaultness::Default { has_value: true } = item.defaultness + { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { + db.span_label( + item.span, + "associated type defaults can't be assumed inside the \ + trait defining them", + ); + return true; + } + } + } + _ => {} + } + } + } + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl { items, .. }, .. + })) => { + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type => { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { + db.span_label(item.span, "expected this associated type"); + return true; + } + } + _ => {} + } + } + } + _ => {} + } + false + } + + /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` + /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`. + fn constrain_generic_bound_associated_type_structured_suggestion( + &self, + db: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::TraitRef<'tcx>, + bounds: hir::GenericBounds<'_>, + assoc: &ty::AssocItem, + ty: Ty<'tcx>, + msg: &str, + ) -> bool { + // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. + bounds.iter().any(|bound| match bound { + hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => { + // Relate the type param against `T` in `::Foo`. + ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) + && self.constrain_associated_type_structured_suggestion( + db, ptr.span, assoc, ty, msg, + ) + } + _ => false, + }) + } + + /// Given a span corresponding to a bound, provide a structured suggestion to set an + /// associated type to a given type `ty`. + fn constrain_associated_type_structured_suggestion( + &self, + db: &mut DiagnosticBuilder<'_>, + span: Span, + assoc: &ty::AssocItem, + ty: Ty<'tcx>, + msg: &str, + ) -> bool { + if let Ok(has_params) = + self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) + { + let (span, sugg) = if has_params { + let pos = span.hi() - BytePos(1); + let span = Span::new(pos, pos, span.ctxt()); + (span, format!(", {} = {}", assoc.ident, ty)) + } else { + (span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty)) + }; + db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); + return true; + } + false + } +} diff --git a/src/librustc/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..b0fb179b18bdf 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 @@ -105,7 +104,7 @@ pub fn simplify_type( } ty::Opaque(def_id, _) => Some(OpaqueSimplifiedType(def_id)), ty::Foreign(def_id) => Some(ForeignSimplifiedType(def_id)), - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) | ty::Error => None, + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None, } } diff --git a/src/librustc_middle/ty/flags.rs b/src/librustc_middle/ty/flags.rs new file mode 100644 index 0000000000000..c782eee938721 --- /dev/null +++ b/src/librustc_middle/ty/flags.rs @@ -0,0 +1,314 @@ +use crate::ty::subst::{GenericArg, GenericArgKind}; +use crate::ty::{self, InferConst, Ty, TypeFlags}; +use std::slice; + +#[derive(Debug)] +pub struct FlagComputation { + pub flags: TypeFlags, + + // see `TyS::outer_exclusive_binder` for details + pub outer_exclusive_binder: ty::DebruijnIndex, +} + +impl FlagComputation { + fn new() -> FlagComputation { + FlagComputation { flags: TypeFlags::empty(), outer_exclusive_binder: ty::INNERMOST } + } + + #[allow(rustc::usage_of_ty_tykind)] + pub fn for_kind(kind: &ty::TyKind<'_>) -> FlagComputation { + let mut result = FlagComputation::new(); + result.add_kind(kind); + result + } + + pub fn for_predicate(kind: &ty::PredicateKind<'_>) -> FlagComputation { + let mut result = FlagComputation::new(); + result.add_predicate_kind(kind); + result + } + + pub fn for_const(c: &ty::Const<'_>) -> TypeFlags { + let mut result = FlagComputation::new(); + result.add_const(c); + result.flags + } + + fn add_flags(&mut self, flags: TypeFlags) { + self.flags = self.flags | flags; + } + + /// indicates that `self` refers to something at binding level `binder` + fn add_bound_var(&mut self, binder: ty::DebruijnIndex) { + let exclusive_binder = binder.shifted_in(1); + self.add_exclusive_binder(exclusive_binder); + } + + /// indicates that `self` refers to something *inside* binding + /// level `binder` -- not bound by `binder`, but bound by the next + /// binder internal to it + fn add_exclusive_binder(&mut self, exclusive_binder: ty::DebruijnIndex) { + self.outer_exclusive_binder = self.outer_exclusive_binder.max(exclusive_binder); + } + + /// Adds the flags/depth from a set of types that appear within the current type, but within a + /// region binder. + fn add_bound_computation(&mut self, computation: FlagComputation) { + self.add_flags(computation.flags); + + // The types that contributed to `computation` occurred within + // a region binder, so subtract one from the region depth + // within when adding the depth to `self`. + let outer_exclusive_binder = computation.outer_exclusive_binder; + if outer_exclusive_binder > ty::INNERMOST { + self.add_exclusive_binder(outer_exclusive_binder.shifted_out(1)); + } // otherwise, this binder captures nothing + } + + #[allow(rustc::usage_of_ty_tykind)] + fn add_kind(&mut self, kind: &ty::TyKind<'_>) { + match kind { + &ty::Bool + | &ty::Char + | &ty::Int(_) + | &ty::Float(_) + | &ty::Uint(_) + | &ty::Never + | &ty::Str + | &ty::Foreign(..) => {} + + &ty::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), + + &ty::Param(_) => { + self.add_flags(TypeFlags::HAS_TY_PARAM); + self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); + } + + &ty::Generator(_, ref substs, _) => { + self.add_substs(substs); + } + + &ty::GeneratorWitness(ref ts) => { + let mut computation = FlagComputation::new(); + computation.add_tys(&ts.skip_binder()[..]); + self.add_bound_computation(computation); + } + + &ty::Closure(_, ref substs) => { + self.add_substs(substs); + } + + &ty::Bound(debruijn, _) => { + self.add_bound_var(debruijn); + } + + &ty::Placeholder(..) => { + self.add_flags(TypeFlags::HAS_TY_PLACEHOLDER); + self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); + } + + &ty::Infer(infer) => { + self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); + match infer { + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => {} + + ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_) => { + self.add_flags(TypeFlags::HAS_TY_INFER) + } + } + } + + &ty::Adt(_, substs) => { + self.add_substs(substs); + } + + &ty::Projection(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); + } + + &ty::Dynamic(ref obj, r) => { + let mut computation = FlagComputation::new(); + for predicate in obj.skip_binder().iter() { + match predicate { + ty::ExistentialPredicate::Trait(tr) => computation.add_substs(tr.substs), + ty::ExistentialPredicate::Projection(p) => { + let mut proj_computation = FlagComputation::new(); + proj_computation.add_existential_projection(&p); + self.add_bound_computation(proj_computation); + } + ty::ExistentialPredicate::AutoTrait(_) => {} + } + } + self.add_bound_computation(computation); + self.add_region(r); + } + + &ty::Array(tt, len) => { + self.add_ty(tt); + self.add_const(len); + } + + &ty::Slice(tt) => self.add_ty(tt), + + &ty::RawPtr(ref m) => { + self.add_ty(m.ty); + } + + &ty::Ref(r, ty, _) => { + self.add_region(r); + self.add_ty(ty); + } + + &ty::Tuple(ref substs) => { + self.add_substs(substs); + } + + &ty::FnDef(_, substs) => { + self.add_substs(substs); + } + + &ty::FnPtr(f) => { + self.add_fn_sig(f); + } + } + } + + fn add_predicate_kind(&mut self, kind: &ty::PredicateKind<'_>) { + match kind { + ty::PredicateKind::Trait(trait_pred, _constness) => { + let mut computation = FlagComputation::new(); + computation.add_substs(trait_pred.skip_binder().trait_ref.substs); + + self.add_bound_computation(computation); + } + ty::PredicateKind::RegionOutlives(poly_outlives) => { + let mut computation = FlagComputation::new(); + let ty::OutlivesPredicate(a, b) = poly_outlives.skip_binder(); + computation.add_region(a); + computation.add_region(b); + + self.add_bound_computation(computation); + } + ty::PredicateKind::TypeOutlives(poly_outlives) => { + let mut computation = FlagComputation::new(); + let ty::OutlivesPredicate(ty, region) = poly_outlives.skip_binder(); + computation.add_ty(ty); + computation.add_region(region); + + self.add_bound_computation(computation); + } + ty::PredicateKind::Subtype(poly_subtype) => { + let mut computation = FlagComputation::new(); + let ty::SubtypePredicate { a_is_expected: _, a, b } = poly_subtype.skip_binder(); + computation.add_ty(a); + computation.add_ty(b); + + self.add_bound_computation(computation); + } + ty::PredicateKind::Projection(projection) => { + let mut computation = FlagComputation::new(); + let ty::ProjectionPredicate { projection_ty, ty } = projection.skip_binder(); + computation.add_projection_ty(projection_ty); + computation.add_ty(ty); + + self.add_bound_computation(computation); + } + ty::PredicateKind::WellFormed(arg) => { + self.add_substs(slice::from_ref(arg)); + } + ty::PredicateKind::ObjectSafe(_def_id) => {} + ty::PredicateKind::ClosureKind(_def_id, substs, _kind) => { + self.add_substs(substs); + } + ty::PredicateKind::ConstEvaluatable(_def_id, substs) => { + self.add_substs(substs); + } + ty::PredicateKind::ConstEquate(expected, found) => { + self.add_const(expected); + self.add_const(found); + } + } + } + + fn add_ty(&mut self, ty: Ty<'_>) { + self.add_flags(ty.flags); + self.add_exclusive_binder(ty.outer_exclusive_binder); + } + + fn add_tys(&mut self, tys: &[Ty<'_>]) { + for &ty in tys { + self.add_ty(ty); + } + } + + fn add_fn_sig(&mut self, fn_sig: ty::PolyFnSig<'_>) { + let mut computation = FlagComputation::new(); + + computation.add_tys(fn_sig.skip_binder().inputs()); + computation.add_ty(fn_sig.skip_binder().output()); + + self.add_bound_computation(computation); + } + + fn add_region(&mut self, r: ty::Region<'_>) { + self.add_flags(r.type_flags()); + if let ty::ReLateBound(debruijn, _) = *r { + self.add_bound_var(debruijn); + } + } + + fn add_const(&mut self, c: &ty::Const<'_>) { + self.add_ty(c.ty); + match c.val { + ty::ConstKind::Unevaluated(_, substs, _) => { + self.add_substs(substs); + self.add_flags(TypeFlags::HAS_CT_PROJECTION); + } + ty::ConstKind::Infer(infer) => { + self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); + match infer { + InferConst::Fresh(_) => {} + InferConst::Var(_) => self.add_flags(TypeFlags::HAS_CT_INFER), + } + } + ty::ConstKind::Bound(debruijn, _) => { + self.add_bound_var(debruijn); + } + 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), + } + } + + fn add_existential_projection(&mut self, projection: &ty::ExistentialProjection<'_>) { + self.add_substs(projection.substs); + self.add_ty(projection.ty); + } + + fn add_projection_ty(&mut self, projection_ty: &ty::ProjectionTy<'_>) { + self.add_substs(projection_ty.substs); + } + + fn add_substs(&mut self, substs: &[GenericArg<'_>]) { + for kind in substs { + match kind.unpack() { + GenericArgKind::Type(ty) => self.add_ty(ty), + GenericArgKind::Lifetime(lt) => self.add_region(lt), + GenericArgKind::Const(ct) => self.add_const(ct), + } + } + } +} diff --git a/src/librustc/ty/fold.rs b/src/librustc_middle/ty/fold.rs similarity index 95% rename from src/librustc/ty/fold.rs rename to src/librustc_middle/ty/fold.rs index 4adca6c7d9772..2d25c7c6ac983 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc_middle/ty/fold.rs @@ -31,6 +31,7 @@ //! These methods return true to indicate that the visitor has found what it is //! looking for, and does not need to visit anything else. +use crate::ty::structural_impls::PredicateVisitor; use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -82,11 +83,14 @@ 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_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_INFER) + } fn has_infer_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_INFER) } @@ -96,9 +100,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 +143,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 +264,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. @@ -915,6 +909,12 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { } } +impl<'tcx> PredicateVisitor<'tcx> for HasEscapingVarsVisitor { + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool { + predicate.inner.outer_exclusive_binder > self.outer_index + } +} + // FIXME: Optimize for checking for infer flags struct HasTypeFlagsVisitor { flags: ty::TypeFlags, @@ -939,6 +939,15 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { } } +impl<'tcx> PredicateVisitor<'tcx> for HasTypeFlagsVisitor { + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool { + debug!( + "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}", + predicate, predicate.inner.flags, self.flags + ); + predicate.inner.flags.intersects(self.flags) + } +} /// Collects all the late-bound regions at the innermost binding level /// into a hash set. struct LateBoundRegionsCollector { @@ -978,17 +987,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_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs new file mode 100644 index 0000000000000..d628d6783d5b0 --- /dev/null +++ b/src/librustc_middle/ty/instance.rs @@ -0,0 +1,485 @@ +use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::ty::print::{FmtPrinter, Printer}; +use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable}; +use rustc_errors::ErrorReported; +use rustc_hir::def::Namespace; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::lang_items::{DropInPlaceFnLangItem, FnOnceTraitLangItem}; +use rustc_macros::HashStable; + +use std::fmt; + +/// A monomorphized `InstanceDef`. +/// +/// Monomorphization happens on-the-fly and no monomorphized MIR is ever created. Instead, this type +/// simply couples a potentially generic `InstanceDef` with some substs, and codegen and const eval +/// will do all required substitution as they run. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable, Lift)] +pub struct Instance<'tcx> { + pub def: InstanceDef<'tcx>, + pub substs: SubstsRef<'tcx>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub enum InstanceDef<'tcx> { + /// A user-defined callable item. + /// + /// This includes: + /// - `fn` items + /// - closures + /// - generators + Item(DefId), + + /// An intrinsic `fn` item (with `"rust-intrinsic"` or `"platform-intrinsic"` ABI). + /// + /// Alongside `Virtual`, this is the only `InstanceDef` that does not have its own callable MIR. + /// Instead, codegen and const eval "magically" evaluate calls to intrinsics purely in the + /// caller. + Intrinsic(DefId), + + /// `::method` where `method` receives unsizeable `self: Self` (part of the + /// `unsized_locals` feature). + /// + /// The generated shim will take `Self` via `*mut Self` - conceptually this is `&owned Self` - + /// and dereference the argument to call the original function. + VtableShim(DefId), + + /// `fn()` pointer where the function itself cannot be turned into a pointer. + /// + /// One example is `::fn`, where the shim contains + /// a virtual call, which codegen supports only via a direct call to the + /// `::fn` instance (an `InstanceDef::Virtual`). + /// + /// Another example is functions annotated with `#[track_caller]`, which + /// must have their implicit caller location argument populated for a call. + /// Because this is a required part of the function's ABI but can't be tracked + /// as a property of the function pointer, we use a single "caller location" + /// (the definition of the function itself). + ReifyShim(DefId), + + /// `::call_*` (generated `FnTrait` implementation for `fn()` pointers). + /// + /// `DefId` is `FnTrait::call_*`. + /// + /// NB: the (`fn` pointer) type must currently be monomorphic to avoid double substitution + /// problems with the MIR shim bodies. `Instance::resolve` enforces this. + // FIXME(#69925) support polymorphic MIR shim bodies properly instead. + FnPtrShim(DefId, Ty<'tcx>), + + /// Dynamic dispatch to `::fn`. + /// + /// This `InstanceDef` does not have callable MIR. Calls to `Virtual` instances must be + /// codegen'd as virtual calls through the vtable. + /// + /// If this is reified to a `fn` pointer, a `ReifyShim` is used (see `ReifyShim` above for more + /// details on that). + Virtual(DefId, usize), + + /// `<[FnMut closure] as FnOnce>::call_once`. + /// + /// The `DefId` is the ID of the `call_once` method in `FnOnce`. + ClosureOnceShim { call_once: DefId }, + + /// `core::ptr::drop_in_place::`. + /// + /// The `DefId` is for `core::ptr::drop_in_place`. + /// The `Option>` is either `Some(T)`, or `None` for empty drop + /// glue. + /// + /// NB: the type must currently be monomorphic to avoid double substitution + /// problems with the MIR shim bodies. `Instance::resolve` enforces this. + // FIXME(#69925) support polymorphic MIR shim bodies properly instead. + DropGlue(DefId, Option>), + + /// Compiler-generated `::clone` implementation. + /// + /// For all types that automatically implement `Copy`, a trivial `Clone` impl is provided too. + /// Additionally, arrays, tuples, and closures get a `Clone` shim even if they aren't `Copy`. + /// + /// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl. + /// + /// NB: the type must currently be monomorphic to avoid double substitution + /// problems with the MIR shim bodies. `Instance::resolve` enforces this. + // FIXME(#69925) support polymorphic MIR shim bodies properly instead. + CloneShim(DefId, Ty<'tcx>), +} + +impl<'tcx> Instance<'tcx> { + /// Returns the `Ty` corresponding to this `Instance`, + /// with generic substitutions applied and lifetimes erased. + /// + /// This method can only be called when the 'substs' for this Instance + /// are fully monomorphic (no `ty::Param`'s are present). + /// This is usually the case (e.g. during codegen). + /// However, during constant evaluation, we may want + /// to try to resolve a `Instance` using generic parameters + /// (e.g. when we are attempting to to do const-propagation). + /// In this case, `Instance.ty_env` should be used to provide + /// the `ParamEnv` for our generic context. + pub fn monomorphic_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + let ty = tcx.type_of(self.def.def_id()); + // 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_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) + } + + /// Like `Instance.ty`, but allows a `ParamEnv` to be specified for use during + /// normalization. This method is only really useful during constant evaluation, + /// where we are dealing with potentially generic types. + pub fn ty_env(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { + let ty = tcx.type_of(self.def.def_id()); + tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty) + } + + /// Finds a crate that contains a monomorphization of this instance that + /// can be linked to from the local crate. A return value of `None` means + /// no upstream crate provides such an exported monomorphization. + /// + /// This method already takes into account the global `-Zshare-generics` + /// setting, always returning `None` if `share-generics` is off. + pub fn upstream_monomorphization(&self, tcx: TyCtxt<'tcx>) -> Option { + // If we are not in share generics mode, we don't link to upstream + // monomorphizations but always instantiate our own internal versions + // instead. + if !tcx.sess.opts.share_generics() { + return None; + } + + // If this is an item that is defined in the local crate, no upstream + // crate can know about it/provide a monomorphization. + if self.def_id().is_local() { + return None; + } + + // If this a non-generic instance, it cannot be a shared monomorphization. + self.substs.non_erasable_generics().next()?; + + match self.def { + InstanceDef::Item(def_id) => tcx + .upstream_monomorphizations_for(def_id) + .and_then(|monos| monos.get(&self.substs).cloned()), + InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.substs), + _ => None, + } + } +} + +impl<'tcx> InstanceDef<'tcx> { + #[inline] + pub fn def_id(&self) -> DefId { + match *self { + InstanceDef::Item(def_id) + | InstanceDef::VtableShim(def_id) + | InstanceDef::ReifyShim(def_id) + | InstanceDef::FnPtrShim(def_id, _) + | InstanceDef::Virtual(def_id, _) + | InstanceDef::Intrinsic(def_id) + | InstanceDef::ClosureOnceShim { call_once: def_id } + | InstanceDef::DropGlue(def_id, _) + | InstanceDef::CloneShim(def_id, _) => def_id, + } + } + + #[inline] + pub fn attrs(&self, tcx: TyCtxt<'tcx>) -> ty::Attributes<'tcx> { + tcx.get_attrs(self.def_id()) + } + + /// Returns `true` if the LLVM version of this instance is unconditionally + /// marked with `inline`. This implies that a copy of this instance is + /// generated in every codegen unit. + /// Note that this is only a hint. See the documentation for + /// `generates_cgu_internal_copy` for more information. + pub fn requires_inline(&self, tcx: TyCtxt<'tcx>) -> bool { + use rustc_hir::definitions::DefPathData; + let def_id = match *self { + ty::InstanceDef::Item(def_id) => def_id, + ty::InstanceDef::DropGlue(_, Some(_)) => return false, + _ => return true, + }; + match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::Ctor | DefPathData::ClosureExpr => true, + _ => false, + } + } + + /// Returns `true` if the machine code for this instance is instantiated in + /// each codegen unit that references it. + /// Note that this is only a hint! The compiler can globally decide to *not* + /// do this in order to speed up compilation. CGU-internal copies are + /// only exist to enable inlining. If inlining is not performed (e.g. at + /// `-Copt-level=0`) then the time for generating them is wasted and it's + /// better to create a single copy with external linkage. + pub fn generates_cgu_internal_copy(&self, tcx: TyCtxt<'tcx>) -> bool { + if self.requires_inline(tcx) { + return true; + } + if let ty::InstanceDef::DropGlue(.., Some(ty)) = *self { + // Drop glue generally wants to be instantiated at every codegen + // unit, but without an #[inline] hint. We should make this + // available to normal end-users. + if tcx.sess.opts.incremental.is_none() { + return true; + } + // When compiling with incremental, we can generate a *lot* of + // codegen units. Including drop glue into all of them has a + // considerable compile time cost. + // + // We include enums without destructors to allow, say, optimizing + // drops of `Option::None` before LTO. We also respect the intent of + // `#[inline]` on `Drop::drop` implementations. + return ty.ty_adt_def().map_or(true, |adt_def| { + adt_def.destructor(tcx).map_or(adt_def.is_enum(), |dtor| { + tcx.codegen_fn_attrs(dtor.did).requests_inline() + }) + }); + } + tcx.codegen_fn_attrs(self.def_id()).requests_inline() + } + + pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { + match *self { + InstanceDef::Item(def_id) => { + tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::TRACK_CALLER) + } + _ => false, + } + } +} + +impl<'tcx> fmt::Display for Instance<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + let substs = tcx.lift(&self.substs).expect("could not lift for printing"); + FmtPrinter::new(tcx, &mut *f, Namespace::ValueNS) + .print_def_path(self.def_id(), substs)?; + Ok(()) + })?; + + match self.def { + InstanceDef::Item(_) => Ok(()), + InstanceDef::VtableShim(_) => write!(f, " - shim(vtable)"), + InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), + InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), + InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), + InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({:?})", ty), + InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), + InstanceDef::DropGlue(_, ty) => write!(f, " - shim({:?})", ty), + InstanceDef::CloneShim(_, ty) => write!(f, " - shim({:?})", ty), + } + } +} + +impl<'tcx> Instance<'tcx> { + pub fn new(def_id: DefId, substs: SubstsRef<'tcx>) -> Instance<'tcx> { + assert!( + !substs.has_escaping_bound_vars(), + "substs of instance {:?} not normalized for codegen: {:?}", + def_id, + substs + ); + Instance { def: InstanceDef::Item(def_id), substs } + } + + pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> { + Instance::new(def_id, tcx.empty_substs_for_def_id(def_id)) + } + + #[inline] + pub fn def_id(&self) -> DefId { + self.def.def_id() + } + + /// Resolves a `(def_id, substs)` pair to an (optional) instance -- most commonly, + /// this is used to find the precise code that will run for a trait method invocation, + /// if known. + /// + /// Returns `Ok(None)` if we cannot resolve `Instance` to a specific instance. + /// For example, in a context like this, + /// + /// ``` + /// fn foo(t: T) { ... } + /// ``` + /// + /// trying to resolve `Debug::fmt` applied to `T` will yield `Ok(None)`, because we do not + /// know what code ought to run. (Note that this setting is also affected by the + /// `RevealMode` in the parameter environment.) + /// + /// Presuming that coherence and type-check have succeeded, if this method is invoked + /// in a monomorphic context (i.e., like during codegen), then it is guaranteed to return + /// `Ok(Some(instance))`. + /// + /// Returns `Err(ErrorReported)` when the `Instance` resolution process + /// couldn't complete due to errors elsewhere - this is distinct + /// from `Ok(None)` to avoid misleading diagnostics when an error + /// has already been/will be emitted, for the original cause + pub fn resolve( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Result>, ErrorReported> { + // 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( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Option> { + debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); + Instance::resolve(tcx, param_env, def_id, substs).ok().flatten().map(|mut resolved| { + match resolved.def { + InstanceDef::Item(def_id) if resolved.def.requires_caller_location(tcx) => { + debug!(" => fn pointer created for function with #[track_caller]"); + resolved.def = InstanceDef::ReifyShim(def_id); + } + InstanceDef::Virtual(def_id, _) => { + debug!(" => fn pointer created for virtual call"); + resolved.def = InstanceDef::ReifyShim(def_id); + } + _ => {} + } + + resolved + }) + } + + pub fn resolve_for_vtable( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> Option> { + debug!("resolve(def_id={:?}, substs={:?})", def_id, substs); + let fn_sig = tcx.fn_sig(def_id); + let is_vtable_shim = !fn_sig.inputs().skip_binder().is_empty() + && fn_sig.input(0).skip_binder().is_param(0) + && tcx.generics_of(def_id).has_self; + if is_vtable_shim { + debug!(" => associated item with unsizeable self: Self"); + Some(Instance { def: InstanceDef::VtableShim(def_id), substs }) + } else { + Instance::resolve(tcx, param_env, def_id, substs).ok().flatten() + } + } + + pub fn resolve_closure( + tcx: TyCtxt<'tcx>, + def_id: DefId, + substs: ty::SubstsRef<'tcx>, + requested_kind: ty::ClosureKind, + ) -> Instance<'tcx> { + let actual_kind = substs.as_closure().kind(); + + match needs_fn_once_adapter_shim(actual_kind, requested_kind) { + Ok(true) => Instance::fn_once_adapter_instance(tcx, def_id, substs), + _ => Instance::new(def_id, substs), + } + } + + pub fn resolve_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> { + let def_id = tcx.require_lang_item(DropInPlaceFnLangItem, None); + let substs = tcx.intern_substs(&[ty.into()]); + Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, substs).unwrap().unwrap() + } + + pub fn fn_once_adapter_instance( + tcx: TyCtxt<'tcx>, + closure_did: DefId, + substs: ty::SubstsRef<'tcx>, + ) -> Instance<'tcx> { + debug!("fn_once_adapter_shim({:?}, {:?})", closure_did, substs); + let fn_once = tcx.require_lang_item(FnOnceTraitLangItem, None); + let call_once = tcx + .associated_items(fn_once) + .in_definition_order() + .find(|it| it.kind == ty::AssocKind::Fn) + .unwrap() + .def_id; + let def = ty::InstanceDef::ClosureOnceShim { call_once }; + + let self_ty = tcx.mk_closure(closure_did, substs); + + let sig = substs.as_closure().sig(); + let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + assert_eq!(sig.inputs().len(), 1); + let substs = tcx.mk_substs_trait(self_ty, &[sig.inputs()[0].into()]); + + debug!("fn_once_adapter_shim: self_ty={:?} sig={:?}", self_ty, sig); + Instance { def, substs } + } + + /// FIXME(#69925) Depending on the kind of `InstanceDef`, the MIR body associated with an + /// instance is expressed in terms of the generic parameters of `self.def_id()`, and in other + /// cases the MIR body is expressed in terms of the types found in the substitution array. + /// In the former case, we want to substitute those generic types and replace them with the + /// values from the substs when monomorphizing the function body. But in the latter case, we + /// don't want to do that substitution, since it has already been done effectively. + /// + /// This function returns `Some(substs)` in the former case and None otherwise -- i.e., if + /// this function returns `None`, then the MIR body does not require substitution during + /// monomorphization. + pub fn substs_for_mir_body(&self) -> Option> { + match self.def { + InstanceDef::CloneShim(..) + | InstanceDef::DropGlue(_, Some(_)) => None, + InstanceDef::ClosureOnceShim { .. } + | InstanceDef::DropGlue(..) + // FIXME(#69925): `FnPtrShim` should be in the other branch. + | InstanceDef::FnPtrShim(..) + | InstanceDef::Item(_) + | InstanceDef::Intrinsic(..) + | InstanceDef::ReifyShim(..) + | InstanceDef::Virtual(..) + | InstanceDef::VtableShim(..) => Some(self.substs), + } + } +} + +fn needs_fn_once_adapter_shim( + actual_closure_kind: ty::ClosureKind, + trait_closure_kind: ty::ClosureKind, +) -> Result { + match (actual_closure_kind, trait_closure_kind) { + (ty::ClosureKind::Fn, ty::ClosureKind::Fn) + | (ty::ClosureKind::FnMut, ty::ClosureKind::FnMut) + | (ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce) => { + // No adapter needed. + Ok(false) + } + (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { + // The closure fn `llfn` is a `fn(&self, ...)`. We want a + // `fn(&mut self, ...)`. In fact, at codegen time, these are + // basically the same thing, so we can just return llfn. + Ok(false) + } + (ty::ClosureKind::Fn | ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { + // The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut + // self, ...)`. We want a `fn(self, ...)`. We can produce + // this by doing something like: + // + // fn call_once(self, ...) { call_mut(&self, ...) } + // fn call_once(mut self, ...) { call_mut(&mut self, ...) } + // + // These are both the same at codegen time. + Ok(true) + } + (ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce, _) => Err(()), + } +} diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs new file mode 100644 index 0000000000000..e4cc96dd83bfb --- /dev/null +++ b/src/librustc_middle/ty/layout.rs @@ -0,0 +1,2776 @@ +use crate::ich::StableHashingContext; +use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::mir::{GeneratorLayout, GeneratorSavedLocal}; +use crate::ty::subst::Subst; +use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable}; + +use rustc_ast::ast::{self, IntTy, UintTy}; +use rustc_attr as attr; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir as hir; +use rustc_hir::lang_items::{GeneratorStateLangItem, PinTypeLangItem}; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::DUMMY_SP; +use rustc_target::abi::call::{ + ArgAbi, ArgAttribute, ArgAttributes, Conv, FnAbi, PassMode, Reg, RegKind, +}; +use rustc_target::abi::*; +use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy}; + +use std::cmp; +use std::fmt; +use std::iter; +use std::mem; +use std::num::NonZeroUsize; +use std::ops::Bound; + +pub trait IntegerExt { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx>; + fn from_attr(cx: &C, ity: attr::IntType) -> Integer; + fn repr_discr<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool); +} + +impl IntegerExt for Integer { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>, signed: bool) -> Ty<'tcx> { + match (*self, signed) { + (I8, false) => tcx.types.u8, + (I16, false) => tcx.types.u16, + (I32, false) => tcx.types.u32, + (I64, false) => tcx.types.u64, + (I128, false) => tcx.types.u128, + (I8, true) => tcx.types.i8, + (I16, true) => tcx.types.i16, + (I32, true) => tcx.types.i32, + (I64, true) => tcx.types.i64, + (I128, true) => tcx.types.i128, + } + } + + /// Gets the Integer type from an attr::IntType. + fn from_attr(cx: &C, ity: attr::IntType) -> Integer { + let dl = cx.data_layout(); + + match ity { + attr::SignedInt(IntTy::I8) | attr::UnsignedInt(UintTy::U8) => I8, + attr::SignedInt(IntTy::I16) | attr::UnsignedInt(UintTy::U16) => I16, + attr::SignedInt(IntTy::I32) | attr::UnsignedInt(UintTy::U32) => I32, + attr::SignedInt(IntTy::I64) | attr::UnsignedInt(UintTy::U64) => I64, + attr::SignedInt(IntTy::I128) | attr::UnsignedInt(UintTy::U128) => I128, + attr::SignedInt(IntTy::Isize) | attr::UnsignedInt(UintTy::Usize) => { + dl.ptr_sized_integer() + } + } + } + + /// Finds the appropriate Integer type and signedness for the given + /// signed discriminant range and `#[repr]` attribute. + /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but + /// that shouldn't affect anything, other than maybe debuginfo. + fn repr_discr<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool) { + // Theoretically, negative values could be larger in unsigned representation + // than the unsigned representation of the signed minimum. However, if there + // are any negative values, the only valid unsigned representation is u128 + // which can fit all i128 values, so the result remains unaffected. + let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); + let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); + + let mut min_from_extern = None; + let min_default = I8; + + if let Some(ity) = repr.int { + let discr = Integer::from_attr(&tcx, ity); + let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; + if discr < fit { + bug!( + "Integer::repr_discr: `#[repr]` hint too small for \ + discriminant range of enum `{}", + ty + ) + } + return (discr, ity.is_signed()); + } + + if repr.c() { + match &tcx.sess.target.target.arch[..] { + // WARNING: the ARM EABI has two variants; the one corresponding + // to `at_least == I32` appears to be used on Linux and NetBSD, + // but some systems may use the variant corresponding to no + // lower bound. However, we don't run on those yet...? + "arm" => min_from_extern = Some(I32), + _ => min_from_extern = Some(I32), + } + } + + let at_least = min_from_extern.unwrap_or(min_default); + + // If there are no negative values, we can use the unsigned fit. + if min >= 0 { + (cmp::max(unsigned_fit, at_least), false) + } else { + (cmp::max(signed_fit, at_least), true) + } + } +} + +pub trait PrimitiveExt { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; + fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; +} + +impl PrimitiveExt for Primitive { + fn to_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match *self { + Int(i, signed) => i.to_ty(tcx, signed), + F32 => tcx.types.f32, + F64 => tcx.types.f64, + Pointer => tcx.mk_mut_ptr(tcx.mk_unit()), + } + } + + /// Return an *integer* type matching this primitive. + /// Useful in particular when dealing with enum discriminants. + fn to_int_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match *self { + Int(i, signed) => i.to_ty(tcx, signed), + Pointer => tcx.types.usize, + F32 | F64 => bug!("floats do not have an int type"), + } + } +} + +/// The first half of a fat pointer. +/// +/// - For a trait object, this is the address of the box. +/// - For a slice, this is the base address. +pub const FAT_PTR_ADDR: usize = 0; + +/// The second half of a fat pointer. +/// +/// - For a trait object, this is the address of the vtable. +/// - For a slice, this is the length. +pub const FAT_PTR_EXTRA: usize = 1; + +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub enum LayoutError<'tcx> { + Unknown(Ty<'tcx>), + SizeOverflow(Ty<'tcx>), +} + +impl<'tcx> fmt::Display for LayoutError<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + LayoutError::Unknown(ty) => write!(f, "the type `{:?}` has an unknown layout", ty), + LayoutError::SizeOverflow(ty) => { + write!(f, "the type `{:?}` is too big for the current architecture", ty) + } + } + } +} + +fn layout_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> Result<&'tcx Layout, LayoutError<'tcx>> { + ty::tls::with_related_context(tcx, move |icx| { + let (param_env, ty) = query.into_parts(); + + if !tcx.sess.recursion_limit().value_within_limit(icx.layout_depth) { + tcx.sess.fatal(&format!("overflow representing the type `{}`", ty)); + } + + // Update the ImplicitCtxt to increase the layout_depth + let icx = ty::tls::ImplicitCtxt { layout_depth: icx.layout_depth + 1, ..icx.clone() }; + + ty::tls::enter_context(&icx, |_| { + let cx = LayoutCx { tcx, param_env }; + let layout = cx.layout_raw_uncached(ty); + // Type-level uninhabitedness should always imply ABI uninhabitedness. + if let Ok(layout) = layout { + if ty.conservative_is_privately_uninhabited(tcx) { + assert!(layout.abi.is_uninhabited()); + } + } + layout + }) + }) +} + +pub fn provide(providers: &mut ty::query::Providers<'_>) { + *providers = ty::query::Providers { layout_raw, ..*providers }; +} + +pub struct LayoutCx<'tcx, C> { + pub tcx: C, + pub param_env: ty::ParamEnv<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +enum StructKind { + /// A tuple, closure, or univariant which cannot be coerced to unsized. + AlwaysSized, + /// A univariant, the last field of which may be coerced to unsized. + MaybeUnsized, + /// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag). + Prefixed(Size, Align), +} + +// Invert a bijective mapping, i.e. `invert(map)[y] = x` if `map[x] = y`. +// This is used to go between `memory_index` (source field order to memory order) +// and `inverse_memory_index` (memory order to source field order). +// See also `FieldsShape::Arbitrary::memory_index` for more details. +// FIXME(eddyb) build a better abstraction for permutations, if possible. +fn invert_mapping(map: &[u32]) -> Vec { + let mut inverse = vec![0; map.len()]; + for i in 0..map.len() { + inverse[map[i] as usize] = i as u32; + } + inverse +} + +impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { + fn scalar_pair(&self, a: Scalar, b: Scalar) -> Layout { + let dl = self.data_layout(); + let b_align = b.value.align(dl); + let align = a.value.align(dl).max(b_align).max(dl.aggregate_align); + let b_offset = a.value.size(dl).align_to(b_align.abi); + let size = (b_offset + b.value.size(dl)).align_to(align.abi); + + // HACK(nox): We iter on `b` and then `a` because `max_by_key` + // returns the last maximum. + let largest_niche = Niche::from_scalar(dl, b_offset, b.clone()) + .into_iter() + .chain(Niche::from_scalar(dl, Size::ZERO, a.clone())) + .max_by_key(|niche| niche.available(dl)); + + Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Arbitrary { + offsets: vec![Size::ZERO, b_offset], + memory_index: vec![0, 1], + }, + abi: Abi::ScalarPair(a, b), + largest_niche, + align, + size, + } + } + + fn univariant_uninterned( + &self, + ty: Ty<'tcx>, + fields: &[TyAndLayout<'_>], + repr: &ReprOptions, + kind: StructKind, + ) -> Result> { + let dl = self.data_layout(); + let pack = repr.pack; + if pack.is_some() && repr.align.is_some() { + bug!("struct cannot be packed and aligned"); + } + + let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; + + let mut inverse_memory_index: Vec = (0..fields.len() as u32).collect(); + + let optimize = !repr.inhibit_struct_field_reordering_opt(); + if optimize { + let end = + if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; + let optimizing = &mut inverse_memory_index[..end]; + let field_align = |f: &TyAndLayout<'_>| { + if let Some(pack) = pack { f.align.abi.min(pack) } else { f.align.abi } + }; + match kind { + StructKind::AlwaysSized | StructKind::MaybeUnsized => { + optimizing.sort_by_key(|&x| { + // Place ZSTs first to avoid "interesting offsets", + // especially with only one or two non-ZST fields. + let f = &fields[x as usize]; + (!f.is_zst(), cmp::Reverse(field_align(f))) + }); + } + StructKind::Prefixed(..) => { + // Sort in ascending alignment so that the layout stay optimal + // regardless of the prefix + optimizing.sort_by_key(|&x| field_align(&fields[x as usize])); + } + } + } + + // inverse_memory_index holds field indices by increasing memory offset. + // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5. + // We now write field offsets to the corresponding offset slot; + // field 5 with offset 0 puts 0 in offsets[5]. + // At the bottom of this function, we invert `inverse_memory_index` to + // produce `memory_index` (see `invert_mapping`). + + let mut sized = true; + let mut offsets = vec![Size::ZERO; fields.len()]; + let mut offset = Size::ZERO; + let mut largest_niche = None; + let mut largest_niche_available = 0; + + if let StructKind::Prefixed(prefix_size, prefix_align) = kind { + let prefix_align = + if let Some(pack) = pack { prefix_align.min(pack) } else { prefix_align }; + align = align.max(AbiAndPrefAlign::new(prefix_align)); + offset = prefix_size.align_to(prefix_align); + } + + for &i in &inverse_memory_index { + let field = fields[i as usize]; + if !sized { + bug!("univariant: field #{} of `{}` comes after unsized field", offsets.len(), ty); + } + + if field.is_unsized() { + sized = false; + } + + // Invariant: offset < dl.obj_size_bound() <= 1<<61 + let field_align = if let Some(pack) = pack { + field.align.min(AbiAndPrefAlign::new(pack)) + } else { + field.align + }; + offset = offset.align_to(field_align.abi); + align = align.max(field_align); + + debug!("univariant offset: {:?} field: {:#?}", offset, field); + offsets[i as usize] = offset; + + if !repr.hide_niche() { + if let Some(mut niche) = field.largest_niche.clone() { + let available = niche.available(dl); + if available > largest_niche_available { + largest_niche_available = available; + niche.offset += offset; + largest_niche = Some(niche); + } + } + } + + offset = offset.checked_add(field.size, dl).ok_or(LayoutError::SizeOverflow(ty))?; + } + + if let Some(repr_align) = repr.align { + align = align.max(AbiAndPrefAlign::new(repr_align)); + } + + debug!("univariant min_size: {:?}", offset); + let min_size = offset; + + // As stated above, inverse_memory_index holds field indices by increasing offset. + // This makes it an already-sorted view of the offsets vec. + // To invert it, consider: + // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. + // Field 5 would be the first element, so memory_index is i: + // Note: if we didn't optimize, it's already right. + + let memory_index = + if optimize { invert_mapping(&inverse_memory_index) } else { inverse_memory_index }; + + let size = min_size.align_to(align.abi); + let mut abi = Abi::Aggregate { sized }; + + // Unpack newtype ABIs and find scalar pairs. + if sized && size.bytes() > 0 { + // All other fields must be ZSTs, and we need them to all start at 0. + let mut zst_offsets = offsets.iter().enumerate().filter(|&(i, _)| fields[i].is_zst()); + if zst_offsets.all(|(_, o)| o.bytes() == 0) { + let mut non_zst_fields = fields.iter().enumerate().filter(|&(_, f)| !f.is_zst()); + + match (non_zst_fields.next(), non_zst_fields.next(), non_zst_fields.next()) { + // We have exactly one non-ZST field. + (Some((i, field)), None, None) => { + // Field fills the struct and it has a scalar or scalar pair ABI. + if offsets[i].bytes() == 0 + && align.abi == field.align.abi + && size == field.size + { + match field.abi { + // For plain scalars, or vectors of them, we can't unpack + // newtypes for `#[repr(C)]`, as that affects C ABIs. + Abi::Scalar(_) | Abi::Vector { .. } if optimize => { + abi = field.abi.clone(); + } + // But scalar pairs are Rust-specific and get + // treated as aggregates by C ABIs anyway. + Abi::ScalarPair(..) => { + abi = field.abi.clone(); + } + _ => {} + } + } + } + + // Two non-ZST fields, and they're both scalars. + ( + Some(( + i, + &TyAndLayout { + layout: &Layout { abi: Abi::Scalar(ref a), .. }, .. + }, + )), + Some(( + j, + &TyAndLayout { + layout: &Layout { abi: Abi::Scalar(ref b), .. }, .. + }, + )), + None, + ) => { + // Order by the memory placement, not source order. + let ((i, a), (j, b)) = if offsets[i] < offsets[j] { + ((i, a), (j, b)) + } else { + ((j, b), (i, a)) + }; + let pair = self.scalar_pair(a.clone(), b.clone()); + let pair_offsets = match pair.fields { + FieldsShape::Arbitrary { ref offsets, ref memory_index } => { + assert_eq!(memory_index, &[0, 1]); + offsets + } + _ => bug!(), + }; + if offsets[i] == pair_offsets[0] + && offsets[j] == pair_offsets[1] + && align == pair.align + && size == pair.size + { + // We can use `ScalarPair` only when it matches our + // already computed layout (including `#[repr(C)]`). + abi = pair.abi; + } + } + + _ => {} + } + } + } + + if sized && fields.iter().any(|f| f.abi.is_uninhabited()) { + abi = Abi::Uninhabited; + } + + Ok(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Arbitrary { offsets, memory_index }, + abi, + largest_niche, + align, + size, + }) + } + + fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx Layout, LayoutError<'tcx>> { + let tcx = self.tcx; + let param_env = self.param_env; + let dl = self.data_layout(); + let scalar_unit = |value: Primitive| { + let bits = value.size(dl).bits(); + assert!(bits <= 128); + Scalar { value, valid_range: 0..=(!0 >> (128 - bits)) } + }; + let scalar = |value: Primitive| tcx.intern_layout(Layout::scalar(self, scalar_unit(value))); + + let univariant = |fields: &[TyAndLayout<'_>], repr: &ReprOptions, kind| { + Ok(tcx.intern_layout(self.univariant_uninterned(ty, fields, repr, kind)?)) + }; + debug_assert!(!ty.has_infer_types_or_consts()); + + Ok(match ty.kind { + // Basic scalars. + ty::Bool => tcx.intern_layout(Layout::scalar( + self, + Scalar { value: Int(I8, false), valid_range: 0..=1 }, + )), + ty::Char => tcx.intern_layout(Layout::scalar( + self, + Scalar { value: Int(I32, false), valid_range: 0..=0x10FFFF }, + )), + ty::Int(ity) => scalar(Int(Integer::from_attr(dl, attr::SignedInt(ity)), true)), + ty::Uint(ity) => scalar(Int(Integer::from_attr(dl, attr::UnsignedInt(ity)), false)), + ty::Float(fty) => scalar(match fty { + ast::FloatTy::F32 => F32, + ast::FloatTy::F64 => F64, + }), + ty::FnPtr(_) => { + let mut ptr = scalar_unit(Pointer); + ptr.valid_range = 1..=*ptr.valid_range.end(); + tcx.intern_layout(Layout::scalar(self, ptr)) + } + + // The never type. + ty::Never => tcx.intern_layout(Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Primitive, + abi: Abi::Uninhabited, + largest_niche: None, + align: dl.i8_align, + size: Size::ZERO, + }), + + // Potentially-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 { + tag: niche_scalar, + tag_encoding: TagEncoding::Niche { + dataful_variant: i, + niche_variants, + niche_start, + }, + tag_field: 0, + variants: st, + }, + fields: FieldsShape::Arbitrary { + offsets: vec![offset], + memory_index: vec![0], + }, + abi, + largest_niche, + size, + align, + })); + } + } + } + + let (mut min, mut max) = (i128::MAX, i128::MIN); + let discr_type = def.repr.discr_type(); + let bits = Integer::from_attr(self, discr_type).size().bits(); + for (i, discr) in def.discriminants(tcx) { + if variants[i].iter().any(|f| f.abi.is_uninhabited()) { + continue; + } + let mut x = discr.val as i128; + if discr_type.is_signed() { + // sign extend the raw representation to be an i128 + x = (x << (128 - bits)) >> (128 - bits); + } + if x < min { + min = x; + } + if x > max { + max = x; + } + } + // We might have no inhabited variants, so pretend there's at least one. + if (min, max) == (i128::MAX, i128::MIN) { + min = 0; + max = 0; + } + assert!(min <= max, "discriminant range is {}...{}", min, max); + let (min_ity, signed) = Integer::repr_discr(tcx, ty, &def.repr, min, max); + + let mut align = dl.aggregate_align; + let mut size = Size::ZERO; + + // We're interested in the smallest alignment, so start large. + let mut start_align = Align::from_bytes(256).unwrap(); + assert_eq!(Integer::for_align(dl, start_align), None); + + // repr(C) on an enum tells us to make a (tag, union) layout, + // so we need to grow the prefix alignment to be at least + // the alignment of the union. (This value is used both for + // determining the alignment of the overall enum, and the + // determining the alignment of the payload after the tag.) + let mut prefix_align = min_ity.align(dl).abi; + if def.repr.c() { + for fields in &variants { + for field in fields { + prefix_align = prefix_align.max(field.align.abi); + } + } + } + + // Create the set of structs that represent each variant. + let mut layout_variants = variants + .iter_enumerated() + .map(|(i, field_layouts)| { + let mut st = self.univariant_uninterned( + ty, + &field_layouts, + &def.repr, + StructKind::Prefixed(min_ity.size(), prefix_align), + )?; + st.variants = Variants::Single { index: i }; + // Find the first field we can't move later + // to make room for a larger discriminant. + for field in + st.fields.index_by_increasing_offset().map(|j| field_layouts[j]) + { + if !field.is_zst() || field.align.abi.bytes() != 1 { + start_align = start_align.min(field.align.abi); + break; + } + } + size = cmp::max(size, st.size); + align = align.max(st.align); + Ok(st) + }) + .collect::, _>>()?; + + // Align the maximum variant size to the largest alignment. + size = size.align_to(align.abi); + + if size.bytes() >= dl.obj_size_bound() { + return Err(LayoutError::SizeOverflow(ty)); + } + + let typeck_ity = Integer::from_attr(dl, def.repr.discr_type()); + if typeck_ity < min_ity { + // It is a bug if Layout decided on a greater discriminant size than typeck for + // some reason at this point (based on values discriminant can take on). Mostly + // because this discriminant will be loaded, and then stored into variable of + // type calculated by typeck. Consider such case (a bug): typeck decided on + // byte-sized discriminant, but layout thinks we need a 16-bit to store all + // discriminant values. That would be a bug, because then, in codegen, in order + // to store this 16-bit discriminant into 8-bit sized temporary some of the + // space necessary to represent would have to be discarded (or layout is wrong + // on thinking it needs 16 bits) + bug!( + "layout decided on a larger discriminant type ({:?}) than typeck ({:?})", + min_ity, + typeck_ity + ); + // However, it is fine to make discr type however large (as an optimisation) + // after this point – we’ll just truncate the value we load in codegen. + } + + // Check to see if we should use a different type for the + // discriminant. We can safely use a type with the same size + // as the alignment of the first field of each variant. + // We increase the size of the discriminant to avoid LLVM copying + // padding when it doesn't need to. This normally causes unaligned + // load/stores and excessive memcpy/memset operations. By using a + // bigger integer size, LLVM can be sure about its contents and + // won't be so conservative. + + // Use the initial field alignment + let mut ity = if def.repr.c() || def.repr.int.is_some() { + min_ity + } else { + Integer::for_align(dl, start_align).unwrap_or(min_ity) + }; + + // If the alignment is not larger than the chosen discriminant size, + // don't use the alignment as the final size. + if ity <= min_ity { + ity = min_ity; + } else { + // Patch up the variants' first few fields. + let old_ity_size = min_ity.size(); + let new_ity_size = ity.size(); + for variant in &mut layout_variants { + match variant.fields { + FieldsShape::Arbitrary { ref mut offsets, .. } => { + for i in offsets { + if *i <= old_ity_size { + assert_eq!(*i, old_ity_size); + *i = new_ity_size; + } + } + // We might be making the struct larger. + if variant.size <= old_ity_size { + variant.size = new_ity_size; + } + } + _ => bug!(), + } + } + } + + let tag_mask = !0u128 >> (128 - ity.size().bits()); + let tag = Scalar { + value: Int(ity, signed), + valid_range: (min as u128 & tag_mask)..=(max as u128 & tag_mask), + }; + let mut abi = Abi::Aggregate { sized: true }; + if tag.value.size(dl) == size { + abi = Abi::Scalar(tag.clone()); + } else { + // Try to use a ScalarPair for all tagged enums. + let mut common_prim = None; + for (field_layouts, layout_variant) in variants.iter().zip(&layout_variants) { + let offsets = match layout_variant.fields { + FieldsShape::Arbitrary { ref offsets, .. } => offsets, + _ => bug!(), + }; + let mut fields = + field_layouts.iter().zip(offsets).filter(|p| !p.0.is_zst()); + let (field, offset) = match (fields.next(), fields.next()) { + (None, None) => continue, + (Some(pair), None) => pair, + _ => { + common_prim = None; + break; + } + }; + let prim = match field.abi { + Abi::Scalar(ref scalar) => scalar.value, + _ => { + common_prim = None; + break; + } + }; + if let Some(pair) = common_prim { + // This is pretty conservative. We could go fancier + // by conflating things like i32 and u32, or even + // realising that (u8, u8) could just cohabit with + // u16 or even u32. + if pair != (prim, offset) { + common_prim = None; + break; + } + } else { + common_prim = Some((prim, offset)); + } + } + if let Some((prim, offset)) = common_prim { + let pair = self.scalar_pair(tag.clone(), scalar_unit(prim)); + let pair_offsets = match pair.fields { + FieldsShape::Arbitrary { ref offsets, ref memory_index } => { + assert_eq!(memory_index, &[0, 1]); + offsets + } + _ => bug!(), + }; + if pair_offsets[0] == Size::ZERO + && pair_offsets[1] == *offset + && align == pair.align + && size == pair.size + { + // We can use `ScalarPair` only when it matches our + // already computed layout (including `#[repr(C)]`). + abi = pair.abi; + } + } + } + + if layout_variants.iter().all(|v| v.abi.is_uninhabited()) { + abi = Abi::Uninhabited; + } + + let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag.clone()); + + tcx.intern_layout(Layout { + variants: Variants::Multiple { + tag, + tag_encoding: TagEncoding::Direct, + tag_field: 0, + variants: layout_variants, + }, + fields: FieldsShape::Arbitrary { + offsets: vec![Size::ZERO], + memory_index: vec![0], + }, + largest_niche, + abi, + align, + size, + }) + } + + // Types with no meaningful known layout. + ty::Projection(_) | ty::Opaque(..) => { + let normalized = tcx.normalize_erasing_regions(param_env, ty); + if ty == normalized { + return Err(LayoutError::Unknown(ty)); + } + tcx.layout_raw(param_env.and(normalized))? + } + + ty::Bound(..) | ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => { + bug!("Layout::compute: unexpected type `{}`", ty) + } + + ty::Param(_) | ty::Error(_) => { + return Err(LayoutError::Unknown(ty)); + } + }) + } +} + +/// Overlap eligibility and variant assignment for each GeneratorSavedLocal. +#[derive(Clone, Debug, PartialEq)] +enum SavedLocalEligibility { + Unassigned, + Assigned(VariantIdx), + // FIXME: Use newtype_index so we aren't wasting bytes + Ineligible(Option), +} + +// When laying out generators, we divide our saved local fields into two +// categories: overlap-eligible and overlap-ineligible. +// +// Those fields which are ineligible for overlap go in a "prefix" at the +// beginning of the layout, and always have space reserved for them. +// +// Overlap-eligible fields are only assigned to one variant, so we lay +// those fields out for each variant and put them right after the +// prefix. +// +// Finally, in the layout details, we point to the fields from the +// variants they are assigned to. It is possible for some fields to be +// included in multiple variants. No field ever "moves around" in the +// layout; its offset is always the same. +// +// Also included in the layout are the upvars and the discriminant. +// These are included as fields on the "outer" layout; they are not part +// of any variant. +impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { + /// Compute the eligibility and assignment of each local. + fn generator_saved_local_eligibility( + &self, + info: &GeneratorLayout<'tcx>, + ) -> (BitSet, IndexVec) { + use SavedLocalEligibility::*; + + let mut assignments: IndexVec = + IndexVec::from_elem_n(Unassigned, info.field_tys.len()); + + // The saved locals not eligible for overlap. These will get + // "promoted" to the prefix of our generator. + let mut ineligible_locals = BitSet::new_empty(info.field_tys.len()); + + // Figure out which of our saved locals are fields in only + // one variant. The rest are deemed ineligible for overlap. + for (variant_index, fields) in info.variant_fields.iter_enumerated() { + for local in fields { + match assignments[*local] { + Unassigned => { + assignments[*local] = Assigned(variant_index); + } + Assigned(idx) => { + // We've already seen this local at another suspension + // point, so it is no longer a candidate. + trace!( + "removing local {:?} in >1 variant ({:?}, {:?})", + local, + variant_index, + idx + ); + ineligible_locals.insert(*local); + assignments[*local] = Ineligible(None); + } + Ineligible(_) => {} + } + } + } + + // Next, check every pair of eligible locals to see if they + // conflict. + for local_a in info.storage_conflicts.rows() { + let conflicts_a = info.storage_conflicts.count(local_a); + if ineligible_locals.contains(local_a) { + continue; + } + + for local_b in info.storage_conflicts.iter(local_a) { + // local_a and local_b are storage live at the same time, therefore they + // cannot overlap in the generator layout. The only way to guarantee + // this is if they are in the same variant, or one is ineligible + // (which means it is stored in every variant). + if ineligible_locals.contains(local_b) + || assignments[local_a] == assignments[local_b] + { + continue; + } + + // If they conflict, we will choose one to make ineligible. + // This is not always optimal; it's just a greedy heuristic that + // seems to produce good results most of the time. + let conflicts_b = info.storage_conflicts.count(local_b); + let (remove, other) = + if conflicts_a > conflicts_b { (local_a, local_b) } else { (local_b, local_a) }; + ineligible_locals.insert(remove); + assignments[remove] = Ineligible(None); + trace!("removing local {:?} due to conflict with {:?}", remove, other); + } + } + + // Count the number of variants in use. If only one of them, then it is + // impossible to overlap any locals in our layout. In this case it's + // always better to make the remaining locals ineligible, so we can + // lay them out with the other locals in the prefix and eliminate + // unnecessary padding bytes. + { + let mut used_variants = BitSet::new_empty(info.variant_fields.len()); + for assignment in &assignments { + if let Assigned(idx) = assignment { + used_variants.insert(*idx); + } + } + if used_variants.count() < 2 { + for assignment in assignments.iter_mut() { + *assignment = Ineligible(None); + } + ineligible_locals.insert_all(); + } + } + + // Write down the order of our locals that will be promoted to the prefix. + { + for (idx, local) in ineligible_locals.iter().enumerate() { + assignments[local] = Ineligible(Some(idx as u32)); + } + } + debug!("generator saved local assignments: {:?}", assignments); + + (ineligible_locals, assignments) + } + + /// Compute the full generator layout. + fn generator_layout( + &self, + ty: Ty<'tcx>, + def_id: hir::def_id::DefId, + substs: SubstsRef<'tcx>, + ) -> Result<&'tcx Layout, LayoutError<'tcx>> { + use SavedLocalEligibility::*; + let tcx = self.tcx; + + let subst_field = |ty: Ty<'tcx>| ty.subst(tcx, substs); + + let info = tcx.generator_layout(def_id); + let (ineligible_locals, assignments) = self.generator_saved_local_eligibility(&info); + + // Build a prefix layout, including "promoting" all ineligible + // locals as part of the prefix. We compute the layout of all of + // these fields at once to get optimal packing. + let tag_index = substs.as_generator().prefix_tys().count(); + + // `info.variant_fields` already accounts for the reserved variants, so no need to add them. + let max_discr = (info.variant_fields.len() - 1) as u128; + let discr_int = Integer::fit_unsigned(max_discr); + let discr_int_ty = discr_int.to_ty(tcx, false); + let tag = Scalar { value: Primitive::Int(discr_int, false), valid_range: 0..=max_discr }; + let tag_layout = self.tcx.intern_layout(Layout::scalar(self, tag.clone())); + let tag_layout = TyAndLayout { ty: discr_int_ty, layout: tag_layout }; + + let promoted_layouts = ineligible_locals + .iter() + .map(|local| subst_field(info.field_tys[local])) + .map(|ty| tcx.mk_maybe_uninit(ty)) + .map(|ty| self.layout_of(ty)); + let prefix_layouts = substs + .as_generator() + .prefix_tys() + .map(|ty| self.layout_of(ty)) + .chain(iter::once(Ok(tag_layout))) + .chain(promoted_layouts) + .collect::, _>>()?; + let prefix = self.univariant_uninterned( + ty, + &prefix_layouts, + &ReprOptions::default(), + StructKind::AlwaysSized, + )?; + + let (prefix_size, prefix_align) = (prefix.size, prefix.align); + + // Split the prefix layout into the "outer" fields (upvars and + // discriminant) and the "promoted" fields. Promoted fields will + // get included in each variant that requested them in + // GeneratorLayout. + debug!("prefix = {:#?}", prefix); + let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields { + FieldsShape::Arbitrary { mut offsets, memory_index } => { + let mut inverse_memory_index = invert_mapping(&memory_index); + + // "a" (`0..b_start`) and "b" (`b_start..`) correspond to + // "outer" and "promoted" fields respectively. + let b_start = (tag_index + 1) as u32; + let offsets_b = offsets.split_off(b_start as usize); + let offsets_a = offsets; + + // Disentangle the "a" and "b" components of `inverse_memory_index` + // by preserving the order but keeping only one disjoint "half" each. + // FIXME(eddyb) build a better abstraction for permutations, if possible. + let inverse_memory_index_b: Vec<_> = + inverse_memory_index.iter().filter_map(|&i| i.checked_sub(b_start)).collect(); + inverse_memory_index.retain(|&i| i < b_start); + let inverse_memory_index_a = inverse_memory_index; + + // Since `inverse_memory_index_{a,b}` each only refer to their + // respective fields, they can be safely inverted + let memory_index_a = invert_mapping(&inverse_memory_index_a); + let memory_index_b = invert_mapping(&inverse_memory_index_b); + + let outer_fields = + FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a }; + (outer_fields, offsets_b, memory_index_b) + } + _ => bug!(), + }; + + let mut size = prefix.size; + let mut align = prefix.align; + let variants = info + .variant_fields + .iter_enumerated() + .map(|(index, variant_fields)| { + // Only include overlap-eligible fields when we compute our variant layout. + let variant_only_tys = variant_fields + .iter() + .filter(|local| match assignments[**local] { + Unassigned => bug!(), + Assigned(v) if v == index => true, + Assigned(_) => bug!("assignment does not match variant"), + Ineligible(_) => false, + }) + .map(|local| subst_field(info.field_tys[*local])); + + let mut variant = self.univariant_uninterned( + ty, + &variant_only_tys + .map(|ty| self.layout_of(ty)) + .collect::, _>>()?, + &ReprOptions::default(), + StructKind::Prefixed(prefix_size, prefix_align.abi), + )?; + variant.variants = Variants::Single { index }; + + let (offsets, memory_index) = match variant.fields { + FieldsShape::Arbitrary { offsets, memory_index } => (offsets, memory_index), + _ => bug!(), + }; + + // Now, stitch the promoted and variant-only fields back together in + // the order they are mentioned by our GeneratorLayout. + // Because we only use some subset (that can differ between variants) + // of the promoted fields, we can't just pick those elements of the + // `promoted_memory_index` (as we'd end up with gaps). + // So instead, we build an "inverse memory_index", as if all of the + // promoted fields were being used, but leave the elements not in the + // subset as `INVALID_FIELD_IDX`, which we can filter out later to + // obtain a valid (bijective) mapping. + const INVALID_FIELD_IDX: u32 = !0; + let mut combined_inverse_memory_index = + vec![INVALID_FIELD_IDX; promoted_memory_index.len() + memory_index.len()]; + let mut offsets_and_memory_index = offsets.into_iter().zip(memory_index); + let combined_offsets = variant_fields + .iter() + .enumerate() + .map(|(i, local)| { + let (offset, memory_index) = match assignments[*local] { + Unassigned => bug!(), + Assigned(_) => { + let (offset, memory_index) = + offsets_and_memory_index.next().unwrap(); + (offset, promoted_memory_index.len() as u32 + memory_index) + } + Ineligible(field_idx) => { + let field_idx = field_idx.unwrap() as usize; + (promoted_offsets[field_idx], promoted_memory_index[field_idx]) + } + }; + combined_inverse_memory_index[memory_index as usize] = i as u32; + offset + }) + .collect(); + + // Remove the unused slots and invert the mapping to obtain the + // combined `memory_index` (also see previous comment). + combined_inverse_memory_index.retain(|&i| i != INVALID_FIELD_IDX); + let combined_memory_index = invert_mapping(&combined_inverse_memory_index); + + variant.fields = FieldsShape::Arbitrary { + offsets: combined_offsets, + memory_index: combined_memory_index, + }; + + size = size.max(variant.size); + align = align.max(variant.align); + Ok(variant) + }) + .collect::, _>>()?; + + size = size.align_to(align.abi); + + let abi = if prefix.abi.is_uninhabited() || variants.iter().all(|v| v.abi.is_uninhabited()) + { + Abi::Uninhabited + } else { + Abi::Aggregate { sized: true } + }; + + let layout = tcx.intern_layout(Layout { + variants: Variants::Multiple { + tag: tag, + tag_encoding: TagEncoding::Direct, + tag_field: tag_index, + variants, + }, + fields: outer_fields, + abi, + largest_niche: prefix.largest_niche, + size, + align, + }); + debug!("generator layout ({:?}): {:#?}", ty, layout); + Ok(layout) + } + + /// This is invoked by the `layout_raw` query to record the final + /// layout of each type. + #[inline(always)] + fn record_layout_for_printing(&self, layout: TyAndLayout<'tcx>) { + // If we are running with `-Zprint-type-sizes`, maybe record layouts + // for dumping later. + if self.tcx.sess.opts.debugging_opts.print_type_sizes { + self.record_layout_for_printing_outlined(layout) + } + } + + fn record_layout_for_printing_outlined(&self, layout: TyAndLayout<'tcx>) { + // Ignore layouts that are done with non-empty environments or + // non-monomorphic layouts, as the user only wants to see the stuff + // resulting from the final codegen session. + if layout.ty.has_param_types_or_consts() || !self.param_env.caller_bounds.is_empty() { + return; + } + + // (delay format until we actually need it) + let record = |kind, packed, opt_discr_size, variants| { + let type_desc = format!("{:?}", layout.ty); + self.tcx.sess.code_stats.record_type_size( + kind, + type_desc, + layout.align.abi, + layout.size, + packed, + opt_discr_size, + variants, + ); + }; + + let adt_def = match layout.ty.kind { + ty::Adt(ref adt_def, _) => { + debug!("print-type-size t: `{:?}` process adt", layout.ty); + adt_def + } + + ty::Closure(..) => { + debug!("print-type-size t: `{:?}` record closure", layout.ty); + record(DataTypeKind::Closure, false, None, vec![]); + return; + } + + _ => { + debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty); + return; + } + }; + + let adt_kind = adt_def.adt_kind(); + let adt_packed = adt_def.repr.pack.is_some(); + + let build_variant_info = |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { + let mut min_size = Size::ZERO; + let field_info: Vec<_> = flds + .iter() + .enumerate() + .map(|(i, &name)| match layout.field(self, i) { + Err(err) => { + bug!("no layout found for field {}: `{:?}`", name, err); + } + Ok(field_layout) => { + let offset = layout.fields.offset(i); + let field_end = offset + field_layout.size; + if min_size < field_end { + min_size = field_end; + } + FieldInfo { + name: name.to_string(), + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi.bytes(), + } + } + }) + .collect(); + + VariantInfo { + name: n.map(|n| n.to_string()), + kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, + align: layout.align.abi.bytes(), + size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, + fields: field_info, + } + }; + + match layout.variants { + Variants::Single { index } => { + debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variants[index].ident); + if !adt_def.variants.is_empty() { + let variant_def = &adt_def.variants[index]; + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.ident.name).collect(); + record( + adt_kind.into(), + adt_packed, + None, + vec![build_variant_info(Some(variant_def.ident), &fields, layout)], + ); + } else { + // (This case arises for *empty* enums; so give it + // zero variants.) + record(adt_kind.into(), adt_packed, None, vec![]); + } + } + + Variants::Multiple { ref tag, ref tag_encoding, .. } => { + debug!( + "print-type-size `{:#?}` adt general variants def {}", + layout.ty, + adt_def.variants.len() + ); + let variant_infos: Vec<_> = adt_def + .variants + .iter_enumerated() + .map(|(i, variant_def)| { + let fields: Vec<_> = + variant_def.fields.iter().map(|f| f.ident.name).collect(); + build_variant_info( + Some(variant_def.ident), + &fields, + layout.for_variant(self, i), + ) + }) + .collect(); + record( + adt_kind.into(), + adt_packed, + match tag_encoding { + TagEncoding::Direct => Some(tag.value.size(self)), + _ => None, + }, + variant_infos, + ); + } + } + } +} + +/// Type size "skeleton", i.e., the only information determining a type's size. +/// While this is conservative, (aside from constant sizes, only pointers, +/// newtypes thereof and null pointer optimized enums are allowed), it is +/// enough to statically check common use cases of transmute. +#[derive(Copy, Clone, Debug)] +pub enum SizeSkeleton<'tcx> { + /// Any statically computable Layout. + Known(Size), + + /// A potentially-fat pointer. + Pointer { + /// If true, this pointer is never null. + non_zero: bool, + /// The type which determines the unsized metadata, if any, + /// of this pointer. Either a type parameter or a projection + /// depending on one, with regions erased. + tail: Ty<'tcx>, + }, +} + +impl<'tcx> SizeSkeleton<'tcx> { + pub fn compute( + ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Result, LayoutError<'tcx>> { + debug_assert!(!ty.has_infer_types_or_consts()); + + // First try computing a static layout. + let err = match tcx.layout_of(param_env.and(ty)) { + Ok(layout) => { + return Ok(SizeSkeleton::Known(layout.size)); + } + Err(err) => err, + }; + + match ty.kind { + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + let non_zero = !ty.is_unsafe_ptr(); + let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); + match tail.kind { + ty::Param(_) | ty::Projection(_) => { + debug_assert!(tail.has_param_types_or_consts()); + Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(&tail) }) + } + _ => bug!( + "SizeSkeleton::compute({}): layout errored ({}), yet \ + tail `{}` is not a type parameter or a projection", + ty, + err, + tail + ), + } + } + + ty::Adt(def, substs) => { + // Only newtypes and enums w/ nullable pointer optimization. + if def.is_union() || def.variants.is_empty() || def.variants.len() > 2 { + return Err(err); + } + + // Get a zero-sized variant or a pointer newtype. + let zero_or_ptr_variant = |i| { + let i = VariantIdx::new(i); + let fields = def.variants[i] + .fields + .iter() + .map(|field| SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env)); + let mut ptr = None; + for field in fields { + let field = field?; + match field { + SizeSkeleton::Known(size) => { + if size.bytes() > 0 { + return Err(err); + } + } + SizeSkeleton::Pointer { .. } => { + if ptr.is_some() { + return Err(err); + } + ptr = Some(field); + } + } + } + Ok(ptr) + }; + + let v0 = zero_or_ptr_variant(0)?; + // Newtype. + if def.variants.len() == 1 { + if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 { + return Ok(SizeSkeleton::Pointer { + non_zero: non_zero + || match tcx.layout_scalar_valid_range(def.did) { + (Bound::Included(start), Bound::Unbounded) => start > 0, + (Bound::Included(start), Bound::Included(end)) => { + 0 < start && start < end + } + _ => false, + }, + tail, + }); + } else { + return Err(err); + } + } + + let v1 = zero_or_ptr_variant(1)?; + // Nullable pointer enum optimization. + match (v0, v1) { + (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None) + | (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => { + Ok(SizeSkeleton::Pointer { non_zero: false, tail }) + } + _ => Err(err), + } + } + + ty::Projection(_) | ty::Opaque(..) => { + let normalized = tcx.normalize_erasing_regions(param_env, ty); + if ty == normalized { + Err(err) + } else { + SizeSkeleton::compute(normalized, tcx, param_env) + } + } + + _ => Err(err), + } + } + + pub fn same_size(self, other: SizeSkeleton<'_>) -> bool { + match (self, other) { + (SizeSkeleton::Known(a), SizeSkeleton::Known(b)) => a == b, + (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => { + a == b + } + _ => false, + } + } +} + +pub trait HasTyCtxt<'tcx>: HasDataLayout { + fn tcx(&self) -> TyCtxt<'tcx>; +} + +pub trait HasParamEnv<'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx>; +} + +impl<'tcx> HasDataLayout for TyCtxt<'tcx> { + fn data_layout(&self) -> &TargetDataLayout { + &self.data_layout + } +} + +impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + *self + } +} + +impl<'tcx, C> HasParamEnv<'tcx> for LayoutCx<'tcx, C> { + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } +} + +impl<'tcx, T: HasDataLayout> HasDataLayout for LayoutCx<'tcx, T> { + fn data_layout(&self) -> &TargetDataLayout { + self.tcx.data_layout() + } +} + +impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx.tcx() + } +} + +pub type TyAndLayout<'tcx> = ::rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>; + +impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { + type Ty = Ty<'tcx>; + type TyAndLayout = Result, LayoutError<'tcx>>; + + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { + let param_env = self.param_env.with_reveal_all(); + let ty = self.tcx.normalize_erasing_regions(param_env, ty); + let layout = self.tcx.layout_raw(param_env.and(ty))?; + let layout = TyAndLayout { ty, layout }; + + // N.B., this recording is normally disabled; when enabled, it + // can however trigger recursive invocations of `layout_of`. + // Therefore, we execute it *after* the main query has + // completed, to avoid problems around recursive structures + // and the like. (Admittedly, I wasn't able to reproduce a problem + // here, but it seems like the right thing to do. -nmatsakis) + self.record_layout_for_printing(layout); + + Ok(layout) + } +} + +impl LayoutOf for LayoutCx<'tcx, ty::query::TyCtxtAt<'tcx>> { + type Ty = Ty<'tcx>; + type TyAndLayout = Result, LayoutError<'tcx>>; + + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { + let param_env = self.param_env.with_reveal_all(); + let ty = self.tcx.normalize_erasing_regions(param_env, ty); + let layout = self.tcx.layout_raw(param_env.and(ty))?; + let layout = TyAndLayout { ty, layout }; + + // N.B., this recording is normally disabled; when enabled, it + // can however trigger recursive invocations of `layout_of`. + // Therefore, we execute it *after* the main query has + // completed, to avoid problems around recursive structures + // and the like. (Admittedly, I wasn't able to reproduce a problem + // here, but it seems like the right thing to do. -nmatsakis) + let cx = LayoutCx { tcx: *self.tcx, param_env: self.param_env }; + cx.record_layout_for_printing(layout); + + Ok(layout) + } +} + +// Helper (inherent) `layout_of` methods to avoid pushing `LayoutCx` to users. +impl TyCtxt<'tcx> { + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + #[inline] + pub fn layout_of( + self, + param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + ) -> Result, LayoutError<'tcx>> { + let cx = LayoutCx { tcx: self, param_env: param_env_and_ty.param_env }; + cx.layout_of(param_env_and_ty.value) + } +} + +impl ty::query::TyCtxtAt<'tcx> { + /// Computes the layout of a type. Note that this implicitly + /// executes in "reveal all" mode. + #[inline] + pub fn layout_of( + self, + param_env_and_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, + ) -> Result, LayoutError<'tcx>> { + let cx = LayoutCx { tcx: self.at(self.span), param_env: param_env_and_ty.param_env }; + cx.layout_of(param_env_and_ty.value) + } +} + +impl<'tcx, C> TyAndLayoutMethods<'tcx, C> for Ty<'tcx> +where + C: LayoutOf, TyAndLayout: MaybeResult>> + + HasTyCtxt<'tcx> + + HasParamEnv<'tcx>, +{ + fn for_variant( + this: TyAndLayout<'tcx>, + cx: &C, + variant_index: VariantIdx, + ) -> TyAndLayout<'tcx> { + let layout = match this.variants { + Variants::Single { index } + // If all variants but one are uninhabited, the variant layout is the enum layout. + if index == variant_index && + // Don't confuse variants of uninhabited enums with the enum itself. + // For more details see https://github.com/rust-lang/rust/issues/69763. + this.fields != FieldsShape::Primitive => + { + this.layout + } + + Variants::Single { index } => { + // Deny calling for_variant more than once for non-Single enums. + if let Ok(original_layout) = cx.layout_of(this.ty).to_result() { + assert_eq!(original_layout.variants, Variants::Single { index }); + } + + let fields = match this.ty.kind { + ty::Adt(def, _) if def.variants.is_empty() => + bug!("for_variant called on zero-variant enum"), + ty::Adt(def, _) => def.variants[variant_index].fields.len(), + _ => bug!(), + }; + let tcx = cx.tcx(); + tcx.intern_layout(Layout { + variants: Variants::Single { index: variant_index }, + fields: match NonZeroUsize::new(fields) { + Some(fields) => FieldsShape::Union(fields), + None => FieldsShape::Arbitrary { offsets: vec![], memory_index: vec![] }, + }, + abi: Abi::Uninhabited, + largest_niche: None, + align: tcx.data_layout.i8_align, + size: Size::ZERO, + }) + } + + Variants::Multiple { ref variants, .. } => &variants[variant_index], + }; + + assert_eq!(layout.variants, Variants::Single { index: variant_index }); + + TyAndLayout { ty: this.ty, layout } + } + + fn field(this: TyAndLayout<'tcx>, cx: &C, i: usize) -> C::TyAndLayout { + let tcx = cx.tcx(); + let tag_layout = |tag: &Scalar| -> C::TyAndLayout { + let layout = Layout::scalar(cx, tag.clone()); + MaybeResult::from(Ok(TyAndLayout { + layout: tcx.intern_layout(layout), + ty: tag.value.to_ty(tcx), + })) + }; + + cx.layout_of(match this.ty.kind { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::FnPtr(_) + | ty::Never + | ty::FnDef(..) + | ty::GeneratorWitness(..) + | ty::Foreign(..) + | ty::Dynamic(..) => bug!("TyAndLayout::field_type({:?}): not applicable", this), + + // Potentially-fat pointers. + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + assert!(i < this.fields.count()); + + // Reuse the fat `*T` type as its own thin pointer data field. + // This provides information about, e.g., DST struct pointees + // (which may have no non-DST form), and will work as long + // as the `Abi` or `FieldsShape` is checked by users. + if i == 0 { + let nil = tcx.mk_unit(); + let ptr_ty = if this.ty.is_unsafe_ptr() { + tcx.mk_mut_ptr(nil) + } else { + tcx.mk_mut_ref(tcx.lifetimes.re_static, nil) + }; + return MaybeResult::from(cx.layout_of(ptr_ty).to_result().map( + |mut ptr_layout| { + ptr_layout.ty = this.ty; + ptr_layout + }, + )); + } + + match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind { + ty::Slice(_) | ty::Str => tcx.types.usize, + ty::Dynamic(_, _) => { + tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3)) + /* FIXME: use actual fn pointers + Warning: naively computing the number of entries in the + vtable by counting the methods on the trait + methods on + all parent traits does not work, because some methods can + be not object safe and thus excluded from the vtable. + Increase this counter if you tried to implement this but + failed to do it without duplicating a lot of code from + other places in the compiler: 2 + tcx.mk_tup(&[ + tcx.mk_array(tcx.types.usize, 3), + tcx.mk_array(Option), + ]) + */ + } + _ => bug!("TyAndLayout::field_type({:?}): not applicable", this), + } + } + + // Arrays and slices. + ty::Array(element, _) | ty::Slice(element) => element, + ty::Str => tcx.types.u8, + + // Tuples, generators and closures. + ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().nth(i).unwrap(), + + ty::Generator(def_id, ref substs, _) => match this.variants { + Variants::Single { index } => substs + .as_generator() + .state_tys(def_id, tcx) + .nth(index.as_usize()) + .unwrap() + .nth(i) + .unwrap(), + Variants::Multiple { ref tag, tag_field, .. } => { + if i == tag_field { + return tag_layout(tag); + } + substs.as_generator().prefix_tys().nth(i).unwrap() + } + }, + + ty::Tuple(tys) => tys[i].expect_ty(), + + // SIMD vector types. + ty::Adt(def, ..) if def.repr.simd() => this.ty.simd_type(tcx), + + // ADTs. + ty::Adt(def, substs) => { + match this.variants { + Variants::Single { index } => def.variants[index].fields[i].ty(tcx, substs), + + // Discriminant field for enums (where applicable). + Variants::Multiple { ref tag, .. } => { + assert_eq!(i, 0); + return tag_layout(tag); + } + } + } + + ty::Projection(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Opaque(..) + | ty::Param(_) + | ty::Infer(_) + | ty::Error(_) => bug!("TyAndLayout::field_type: unexpected type `{}`", this.ty), + }) + } + + fn pointee_info_at(this: TyAndLayout<'tcx>, cx: &C, offset: Size) -> Option { + 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.at(DUMMY_SP), cx.param_env()); + let kind = match mt { + hir::Mutability::Not => { + if is_freeze { + PointerKind::Frozen + } else { + PointerKind::Shared + } + } + hir::Mutability::Mut => { + // Previously we would only emit noalias annotations for LLVM >= 6 or in + // panic=abort mode. That was deemed right, as prior versions had many bugs + // in conjunction with unwinding, but later versions didn’t seem to have + // said issues. See issue #31681. + // + // Alas, later on we encountered a case where noalias would generate wrong + // code altogether even with recent versions of LLVM in *safe* code with no + // unwinding involved. See #54462. + // + // For now, do not enable mutable_noalias by default at all, while the + // issue is being figured out. + if tcx.sess.opts.debugging_opts.mutable_noalias { + PointerKind::UniqueBorrowed + } else { + PointerKind::Shared + } + } + }; + + cx.layout_of(ty).to_result().ok().map(|layout| PointeeInfo { + size: layout.size, + align: layout.align.abi, + safe: Some(kind), + }) + } + + _ => { + let mut data_variant = match this.variants { + // Within the discriminant field, only the niche itself is + // always initialized, so we only check for a pointer at its + // offset. + // + // If the niche is a pointer, it's either valid (according + // to its type), or null (which the niche field's scalar + // validity range encodes). This allows using + // `dereferenceable_or_null` for e.g., `Option<&T>`, and + // this will continue to work as long as we don't start + // using more niches than just null (e.g., the first page of + // the address space, or unaligned pointers). + Variants::Multiple { + tag_encoding: TagEncoding::Niche { dataful_variant, .. }, + tag_field, + .. + } if this.fields.offset(tag_field) == offset => { + Some(this.for_variant(cx, dataful_variant)) + } + _ => Some(this), + }; + + if let Some(variant) = data_variant { + // We're not interested in any unions. + if let FieldsShape::Union(_) = variant.fields { + data_variant = None; + } + } + + let mut result = None; + + if let Some(variant) = data_variant { + let ptr_end = offset + Pointer.size(cx); + for i in 0..variant.fields.count() { + let field_start = variant.fields.offset(i); + if field_start <= offset { + let field = variant.field(cx, i); + result = field.to_result().ok().and_then(|field| { + if ptr_end <= field_start + field.size { + // We found the right field, look inside it. + 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, + AvrInterrupt => Conv::AvrInterrupt, + AvrNonBlockingInterrupt => Conv::AvrNonBlockingInterrupt, + + // These API constants ought to be more specific... + Cdecl => Conv::C, + }; + + let mut inputs = sig.inputs(); + let extra_args = if sig.abi == RustCall { + assert!(!sig.c_variadic && extra_args.is_empty()); + + if let Some(input) = sig.inputs().last() { + if let ty::Tuple(tupled_arguments) = input.kind { + inputs = &sig.inputs()[0..sig.inputs().len() - 1]; + tupled_arguments.iter().map(|k| k.expect_ty()).collect() + } else { + bug!( + "argument to function with \"rust-call\" ABI \ + is not a tuple" + ); + } + } else { + bug!( + "argument to function with \"rust-call\" ABI \ + is not a tuple" + ); + } + } else { + assert!(sig.c_variadic || extra_args.is_empty()); + extra_args.to_vec() + }; + + let target = &cx.tcx().sess.target.target; + let target_env_gnu_like = matches!(&target.target_env[..], "gnu" | "musl"); + let win_x64_gnu = + target.target_os == "windows" && target.arch == "x86_64" && target.target_env == "gnu"; + let linux_s390x_gnu_like = + target.target_os == "linux" && target.arch == "s390x" && target_env_gnu_like; + let linux_sparc64_gnu_like = + target.target_os == "linux" && target.arch == "sparc64" && target_env_gnu_like; + let linux_powerpc_gnu_like = + target.target_os == "linux" && target.arch == "powerpc" && target_env_gnu_like; + let rust_abi = match sig.abi { + RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, + _ => false, + }; + + // Handle safe Rust thin and fat pointers. + let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, + scalar: &Scalar, + layout: TyAndLayout<'tcx>, + offset: Size, + is_return: bool| { + // Booleans are always an i1 that needs to be zero-extended. + if scalar.is_bool() { + attrs.set(ArgAttribute::ZExt); + return; + } + + // Only pointer types handled below. + if scalar.value != Pointer { + return; + } + + if scalar.valid_range.start() < scalar.valid_range.end() { + if *scalar.valid_range.start() > 0 { + attrs.set(ArgAttribute::NonNull); + } + } + + if let Some(pointee) = layout.pointee_info_at(cx, offset) { + if let Some(kind) = pointee.safe { + attrs.pointee_align = Some(pointee.align); + + // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable + // for the entire duration of the function as they can be deallocated + // at any time. Set their valid size to 0. + attrs.pointee_size = match kind { + PointerKind::UniqueOwned => Size::ZERO, + _ => pointee.size, + }; + + // `Box` pointer parameters never alias because ownership is transferred + // `&mut` pointer parameters never alias other parameters, + // or mutable global data + // + // `&T` where `T` contains no `UnsafeCell` is immutable, + // and can be marked as both `readonly` and `noalias`, as + // LLVM's definition of `noalias` is based solely on memory + // dependencies rather than pointer equality + let no_alias = match kind { + PointerKind::Shared => false, + PointerKind::UniqueOwned => true, + PointerKind::Frozen | PointerKind::UniqueBorrowed => !is_return, + }; + if no_alias { + attrs.set(ArgAttribute::NoAlias); + } + + if kind == PointerKind::Frozen && !is_return { + attrs.set(ArgAttribute::ReadOnly); + } + } + } + }; + + let arg_of = |ty: Ty<'tcx>, arg_idx: Option| { + let is_return = arg_idx.is_none(); + let mut arg = mk_arg_type(ty, arg_idx); + if arg.layout.is_zst() { + // For some forsaken reason, x86_64-pc-windows-gnu + // doesn't ignore zero-sized struct arguments. + // The same is true for {s390x,sparc64,powerpc}-unknown-linux-{gnu,musl}. + if is_return + || rust_abi + || (!win_x64_gnu + && !linux_s390x_gnu_like + && !linux_sparc64_gnu_like + && !linux_powerpc_gnu_like) + { + arg.mode = PassMode::Ignore; + } + } + + // FIXME(eddyb) other ABIs don't have logic for scalar pairs. + if !is_return && rust_abi { + if let Abi::ScalarPair(ref a, ref b) = arg.layout.abi { + let mut a_attrs = ArgAttributes::new(); + let mut b_attrs = ArgAttributes::new(); + adjust_for_rust_scalar(&mut a_attrs, a, arg.layout, Size::ZERO, false); + adjust_for_rust_scalar( + &mut b_attrs, + b, + arg.layout, + a.value.size(cx).align_to(b.value.align(cx).abi), + false, + ); + arg.mode = PassMode::Pair(a_attrs, b_attrs); + return arg; + } + } + + if let Abi::Scalar(ref scalar) = arg.layout.abi { + if let PassMode::Direct(ref mut attrs) = arg.mode { + adjust_for_rust_scalar(attrs, scalar, arg.layout, Size::ZERO, is_return); + } + } + + arg + }; + + let mut fn_abi = FnAbi { + ret: arg_of(sig.output(), None), + args: inputs + .iter() + .cloned() + .chain(extra_args) + .chain(caller_location) + .enumerate() + .map(|(i, ty)| arg_of(ty, Some(i))) + .collect(), + c_variadic: sig.c_variadic, + fixed_count: inputs.len(), + conv, + can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv), + }; + fn_abi.adjust_for_abi(cx, sig.abi); + fn_abi + } + + fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi) { + if abi == SpecAbi::Unadjusted { + return; + } + + if abi == SpecAbi::Rust + || abi == SpecAbi::RustCall + || abi == SpecAbi::RustIntrinsic + || abi == SpecAbi::PlatformIntrinsic + { + let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>| { + if arg.is_ignore() { + return; + } + + match arg.layout.abi { + Abi::Aggregate { .. } => {} + + // This is a fun case! The gist of what this is doing is + // that we want callers and callees to always agree on the + // ABI of how they pass SIMD arguments. If we were to *not* + // make these arguments indirect then they'd be immediates + // in LLVM, which means that they'd used whatever the + // appropriate ABI is for the callee and the caller. That + // means, for example, if the caller doesn't have AVX + // enabled but the callee does, then passing an AVX argument + // across this boundary would cause corrupt data to show up. + // + // This problem is fixed by unconditionally passing SIMD + // arguments through memory between callers and callees + // which should get them all to agree on ABI regardless of + // target feature sets. Some more information about this + // issue can be found in #44367. + // + // Note that the platform intrinsic ABI is exempt here as + // that's how we connect up to LLVM and it's unstable + // anyway, we control all calls to it in libstd. + Abi::Vector { .. } + if abi != SpecAbi::PlatformIntrinsic + && cx.tcx().sess.target.target.options.simd_types_indirect => + { + arg.make_indirect(); + return; + } + + _ => return, + } + + let size = arg.layout.size; + if arg.layout.is_unsized() || size > Pointer.size(cx) { + arg.make_indirect(); + } else { + // We want to pass small aggregates as immediates, but using + // a LLVM aggregate type for this leads to bad optimizations, + // so we pick an appropriately sized integer type instead. + arg.cast_to(Reg { kind: RegKind::Integer, size }); + } + }; + fixup(&mut self.ret); + for arg in &mut self.args { + fixup(arg); + } + if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); + } + return; + } + + if let Err(msg) = self.adjust_for_cabi(cx, abi) { + cx.tcx().sess.fatal(&msg); + } + } +} diff --git a/src/librustc_middle/ty/list.rs b/src/librustc_middle/ty/list.rs new file mode 100644 index 0000000000000..92d6dbb5f90f5 --- /dev/null +++ b/src/librustc_middle/ty/list.rs @@ -0,0 +1,156 @@ +use crate::arena::Arena; + +use rustc_serialize::{Encodable, Encoder}; + +use std::alloc::Layout; +use std::cmp::Ordering; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::iter; +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. +/// +/// Unlike slices, The types contained in `List` are expected to be `Copy` +/// and iterating over a `List` returns `T` instead of a reference. +/// +/// 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()); + + let (layout, _offset) = + Layout::new::().extend(Layout::for_value::<[T]>(slice)).unwrap(); + let mem = arena.dropless.alloc_raw(layout); + unsafe { + let result = &mut *(mem 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 + } + } + + // If this method didn't exist, we would use `slice.iter` due to + // deref coercion. + // + // This would be weird, as `self.into_iter` iterates over `T` directly. + #[inline(always)] + pub fn iter(&self) -> <&'_ List as IntoIterator>::IntoIter { + self.into_iter() + } +} + +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: Copy> IntoIterator for &'a List { + type Item = T; + type IntoIter = iter::Copied<<&'a [T] as IntoIterator>::IntoIter>; + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + self[..].iter().copied() + } +} + +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..6b7940ed7abcc --- /dev/null +++ b/src/librustc_middle/ty/mod.rs @@ -0,0 +1,2922 @@ +pub use self::fold::{TypeFoldable, TypeVisitor}; +pub use self::AssocItemContainer::*; +pub use self::BorrowKind::*; +pub use self::IntVarValue::*; +pub use self::Variance::*; + +use crate::hir::exports::ExportMap; +use crate::ich::StableHashingContext; +use crate::infer::canonical::Canonical; +use crate::middle::cstore::CrateStoreDyn; +use crate::middle::resolve_lifetime::ObjectLifetimeDefault; +use crate::mir::interpret::ErrorHandled; +use crate::mir::Body; +use crate::mir::GeneratorLayout; +use crate::traits::{self, Reveal}; +use crate::ty; +use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef}; +use crate::ty::util::{Discr, IntTypeExt}; +use rustc_ast::ast; +use rustc_attr as attr; +use rustc_data_structures::captures::Captures; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::sorted_map::SortedIndexMultiMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::{self, par_iter, ParallelIterator}; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; +use rustc_hir::lang_items::{FnMutTraitLangItem, FnOnceTraitLangItem, FnTraitLangItem}; +use rustc_hir::{Constness, Node}; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_macros::HashStable; +use rustc_serialize::{self, Encodable, Encoder}; +use rustc_session::DataTypeKind; +use rustc_span::hygiene::ExpnId; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::Span; +use rustc_target::abi::{Align, VariantIdx}; + +use std::cell::RefCell; +use std::cmp::Ordering; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::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: FxHashMap, + pub maybe_unused_trait_imports: FxHashSet, + pub maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, + pub export_map: ExportMap, + pub glob_map: FxHashMap>, + /// Extern prelude entries. The value is `true` if the entry was introduced + /// via `extern crate` item and not `--extern` option or compiler built-in. + pub extern_prelude: FxHashMap, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)] +pub enum AssocItemContainer { + TraitContainer(DefId), + ImplContainer(DefId), +} + +impl AssocItemContainer { + /// Asserts that this is the `DefId` of an associated item declared + /// in a trait, and returns the trait `DefId`. + pub fn assert_trait(&self) -> DefId { + match *self { + TraitContainer(id) => id, + _ => bug!("associated item has wrong container type: {:?}", self), + } + } + + pub fn id(&self) -> DefId { + match *self { + TraitContainer(id) => id, + ImplContainer(id) => id, + } + } +} + +/// The "header" of an impl is everything outside the body: a Self type, a trait +/// ref (in the case of a trait impl), and a set of predicates (from the +/// bounds / where-clauses). +#[derive(Clone, Debug, TypeFoldable)] +pub struct ImplHeader<'tcx> { + pub impl_def_id: DefId, + pub self_ty: Ty<'tcx>, + pub trait_ref: Option>, + pub predicates: Vec>, +} + +#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)] +pub enum ImplPolarity { + /// `impl Trait for Type` + Positive, + /// `impl !Trait for Type` + Negative, + /// `#[rustc_reservation_impl] impl Trait for Type` + /// + /// This is a "stability hack", not a real Rust feature. + /// See #64631 for details. + Reservation, +} + +#[derive(Copy, Clone, Debug, PartialEq, HashStable)] +pub struct AssocItem { + pub def_id: DefId, + #[stable_hasher(project(name))] + pub ident: Ident, + pub kind: AssocKind, + pub vis: Visibility, + pub defaultness: hir::Defaultness, + pub container: AssocItemContainer, + + /// Whether this is a method with an explicit self + /// as its first parameter, allowing method calls. + pub fn_has_self_parameter: bool, +} + +#[derive(Copy, Clone, PartialEq, Debug, HashStable)] +pub enum AssocKind { + Const, + Fn, + Type, +} + +impl AssocKind { + pub fn namespace(&self) -> Namespace { + match *self { + ty::AssocKind::Type => Namespace::TypeNS, + ty::AssocKind::Const | ty::AssocKind::Fn => Namespace::ValueNS, + } + } + + pub fn as_def_kind(&self) -> DefKind { + match self { + AssocKind::Const => DefKind::AssocConst, + AssocKind::Fn => DefKind::AssocFn, + AssocKind::Type => DefKind::AssocTy, + } + } +} + +impl AssocItem { + pub fn signature(&self, tcx: TyCtxt<'_>) -> String { + match self.kind { + ty::AssocKind::Fn => { + // We skip the binder here because the binder would deanonymize all + // late-bound regions, and we don't want method signatures to show up + // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound + // regions just fine, showing `fn(&MyType)`. + tcx.fn_sig(self.def_id).skip_binder().to_string() + } + ty::AssocKind::Type => format!("type {};", self.ident), + ty::AssocKind::Const => { + format!("const {}: {:?};", self.ident, tcx.type_of(self.def_id)) + } + } + } +} + +/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name. +/// +/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since +/// it is relatively expensive. Instead, items are indexed by `Symbol` and hygienic comparison is +/// done only on items with the same name. +#[derive(Debug, Clone, PartialEq, HashStable)] +pub struct AssociatedItems<'tcx> { + items: SortedIndexMultiMap, +} + +impl<'tcx> AssociatedItems<'tcx> { + /// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order. + pub fn new(items_in_def_order: impl IntoIterator) -> Self { + let items = items_in_def_order.into_iter().map(|item| (item.ident.name, item)).collect(); + AssociatedItems { items } + } + + /// Returns a slice of associated items in the order they were defined. + /// + /// New code should avoid relying on definition order. If you need a particular associated item + /// for a known trait, make that trait a lang item instead of indexing this array. + pub fn in_definition_order(&self) -> impl '_ + Iterator { + self.items.iter().map(|(_, v)| *v) + } + + /// Returns an iterator over all associated items with the given name, ignoring hygiene. + pub fn filter_by_name_unhygienic( + &self, + name: Symbol, + ) -> impl '_ + Iterator { + self.items.get_by_key(&name).copied() + } + + /// Returns an iterator over all associated items with the given name. + /// + /// Multiple items may have the same name if they are in different `Namespace`s. For example, + /// an associated type can have the same name as a method. Use one of the `find_by_name_and_*` + /// methods below if you know which item you are looking for. + pub fn filter_by_name( + &'a self, + tcx: TyCtxt<'a>, + ident: Ident, + parent_def_id: DefId, + ) -> impl 'a + Iterator { + self.filter_by_name_unhygienic(ident.name) + .filter(move |item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } + + /// Returns the associated item with the given name and `AssocKind`, if one exists. + pub fn find_by_name_and_kind( + &self, + tcx: TyCtxt<'_>, + ident: Ident, + kind: AssocKind, + parent_def_id: DefId, + ) -> Option<&ty::AssocItem> { + self.filter_by_name_unhygienic(ident.name) + .filter(|item| item.kind == kind) + .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } + + /// Returns the associated item with the given name in the given `Namespace`, if one exists. + pub fn find_by_name_and_namespace( + &self, + tcx: TyCtxt<'_>, + ident: Ident, + ns: Namespace, + parent_def_id: DefId, + ) -> Option<&ty::AssocItem> { + self.filter_by_name_unhygienic(ident.name) + .filter(|item| item.kind.namespace() == ns) + .find(|item| tcx.hygienic_eq(ident, item.ident, parent_def_id)) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Copy, RustcEncodable, RustcDecodable, HashStable)] +pub enum Visibility { + /// Visible everywhere (including in other crates). + Public, + /// Visible only in the given crate-local module. + Restricted(DefId), + /// Not visible anywhere in the local crate. This is the visibility of private external items. + Invisible, +} + +pub trait DefIdTree: Copy { + fn parent(self, id: DefId) -> Option; + + fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { + if descendant.krate != ancestor.krate { + return false; + } + + while descendant != ancestor { + match self.parent(descendant) { + Some(parent) => descendant = parent, + None => return false, + } + } + true + } +} + +impl<'tcx> DefIdTree for TyCtxt<'tcx> { + fn parent(self, id: DefId) -> Option { + self.def_key(id).parent.map(|index| DefId { index, ..id }) + } +} + +impl Visibility { + pub fn from_hir(visibility: &hir::Visibility<'_>, id: hir::HirId, tcx: TyCtxt<'_>) -> Self { + match visibility.node { + hir::VisibilityKind::Public => Visibility::Public, + hir::VisibilityKind::Crate(_) => Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)), + hir::VisibilityKind::Restricted { ref path, .. } => match path.res { + // If there is no resolution, `resolve` will have already reported an error, so + // assume that the visibility is public to avoid reporting more privacy errors. + Res::Err => Visibility::Public, + def => Visibility::Restricted(def.def_id()), + }, + hir::VisibilityKind::Inherited => { + Visibility::Restricted(tcx.parent_module(id).to_def_id()) + } + } + } + + /// Returns `true` if an item with this visibility is accessible from the given block. + pub fn is_accessible_from(self, module: DefId, tree: T) -> bool { + let restriction = match self { + // Public items are visible everywhere. + Visibility::Public => return true, + // Private items from other crates are visible nowhere. + Visibility::Invisible => return false, + // Restricted items are visible in an arbitrary local module. + Visibility::Restricted(other) if other.krate != module.krate => return false, + Visibility::Restricted(module) => module, + }; + + tree.is_descendant_of(module, restriction) + } + + /// Returns `true` if this visibility is at least as accessible as the given visibility + pub fn is_at_least(self, vis: Visibility, tree: T) -> bool { + let vis_restriction = match vis { + Visibility::Public => return self == Visibility::Public, + Visibility::Invisible => return true, + Visibility::Restricted(module) => module, + }; + + self.is_accessible_from(vis_restriction, tree) + } + + // Returns `true` if this item is visible anywhere in the local crate. + pub fn is_visible_locally(self) -> bool { + match self { + Visibility::Public => true, + Visibility::Restricted(def_id) => def_id.is_local(), + Visibility::Invisible => false, + } + } +} + +#[derive(Copy, Clone, PartialEq, RustcDecodable, RustcEncodable, HashStable)] +pub enum Variance { + Covariant, // T <: T iff A <: B -- e.g., function return type + Invariant, // T <: T iff B == A -- e.g., type of mutable cell + Contravariant, // T <: T iff B <: A -- e.g., function param type + Bivariant, // T <: T -- e.g., unused type parameter +} + +/// The crate variances map is computed during typeck and contains the +/// variance of every item in the local crate. You should not use it +/// directly, because to do so will make your pass dependent on the +/// HIR of every item in the local crate. Instead, use +/// `tcx.variances_of()` to get the variance for a *particular* +/// item. +#[derive(HashStable)] +pub struct CrateVariancesMap<'tcx> { + /// For each item with generics, maps to a vector of the variance + /// of its generics. If an item has no generics, it will have no + /// entry. + pub variances: FxHashMap, +} + +impl Variance { + /// `a.xform(b)` combines the variance of a context with the + /// variance of a type with the following meaning. If we are in a + /// context with variance `a`, and we encounter a type argument in + /// a position with variance `b`, then `a.xform(b)` is the new + /// variance with which the argument appears. + /// + /// Example 1: + /// + /// *mut Vec + /// + /// Here, the "ambient" variance starts as covariant. `*mut T` is + /// invariant with respect to `T`, so the variance in which the + /// `Vec` appears is `Covariant.xform(Invariant)`, which + /// yields `Invariant`. Now, the type `Vec` is covariant with + /// respect to its type argument `T`, and hence the variance of + /// the `i32` here is `Invariant.xform(Covariant)`, which results + /// (again) in `Invariant`. + /// + /// Example 2: + /// + /// fn(*const Vec, *mut Vec` appears is + /// `Contravariant.xform(Covariant)` or `Contravariant`. The same + /// is true for its `i32` argument. In the `*mut T` case, the + /// variance of `Vec` is `Contravariant.xform(Invariant)`, + /// and hence the outermost type is `Invariant` with respect to + /// `Vec` (and its `i32` argument). + /// + /// Source: Figure 1 of "Taming the Wildcards: + /// Combining Definition- and Use-Site Variance" published in PLDI'11. + pub fn xform(self, v: ty::Variance) -> ty::Variance { + match (self, v) { + // Figure 1, column 1. + (ty::Covariant, ty::Covariant) => ty::Covariant, + (ty::Covariant, ty::Contravariant) => ty::Contravariant, + (ty::Covariant, ty::Invariant) => ty::Invariant, + (ty::Covariant, ty::Bivariant) => ty::Bivariant, + + // Figure 1, column 2. + (ty::Contravariant, ty::Covariant) => ty::Contravariant, + (ty::Contravariant, ty::Contravariant) => ty::Covariant, + (ty::Contravariant, ty::Invariant) => ty::Invariant, + (ty::Contravariant, ty::Bivariant) => ty::Bivariant, + + // Figure 1, column 3. + (ty::Invariant, _) => ty::Invariant, + + // Figure 1, column 4. + (ty::Bivariant, _) => ty::Bivariant, + } + } +} + +// Contains information needed to resolve types and (in the future) look up +// the types of AST nodes. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct CReaderCacheKey { + pub cnum: CrateNum, + pub pos: usize, +} + +bitflags! { + /// Flags that we track on types. These flags are propagated upwards + /// through the type during type construction, so that we can quickly check + /// whether the type has various kinds of types in it without recursing + /// over the type itself. + pub struct TypeFlags: u32 { + // Does this have parameters? Used to determine whether substitution is + // required. + /// Does this have [Param]? + const HAS_TY_PARAM = 1 << 0; + /// Does this have [ReEarlyBound]? + const HAS_RE_PARAM = 1 << 1; + /// Does this have [ConstKind::Param]? + const HAS_CT_PARAM = 1 << 2; + + const NEEDS_SUBST = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_RE_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits; + + /// Does this have [Infer]? + const HAS_TY_INFER = 1 << 3; + /// Does this have [ReVar]? + const HAS_RE_INFER = 1 << 4; + /// Does this have [ConstKind::Infer]? + const HAS_CT_INFER = 1 << 5; + + /// Does this have inference variables? Used to determine whether + /// inference is required. + const NEEDS_INFER = TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_RE_INFER.bits + | TypeFlags::HAS_CT_INFER.bits; + + /// Does this have [Placeholder]? + const HAS_TY_PLACEHOLDER = 1 << 6; + /// Does this have [RePlaceholder]? + const HAS_RE_PLACEHOLDER = 1 << 7; + /// Does this have [ConstKind::Placeholder]? + const HAS_CT_PLACEHOLDER = 1 << 8; + + /// `true` if there are "names" of regions and so forth + /// that are local to a particular fn/inferctxt + const HAS_FREE_LOCAL_REGIONS = 1 << 9; + + /// `true` if there are "names" of types and regions and so forth + /// that are local to a particular fn + const HAS_FREE_LOCAL_NAMES = TypeFlags::HAS_TY_PARAM.bits + | TypeFlags::HAS_CT_PARAM.bits + | TypeFlags::HAS_TY_INFER.bits + | TypeFlags::HAS_CT_INFER.bits + | TypeFlags::HAS_TY_PLACEHOLDER.bits + | TypeFlags::HAS_CT_PLACEHOLDER.bits + | TypeFlags::HAS_FREE_LOCAL_REGIONS.bits; + + /// Does this have [Projection]? + const HAS_TY_PROJECTION = 1 << 10; + /// Does this have [Opaque]? + const HAS_TY_OPAQUE = 1 << 11; + /// Does this have [ConstKind::Unevaluated]? + const HAS_CT_PROJECTION = 1 << 12; + + /// Could this type be normalized further? + const HAS_PROJECTION = TypeFlags::HAS_TY_PROJECTION.bits + | TypeFlags::HAS_TY_OPAQUE.bits + | TypeFlags::HAS_CT_PROJECTION.bits; + + /// Is an error type/const reachable? + const HAS_ERROR = 1 << 13; + + /// Does this have any region that "appears free" in the type? + /// Basically anything but [ReLateBound] and [ReErased]. + const HAS_FREE_REGIONS = 1 << 14; + + /// Does this have any [ReLateBound] regions? Used to check + /// if a global bound is safe to evaluate. + const HAS_RE_LATE_BOUND = 1 << 15; + + /// Does this have any [ReErased] regions? + const HAS_RE_ERASED = 1 << 16; + + /// Does this value have parameters/placeholders/inference variables which could be + /// replaced later, in a way that would change the results of `impl` specialization? + const STILL_FURTHER_SPECIALIZABLE = 1 << 17; + } +} + +#[allow(rustc::usage_of_ty_tykind)] +pub struct TyS<'tcx> { + pub kind: TyKind<'tcx>, + pub flags: TypeFlags, + + /// This is a kind of confusing thing: it stores the smallest + /// binder such that + /// + /// (a) the binder itself captures nothing but + /// (b) all the late-bound things within the type are captured + /// by some sub-binder. + /// + /// So, for a type without any late-bound things, like `u32`, this + /// will be *innermost*, because that is the innermost binder that + /// captures nothing. But for a type `&'D u32`, where `'D` is a + /// late-bound region with De Bruijn index `D`, this would be `D + 1` + /// -- the binder itself does not capture `D`, but `D` is captured + /// by an inner binder. + /// + /// We call this concept an "exclusive" binder `D` because all + /// De Bruijn indices within the type are contained within `0..D` + /// (exclusive). + outer_exclusive_binder: ty::DebruijnIndex, +} + +// `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(target_arch = "x86_64")] +static_assert_size!(TyS<'_>, 32); + +impl<'tcx> Ord for TyS<'tcx> { + fn cmp(&self, other: &TyS<'tcx>) -> Ordering { + self.kind.cmp(&other.kind) + } +} + +impl<'tcx> PartialOrd for TyS<'tcx> { + fn partial_cmp(&self, other: &TyS<'tcx>) -> Option { + Some(self.kind.cmp(&other.kind)) + } +} + +impl<'tcx> PartialEq for TyS<'tcx> { + #[inline] + fn eq(&self, other: &TyS<'tcx>) -> bool { + ptr::eq(self, other) + } +} +impl<'tcx> Eq for TyS<'tcx> {} + +impl<'tcx> Hash for TyS<'tcx> { + fn hash(&self, s: &mut H) { + (self as *const TyS<'_>).hash(s) + } +} + +impl<'a, 'tcx> HashStable> for TyS<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let ty::TyS { + ref kind, + + // The other fields just provide fast access to information that is + // also contained in `kind`, so no need to hash them. + flags: _, + + outer_exclusive_binder: _, + } = *self; + + kind.hash_stable(hcx, hasher); + } +} + +#[rustc_diagnostic_item = "Ty"] +pub type Ty<'tcx> = &'tcx TyS<'tcx>; + +impl<'tcx> rustc_serialize::UseSpecializedEncodable for Ty<'tcx> {} +impl<'tcx> rustc_serialize::UseSpecializedDecodable for Ty<'tcx> {} +impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx List> {} + +pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>; + +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +pub struct UpvarPath { + pub hir_id: hir::HirId, +} + +/// Upvars do not get their own `NodeId`. Instead, we use the pair of +/// the original var ID (that is, the root variable that is referenced +/// by the upvar) and the ID of the closure expression. +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, HashStable)] +pub struct UpvarId { + pub var_path: UpvarPath, + pub closure_expr_id: LocalDefId, +} + +#[derive(Clone, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, HashStable)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + ImmBorrow, + + /// Data must be immutable but not aliasable. This kind of borrow + /// cannot currently be expressed by the user and is used only in + /// implicit closure bindings. It is needed when the closure + /// is borrowing or mutating a mutable referent, e.g.: + /// + /// let x: &mut isize = ...; + /// let y = || *x += 5; + /// + /// If we were to try to translate this closure into a more explicit + /// form, we'd encounter an error with the code as written: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// This is then illegal because you cannot mutate a `&mut` found + /// in an aliasable location. To solve, you'd have to translate with + /// an `&mut` borrow: + /// + /// struct Env { x: & &mut isize } + /// let x: &mut isize = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// Now the assignment to `**env.x` is legal, but creating a + /// mutable pointer to `x` is not because `x` is not mutable. We + /// could fix this by declaring `x` as `let mut x`. This is ok in + /// user code, if awkward, but extra weird for closures, since the + /// borrow is hidden. + /// + /// So we introduce a "unique imm" borrow -- the referent is + /// immutable, but not aliasable. This solves the problem. For + /// simplicity, we don't give users the way to express this + /// borrow, it's just used when translating closures. + UniqueImmBorrow, + + /// Data is mutable and not aliasable. + MutBorrow, +} + +/// Information describing the capture of an upvar. This is computed +/// during `typeck`, specifically by `regionck`. +#[derive(PartialEq, Clone, Debug, Copy, RustcEncodable, RustcDecodable, HashStable)] +pub enum UpvarCapture<'tcx> { + /// Upvar is captured by value. This is always true when the + /// closure is labeled `move`, but can also be true in other cases + /// depending on inference. + ByValue, + + /// Upvar is captured by reference. + ByRef(UpvarBorrow<'tcx>), +} + +#[derive(PartialEq, Clone, Copy, RustcEncodable, RustcDecodable, HashStable)] +pub struct UpvarBorrow<'tcx> { + /// The kind of borrow: by-ref upvars have access to shared + /// immutable borrows, which are not part of the normal language + /// syntax. + pub kind: BorrowKind, + + /// Region of the resulting reference. + pub region: ty::Region<'tcx>, +} + +pub type UpvarListMap = FxHashMap>; +pub type UpvarCaptureMap<'tcx> = FxHashMap>; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum IntVarValue { + IntType(ast::IntTy), + UintType(ast::UintTy), +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct FloatVarValue(pub ast::FloatTy); + +impl ty::EarlyBoundRegion { + pub fn to_bound_region(&self) -> ty::BoundRegion { + ty::BoundRegion::BrNamed(self.def_id, self.name) + } + + /// Does this early bound region have a name? Early bound regions normally + /// always have names except when using anonymous lifetimes (`'_`). + pub fn has_name(&self) -> bool { + self.name != kw::UnderscoreLifetime + } +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub enum GenericParamDefKind { + Lifetime, + Type { + has_default: bool, + object_lifetime_default: ObjectLifetimeDefault, + synthetic: Option, + }, + Const, +} + +impl GenericParamDefKind { + pub fn descr(&self) -> &'static str { + match self { + GenericParamDefKind::Lifetime => "lifetime", + GenericParamDefKind::Type { .. } => "type", + GenericParamDefKind::Const => "constant", + } + } +} + +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct GenericParamDef { + pub name: Symbol, + pub def_id: DefId, + pub index: u32, + + /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute + /// on generic parameter `'a`/`T`, asserts data behind the parameter + /// `'a`/`T` won't be accessed during the parent type's `Drop` impl. + pub pure_wrt_drop: bool, + + pub kind: GenericParamDefKind, +} + +impl GenericParamDef { + pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion { + if let GenericParamDefKind::Lifetime = self.kind { + ty::EarlyBoundRegion { def_id: self.def_id, index: self.index, name: self.name } + } else { + bug!("cannot convert a non-lifetime parameter def to an early bound region") + } + } + + pub fn to_bound_region(&self) -> ty::BoundRegion { + if let GenericParamDefKind::Lifetime = self.kind { + self.to_early_bound_region_data().to_bound_region() + } else { + bug!("cannot convert a non-lifetime parameter def to an early bound region") + } + } +} + +#[derive(Default)] +pub struct GenericParamCount { + pub lifetimes: usize, + pub types: usize, + pub consts: usize, +} + +/// Information about the formal type/lifetime parameters associated +/// with an item or method. Analogous to `hir::Generics`. +/// +/// The ordering of parameters is the same as in `Subst` (excluding child generics): +/// `Self` (optionally), `Lifetime` params..., `Type` params... +#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +pub struct Generics { + pub parent: Option, + pub parent_count: usize, + pub params: Vec, + + /// Reverse map to the `index` field of each `GenericParamDef`. + #[stable_hasher(ignore)] + pub param_def_id_to_index: FxHashMap, + + pub has_self: bool, + pub has_late_bound_regions: Option, +} + +impl<'tcx> Generics { + pub fn count(&self) -> usize { + self.parent_count + self.params.len() + } + + pub fn own_counts(&self) -> GenericParamCount { + // We could cache this as a property of `GenericParamCount`, but + // the aim is to refactor this away entirely eventually and the + // presence of this method will be a constant reminder. + let mut own_counts: GenericParamCount = Default::default(); + + for param in &self.params { + match param.kind { + GenericParamDefKind::Lifetime => own_counts.lifetimes += 1, + GenericParamDefKind::Type { .. } => own_counts.types += 1, + GenericParamDefKind::Const => own_counts.consts += 1, + }; + } + + own_counts + } + + pub fn requires_monomorphization(&self, tcx: TyCtxt<'tcx>) -> bool { + if self.own_requires_monomorphization() { + return true; + } + + if let Some(parent_def_id) = self.parent { + let parent = tcx.generics_of(parent_def_id); + parent.requires_monomorphization(tcx) + } else { + false + } + } + + pub fn own_requires_monomorphization(&self) -> bool { + for param in &self.params { + match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => return true, + GenericParamDefKind::Lifetime => {} + } + } + false + } + + 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(Debug)] +crate struct PredicateInner<'tcx> { + kind: PredicateKind<'tcx>, + flags: TypeFlags, + /// See the comment for the corresponding field of [TyS]. + outer_exclusive_binder: ty::DebruijnIndex, +} + +#[cfg(target_arch = "x86_64")] +static_assert_size!(PredicateInner<'_>, 40); + +#[derive(Clone, Copy, Lift)] +pub struct Predicate<'tcx> { + inner: &'tcx PredicateInner<'tcx>, +} + +impl rustc_serialize::UseSpecializedEncodable for Predicate<'_> {} +impl rustc_serialize::UseSpecializedDecodable for Predicate<'_> {} + +impl<'tcx> PartialEq for Predicate<'tcx> { + fn eq(&self, other: &Self) -> bool { + // `self.kind` is always interned. + ptr::eq(self.inner, other.inner) + } +} + +impl Hash for Predicate<'_> { + fn hash(&self, s: &mut H) { + (self.inner as *const PredicateInner<'_>).hash(s) + } +} + +impl<'tcx> Eq for Predicate<'tcx> {} + +impl<'tcx> Predicate<'tcx> { + #[inline(always)] + pub fn kind(self) -> &'tcx PredicateKind<'tcx> { + &self.inner.kind + } +} + +impl<'a, 'tcx> HashStable> for Predicate<'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let PredicateInner { + ref kind, + + // The other fields just provide fast access to information that is + // also contained in `kind`, so no need to hash them. + flags: _, + outer_exclusive_binder: _, + } = self.inner; + + kind.hash_stable(hcx, hasher); + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub enum PredicateKind<'tcx> { + /// Corresponds to `where Foo: Bar`. `Foo` here would be + /// the `Self` type of the trait reference and `A`, `B`, and `C` + /// would be the type parameters. + /// + /// A trait predicate will have `Constness::Const` if it originates + /// from a bound on a `const fn` without the `?const` opt-out (e.g., + /// `const fn foobar() {}`). + Trait(PolyTraitPredicate<'tcx>, Constness), + + /// `where 'a: 'b` + RegionOutlives(PolyRegionOutlivesPredicate<'tcx>), + + /// `where T: 'a` + TypeOutlives(PolyTypeOutlivesPredicate<'tcx>), + + /// `where ::Name == X`, approximately. + /// See the `ProjectionPredicate` struct for details. + Projection(PolyProjectionPredicate<'tcx>), + + /// No syntax: `T` well-formed. + WellFormed(GenericArg<'tcx>), + + /// Trait must be object-safe. + ObjectSafe(DefId), + + /// No direct syntax. May be thought of as `where T: FnFoo<...>` + /// for some substitutions `...` and `T` being a closure type. + /// Satisfied (or refuted) once we know the closure's kind. + ClosureKind(DefId, SubstsRef<'tcx>, ClosureKind), + + /// `T1 <: T2` + Subtype(PolySubtypePredicate<'tcx>), + + /// Constant initializer must evaluate successfully. + ConstEvaluatable(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> Predicate<'tcx> { + /// Performs a substitution suitable for going from a + /// poly-trait-ref to supertraits that must hold if that + /// poly-trait-ref holds. This is slightly different from a normal + /// substitution in terms of what happens with bound regions. See + /// lengthy comment below for details. + pub fn subst_supertrait( + self, + tcx: TyCtxt<'tcx>, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) -> ty::Predicate<'tcx> { + // The interaction between HRTB and supertraits is not entirely + // obvious. Let me walk you (and myself) through an example. + // + // Let's start with an easy case. Consider two traits: + // + // trait Foo<'a>: Bar<'a,'a> { } + // trait Bar<'b,'c> { } + // + // Now, if we have a trait reference `for<'x> T: Foo<'x>`, then + // we can deduce that `for<'x> T: Bar<'x,'x>`. Basically, if we + // knew that `Foo<'x>` (for any 'x) then we also know that + // `Bar<'x,'x>` (for any 'x). This more-or-less falls out from + // normal substitution. + // + // In terms of why this is sound, the idea is that whenever there + // is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>` + // holds. So if there is an impl of `T:Foo<'a>` that applies to + // all `'a`, then we must know that `T:Bar<'a,'a>` holds for all + // `'a`. + // + // Another example to be careful of is this: + // + // trait Foo1<'a>: for<'b> Bar1<'a,'b> { } + // trait Bar1<'b,'c> { } + // + // Here, if we have `for<'x> T: Foo1<'x>`, then what do we know? + // The answer is that we know `for<'x,'b> T: Bar1<'x,'b>`. The + // reason is similar to the previous example: any impl of + // `T:Foo1<'x>` must show that `for<'b> T: Bar1<'x, 'b>`. So + // basically we would want to collapse the bound lifetimes from + // the input (`trait_ref`) and the supertraits. + // + // To achieve this in practice is fairly straightforward. Let's + // consider the more complicated scenario: + // + // - We start out with `for<'x> T: Foo1<'x>`. In this case, `'x` + // has a De Bruijn index of 1. We want to produce `for<'x,'b> T: Bar1<'x,'b>`, + // where both `'x` and `'b` would have a DB index of 1. + // The substitution from the input trait-ref is therefore going to be + // `'a => 'x` (where `'x` has a DB index of 1). + // - The super-trait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an + // early-bound parameter and `'b' is a late-bound parameter with a + // DB index of 1. + // - If we replace `'a` with `'x` from the input, it too will have + // a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>` + // just as we wanted. + // + // There is only one catch. If we just apply the substitution `'a + // => 'x` to `for<'b> Bar1<'a,'b>`, the substitution code will + // adjust the DB index because we substituting into a binder (it + // tries to be so smart...) resulting in `for<'x> for<'b> + // Bar1<'x,'b>` (we have no syntax for this, so use your + // imagination). Basically the 'x will have DB index of 2 and 'b + // will have DB index of 1. Not quite what we want. So we apply + // the substitution to the *contents* of the trait reference, + // rather than the trait reference itself (put another way, the + // substitution code expects equal binding levels in the values + // from the substitution and the value being substituted into, and + // this trick achieves that). + + let substs = &trait_ref.skip_binder().substs; + let kind = self.kind(); + let new = match kind { + &PredicateKind::Trait(ref binder, constness) => { + PredicateKind::Trait(binder.map_bound(|data| data.subst(tcx, substs)), constness) + } + PredicateKind::Subtype(binder) => { + PredicateKind::Subtype(binder.map_bound(|data| data.subst(tcx, substs))) + } + PredicateKind::RegionOutlives(binder) => { + PredicateKind::RegionOutlives(binder.map_bound(|data| data.subst(tcx, substs))) + } + PredicateKind::TypeOutlives(binder) => { + PredicateKind::TypeOutlives(binder.map_bound(|data| data.subst(tcx, substs))) + } + PredicateKind::Projection(binder) => { + PredicateKind::Projection(binder.map_bound(|data| data.subst(tcx, substs))) + } + &PredicateKind::WellFormed(data) => PredicateKind::WellFormed(data.subst(tcx, substs)), + &PredicateKind::ObjectSafe(trait_def_id) => PredicateKind::ObjectSafe(trait_def_id), + &PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { + PredicateKind::ClosureKind(closure_def_id, closure_substs.subst(tcx, substs), kind) + } + &PredicateKind::ConstEvaluatable(def_id, const_substs) => { + PredicateKind::ConstEvaluatable(def_id, const_substs.subst(tcx, substs)) + } + PredicateKind::ConstEquate(c1, c2) => { + PredicateKind::ConstEquate(c1.subst(tcx, substs), c2.subst(tcx, substs)) + } + }; + + if new != *kind { new.to_predicate(tcx) } else { self } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct TraitPredicate<'tcx> { + pub trait_ref: TraitRef<'tcx>, +} + +pub type PolyTraitPredicate<'tcx> = ty::Binder>; + +impl<'tcx> TraitPredicate<'tcx> { + pub fn def_id(self) -> DefId { + self.trait_ref.def_id + } + + pub fn self_ty(self) -> Ty<'tcx> { + self.trait_ref.self_ty() + } +} + +impl<'tcx> PolyTraitPredicate<'tcx> { + pub fn def_id(self) -> DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().def_id() + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct OutlivesPredicate(pub A, pub B); // `A: B` +pub type PolyOutlivesPredicate = ty::Binder>; +pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; +pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; +pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder>; +pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder>; + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct SubtypePredicate<'tcx> { + pub a_is_expected: bool, + pub a: Ty<'tcx>, + pub b: Ty<'tcx>, +} +pub type PolySubtypePredicate<'tcx> = ty::Binder>; + +/// This kind of predicate has no *direct* correspondent in the +/// syntax, but it roughly corresponds to the syntactic forms: +/// +/// 1. `T: TraitRef<..., Item = Type>` +/// 2. `>::Item == Type` (NYI) +/// +/// In particular, form #1 is "desugared" to the combination of a +/// normal trait predicate (`T: TraitRef<...>`) and one of these +/// predicates. Form #2 is a broader form in that it also permits +/// equality between arbitrary types. Processing an instance of +/// Form #2 eventually yields one of these `ProjectionPredicate` +/// instances to normalize the LHS. +#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable)] +pub struct ProjectionPredicate<'tcx> { + pub projection_ty: ProjectionTy<'tcx>, + pub ty: Ty<'tcx>, +} + +pub type PolyProjectionPredicate<'tcx> = Binder>; + +impl<'tcx> PolyProjectionPredicate<'tcx> { + /// Returns the `DefId` of the associated item being projected. + pub fn item_def_id(&self) -> DefId { + self.skip_binder().projection_ty.item_def_id + } + + #[inline] + pub fn to_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> { + // Note: unlike with `TraitRef::to_poly_trait_ref()`, + // `self.0.trait_ref` is permitted to have escaping regions. + // This is because here `self` has a `Binder` and so does our + // return value, so we are preserving the number of binding + // levels. + self.map_bound(|predicate| predicate.projection_ty.trait_ref(tcx)) + } + + pub fn ty(&self) -> Binder> { + self.map_bound(|predicate| predicate.ty) + } + + /// The `DefId` of the `TraitItem` for the associated type. + /// + /// Note that this is not the `DefId` of the `TraitRef` containing this + /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. + pub fn projection_def_id(&self) -> DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().projection_ty.item_def_id + } +} + +pub trait ToPolyTraitRef<'tcx> { + fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; +} + +impl<'tcx> ToPolyTraitRef<'tcx> for TraitRef<'tcx> { + fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { + ty::Binder::dummy(*self) + } +} + +impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> { + fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { + self.map_bound_ref(|trait_pred| trait_pred.trait_ref) + } +} + +pub trait ToPredicate<'tcx> { + fn to_predicate(&self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx>; +} + +impl ToPredicate<'tcx> for PredicateKind<'tcx> { + #[inline(always)] + fn to_predicate(&self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + tcx.mk_predicate(*self) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { + fn to_predicate(&self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::PredicateKind::Trait( + ty::Binder::dummy(ty::TraitPredicate { trait_ref: self.value }), + self.constness, + ) + .to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&TraitRef<'tcx>> { + fn to_predicate(&self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::PredicateKind::Trait( + ty::Binder::dummy(ty::TraitPredicate { trait_ref: *self.value }), + self.constness, + ) + .to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { + fn to_predicate(&self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::PredicateKind::Trait(self.value.to_poly_trait_predicate(), self.constness) + .to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&PolyTraitRef<'tcx>> { + fn to_predicate(&self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::PredicateKind::Trait(self.value.to_poly_trait_predicate(), self.constness) + .to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> { + fn to_predicate(&self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + PredicateKind::RegionOutlives(*self).to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { + fn to_predicate(&self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + PredicateKind::TypeOutlives(*self).to_predicate(tcx) + } +} + +impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { + fn to_predicate(&self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + PredicateKind::Projection(*self).to_predicate(tcx) + } +} + +impl<'tcx> Predicate<'tcx> { + pub fn to_opt_poly_trait_ref(self) -> Option> { + match self.kind() { + &PredicateKind::Trait(ref t, _) => Some(t.to_poly_trait_ref()), + PredicateKind::Projection(..) + | PredicateKind::Subtype(..) + | PredicateKind::RegionOutlives(..) + | PredicateKind::WellFormed(..) + | PredicateKind::ObjectSafe(..) + | PredicateKind::ClosureKind(..) + | PredicateKind::TypeOutlives(..) + | PredicateKind::ConstEvaluatable(..) + | PredicateKind::ConstEquate(..) => None, + } + } + + pub fn to_opt_type_outlives(self) -> Option> { + match self.kind() { + &PredicateKind::TypeOutlives(data) => Some(data), + PredicateKind::Trait(..) + | PredicateKind::Projection(..) + | PredicateKind::Subtype(..) + | PredicateKind::RegionOutlives(..) + | PredicateKind::WellFormed(..) + | PredicateKind::ObjectSafe(..) + | PredicateKind::ClosureKind(..) + | PredicateKind::ConstEvaluatable(..) + | PredicateKind::ConstEquate(..) => None, + } + } +} + +/// Represents the bounds declared on a particular set of type +/// parameters. Should eventually be generalized into a flag list of +/// where-clauses. You can obtain a `InstantiatedPredicates` list from a +/// `GenericPredicates` by using the `instantiate` method. Note that this method +/// reflects an important semantic invariant of `InstantiatedPredicates`: while +/// the `GenericPredicates` are expressed in terms of the bound type +/// parameters of the impl/trait/whatever, an `InstantiatedPredicates` instance +/// represented a set of bounds for some particular instantiation, +/// meaning that the generic parameters have been substituted with +/// their values. +/// +/// Example: +/// +/// struct Foo> { ... } +/// +/// Here, the `GenericPredicates` for `Foo` would contain a list of bounds like +/// `[[], [U:Bar]]`. Now if there were some particular reference +/// like `Foo`, then the `InstantiatedPredicates` would be `[[], +/// [usize:Bar]]`. +#[derive(Clone, Debug, TypeFoldable)] +pub struct InstantiatedPredicates<'tcx> { + pub predicates: Vec>, + pub spans: Vec, +} + +impl<'tcx> InstantiatedPredicates<'tcx> { + pub fn empty() -> InstantiatedPredicates<'tcx> { + InstantiatedPredicates { predicates: vec![], spans: vec![] } + } + + pub fn is_empty(&self) -> bool { + self.predicates.is_empty() + } +} + +rustc_index::newtype_index! { + /// "Universes" are used during type- and trait-checking in the + /// presence of `for<..>` binders to control what sets of names are + /// visible. Universes are arranged into a tree: the root universe + /// contains names that are always visible. Each child then adds a new + /// set of names that are visible, in addition to those of its parent. + /// We say that the child universe "extends" the parent universe with + /// new names. + /// + /// To make this more concrete, consider this program: + /// + /// ``` + /// struct Foo { } + /// fn bar(x: T) { + /// let y: for<'a> fn(&'a u8, Foo) = ...; + /// } + /// ``` + /// + /// The struct name `Foo` is in the root universe U0. But the type + /// parameter `T`, introduced on `bar`, is in an extended universe U1 + /// -- i.e., within `bar`, we can name both `T` and `Foo`, but outside + /// of `bar`, we cannot name `T`. Then, within the type of `y`, the + /// region `'a` is in a universe U2 that extends U1, because we can + /// name it inside the fn type but not outside. + /// + /// Universes are used to do type- and trait-checking around these + /// "forall" binders (also called **universal quantification**). The + /// idea is that when, in the body of `bar`, we refer to `T` as a + /// type, we aren't referring to any type in particular, but rather a + /// kind of "fresh" type that is distinct from all other types we have + /// actually declared. This is called a **placeholder** type, and we + /// use universes to talk about this. In other words, a type name in + /// universe 0 always corresponds to some "ground" type that the user + /// declared, but a type name in a non-zero universe is a placeholder + /// type -- an idealized representative of "types in general" that we + /// use for checking generic functions. + pub struct UniverseIndex { + derive [HashStable] + DEBUG_FORMAT = "U{}", + } +} + +impl UniverseIndex { + pub const ROOT: UniverseIndex = UniverseIndex::from_u32(0); + + /// Returns the "next" universe index in order -- this new index + /// is considered to extend all previous universes. This + /// corresponds to entering a `forall` quantifier. So, for + /// example, suppose we have this type in universe `U`: + /// + /// ``` + /// for<'a> fn(&'a u32) + /// ``` + /// + /// Once we "enter" into this `for<'a>` quantifier, we are in a + /// new universe that extends `U` -- in this new universe, we can + /// name the region `'a`, but that region was not nameable from + /// `U` because it was not in scope there. + pub fn next_universe(self) -> UniverseIndex { + UniverseIndex::from_u32(self.private.checked_add(1).unwrap()) + } + + /// Returns `true` if `self` can name a name from `other` -- in other words, + /// if the set of names in `self` is a superset of those in + /// `other` (`self >= other`). + pub fn can_name(self, other: UniverseIndex) -> bool { + self.private >= other.private + } + + /// Returns `true` if `self` cannot name some names from `other` -- in other + /// words, if the set of names in `self` is a strict subset of + /// those in `other` (`self < other`). + pub fn cannot_name(self, other: UniverseIndex) -> bool { + self.private < other.private + } +} + +/// The "placeholder index" fully defines a placeholder region. +/// Placeholder regions are identified by both a **universe** as well +/// as a "bound-region" within that universe. The `bound_region` is +/// basically a name -- distinct bound regions within the same +/// universe are just two regions with an unknown relationship to one +/// another. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, PartialOrd, Ord)] +pub struct Placeholder { + pub universe: UniverseIndex, + pub name: T, +} + +impl<'a, T> HashStable> for Placeholder +where + T: HashStable>, +{ + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + self.universe.hash_stable(hcx, hasher); + self.name.hash_stable(hcx, hasher); + } +} + +pub type PlaceholderRegion = Placeholder; + +pub type PlaceholderType = Placeholder; + +pub type PlaceholderConst = Placeholder; + +/// 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(tcx)` to ensure that +// the constness of trait bounds is being propagated correctly. +pub trait WithConstness: Sized { + #[inline] + fn with_constness(self, constness: Constness) -> ConstnessAnd { + ConstnessAnd { constness, value: self } + } + + #[inline] + fn with_const(self) -> ConstnessAnd { + self.with_constness(Constness::Const) + } + + #[inline] + fn without_const(self) -> ConstnessAnd { + self.with_constness(Constness::NotConst) + } +} + +impl WithConstness for T {} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable)] +pub struct ParamEnvAnd<'tcx, T> { + pub param_env: ParamEnv<'tcx>, + pub value: T, +} + +impl<'tcx, T> ParamEnvAnd<'tcx, T> { + pub fn into_parts(self) -> (ParamEnv<'tcx>, T) { + (self.param_env, self.value) + } +} + +impl<'a, 'tcx, T> HashStable> for ParamEnvAnd<'tcx, T> +where + T: HashStable>, +{ + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + let ParamEnvAnd { ref param_env, ref value } = *self; + + param_env.hash_stable(hcx, hasher); + value.hash_stable(hcx, hasher); + } +} + +#[derive(Copy, Clone, Debug, HashStable)] +pub struct Destructor { + /// The `DefId` of the destructor method + pub did: DefId, +} + +bitflags! { + #[derive(HashStable)] + pub struct AdtFlags: u32 { + const NO_ADT_FLAGS = 0; + /// Indicates whether the ADT is an enum. + const IS_ENUM = 1 << 0; + /// Indicates whether the ADT is a union. + const IS_UNION = 1 << 1; + /// Indicates whether the ADT is a struct. + const IS_STRUCT = 1 << 2; + /// Indicates whether the ADT is a struct and has a constructor. + const HAS_CTOR = 1 << 3; + /// Indicates whether the type is `PhantomData`. + const IS_PHANTOM_DATA = 1 << 4; + /// Indicates whether the type has a `#[fundamental]` attribute. + const IS_FUNDAMENTAL = 1 << 5; + /// Indicates whether the type is `Box`. + const IS_BOX = 1 << 6; + /// Indicates whether the type is `ManuallyDrop`. + const IS_MANUALLY_DROP = 1 << 7; + /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`. + /// (i.e., this flag is never set unless this ADT is an enum). + const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8; + } +} + +bitflags! { + #[derive(HashStable)] + pub struct VariantFlags: u32 { + const NO_VARIANT_FLAGS = 0; + /// Indicates whether the field list of this variant is `#[non_exhaustive]`. + const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0; + } +} + +/// Definition of a variant -- a struct's fields or a enum variant. +#[derive(Debug, HashStable)] +pub struct VariantDef { + /// `DefId` that identifies the variant itself. + /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. + pub def_id: DefId, + /// `DefId` that identifies the variant's constructor. + /// If this variant is a struct variant, then this is `None`. + pub ctor_def_id: Option, + /// Variant or struct name. + #[stable_hasher(project(name))] + pub ident: Ident, + /// Discriminant of this variant. + pub discr: VariantDiscr, + /// Fields of this variant. + pub fields: Vec, + /// Type of constructor of variant. + pub ctor_kind: CtorKind, + /// Flags of the variant (e.g. is field list non-exhaustive)? + flags: VariantFlags, + /// Variant is obtained as part of recovering from a syntactic error. + /// May be incomplete or bogus. + pub recovered: bool, +} + +impl<'tcx> VariantDef { + /// Creates a new `VariantDef`. + /// + /// `variant_did` is the `DefId` that identifies the enum variant (if this `VariantDef` + /// represents an enum variant). + /// + /// `ctor_did` is the `DefId` that identifies the constructor of unit or + /// tuple-variants/structs. If this is a `struct`-variant then this should be `None`. + /// + /// `parent_did` is the `DefId` of the `AdtDef` representing the enum or struct that + /// owns this variant. It is used for checking if a struct has `#[non_exhaustive]` w/out having + /// to go through the redirect of checking the ctor's attributes - but compiling a small crate + /// requires loading the `AdtDef`s for all the structs in the universe (e.g., coherence for any + /// built-in trait), and we do not want to load attributes twice. + /// + /// If someone speeds up attribute loading to not be a performance concern, they can + /// remove this hack and use the constructor `DefId` everywhere. + pub fn new( + tcx: TyCtxt<'tcx>, + ident: Ident, + variant_did: Option, + ctor_def_id: Option, + discr: VariantDiscr, + fields: Vec, + ctor_kind: CtorKind, + adt_kind: AdtKind, + parent_did: DefId, + recovered: bool, + ) -> Self { + debug!( + "VariantDef::new(ident = {:?}, variant_did = {:?}, ctor_def_id = {:?}, discr = {:?}, + fields = {:?}, ctor_kind = {:?}, adt_kind = {:?}, parent_did = {:?})", + ident, variant_did, ctor_def_id, discr, fields, ctor_kind, adt_kind, parent_did, + ); + + let mut flags = VariantFlags::NO_VARIANT_FLAGS; + if adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, sym::non_exhaustive) { + debug!("found non-exhaustive field list for {:?}", parent_did); + flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; + } else if let Some(variant_did) = variant_did { + if tcx.has_attr(variant_did, sym::non_exhaustive) { + debug!("found non-exhaustive field list for {:?}", variant_did); + flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE; + } + } + + VariantDef { + def_id: variant_did.unwrap_or(parent_did), + ctor_def_id, + ident, + discr, + fields, + ctor_kind, + flags, + recovered, + } + } + + /// Is this field list non-exhaustive? + #[inline] + pub fn is_field_list_non_exhaustive(&self) -> bool { + self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE) + } + + /// `repr(transparent)` structs can have a single non-ZST field, this function returns that + /// field. + pub fn transparent_newtype_field(&self, tcx: TyCtxt<'tcx>) -> Option<&FieldDef> { + for field in &self.fields { + let field_ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, self.def_id)); + if !field_ty.is_zst(tcx, self.def_id) { + return Some(field); + } + } + + None + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)] +pub enum VariantDiscr { + /// Explicit value for this variant, i.e., `X = 123`. + /// The `DefId` corresponds to the embedded constant. + Explicit(DefId), + + /// The previous variant's discriminant plus one. + /// For efficiency reasons, the distance from the + /// last `Explicit` discriminant is being stored, + /// or `0` for the first variant, if it has none. + Relative(u32), +} + +#[derive(Debug, HashStable)] +pub struct FieldDef { + pub did: DefId, + #[stable_hasher(project(name))] + pub ident: Ident, + pub vis: Visibility, +} + +/// The definition of a user-defined type, e.g., a `struct`, `enum`, or `union`. +/// +/// These are all interned (by `alloc_adt_def`) into the global arena. +/// +/// The initialism *ADT* stands for an [*algebraic data type (ADT)*][adt]. +/// This is slightly wrong because `union`s are not ADTs. +/// Moreover, Rust only allows recursive data types through indirection. +/// +/// [adt]: https://en.wikipedia.org/wiki/Algebraic_data_type +pub struct AdtDef { + /// The `DefId` of the struct, enum or union item. + pub did: DefId, + /// Variants of the ADT. If this is a struct or union, then there will be a single variant. + pub variants: IndexVec, + /// Flags of the ADT (e.g., is this a struct? is this non-exhaustive?). + flags: AdtFlags, + /// Repr options provided by the user. + pub repr: ReprOptions, +} + +impl PartialOrd for AdtDef { + fn partial_cmp(&self, other: &AdtDef) -> Option { + Some(self.cmp(&other)) + } +} + +/// There should be only one AdtDef for each `did`, therefore +/// it is fine to implement `Ord` only based on `did`. +impl Ord for AdtDef { + fn cmp(&self, other: &AdtDef) -> Ordering { + self.did.cmp(&other.did) + } +} + +impl PartialEq for AdtDef { + // `AdtDef`s are always interned, and this is part of `TyS` equality. + #[inline] + fn eq(&self, other: &Self) -> bool { + ptr::eq(self, other) + } +} + +impl Eq for AdtDef {} + +impl Hash for AdtDef { + #[inline] + fn hash(&self, s: &mut H) { + (self as *const AdtDef).hash(s) + } +} + +impl<'tcx> rustc_serialize::UseSpecializedEncodable for &'tcx AdtDef { + fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { + self.did.encode(s) + } +} + +impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx AdtDef {} + +impl<'a> HashStable> for AdtDef { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + thread_local! { + static CACHE: RefCell> = Default::default(); + } + + let hash: Fingerprint = CACHE.with(|cache| { + let addr = self as *const AdtDef as usize; + *cache.borrow_mut().entry(addr).or_insert_with(|| { + let ty::AdtDef { did, ref variants, ref flags, ref repr } = *self; + + let mut hasher = StableHasher::new(); + did.hash_stable(hcx, &mut hasher); + variants.hash_stable(hcx, &mut hasher); + flags.hash_stable(hcx, &mut hasher); + repr.hash_stable(hcx, &mut hasher); + + hasher.finish() + }) + }); + + hash.hash_stable(hcx, hasher); + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum AdtKind { + Struct, + Union, + Enum, +} + +impl Into for AdtKind { + fn into(self) -> DataTypeKind { + match self { + AdtKind::Struct => DataTypeKind::Struct, + AdtKind::Union => DataTypeKind::Union, + AdtKind::Enum => DataTypeKind::Enum, + } + } +} + +bitflags! { + #[derive(RustcEncodable, RustcDecodable, Default, HashStable)] + pub struct ReprFlags: u8 { + const IS_C = 1 << 0; + const IS_SIMD = 1 << 1; + const IS_TRANSPARENT = 1 << 2; + // Internal only for now. If true, don't reorder fields. + const IS_LINEAR = 1 << 3; + // If true, don't expose any niche to type's context. + const HIDE_NICHE = 1 << 4; + // Any of these flags being set prevent field reordering optimisation. + const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits | + ReprFlags::IS_SIMD.bits | + ReprFlags::IS_LINEAR.bits; + } +} + +/// Represents the repr options provided by the user, +#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Default, HashStable)] +pub struct ReprOptions { + pub int: Option, + pub align: Option, + pub pack: Option, + pub flags: ReprFlags, +} + +impl ReprOptions { + pub fn new(tcx: TyCtxt<'_>, did: DefId) -> ReprOptions { + let mut flags = ReprFlags::empty(); + let mut size = None; + let mut max_align: Option = None; + let mut min_pack: Option = None; + for attr in tcx.get_attrs(did).iter() { + for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) { + flags.insert(match r { + attr::ReprC => ReprFlags::IS_C, + attr::ReprPacked(pack) => { + let pack = Align::from_bytes(pack as u64).unwrap(); + min_pack = Some(if let Some(min_pack) = min_pack { + min_pack.min(pack) + } else { + pack + }); + ReprFlags::empty() + } + attr::ReprTransparent => ReprFlags::IS_TRANSPARENT, + attr::ReprNoNiche => ReprFlags::HIDE_NICHE, + attr::ReprSimd => ReprFlags::IS_SIMD, + attr::ReprInt(i) => { + size = Some(i); + ReprFlags::empty() + } + attr::ReprAlign(align) => { + max_align = max_align.max(Some(Align::from_bytes(align as u64).unwrap())); + ReprFlags::empty() + } + }); + } + } + + // This is here instead of layout because the choice must make it into metadata. + if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.def_path_str(did))) { + flags.insert(ReprFlags::IS_LINEAR); + } + ReprOptions { int: size, align: max_align, pack: min_pack, flags } + } + + #[inline] + pub fn simd(&self) -> bool { + self.flags.contains(ReprFlags::IS_SIMD) + } + #[inline] + pub fn c(&self) -> bool { + self.flags.contains(ReprFlags::IS_C) + } + #[inline] + pub fn packed(&self) -> bool { + self.pack.is_some() + } + #[inline] + pub fn transparent(&self) -> bool { + self.flags.contains(ReprFlags::IS_TRANSPARENT) + } + #[inline] + pub fn linear(&self) -> bool { + self.flags.contains(ReprFlags::IS_LINEAR) + } + #[inline] + pub fn hide_niche(&self) -> bool { + self.flags.contains(ReprFlags::HIDE_NICHE) + } + + /// Returns the discriminant type, given these `repr` options. + /// This must only be called on enums! + pub fn discr_type(&self) -> attr::IntType { + self.int.unwrap_or(attr::SignedInt(ast::IntTy::Isize)) + } + + /// Returns `true` if this `#[repr()]` should inhabit "smart enum + /// layout" optimizations, such as representing `Foo<&T>` as a + /// single pointer. + pub fn inhibit_enum_layout_opt(&self) -> bool { + self.c() || self.int.is_some() + } + + /// Returns `true` if this `#[repr()]` should inhibit struct field reordering + /// optimizations, such as with `repr(C)`, `repr(packed(1))`, or `repr()`. + pub fn inhibit_struct_field_reordering_opt(&self) -> bool { + if let Some(pack) = self.pack { + if pack.bytes() == 1 { + return true; + } + } + self.flags.intersects(ReprFlags::IS_UNOPTIMISABLE) || self.int.is_some() + } + + /// Returns `true` if this `#[repr()]` should inhibit union ABI optimisations. + pub fn inhibit_union_abi_opt(&self) -> bool { + self.c() + } +} + +impl<'tcx> AdtDef { + /// Creates a new `AdtDef`. + fn new( + tcx: TyCtxt<'_>, + did: DefId, + kind: AdtKind, + variants: IndexVec, + repr: ReprOptions, + ) -> Self { + debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); + let mut flags = AdtFlags::NO_ADT_FLAGS; + + if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) { + debug!("found non-exhaustive variant list for {:?}", did); + flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; + } + + flags |= match kind { + AdtKind::Enum => AdtFlags::IS_ENUM, + AdtKind::Union => AdtFlags::IS_UNION, + AdtKind::Struct => AdtFlags::IS_STRUCT, + }; + + if kind == AdtKind::Struct && variants[VariantIdx::new(0)].ctor_def_id.is_some() { + flags |= AdtFlags::HAS_CTOR; + } + + let attrs = tcx.get_attrs(did); + if attr::contains_name(&attrs, sym::fundamental) { + flags |= AdtFlags::IS_FUNDAMENTAL; + } + if Some(did) == tcx.lang_items().phantom_data() { + flags |= AdtFlags::IS_PHANTOM_DATA; + } + if Some(did) == tcx.lang_items().owned_box() { + flags |= AdtFlags::IS_BOX; + } + if Some(did) == tcx.lang_items().manually_drop() { + flags |= AdtFlags::IS_MANUALLY_DROP; + } + + AdtDef { did, variants, flags, repr } + } + + /// Returns `true` if this is a struct. + #[inline] + pub fn is_struct(&self) -> bool { + self.flags.contains(AdtFlags::IS_STRUCT) + } + + /// Returns `true` if this is a union. + #[inline] + pub fn is_union(&self) -> bool { + self.flags.contains(AdtFlags::IS_UNION) + } + + /// Returns `true` if this is a enum. + #[inline] + pub fn is_enum(&self) -> bool { + self.flags.contains(AdtFlags::IS_ENUM) + } + + /// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`. + #[inline] + pub fn is_variant_list_non_exhaustive(&self) -> bool { + self.flags.contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE) + } + + /// Returns the kind of the ADT. + #[inline] + pub fn adt_kind(&self) -> AdtKind { + if self.is_enum() { + AdtKind::Enum + } else if self.is_union() { + AdtKind::Union + } else { + AdtKind::Struct + } + } + + /// Returns a description of this abstract data type. + pub fn descr(&self) -> &'static str { + match self.adt_kind() { + AdtKind::Struct => "struct", + AdtKind::Union => "union", + AdtKind::Enum => "enum", + } + } + + /// Returns a description of a variant of this abstract data type. + #[inline] + pub fn variant_descr(&self) -> &'static str { + match self.adt_kind() { + AdtKind::Struct => "struct", + AdtKind::Union => "union", + AdtKind::Enum => "variant", + } + } + + /// If this function returns `true`, it implies that `is_struct` must return `true`. + #[inline] + pub fn has_ctor(&self) -> bool { + self.flags.contains(AdtFlags::HAS_CTOR) + } + + /// Returns `true` if this type is `#[fundamental]` for the purposes + /// of coherence checking. + #[inline] + pub fn is_fundamental(&self) -> bool { + self.flags.contains(AdtFlags::IS_FUNDAMENTAL) + } + + /// Returns `true` if this is `PhantomData`. + #[inline] + pub fn is_phantom_data(&self) -> bool { + self.flags.contains(AdtFlags::IS_PHANTOM_DATA) + } + + /// Returns `true` if this is Box. + #[inline] + pub fn is_box(&self) -> bool { + self.flags.contains(AdtFlags::IS_BOX) + } + + /// Returns `true` if this is `ManuallyDrop`. + #[inline] + pub fn is_manually_drop(&self) -> bool { + self.flags.contains(AdtFlags::IS_MANUALLY_DROP) + } + + /// Returns `true` if this type has a destructor. + pub fn has_dtor(&self, tcx: TyCtxt<'tcx>) -> bool { + self.destructor(tcx).is_some() + } + + /// Asserts this is a struct or union and returns its unique variant. + pub fn non_enum_variant(&self) -> &VariantDef { + assert!(self.is_struct() || self.is_union()); + &self.variants[VariantIdx::new(0)] + } + + #[inline] + pub fn predicates(&self, tcx: TyCtxt<'tcx>) -> GenericPredicates<'tcx> { + tcx.predicates_of(self.did) + } + + /// Returns an iterator over all fields contained + /// by this ADT. + #[inline] + pub fn all_fields(&self) -> impl Iterator + Clone { + self.variants.iter().flat_map(|v| v.fields.iter()) + } + + pub fn is_payloadfree(&self) -> bool { + !self.variants.is_empty() && self.variants.iter().all(|v| v.fields.is_empty()) + } + + /// Return a `VariantDef` given a variant id. + pub fn variant_with_id(&self, vid: DefId) -> &VariantDef { + self.variants.iter().find(|v| v.def_id == vid).expect("variant_with_id: unknown variant") + } + + /// Return a `VariantDef` given a constructor id. + pub fn variant_with_ctor_id(&self, cid: DefId) -> &VariantDef { + self.variants + .iter() + .find(|v| v.ctor_def_id == Some(cid)) + .expect("variant_with_ctor_id: unknown variant") + } + + /// Return the index of `VariantDef` given a variant id. + pub fn variant_index_with_id(&self, vid: DefId) -> VariantIdx { + self.variants + .iter_enumerated() + .find(|(_, v)| v.def_id == vid) + .expect("variant_index_with_id: unknown variant") + .0 + } + + /// Return the index of `VariantDef` given a constructor id. + pub fn variant_index_with_ctor_id(&self, cid: DefId) -> VariantIdx { + self.variants + .iter_enumerated() + .find(|(_, v)| v.ctor_def_id == Some(cid)) + .expect("variant_index_with_ctor_id: unknown variant") + .0 + } + + pub fn variant_of_res(&self, res: Res) -> &VariantDef { + match res { + Res::Def(DefKind::Variant, vid) => self.variant_with_id(vid), + Res::Def(DefKind::Ctor(..), cid) => self.variant_with_ctor_id(cid), + Res::Def(DefKind::Struct, _) + | Res::Def(DefKind::Union, _) + | Res::Def(DefKind::TyAlias, _) + | Res::Def(DefKind::AssocTy, _) + | Res::SelfTy(..) + | Res::SelfCtor(..) => self.non_enum_variant(), + _ => bug!("unexpected res {:?} in variant_of_res", res), + } + } + + #[inline] + pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { + assert!(self.is_enum()); + let param_env = tcx.param_env(expr_did); + let repr_type = self.repr.discr_type(); + match tcx.const_eval_poly(expr_did) { + Ok(val) => { + let ty = repr_type.to_ty(tcx); + if let Some(b) = val.try_to_bits_for_ty(tcx, param_env, ty) { + trace!("discriminants: {} ({:?})", b, repr_type); + Some(Discr { val: b, ty }) + } else { + info!("invalid enum discriminant: {:#?}", val); + crate::mir::interpret::struct_error( + tcx.at(tcx.def_span(expr_did)), + "constant evaluation of enum discriminant resulted in non-integer", + ) + .emit(); + None + } + } + Err(err) => { + let msg = match err { + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { + "enum discriminant evaluation failed" + } + ErrorHandled::TooGeneric => "enum discriminant depends on generics", + }; + tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg); + None + } + } + } + + #[inline] + pub fn discriminants( + &'tcx self, + tcx: TyCtxt<'tcx>, + ) -> impl Iterator)> + Captures<'tcx> { + assert!(self.is_enum()); + let repr_type = self.repr.discr_type(); + let initial = repr_type.initial_discriminant(tcx); + let mut prev_discr = None::>; + self.variants.iter_enumerated().map(move |(i, v)| { + let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); + if let VariantDiscr::Explicit(expr_did) = v.discr { + if let Some(new_discr) = self.eval_explicit_discr(tcx, expr_did) { + discr = new_discr; + } + } + prev_discr = Some(discr); + + (i, discr) + }) + } + + #[inline] + pub fn variant_range(&self) -> Range { + VariantIdx::new(0)..VariantIdx::new(self.variants.len()) + } + + /// Computes the discriminant value used by a specific variant. + /// Unlike `discriminants`, this is (amortized) constant-time, + /// only doing at most one query for evaluating an explicit + /// discriminant (the last one before the requested variant), + /// assuming there are no constant-evaluation errors there. + #[inline] + pub fn discriminant_for_variant( + &self, + tcx: TyCtxt<'tcx>, + variant_index: VariantIdx, + ) -> Discr<'tcx> { + assert!(self.is_enum()); + let (val, offset) = self.discriminant_def_for_variant(variant_index); + let explicit_value = val + .and_then(|expr_did| self.eval_explicit_discr(tcx, expr_did)) + .unwrap_or_else(|| self.repr.discr_type().initial_discriminant(tcx)); + explicit_value.checked_add(tcx, offset as u128).0 + } + + /// Yields a `DefId` for the discriminant and an offset to add to it + /// Alternatively, if there is no explicit discriminant, returns the + /// inferred discriminant directly. + pub fn discriminant_def_for_variant(&self, variant_index: VariantIdx) -> (Option, u32) { + assert!(!self.variants.is_empty()); + let mut explicit_index = variant_index.as_u32(); + let expr_did; + loop { + match self.variants[VariantIdx::from_u32(explicit_index)].discr { + ty::VariantDiscr::Relative(0) => { + expr_did = None; + break; + } + ty::VariantDiscr::Relative(distance) => { + explicit_index -= distance; + } + ty::VariantDiscr::Explicit(did) => { + expr_did = Some(did); + break; + } + } + } + (expr_did, variant_index.as_u32() - explicit_index) + } + + pub fn destructor(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.adt_destructor(self.did) + } + + /// Returns a list of types such that `Self: Sized` if and only + /// if that type is `Sized`, or `TyErr` if this type is recursive. + /// + /// Oddly enough, checking that the sized-constraint is `Sized` is + /// actually more expressive than checking all members: + /// the `Sized` trait is inductive, so an associated type that references + /// `Self` would prevent its containing ADT from being `Sized`. + /// + /// Due to normalization being eager, this applies even if + /// the associated type is behind a pointer (e.g., issue #31299). + pub fn sized_constraint(&self, tcx: TyCtxt<'tcx>) -> &'tcx [Ty<'tcx>] { + tcx.adt_sized_constraint(self.did).0 + } +} + +impl<'tcx> FieldDef { + /// Returns the type of this field. The `subst` is typically obtained + /// via the second field of `TyKind::AdtDef`. + pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { + tcx.type_of(self.did).subst(tcx, subst) + } +} + +/// Represents the various closure traits in the language. This +/// will determine the type of the environment (`self`, in the +/// desugaring) argument that the closure expects. +/// +/// You can get the environment type of a closure using +/// `tcx.closure_env_ty()`. +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] +pub enum ClosureKind { + // Warning: Ordering is significant here! The ordering is chosen + // because the trait Fn is a subtrait of FnMut and so in turn, and + // hence we order it so that Fn < FnMut < FnOnce. + Fn, + FnMut, + FnOnce, +} + +impl<'tcx> ClosureKind { + // This is the initial value used when doing upvar inference. + pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn; + + pub fn trait_did(&self, tcx: TyCtxt<'tcx>) -> DefId { + match *self { + ClosureKind::Fn => tcx.require_lang_item(FnTraitLangItem, None), + ClosureKind::FnMut => tcx.require_lang_item(FnMutTraitLangItem, None), + ClosureKind::FnOnce => tcx.require_lang_item(FnOnceTraitLangItem, None), + } + } + + /// Returns `true` if this a type that impls this closure kind + /// must also implement `other`. + pub fn extends(self, other: ty::ClosureKind) -> bool { + match (self, other) { + (ClosureKind::Fn, ClosureKind::Fn) => true, + (ClosureKind::Fn, ClosureKind::FnMut) => true, + (ClosureKind::Fn, ClosureKind::FnOnce) => true, + (ClosureKind::FnMut, ClosureKind::FnMut) => true, + (ClosureKind::FnMut, ClosureKind::FnOnce) => true, + (ClosureKind::FnOnce, ClosureKind::FnOnce) => true, + _ => false, + } + } + + /// Returns the representative scalar type for this closure kind. + /// See `TyS::to_opt_closure_kind` for more details. + pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match self { + ty::ClosureKind::Fn => tcx.types.i8, + ty::ClosureKind::FnMut => tcx.types.i16, + ty::ClosureKind::FnOnce => tcx.types.i32, + } + } +} + +impl BorrowKind { + pub fn from_mutbl(m: hir::Mutability) -> BorrowKind { + match m { + hir::Mutability::Mut => MutBorrow, + hir::Mutability::Not => ImmBorrow, + } + } + + /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow + /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a + /// mutability that is stronger than necessary so that it at least *would permit* the borrow in + /// question. + pub fn to_mutbl_lossy(self) -> hir::Mutability { + match self { + MutBorrow => hir::Mutability::Mut, + ImmBorrow => hir::Mutability::Not, + + // We have no type corresponding to a unique imm borrow, so + // use `&mut`. It gives all the capabilities of an `&uniq` + // and hence is a safe "over approximation". + UniqueImmBorrow => hir::Mutability::Mut, + } + } + + pub fn to_user_str(&self) -> &'static str { + match *self { + MutBorrow => "mutable", + ImmBorrow => "immutable", + UniqueImmBorrow => "uniquely immutable", + } + } +} + +pub type Attributes<'tcx> = &'tcx [ast::Attribute]; + +#[derive(Debug, PartialEq, Eq)] +pub enum ImplOverlapKind { + /// These impls are always allowed to overlap. + Permitted { + /// Whether or not the impl is permitted due to the trait being a `#[marker]` trait + marker: bool, + }, + /// These impls are allowed to overlap, but that raises + /// an issue #33140 future-compatibility warning. + /// + /// Some background: in Rust 1.0, the trait-object types `Send + Sync` (today's + /// `dyn Send + Sync`) and `Sync + Send` (now `dyn Sync + Send`) were different. + /// + /// The widely-used version 0.1.0 of the crate `traitobject` had accidentally relied + /// that difference, making what reduces to the following set of impls: + /// + /// ``` + /// trait Trait {} + /// impl Trait for dyn Send + Sync {} + /// impl Trait for dyn Sync + Send {} + /// ``` + /// + /// Obviously, once we made these types be identical, that code causes a coherence + /// error and a fairly big headache for us. However, luckily for us, the trait + /// `Trait` used in this case is basically a marker trait, and therefore having + /// overlapping impls for it is sound. + /// + /// To handle this, we basically regard the trait as a marker trait, with an additional + /// future-compatibility warning. To avoid accidentally "stabilizing" this feature, + /// it has the following restrictions: + /// + /// 1. The trait must indeed be a marker-like trait (i.e., no items), and must be + /// positive impls. + /// 2. The trait-ref of both impls must be equal. + /// 3. The trait-ref of both impls must be a trait object type consisting only of + /// marker traits. + /// 4. Neither of the impls can have any where-clauses. + /// + /// Once `traitobject` 0.1.0 is no longer an active concern, this hack can be removed. + Issue33140, +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn 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 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..1a8693b8df711 --- /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..6c8f23c139f6e --- /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 97% rename from src/librustc/ty/print/obsolete.rs rename to src/librustc_middle/ty/print/obsolete.rs index 7605d44c7f30f..67b6433b61143 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; @@ -47,7 +47,7 @@ impl DefPathBasedNames<'tcx> { } ty::Tuple(component_types) => { output.push('('); - for &component_type in component_types { + for component_type in component_types { self.push_type_name(component_type.expect_ty(), output, debug); output.push_str(", "); } @@ -144,11 +144,10 @@ impl DefPathBasedNames<'tcx> { let substs = substs.truncate_to(self.tcx, generics); self.push_generic_params(substs, iter::empty(), output, debug); } - ty::Error + ty::Error(_) | ty::Bound(..) | ty::Infer(_) | ty::Placeholder(..) - | ty::UnnormalizedProjection(..) | ty::Projection(..) | ty::Param(_) | ty::GeneratorWitness(_) diff --git a/src/librustc_middle/ty/print/pretty.rs b/src/librustc_middle/ty/print/pretty.rs new file mode 100644 index 0000000000000..1a08639a533d5 --- /dev/null +++ b/src/librustc_middle/ty/print/pretty.rs @@ -0,0 +1,2074 @@ +use crate::middle::cstore::{ExternCrate, ExternCrateSource}; +use crate::mir::interpret::{ + sign_extend, truncate, AllocId, ConstValue, GlobalAlloc, Pointer, Scalar, +}; +use crate::ty::layout::IntegerExt; +use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; +use crate::ty::{self, DefIdTree, ParamConst, Ty, TyCtxt, TypeFoldable}; +use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::Float; +use rustc_ast::ast; +use rustc_attr::{SignedInt, UnsignedInt}; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, DefKind, Namespace}; +use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_target::abi::{Integer, Size}; +use rustc_target::spec::abi::Abi; + +use std::cell::Cell; +use std::char; +use std::collections::BTreeMap; +use std::fmt::{self, Write as _}; +use std::ops::{Deref, DerefMut}; + +// `pretty` is a separate module only for organization. +use super::*; + +macro_rules! p { + (@write($($data:expr),+)) => { + write!(scoped_cx!(), $($data),+)? + }; + (@print($x:expr)) => { + scoped_cx!() = $x.print(scoped_cx!())? + }; + (@$method:ident($($arg:expr),*)) => { + scoped_cx!() = scoped_cx!().$method($($arg),*)? + }; + ($($kind:ident $data:tt),+) => {{ + $(p!(@$kind $data);)+ + }}; +} +macro_rules! define_scoped_cx { + ($cx:ident) => { + #[allow(unused_macros)] + macro_rules! scoped_cx { + () => { + $cx + }; + } + }; +} + +thread_local! { + static FORCE_IMPL_FILENAME_LINE: Cell = Cell::new(false); + static SHOULD_PREFIX_WITH_CRATE: Cell = Cell::new(false); + static NO_QUERIES: Cell = Cell::new(false); +} + +/// Avoids running any queries during any prints that occur +/// during the closure. This may alter the appearance of some +/// types (e.g. forcing verbose printing for opaque types). +/// This method is used during some queries (e.g. `predicates_of` +/// for opaque types), to ensure that any debug printing that +/// occurs during the query computation does not end up recursively +/// calling the same query. +pub fn with_no_queries R, R>(f: F) -> R { + NO_QUERIES.with(|no_queries| { + let old = no_queries.replace(true); + let result = f(); + no_queries.set(old); + result + }) +} + +/// Force us to name impls with just the filename/line number. We +/// normally try to use types. But at some points, notably while printing +/// cycle errors, this can result in extra or suboptimal error output, +/// so this variable disables that check. +pub fn with_forced_impl_filename_line R, R>(f: F) -> R { + FORCE_IMPL_FILENAME_LINE.with(|force| { + let old = force.replace(true); + let result = f(); + force.set(old); + result + }) +} + +/// Adds the `crate::` prefix to paths where appropriate. +pub fn with_crate_prefix R, R>(f: F) -> R { + SHOULD_PREFIX_WITH_CRATE.with(|flag| { + let old = flag.replace(true); + let result = f(); + flag.set(old); + result + }) +} + +/// The "region highlights" are used to control region printing during +/// specific error messages. When a "region highlight" is enabled, it +/// gives an alternate way to print specific regions. For now, we +/// always print those regions using a number, so something like "`'0`". +/// +/// Regions not selected by the region highlight mode are presently +/// unaffected. +#[derive(Copy, Clone, Default)] +pub struct RegionHighlightMode { + /// If enabled, when we see the selected region, use "`'N`" + /// instead of the ordinary behavior. + highlight_regions: [Option<(ty::RegionKind, usize)>; 3], + + /// If enabled, when printing a "free region" that originated from + /// the given `ty::BoundRegion`, print it as "`'1`". Free regions that would ordinarily + /// have names print as normal. + /// + /// This is used when you have a signature like `fn foo(x: &u32, + /// y: &'a u32)` and we want to give a name to the region of the + /// reference `x`. + highlight_bound_region: Option<(ty::BoundRegion, usize)>, +} + +impl RegionHighlightMode { + /// If `region` and `number` are both `Some`, invokes + /// `highlighting_region`. + pub fn maybe_highlighting_region( + &mut self, + region: Option>, + number: Option, + ) { + if let Some(k) = region { + if let Some(n) = number { + self.highlighting_region(k, n); + } + } + } + + /// Highlights the region inference variable `vid` as `'N`. + pub fn highlighting_region(&mut self, region: ty::Region<'_>, number: usize) { + let num_slots = self.highlight_regions.len(); + let first_avail_slot = + self.highlight_regions.iter_mut().find(|s| s.is_none()).unwrap_or_else(|| { + bug!("can only highlight {} placeholders at a time", num_slots,) + }); + *first_avail_slot = Some((*region, number)); + } + + /// Convenience wrapper for `highlighting_region`. + pub fn highlighting_region_vid(&mut self, vid: ty::RegionVid, number: usize) { + self.highlighting_region(&ty::ReVar(vid), number) + } + + /// Returns `Some(n)` with the number to use for the given region, if any. + fn region_highlighted(&self, region: ty::Region<'_>) -> Option { + self.highlight_regions.iter().find_map(|h| match h { + Some((r, n)) if r == region => Some(*n), + _ => None, + }) + } + + /// Highlight the given bound region. + /// We can only highlight one bound region at a time. See + /// the field `highlight_bound_region` for more detailed notes. + pub fn highlighting_bound_region(&mut self, br: ty::BoundRegion, number: usize) { + assert!(self.highlight_bound_region.is_none()); + self.highlight_bound_region = Some((br, number)); + } +} + +/// Trait for printers that pretty-print using `fmt::Write` to the printer. +pub trait PrettyPrinter<'tcx>: + Printer< + 'tcx, + Error = fmt::Error, + Path = Self, + Region = Self, + Type = Self, + DynExistential = Self, + Const = Self, + > + fmt::Write +{ + /// Like `print_def_path` but for value paths. + fn print_value_path( + self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + self.print_def_path(def_id, substs) + } + + fn in_binder(self, value: &ty::Binder) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, + { + value.skip_binder().print(self) + } + + /// Prints comma-separated elements. + fn comma_sep(mut self, mut elems: impl Iterator) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = Self::Error>, + { + if let Some(first) = elems.next() { + self = first.print(self)?; + for elem in elems { + self.write_str(", ")?; + self = elem.print(self)?; + } + } + Ok(self) + } + + /// Prints `{f: t}` or `{f as t}` depending on the `cast` argument + fn typed_value( + mut self, + f: impl FnOnce(Self) -> Result, + t: impl FnOnce(Self) -> Result, + conversion: &str, + ) -> Result { + self.write_str("{")?; + self = f(self)?; + self.write_str(conversion)?; + self = t(self)?; + self.write_str("}")?; + Ok(self) + } + + /// Prints `<...>` around what `f` prints. + fn generic_delimiters( + self, + f: impl FnOnce(Self) -> Result, + ) -> Result; + + /// Returns `true` if the region should be printed in + /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`. + /// This is typically the case for all non-`'_` regions. + fn region_should_not_be_omitted(&self, region: ty::Region<'_>) -> bool; + + // Defaults (should not be overridden): + + /// If possible, this returns a global path resolving to `def_id` that is visible + /// from at least one local module, and returns `true`. If the crate defining `def_id` is + /// declared with an `extern crate`, the path is guaranteed to use the `extern crate`. + fn try_print_visible_def_path(self, def_id: DefId) -> Result<(Self, bool), Self::Error> { + let mut callers = Vec::new(); + self.try_print_visible_def_path_recur(def_id, &mut callers) + } + + /// Does the work of `try_print_visible_def_path`, building the + /// full definition path recursively before attempting to + /// post-process it into the valid and visible version that + /// accounts for re-exports. + /// + /// This method should only be called by itself or + /// `try_print_visible_def_path`. + /// + /// `callers` is a chain of visible_parent's leading to `def_id`, + /// to support cycle detection during recursion. + fn try_print_visible_def_path_recur( + mut self, + def_id: DefId, + callers: &mut Vec, + ) -> Result<(Self, bool), Self::Error> { + define_scoped_cx!(self); + + debug!("try_print_visible_def_path: def_id={:?}", def_id); + + // If `def_id` is a direct or injected extern crate, return the + // path to the crate followed by the path to the item within the crate. + if def_id.index == CRATE_DEF_INDEX { + let cnum = def_id.krate; + + if cnum == LOCAL_CRATE { + return Ok((self.path_crate(cnum)?, true)); + } + + // In local mode, when we encounter a crate other than + // LOCAL_CRATE, execution proceeds in one of two ways: + // + // 1. For a direct dependency, where user added an + // `extern crate` manually, we put the `extern + // crate` as the parent. So you wind up with + // something relative to the current crate. + // 2. For an extern inferred from a path or an indirect crate, + // where there is no explicit `extern crate`, we just prepend + // the crate name. + match self.tcx().extern_crate(def_id) { + Some(&ExternCrate { + src: ExternCrateSource::Extern(def_id), + dependency_of: LOCAL_CRATE, + span, + .. + }) => { + debug!("try_print_visible_def_path: def_id={:?}", def_id); + return Ok(( + if !span.is_dummy() { + self.print_def_path(def_id, &[])? + } else { + self.path_crate(cnum)? + }, + true, + )); + } + None => { + return Ok((self.path_crate(cnum)?, true)); + } + _ => {} + } + } + + if def_id.is_local() { + return Ok((self, false)); + } + + let visible_parent_map = self.tcx().visible_parent_map(LOCAL_CRATE); + + let mut cur_def_key = self.tcx().def_key(def_id); + debug!("try_print_visible_def_path: cur_def_key={:?}", cur_def_key); + + // For a constructor, we want the name of its parent rather than . + if let DefPathData::Ctor = cur_def_key.disambiguated_data.data { + let parent = DefId { + krate: def_id.krate, + index: cur_def_key + .parent + .expect("`DefPathData::Ctor` / `VariantData` missing a parent"), + }; + + cur_def_key = self.tcx().def_key(parent); + } + + let visible_parent = match visible_parent_map.get(&def_id).cloned() { + Some(parent) => parent, + None => return Ok((self, false)), + }; + if callers.contains(&visible_parent) { + return Ok((self, false)); + } + callers.push(visible_parent); + // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid + // knowing ahead of time whether the entire path will succeed or not. + // To support printers that do not implement `PrettyPrinter`, a `Vec` or + // linked list on the stack would need to be built, before any printing. + match self.try_print_visible_def_path_recur(visible_parent, callers)? { + (cx, false) => return Ok((cx, false)), + (cx, true) => self = cx, + } + callers.pop(); + let actual_parent = self.tcx().parent(def_id); + debug!( + "try_print_visible_def_path: visible_parent={:?} actual_parent={:?}", + visible_parent, actual_parent, + ); + + let mut data = cur_def_key.disambiguated_data.data; + debug!( + "try_print_visible_def_path: data={:?} visible_parent={:?} actual_parent={:?}", + data, visible_parent, actual_parent, + ); + + match data { + // In order to output a path that could actually be imported (valid and visible), + // we need to handle re-exports correctly. + // + // For example, take `std::os::unix::process::CommandExt`, this trait is actually + // defined at `std::sys::unix::ext::process::CommandExt` (at time of writing). + // + // `std::os::unix` rexports the contents of `std::sys::unix::ext`. `std::sys` is + // private so the "true" path to `CommandExt` isn't accessible. + // + // In this case, the `visible_parent_map` will look something like this: + // + // (child) -> (parent) + // `std::sys::unix::ext::process::CommandExt` -> `std::sys::unix::ext::process` + // `std::sys::unix::ext::process` -> `std::sys::unix::ext` + // `std::sys::unix::ext` -> `std::os` + // + // This is correct, as the visible parent of `std::sys::unix::ext` is in fact + // `std::os`. + // + // When printing the path to `CommandExt` and looking at the `cur_def_key` that + // corresponds to `std::sys::unix::ext`, we would normally print `ext` and then go + // to the parent - resulting in a mangled path like + // `std::os::ext::process::CommandExt`. + // + // Instead, we must detect that there was a re-export and instead print `unix` + // (which is the name `std::sys::unix::ext` was re-exported as in `std::os`). To + // do this, we compare the parent of `std::sys::unix::ext` (`std::sys::unix`) with + // the visible parent (`std::os`). If these do not match, then we iterate over + // the children of the visible parent (as was done when computing + // `visible_parent_map`), looking for the specific child we currently have and then + // have access to the re-exported name. + DefPathData::TypeNs(ref mut name) if Some(visible_parent) != actual_parent => { + let reexport = self + .tcx() + .item_children(visible_parent) + .iter() + .find(|child| child.res.def_id() == def_id) + .map(|child| child.ident.name); + if let Some(reexport) = reexport { + *name = reexport; + } + } + // Re-exported `extern crate` (#43189). + DefPathData::CrateRoot => { + data = DefPathData::TypeNs(self.tcx().original_crate_name(def_id.krate)); + } + _ => {} + } + debug!("try_print_visible_def_path: data={:?}", data); + + Ok((self.path_append(Ok, &DisambiguatedDefPathData { data, disambiguator: 0 })?, true)) + } + + fn pretty_path_qualified( + self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + if trait_ref.is_none() { + // Inherent impls. Try to print `Foo::bar` for an inherent + // impl on `Foo`, but fallback to `::bar` if self-type is + // anything other than a simple path. + match self_ty.kind { + ty::Adt(..) + | ty::Foreign(_) + | ty::Bool + | ty::Char + | ty::Str + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) => { + return self_ty.print(self); + } + + _ => {} + } + } + + self.generic_delimiters(|mut cx| { + define_scoped_cx!(cx); + + p!(print(self_ty)); + if let Some(trait_ref) = trait_ref { + p!(write(" as "), print(trait_ref.print_only_trait_path())); + } + Ok(cx) + }) + } + + fn pretty_path_append_impl( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self = print_prefix(self)?; + + self.generic_delimiters(|mut cx| { + define_scoped_cx!(cx); + + p!(write("impl ")); + if let Some(trait_ref) = trait_ref { + p!(print(trait_ref.print_only_trait_path()), write(" for ")); + } + p!(print(self_ty)); + + Ok(cx) + }) + } + + fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result { + define_scoped_cx!(self); + + match ty.kind { + ty::Bool => p!(write("bool")), + ty::Char => p!(write("char")), + ty::Int(t) => p!(write("{}", t.name_str())), + ty::Uint(t) => p!(write("{}", t.name_str())), + ty::Float(t) => p!(write("{}", t.name_str())), + ty::RawPtr(ref tm) => { + p!(write( + "*{} ", + match tm.mutbl { + hir::Mutability::Mut => "mut", + hir::Mutability::Not => "const", + } + )); + p!(print(tm.ty)) + } + ty::Ref(r, ty, mutbl) => { + p!(write("&")); + if self.region_should_not_be_omitted(r) { + p!(print(r), write(" ")); + } + p!(print(ty::TypeAndMut { ty, mutbl })) + } + ty::Never => p!(write("!")), + ty::Tuple(ref tys) => { + p!(write("("), comma_sep(tys.iter())); + if tys.len() == 1 { + p!(write(",")); + } + p!(write(")")) + } + ty::FnDef(def_id, substs) => { + let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs); + p!(print(sig), write(" {{"), print_value_path(def_id, substs), write("}}")); + } + ty::FnPtr(ref bare_fn) => p!(print(bare_fn)), + ty::Infer(infer_ty) => { + if let ty::TyVar(ty_vid) = infer_ty { + if let Some(name) = self.infer_ty_name(ty_vid) { + p!(write("{}", name)) + } else { + p!(write("{}", infer_ty)) + } + } else { + p!(write("{}", infer_ty)) + } + } + ty::Error(_) => p!(write("[type error]")), + ty::Param(ref param_ty) => p!(write("{}", param_ty)), + ty::Bound(debruijn, bound_ty) => match bound_ty.kind { + ty::BoundTyKind::Anon => self.pretty_print_bound_var(debruijn, bound_ty.var)?, + ty::BoundTyKind::Param(p) => p!(write("{}", p)), + }, + ty::Adt(def, substs) => { + p!(print_def_path(def.did, substs)); + } + ty::Dynamic(data, r) => { + let print_r = self.region_should_not_be_omitted(r); + if print_r { + p!(write("(")); + } + p!(write("dyn "), print(data)); + if print_r { + p!(write(" + "), print(r), write(")")); + } + } + ty::Foreign(def_id) => { + p!(print_def_path(def_id, &[])); + } + ty::Projection(ref data) => p!(print(data)), + ty::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), + ty::Opaque(def_id, substs) => { + // FIXME(eddyb) print this with `print_def_path`. + // We use verbose printing in 'NO_QUERIES' mode, to + // avoid needing to call `predicates_of`. This should + // only affect certain debug messages (e.g. messages printed + // from `rustc_middle::ty` during the computation of `tcx.predicates_of`), + // and should have no effect on any compiler output. + if self.tcx().sess.verbose() || NO_QUERIES.with(|q| q.get()) { + p!(write("Opaque({:?}, {:?})", def_id, substs)); + return Ok(self); + } + + return Ok(with_no_queries(|| { + let def_key = self.tcx().def_key(def_id); + if let Some(name) = def_key.disambiguated_data.data.get_opt_name() { + p!(write("{}", name)); + // FIXME(eddyb) print this with `print_def_path`. + if !substs.is_empty() { + p!(write("::")); + p!(generic_delimiters(|cx| cx.comma_sep(substs.iter()))); + } + return Ok(self); + } + // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, + // by looking up the projections associated with the def_id. + let bounds = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs); + + let mut first = true; + let mut is_sized = false; + p!(write("impl")); + for predicate in bounds.predicates { + if let Some(trait_ref) = predicate.to_opt_poly_trait_ref() { + // Don't print +Sized, but rather +?Sized if absent. + if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() { + is_sized = true; + continue; + } + + p!( + write("{}", if first { " " } else { "+" }), + print(trait_ref.print_only_trait_path()) + ); + first = false; + } + } + if !is_sized { + p!(write("{}?Sized", if first { " " } else { "+" })); + } else if first { + p!(write(" Sized")); + } + Ok(self) + })?); + } + ty::Str => p!(write("str")), + ty::Generator(did, substs, movability) => { + match movability { + hir::Movability::Movable => p!(write("[generator")), + hir::Movability::Static => p!(write("[static generator")), + } + + // FIXME(eddyb) should use `def_span`. + if let Some(did) = did.as_local() { + let hir_id = self.tcx().hir().as_local_hir_id(did); + let span = self.tcx().hir().span(hir_id); + p!(write("@{}", self.tcx().sess.source_map().span_to_string(span))); + + if substs.as_generator().is_valid() { + let upvar_tys = substs.as_generator().upvar_tys(); + let mut sep = " "; + for (&var_id, upvar_ty) in self + .tcx() + .upvars_mentioned(did) + .as_ref() + .iter() + .flat_map(|v| v.keys()) + .zip(upvar_tys) + { + p!(write("{}{}:", sep, self.tcx().hir().name(var_id)), print(upvar_ty)); + sep = ", "; + } + } + } else { + p!(write("@{}", self.tcx().def_path_str(did))); + + if substs.as_generator().is_valid() { + let upvar_tys = substs.as_generator().upvar_tys(); + let mut sep = " "; + for (index, upvar_ty) in upvar_tys.enumerate() { + p!(write("{}{}:", sep, index), print(upvar_ty)); + sep = ", "; + } + } + } + + if substs.as_generator().is_valid() { + p!(write(" "), print(substs.as_generator().witness())); + } + + p!(write("]")) + } + ty::GeneratorWitness(types) => { + p!(in_binder(&types)); + } + ty::Closure(did, substs) => { + p!(write("[closure")); + + // FIXME(eddyb) should use `def_span`. + if let Some(did) = did.as_local() { + let hir_id = self.tcx().hir().as_local_hir_id(did); + if self.tcx().sess.opts.debugging_opts.span_free_formats { + p!(write("@"), print_def_path(did.to_def_id(), substs)); + } else { + let span = self.tcx().hir().span(hir_id); + p!(write("@{}", self.tcx().sess.source_map().span_to_string(span))); + } + + if substs.as_closure().is_valid() { + let upvar_tys = substs.as_closure().upvar_tys(); + let mut sep = " "; + for (&var_id, upvar_ty) in self + .tcx() + .upvars_mentioned(did) + .as_ref() + .iter() + .flat_map(|v| v.keys()) + .zip(upvar_tys) + { + p!(write("{}{}:", sep, self.tcx().hir().name(var_id)), print(upvar_ty)); + sep = ", "; + } + } + } else { + p!(write("@{}", self.tcx().def_path_str(did))); + + if substs.as_closure().is_valid() { + let upvar_tys = substs.as_closure().upvar_tys(); + let mut sep = " "; + for (index, upvar_ty) in upvar_tys.enumerate() { + p!(write("{}{}:", sep, index), print(upvar_ty)); + sep = ", "; + } + } + } + + if self.tcx().sess.verbose() && substs.as_closure().is_valid() { + p!(write(" closure_kind_ty="), print(substs.as_closure().kind_ty())); + p!( + write(" closure_sig_as_fn_ptr_ty="), + print(substs.as_closure().sig_as_fn_ptr_ty()) + ); + } + + p!(write("]")) + } + ty::Array(ty, sz) => { + p!(write("["), print(ty), write("; ")); + if self.tcx().sess.verbose() { + p!(write("{:?}", sz)); + } else if let ty::ConstKind::Unevaluated(..) = sz.val { + // Do not try to evaluate unevaluated constants. If we are const evaluating an + // array length anon const, rustc will (with debug assertions) print the + // constant's path. Which will end up here again. + p!(write("_")); + } else if let Some(n) = sz.val.try_to_bits(self.tcx().data_layout.pointer_size) { + p!(write("{}", n)); + } else if let ty::ConstKind::Param(param) = sz.val { + p!(write("{}", param)); + } else { + p!(write("_")); + } + p!(write("]")) + } + ty::Slice(ty) => p!(write("["), print(ty), write("]")), + } + + Ok(self) + } + + fn pretty_print_bound_var( + &mut self, + debruijn: ty::DebruijnIndex, + var: ty::BoundVar, + ) -> Result<(), Self::Error> { + if debruijn == ty::INNERMOST { + write!(self, "^{}", var.index()) + } else { + write!(self, "^{}_{}", debruijn.index(), var.index()) + } + } + + fn infer_ty_name(&self, _: ty::TyVid) -> Option { + None + } + + fn pretty_print_dyn_existential( + mut self, + predicates: &'tcx ty::List>, + ) -> Result { + define_scoped_cx!(self); + + // Generate the main trait ref, including associated types. + let mut first = true; + + if let Some(principal) = predicates.principal() { + p!(print_def_path(principal.def_id, &[])); + + let mut resugared = false; + + // Special-case `Fn(...) -> ...` and resugar it. + let fn_trait_kind = self.tcx().fn_trait_kind_from_lang_item(principal.def_id); + if !self.tcx().sess.verbose() && fn_trait_kind.is_some() { + if let ty::Tuple(ref args) = principal.substs.type_at(0).kind { + let mut projections = predicates.projection_bounds(); + if let (Some(proj), None) = (projections.next(), projections.next()) { + let tys: Vec<_> = args.iter().map(|k| k.expect_ty()).collect(); + p!(pretty_fn_sig(&tys, false, proj.ty)); + resugared = true; + } + } + } + + // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`, + // in order to place the projections inside the `<...>`. + if !resugared { + // Use a type that can't appear in defaults of type parameters. + let dummy_self = self.tcx().mk_ty_infer(ty::FreshTy(0)); + let principal = principal.with_self_ty(self.tcx(), dummy_self); + + let args = self.generic_args_to_print( + self.tcx().generics_of(principal.def_id), + principal.substs, + ); + + // Don't print `'_` if there's no unerased regions. + let print_regions = args.iter().any(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => *r != ty::ReErased, + _ => false, + }); + let mut args = args.iter().cloned().filter(|arg| match arg.unpack() { + GenericArgKind::Lifetime(_) => print_regions, + _ => true, + }); + let mut projections = predicates.projection_bounds(); + + let arg0 = args.next(); + let projection0 = projections.next(); + if arg0.is_some() || projection0.is_some() { + let args = arg0.into_iter().chain(args); + let projections = projection0.into_iter().chain(projections); + + p!(generic_delimiters(|mut cx| { + cx = cx.comma_sep(args)?; + if arg0.is_some() && projection0.is_some() { + write!(cx, ", ")?; + } + cx.comma_sep(projections) + })); + } + } + first = false; + } + + // Builtin bounds. + // FIXME(eddyb) avoid printing twice (needed to ensure + // that the auto traits are sorted *and* printed via cx). + let mut auto_traits: Vec<_> = + predicates.auto_traits().map(|did| (self.tcx().def_path_str(did), did)).collect(); + + // The auto traits come ordered by `DefPathHash`. While + // `DefPathHash` is *stable* in the sense that it depends on + // neither the host nor the phase of the moon, it depends + // "pseudorandomly" on the compiler version and the target. + // + // To avoid that causing instabilities in compiletest + // output, sort the auto-traits alphabetically. + auto_traits.sort(); + + for (_, def_id) in auto_traits { + if !first { + p!(write(" + ")); + } + first = false; + + p!(print_def_path(def_id, &[])); + } + + Ok(self) + } + + fn pretty_fn_sig( + mut self, + inputs: &[Ty<'tcx>], + c_variadic: bool, + output: Ty<'tcx>, + ) -> Result { + define_scoped_cx!(self); + + p!(write("("), comma_sep(inputs.iter().copied())); + if c_variadic { + if !inputs.is_empty() { + p!(write(", ")); + } + p!(write("...")); + } + p!(write(")")); + if !output.is_unit() { + p!(write(" -> "), print(output)); + } + + Ok(self) + } + + fn pretty_print_const( + mut self, + ct: &'tcx ty::Const<'tcx>, + print_ty: bool, + ) -> Result { + define_scoped_cx!(self); + + if self.tcx().sess.verbose() { + p!(write("Const({:?}: {:?})", ct.val, ct.ty)); + return Ok(self); + } + + macro_rules! print_underscore { + () => {{ + if print_ty { + self = self.typed_value( + |mut this| { + write!(this, "_")?; + Ok(this) + }, + |this| this.print_type(ct.ty), + ": ", + )?; + } else { + write!(self, "_")?; + } + }}; + } + + match ct.val { + ty::ConstKind::Unevaluated(did, substs, promoted) => { + if let Some(promoted) = promoted { + p!(print_value_path(did, substs)); + p!(write("::{:?}", promoted)); + } else { + match self.tcx().def_kind(did) { + DefKind::Static | DefKind::Const | DefKind::AssocConst => { + p!(print_value_path(did, substs)) + } + _ => { + if did.is_local() { + let span = self.tcx().def_span(did); + if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) + { + p!(write("{}", snip)) + } else { + print_underscore!() + } + } else { + print_underscore!() + } + } + } + } + } + ty::ConstKind::Infer(..) => print_underscore!(), + ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), + ty::ConstKind::Value(value) => { + return self.pretty_print_const_value(value, ct.ty, print_ty); + } + + ty::ConstKind::Bound(debruijn, bound_var) => { + self.pretty_print_bound_var(debruijn, bound_var)? + } + ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), + ty::ConstKind::Error(_) => p!(write("[const error]")), + }; + Ok(self) + } + + fn pretty_print_const_scalar( + mut self, + scalar: Scalar, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + define_scoped_cx!(self); + + match (scalar, &ty.kind) { + // Byte strings (&[u8; N]) + ( + Scalar::Ptr(ptr), + ty::Ref( + _, + ty::TyS { + kind: + ty::Array( + ty::TyS { kind: ty::Uint(ast::UintTy::U8), .. }, + ty::Const { + val: + ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { + data, + .. + })), + .. + }, + ), + .. + }, + _, + ), + ) => match self.tcx().get_global_alloc(ptr.alloc_id) { + Some(GlobalAlloc::Memory(alloc)) => { + if let Ok(byte_str) = alloc.get_bytes(&self.tcx(), ptr, Size::from_bytes(*data)) + { + p!(pretty_print_byte_str(byte_str)) + } else { + p!(write("")) + } + } + // FIXME: for statics and functions, we could in principle print more detail. + Some(GlobalAlloc::Static(def_id)) => p!(write("", def_id)), + Some(GlobalAlloc::Function(_)) => p!(write("")), + None => p!(write("")), + }, + // Bool + (Scalar::Raw { data: 0, .. }, ty::Bool) => p!(write("false")), + (Scalar::Raw { data: 1, .. }, ty::Bool) => p!(write("true")), + // Float + (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F32)) => { + p!(write("{}f32", Single::from_bits(data))) + } + (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F64)) => { + p!(write("{}f64", Double::from_bits(data))) + } + // Int + (Scalar::Raw { data, .. }, ty::Uint(ui)) => { + let bit_size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size(); + let max = truncate(u128::MAX, bit_size); + + let ui_str = ui.name_str(); + if data == max { + p!(write("{}::MAX", ui_str)) + } else { + if print_ty { p!(write("{}{}", data, ui_str)) } else { p!(write("{}", data)) } + }; + } + (Scalar::Raw { data, .. }, ty::Int(i)) => { + let size = Integer::from_attr(&self.tcx(), SignedInt(*i)).size(); + let bit_size = size.bits() as u128; + let min = 1u128 << (bit_size - 1); + let max = min - 1; + + let i_str = i.name_str(); + match data { + d if d == min => p!(write("{}::MIN", i_str)), + d if d == max => p!(write("{}::MAX", i_str)), + _ => { + let data = sign_extend(data, size) as i128; + if print_ty { + p!(write("{}{}", data, i_str)) + } else { + p!(write("{}", data)) + } + } + } + } + // Char + (Scalar::Raw { data, .. }, ty::Char) if char::from_u32(data as u32).is_some() => { + p!(write("{:?}", char::from_u32(data as u32).unwrap())) + } + // Raw pointers + (Scalar::Raw { data, .. }, ty::RawPtr(_)) => { + self = self.typed_value( + |mut this| { + write!(this, "0x{:x}", data)?; + Ok(this) + }, + |this| this.print_type(ty), + " as ", + )?; + } + (Scalar::Ptr(ptr), ty::FnPtr(_)) => { + // FIXME: this can ICE when the ptr is dangling or points to a non-function. + // We should probably have a helper method to share code with the "Byte strings" + // printing above (which also has to handle pointers to all sorts of things). + let instance = self.tcx().global_alloc(ptr.alloc_id).unwrap_fn(); + self = self.typed_value( + |this| this.print_value_path(instance.def_id(), instance.substs), + |this| this.print_type(ty), + " as ", + )?; + } + // For function type zsts just printing the path is enough + (Scalar::Raw { size: 0, .. }, ty::FnDef(d, s)) => p!(print_value_path(*d, s)), + // Nontrivial types with scalar bit representation + (Scalar::Raw { data, size }, _) => { + let print = |mut this: Self| { + if size == 0 { + write!(this, "transmute(())")?; + } else { + write!(this, "transmute(0x{:01$x})", data, size as usize * 2)?; + } + Ok(this) + }; + self = if print_ty { + self.typed_value(print, |this| this.print_type(ty), ": ")? + } else { + print(self)? + }; + } + // Any pointer values not covered by a branch above + (Scalar::Ptr(p), _) => { + self = self.pretty_print_const_pointer(p, ty, print_ty)?; + } + } + Ok(self) + } + + /// This is overridden for MIR printing because we only want to hide alloc ids from users, not + /// from MIR where it is actually useful. + fn pretty_print_const_pointer( + mut self, + _: Pointer, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + if print_ty { + self.typed_value( + |mut this| { + this.write_str("&_")?; + Ok(this) + }, + |this| this.print_type(ty), + ": ", + ) + } else { + self.write_str("&_")?; + Ok(self) + } + } + + fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result { + define_scoped_cx!(self); + p!(write("b\"")); + for &c in byte_str { + for e in std::ascii::escape_default(c) { + self.write_char(e as char)?; + } + } + p!(write("\"")); + Ok(self) + } + + fn pretty_print_const_value( + mut self, + ct: ConstValue<'tcx>, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + define_scoped_cx!(self); + + if self.tcx().sess.verbose() { + p!(write("ConstValue({:?}: ", ct), print(ty), write(")")); + return Ok(self); + } + + let u8_type = self.tcx().types.u8; + + match (ct, &ty.kind) { + // Byte/string slices, printed as (byte) string literals. + ( + ConstValue::Slice { data, start, end }, + ty::Ref(_, ty::TyS { kind: ty::Slice(t), .. }, _), + ) if *t == u8_type => { + // The `inspect` here is okay since we checked the bounds, and there are + // no relocations (we have an active slice reference here). We don't use + // this result to affect interpreter execution. + let byte_str = data.inspect_with_undef_and_ptr_outside_interpreter(start..end); + self.pretty_print_byte_str(byte_str) + } + ( + ConstValue::Slice { data, start, end }, + ty::Ref(_, ty::TyS { kind: ty::Str, .. }, _), + ) => { + // The `inspect` here is okay since we checked the bounds, and there are no + // relocations (we have an active `str` reference here). We don't use this + // result to affect interpreter execution. + let slice = data.inspect_with_undef_and_ptr_outside_interpreter(start..end); + let s = ::std::str::from_utf8(slice).expect("non utf8 str from miri"); + p!(write("{:?}", s)); + Ok(self) + } + (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => { + let n = n.val.try_to_bits(self.tcx().data_layout.pointer_size).unwrap(); + // cast is ok because we already checked for pointer size (32 or 64 bit) above + let n = Size::from_bytes(n); + let ptr = Pointer::new(AllocId(0), offset); + + let byte_str = alloc.get_bytes(&self.tcx(), ptr, n).unwrap(); + p!(write("*")); + p!(pretty_print_byte_str(byte_str)); + Ok(self) + } + + // Aggregates, printed as array/tuple/struct/variant construction syntax. + // + // NB: the `has_param_types_or_consts` check ensures that we can use + // the `destructure_const` query with an empty `ty::ParamEnv` without + // introducing ICEs (e.g. via `layout_of`) from missing bounds. + // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` + // to be able to destructure the tuple into `(0u8, *mut T) + // + // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the + // correct `ty::ParamEnv` to allow printing *all* constant values. + (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => { + let contents = self.tcx().destructure_const( + ty::ParamEnv::reveal_all() + .and(self.tcx().mk_const(ty::Const { val: ty::ConstKind::Value(ct), ty })), + ); + let fields = contents.fields.iter().copied(); + + match ty.kind { + ty::Array(..) => { + p!(write("["), comma_sep(fields), write("]")); + } + ty::Tuple(..) => { + p!(write("("), comma_sep(fields)); + if contents.fields.len() == 1 { + p!(write(",")); + } + p!(write(")")); + } + ty::Adt(def, substs) if def.variants.is_empty() => { + p!(print_value_path(def.did, substs)); + } + ty::Adt(def, substs) => { + let variant_id = + contents.variant.expect("destructed const of adt without variant id"); + let variant_def = &def.variants[variant_id]; + p!(print_value_path(variant_def.def_id, substs)); + + match variant_def.ctor_kind { + CtorKind::Const => {} + CtorKind::Fn => { + p!(write("("), comma_sep(fields), write(")")); + } + CtorKind::Fictive => { + p!(write(" {{ ")); + let mut first = true; + for (field_def, field) in variant_def.fields.iter().zip(fields) { + if !first { + p!(write(", ")); + } + p!(write("{}: ", field_def.ident), print(field)); + first = false; + } + p!(write(" }}")); + } + } + } + _ => unreachable!(), + } + + Ok(self) + } + + (ConstValue::Scalar(scalar), _) => self.pretty_print_const_scalar(scalar, ty, print_ty), + + // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading + // their fields instead of just dumping the memory. + _ => { + // fallback + p!(write("{:?}", ct)); + if print_ty { + p!(write(": "), print(ty)); + } + Ok(self) + } + } + } +} + +// HACK(eddyb) boxed to avoid moving around a large struct by-value. +pub struct FmtPrinter<'a, 'tcx, F>(Box>); + +pub struct FmtPrinterData<'a, 'tcx, F> { + tcx: TyCtxt<'tcx>, + fmt: F, + + empty_path: bool, + in_value: bool, + pub print_alloc_ids: bool, + + used_region_names: FxHashSet, + region_index: usize, + binder_depth: usize, + + pub region_highlight_mode: RegionHighlightMode, + + pub name_resolver: Option Option>>, +} + +impl Deref for FmtPrinter<'a, 'tcx, F> { + type Target = FmtPrinterData<'a, 'tcx, F>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for FmtPrinter<'_, '_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl FmtPrinter<'a, 'tcx, F> { + pub fn new(tcx: TyCtxt<'tcx>, fmt: F, ns: Namespace) -> Self { + FmtPrinter(Box::new(FmtPrinterData { + tcx, + fmt, + empty_path: false, + in_value: ns == Namespace::ValueNS, + print_alloc_ids: false, + used_region_names: Default::default(), + region_index: 0, + binder_depth: 0, + region_highlight_mode: RegionHighlightMode::default(), + name_resolver: None, + })) + } +} + +// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always +// (but also some things just print a `DefId` generally so maybe we need this?) +fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace { + match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::ImplTrait => { + Namespace::TypeNS + } + + DefPathData::ValueNs(..) + | DefPathData::AnonConst + | DefPathData::ClosureExpr + | DefPathData::Ctor => Namespace::ValueNS, + + DefPathData::MacroNs(..) => Namespace::MacroNS, + + _ => Namespace::TypeNS, + } +} + +impl TyCtxt<'t> { + /// Returns a string identifying this `DefId`. This string is + /// suitable for user output. + pub fn def_path_str(self, def_id: DefId) -> String { + self.def_path_str_with_substs(def_id, &[]) + } + + pub fn def_path_str_with_substs(self, def_id: DefId, substs: &'t [GenericArg<'t>]) -> String { + let ns = guess_def_namespace(self, def_id); + debug!("def_path_str: def_id={:?}, ns={:?}", def_id, ns); + let mut s = String::new(); + let _ = FmtPrinter::new(self, &mut s, ns).print_def_path(def_id, substs); + s + } +} + +impl fmt::Write for FmtPrinter<'_, '_, F> { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.fmt.write_str(s) + } +} + +impl Printer<'tcx> for FmtPrinter<'_, 'tcx, F> { + type Error = fmt::Error; + + type Path = Self; + type Region = Self; + type Type = Self; + type DynExistential = Self; + type Const = Self; + + fn tcx(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn print_def_path( + mut self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + define_scoped_cx!(self); + + if substs.is_empty() { + match self.try_print_visible_def_path(def_id)? { + (cx, true) => return Ok(cx), + (cx, false) => self = cx, + } + } + + let key = self.tcx.def_key(def_id); + if let DefPathData::Impl = key.disambiguated_data.data { + // Always use types for non-local impls, where types are always + // available, and filename/line-number is mostly uninteresting. + let use_types = !def_id.is_local() || { + // Otherwise, use filename/line-number if forced. + let force_no_types = FORCE_IMPL_FILENAME_LINE.with(|f| f.get()); + !force_no_types + }; + + if !use_types { + // If no type info is available, fall back to + // pretty printing some span information. This should + // only occur very early in the compiler pipeline. + let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; + let span = self.tcx.def_span(def_id); + + self = self.print_def_path(parent_def_id, &[])?; + + // HACK(eddyb) copy of `path_append` to avoid + // constructing a `DisambiguatedDefPathData`. + if !self.empty_path { + write!(self, "::")?; + } + write!(self, "", self.tcx.sess.source_map().span_to_string(span))?; + self.empty_path = false; + + return Ok(self); + } + } + + self.default_print_def_path(def_id, substs) + } + + fn print_region(self, region: ty::Region<'_>) -> Result { + self.pretty_print_region(region) + } + + fn print_type(self, ty: Ty<'tcx>) -> Result { + self.pretty_print_type(ty) + } + + fn print_dyn_existential( + self, + predicates: &'tcx ty::List>, + ) -> Result { + self.pretty_print_dyn_existential(predicates) + } + + fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result { + self.pretty_print_const(ct, true) + } + + fn path_crate(mut self, cnum: CrateNum) -> Result { + self.empty_path = true; + if cnum == LOCAL_CRATE { + if self.tcx.sess.rust_2018() { + // We add the `crate::` keyword on Rust 2018, only when desired. + if SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) { + write!(self, "{}", kw::Crate)?; + self.empty_path = false; + } + } + } else { + write!(self, "{}", self.tcx.crate_name(cnum))?; + self.empty_path = false; + } + Ok(self) + } + + fn path_qualified( + mut self, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self = self.pretty_path_qualified(self_ty, trait_ref)?; + self.empty_path = false; + Ok(self) + } + + fn path_append_impl( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + _disambiguated_data: &DisambiguatedDefPathData, + self_ty: Ty<'tcx>, + trait_ref: Option>, + ) -> Result { + self = self.pretty_path_append_impl( + |mut cx| { + cx = print_prefix(cx)?; + if !cx.empty_path { + write!(cx, "::")?; + } + + Ok(cx) + }, + self_ty, + trait_ref, + )?; + self.empty_path = false; + Ok(self) + } + + fn path_append( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + disambiguated_data: &DisambiguatedDefPathData, + ) -> Result { + self = print_prefix(self)?; + + // Skip `::{{constructor}}` on tuple/unit structs. + if let DefPathData::Ctor = disambiguated_data.data { + return Ok(self); + } + + // FIXME(eddyb) `name` should never be empty, but it + // currently is for `extern { ... }` "foreign modules". + let name = disambiguated_data.data.as_symbol().as_str(); + if !name.is_empty() { + if !self.empty_path { + write!(self, "::")?; + } + if Ident::from_str(&name).is_raw_guess() { + write!(self, "r#")?; + } + write!(self, "{}", name)?; + + // FIXME(eddyb) this will print e.g. `{{closure}}#3`, but it + // might be nicer to use something else, e.g. `{closure#3}`. + let dis = disambiguated_data.disambiguator; + let print_dis = disambiguated_data.data.get_opt_name().is_none() + || dis != 0 && self.tcx.sess.verbose(); + if print_dis { + write!(self, "#{}", dis)?; + } + + self.empty_path = false; + } + + Ok(self) + } + + fn path_generic_args( + mut self, + print_prefix: impl FnOnce(Self) -> Result, + args: &[GenericArg<'tcx>], + ) -> Result { + self = print_prefix(self)?; + + // Don't print `'_` if there's no unerased regions. + let print_regions = args.iter().any(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => *r != ty::ReErased, + _ => false, + }); + let args = args.iter().cloned().filter(|arg| match arg.unpack() { + GenericArgKind::Lifetime(_) => print_regions, + _ => true, + }); + + if args.clone().next().is_some() { + if self.in_value { + write!(self, "::")?; + } + self.generic_delimiters(|cx| cx.comma_sep(args)) + } else { + Ok(self) + } + } +} + +impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { + fn infer_ty_name(&self, id: ty::TyVid) -> Option { + self.0.name_resolver.as_ref().and_then(|func| func(id)) + } + + fn print_value_path( + mut self, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + ) -> Result { + let was_in_value = std::mem::replace(&mut self.in_value, true); + self = self.print_def_path(def_id, substs)?; + self.in_value = was_in_value; + + Ok(self) + } + + fn in_binder(self, value: &ty::Binder) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<'tcx>, + { + self.pretty_in_binder(value) + } + + fn typed_value( + mut self, + f: impl FnOnce(Self) -> Result, + t: impl FnOnce(Self) -> Result, + conversion: &str, + ) -> Result { + self.write_str("{")?; + self = f(self)?; + self.write_str(conversion)?; + let was_in_value = std::mem::replace(&mut self.in_value, false); + self = t(self)?; + self.in_value = was_in_value; + self.write_str("}")?; + Ok(self) + } + + fn generic_delimiters( + mut self, + f: impl FnOnce(Self) -> Result, + ) -> Result { + write!(self, "<")?; + + let was_in_value = std::mem::replace(&mut self.in_value, false); + let mut inner = f(self)?; + inner.in_value = was_in_value; + + write!(inner, ">")?; + Ok(inner) + } + + fn region_should_not_be_omitted(&self, region: ty::Region<'_>) -> bool { + let highlight = self.region_highlight_mode; + if highlight.region_highlighted(region).is_some() { + return true; + } + + if self.tcx.sess.verbose() { + return true; + } + + let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; + + match *region { + ty::ReEarlyBound(ref data) => { + data.name != kw::Invalid && data.name != kw::UnderscoreLifetime + } + + ty::ReLateBound(_, br) + | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) + | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { + if let ty::BrNamed(_, name) = br { + if name != kw::Invalid && name != kw::UnderscoreLifetime { + return true; + } + } + + if let Some((region, _)) = highlight.highlight_bound_region { + if br == region { + return true; + } + } + + false + } + + ty::ReVar(_) if identify_regions => true, + + ty::ReVar(_) | ty::ReErased => false, + + ty::ReStatic | ty::ReEmpty(_) => true, + } + } + + fn pretty_print_const_pointer( + self, + p: Pointer, + ty: Ty<'tcx>, + print_ty: bool, + ) -> Result { + let print = |mut this: Self| { + define_scoped_cx!(this); + if this.print_alloc_ids { + p!(write("{:?}", p)); + } else { + p!(write("&_")); + } + Ok(this) + }; + if print_ty { + self.typed_value(print, |this| this.print_type(ty), ": ") + } else { + print(self) + } + } +} + +// HACK(eddyb) limited to `FmtPrinter` because of `region_highlight_mode`. +impl FmtPrinter<'_, '_, F> { + pub fn pretty_print_region(mut self, region: ty::Region<'_>) -> Result { + define_scoped_cx!(self); + + // Watch out for region highlights. + let highlight = self.region_highlight_mode; + if let Some(n) = highlight.region_highlighted(region) { + p!(write("'{}", n)); + return Ok(self); + } + + if self.tcx.sess.verbose() { + p!(write("{:?}", region)); + return Ok(self); + } + + let identify_regions = self.tcx.sess.opts.debugging_opts.identify_regions; + + // These printouts are concise. They do not contain all the information + // the user might want to diagnose an error, but there is basically no way + // to fit that into a short string. Hence the recommendation to use + // `explain_region()` or `note_and_explain_region()`. + match *region { + ty::ReEarlyBound(ref data) => { + if data.name != kw::Invalid { + p!(write("{}", data.name)); + return Ok(self); + } + } + ty::ReLateBound(_, br) + | ty::ReFree(ty::FreeRegion { bound_region: br, .. }) + | ty::RePlaceholder(ty::Placeholder { name: br, .. }) => { + if let ty::BrNamed(_, name) = br { + if name != kw::Invalid && name != kw::UnderscoreLifetime { + p!(write("{}", name)); + return Ok(self); + } + } + + if let Some((region, counter)) = highlight.highlight_bound_region { + if br == region { + p!(write("'{}", counter)); + return Ok(self); + } + } + } + ty::ReVar(region_vid) if identify_regions => { + p!(write("{:?}", region_vid)); + return Ok(self); + } + ty::ReVar(_) => {} + ty::ReErased => {} + ty::ReStatic => { + p!(write("'static")); + return Ok(self); + } + ty::ReEmpty(ty::UniverseIndex::ROOT) => { + p!(write("'")); + return Ok(self); + } + ty::ReEmpty(ui) => { + p!(write("'", ui)); + return Ok(self); + } + } + + p!(write("'_")); + + Ok(self) + } +} + +// HACK(eddyb) limited to `FmtPrinter` because of `binder_depth`, +// `region_index` and `used_region_names`. +impl FmtPrinter<'_, 'tcx, F> { + pub fn name_all_regions( + mut self, + value: &ty::Binder, + ) -> Result<(Self, (T, BTreeMap>)), fmt::Error> + where + T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, + { + fn name_by_region_index(index: usize) -> Symbol { + match index { + 0 => Symbol::intern("'r"), + 1 => Symbol::intern("'s"), + i => Symbol::intern(&format!("'t{}", i - 2)), + } + } + + // Replace any anonymous late-bound regions with named + // variants, using new unique identifiers, so that we can + // clearly differentiate between named and unnamed regions in + // the output. We'll probably want to tweak this over time to + // decide just how much information to give. + if self.binder_depth == 0 { + self.prepare_late_bound_region_info(value); + } + + let mut empty = true; + let mut start_or_continue = |cx: &mut Self, start: &str, cont: &str| { + write!( + cx, + "{}", + if empty { + empty = false; + start + } else { + cont + } + ) + }; + + define_scoped_cx!(self); + + let mut region_index = self.region_index; + let new_value = self.tcx.replace_late_bound_regions(value, |br| { + let _ = start_or_continue(&mut self, "for<", ", "); + let br = match br { + ty::BrNamed(_, name) => { + let _ = write!(self, "{}", name); + br + } + ty::BrAnon(_) | ty::BrEnv => { + let name = loop { + let name = name_by_region_index(region_index); + region_index += 1; + if !self.used_region_names.contains(&name) { + break name; + } + }; + let _ = write!(self, "{}", name); + ty::BrNamed(DefId::local(CRATE_DEF_INDEX), name) + } + }; + self.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)) + }); + start_or_continue(&mut self, "", "> ")?; + + self.binder_depth += 1; + self.region_index = region_index; + Ok((self, new_value)) + } + + pub fn pretty_in_binder(self, value: &ty::Binder) -> Result + where + T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<'tcx>, + { + let old_region_index = self.region_index; + let (new, new_value) = self.name_all_regions(value)?; + let mut inner = new_value.0.print(new)?; + inner.region_index = old_region_index; + inner.binder_depth -= 1; + Ok(inner) + } + + fn prepare_late_bound_region_info(&mut self, value: &ty::Binder) + where + T: TypeFoldable<'tcx>, + { + struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet); + impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + if let ty::ReLateBound(_, ty::BrNamed(_, name)) = *r { + self.0.insert(name); + } + r.super_visit_with(self) + } + } + + self.used_region_names.clear(); + let mut collector = LateBoundRegionNameCollector(&mut self.used_region_names); + value.visit_with(&mut collector); + self.region_index = 0; + } +} + +impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::Binder +where + T: Print<'tcx, P, Output = P, Error = P::Error> + TypeFoldable<'tcx>, +{ + type Output = P; + type Error = P::Error; + fn print(&self, cx: P) -> Result { + cx.in_binder(self) + } +} + +impl<'tcx, T, U, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate +where + T: Print<'tcx, P, Output = P, Error = P::Error>, + U: Print<'tcx, P, Output = P, Error = P::Error>, +{ + type Output = P; + type Error = P::Error; + fn print(&self, mut cx: P) -> Result { + define_scoped_cx!(cx); + p!(print(self.0), write(": "), print(self.1)); + Ok(cx) + } +} + +macro_rules! forward_display_to_print { + ($($ty:ty),+) => { + $(impl fmt::Display for $ty { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + tcx.lift(self) + .expect("could not lift for printing") + .print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; + Ok(()) + }) + } + })+ + }; +} + +macro_rules! define_print_and_forward_display { + (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { + $(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty { + type Output = P; + type Error = fmt::Error; + fn print(&$self, $cx: P) -> Result { + #[allow(unused_mut)] + let mut $cx = $cx; + define_scoped_cx!($cx); + let _: () = $print; + #[allow(unreachable_code)] + Ok($cx) + } + })+ + + forward_display_to_print!($($ty),+); + }; +} + +// HACK(eddyb) this is separate because `ty::RegionKind` doesn't need lifting. +impl fmt::Display for ty::RegionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + self.print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; + Ok(()) + }) + } +} + +/// Wrapper type for `ty::TraitRef` which opts-in to pretty printing only +/// the trait path. That is, it will print `Trait` instead of +/// `>`. +#[derive(Copy, Clone, TypeFoldable, Lift)] +pub struct TraitRefPrintOnlyTraitPath<'tcx>(ty::TraitRef<'tcx>); + +impl fmt::Debug for TraitRefPrintOnlyTraitPath<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl ty::TraitRef<'tcx> { + pub fn print_only_trait_path(self) -> TraitRefPrintOnlyTraitPath<'tcx> { + TraitRefPrintOnlyTraitPath(self) + } +} + +impl ty::Binder> { + pub fn print_only_trait_path(self) -> ty::Binder> { + self.map_bound(|tr| tr.print_only_trait_path()) + } +} + +forward_display_to_print! { + Ty<'tcx>, + &'tcx ty::List>, + &'tcx ty::Const<'tcx>, + + // HACK(eddyb) these are exhaustive instead of generic, + // because `for<'tcx>` isn't possible yet. + ty::Binder<&'tcx ty::List>>, + ty::Binder>, + ty::Binder>, + ty::Binder>, + ty::Binder>, + ty::Binder>, + ty::Binder>, + ty::Binder, ty::Region<'tcx>>>, + ty::Binder, ty::Region<'tcx>>>, + + ty::OutlivesPredicate, ty::Region<'tcx>>, + ty::OutlivesPredicate, ty::Region<'tcx>> +} + +define_print_and_forward_display! { + (self, cx): + + &'tcx ty::List> { + p!(write("{{"), comma_sep(self.iter()), write("}}")) + } + + ty::TypeAndMut<'tcx> { + p!(write("{}", self.mutbl.prefix_str()), print(self.ty)) + } + + ty::ExistentialTraitRef<'tcx> { + // Use a type that can't appear in defaults of type parameters. + let dummy_self = cx.tcx().mk_ty_infer(ty::FreshTy(0)); + let trait_ref = self.with_self_ty(cx.tcx(), dummy_self); + p!(print(trait_ref.print_only_trait_path())) + } + + ty::ExistentialProjection<'tcx> { + let name = cx.tcx().associated_item(self.item_def_id).ident; + p!(write("{} = ", name), print(self.ty)) + } + + ty::ExistentialPredicate<'tcx> { + match *self { + ty::ExistentialPredicate::Trait(x) => p!(print(x)), + ty::ExistentialPredicate::Projection(x) => p!(print(x)), + ty::ExistentialPredicate::AutoTrait(def_id) => { + p!(print_def_path(def_id, &[])); + } + } + } + + ty::FnSig<'tcx> { + p!(write("{}", self.unsafety.prefix_str())); + + if self.abi != Abi::Rust { + p!(write("extern {} ", self.abi)); + } + + p!(write("fn"), pretty_fn_sig(self.inputs(), self.c_variadic, self.output())); + } + + ty::InferTy { + if cx.tcx().sess.verbose() { + p!(write("{:?}", self)); + return Ok(cx); + } + match *self { + ty::TyVar(_) => p!(write("_")), + ty::IntVar(_) => p!(write("{}", "{integer}")), + ty::FloatVar(_) => p!(write("{}", "{float}")), + ty::FreshTy(v) => p!(write("FreshTy({})", v)), + ty::FreshIntTy(v) => p!(write("FreshIntTy({})", v)), + ty::FreshFloatTy(v) => p!(write("FreshFloatTy({})", v)) + } + } + + ty::TraitRef<'tcx> { + p!(write("<{} as {}>", self.self_ty(), self.print_only_trait_path())) + } + + TraitRefPrintOnlyTraitPath<'tcx> { + p!(print_def_path(self.0.def_id, self.0.substs)); + } + + ty::ParamTy { + p!(write("{}", self.name)) + } + + ty::ParamConst { + p!(write("{}", self.name)) + } + + ty::SubtypePredicate<'tcx> { + p!(print(self.a), write(" <: "), print(self.b)) + } + + ty::TraitPredicate<'tcx> { + p!(print(self.trait_ref.self_ty()), write(": "), + print(self.trait_ref.print_only_trait_path())) + } + + ty::ProjectionPredicate<'tcx> { + p!(print(self.projection_ty), write(" == "), print(self.ty)) + } + + ty::ProjectionTy<'tcx> { + p!(print_def_path(self.item_def_id, self.substs)); + } + + ty::ClosureKind { + match *self { + ty::ClosureKind::Fn => p!(write("Fn")), + ty::ClosureKind::FnMut => p!(write("FnMut")), + ty::ClosureKind::FnOnce => p!(write("FnOnce")), + } + } + + ty::Predicate<'tcx> { + match self.kind() { + &ty::PredicateKind::Trait(ref data, constness) => { + if let hir::Constness::Const = constness { + p!(write("const ")); + } + p!(print(data)) + } + ty::PredicateKind::Subtype(predicate) => p!(print(predicate)), + ty::PredicateKind::RegionOutlives(predicate) => p!(print(predicate)), + ty::PredicateKind::TypeOutlives(predicate) => p!(print(predicate)), + ty::PredicateKind::Projection(predicate) => p!(print(predicate)), + ty::PredicateKind::WellFormed(arg) => p!(print(arg), write(" well-formed")), + &ty::PredicateKind::ObjectSafe(trait_def_id) => { + p!(write("the trait `"), + print_def_path(trait_def_id, &[]), + write("` is object-safe")) + } + &ty::PredicateKind::ClosureKind(closure_def_id, _closure_substs, kind) => { + p!(write("the closure `"), + print_value_path(closure_def_id, &[]), + write("` implements the trait `{}`", kind)) + } + &ty::PredicateKind::ConstEvaluatable(def_id, substs) => { + p!(write("the constant `"), + print_value_path(def_id, substs), + write("` can be evaluated")) + } + ty::PredicateKind::ConstEquate(c1, c2) => { + p!(write("the constant `"), + print(c1), + write("` equals `"), + print(c2), + write("`")) + } + } + } + + GenericArg<'tcx> { + match self.unpack() { + GenericArgKind::Lifetime(lt) => p!(print(lt)), + GenericArgKind::Type(ty) => p!(print(ty)), + GenericArgKind::Const(ct) => p!(print(ct)), + } + } +} diff --git a/src/librustc/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 89% rename from src/librustc/ty/query/keys.rs rename to src/librustc_middle/ty/query/keys.rs index 09fb307a1ceb4..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, DefIndex, LOCAL_CRATE}; +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}; @@ -84,14 +83,14 @@ impl Key for CrateNum { } } -impl Key for DefIndex { +impl Key for LocalDefId { type CacheSelector = DefaultCacheSelector; fn query_crate(&self) -> CrateNum { - LOCAL_CRATE + self.to_def_id().query_crate() } - fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { - DUMMY_SP + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { + self.to_def_id().default_span(tcx) } } @@ -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..35d19b7603faf --- /dev/null +++ b/src/librustc_middle/ty/query/mod.rs @@ -0,0 +1,219 @@ +use crate::dep_graph::{self, DepNode, DepNodeParams}; +use crate::hir::exports::Export; +use crate::hir::map; +use crate::infer::canonical::{self, Canonical}; +use crate::lint::LintLevelMap; +use crate::middle::codegen_fn_attrs::CodegenFnAttrs; +use crate::middle::cstore::{CrateSource, DepKind}; +use crate::middle::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib}; +use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; +use crate::middle::lib_features::LibFeatures; +use crate::middle::privacy::AccessLevels; +use crate::middle::region; +use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLifetimes}; +use crate::middle::stability::{self, DeprecationEntry}; +use crate::mir; +use crate::mir::interpret::GlobalId; +use crate::mir::interpret::{ConstEvalRawResult, ConstEvalResult, ConstValue}; +use crate::mir::interpret::{LitToConstError, LitToConstInput}; +use crate::mir::mono::CodegenUnit; +use crate::traits::query::{ + CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution, +}; +use crate::traits::query::{ + DropckOutlivesResult, DtorckConstraint, MethodAutoderefStepsResult, NormalizationResult, + OutlivesBound, +}; +use crate::traits::specialization_graph; +use crate::traits::{self, ImplSource}; +use crate::ty::steal::Steal; +use crate::ty::subst::{GenericArg, SubstsRef}; +use crate::ty::util::AlwaysRequiresDrop; +use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_data_structures::profiling::ProfileCategory::*; +use rustc_data_structures::stable_hasher::StableVec; +use rustc_data_structures::svh::Svh; +use rustc_data_structures::sync::Lrc; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId}; +use rustc_hir::lang_items::{LangItem, LanguageItems}; +use rustc_hir::{Crate, HirIdSet, ItemLocalId, TraitCandidate}; +use rustc_index::vec::IndexVec; +use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; +use rustc_session::utils::NativeLibKind; +use rustc_session::CrateDisambiguator; +use rustc_target::spec::PanicStrategy; + +use rustc_ast::ast; +use rustc_attr as attr; +use rustc_span::symbol::Symbol; +use rustc_span::{Span, DUMMY_SP}; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::ops::Deref; +use std::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 90% rename from src/librustc/ty/query/on_disk_cache.rs rename to src/librustc_middle/ty/query/on_disk_cache.rs index 9c1db7c5f2b70..c84a7c38d0a0e 100644 --- a/src/librustc/ty/query/on_disk_cache.rs +++ b/src/librustc_middle/ty/query/on_disk_cache.rs @@ -1,34 +1,31 @@ use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; -use crate::hir::map::definitions::DefPathHash; -use crate::ich::{CachingSourceMapView, Fingerprint}; +use crate::mir::interpret; use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; -use crate::mir::{self, interpret}; -use crate::session::{CrateDisambiguator, Session}; 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::sync::{HashMapExt, Lock, Lrc, OnceCell}; use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::Diagnostic; -use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; +use rustc_hir::definitions::DefPathHash; use rustc_index::vec::{Idx, IndexVec}; use rustc_serialize::{ opaque, Decodable, Decoder, Encodable, Encoder, SpecializedDecoder, SpecializedEncoder, UseSpecializedDecodable, UseSpecializedEncodable, }; +use rustc_session::{CrateDisambiguator, Session}; use rustc_span::hygiene::{ExpnId, SyntaxContext}; use rustc_span::source_map::{SourceMap, StableSourceFileId}; +use rustc_span::symbol::Ident; +use rustc_span::CachingSourceMapView; use rustc_span::{BytePos, SourceFile, Span, DUMMY_SP}; use std::mem; const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; -const TAG_CLEAR_CROSS_CRATE_CLEAR: u8 = 0; -const TAG_CLEAR_CROSS_CRATE_SET: u8 = 1; - const TAG_NO_EXPN_DATA: u8 = 0; const TAG_EXPN_DATA_SHORTHAND: u8 = 1; const TAG_EXPN_DATA_INLINE: u8 = 2; @@ -49,7 +46,7 @@ pub struct OnDiskCache<'sess> { current_diagnostics: Lock>>, prev_cnums: Vec<(u32, String, CrateDisambiguator)>, - cnum_map: Once>>, + cnum_map: OnceCell>>, source_map: &'sess SourceMap, file_index_to_stable_id: FxHashMap, @@ -128,7 +125,7 @@ impl<'sess> OnDiskCache<'sess> { file_index_to_stable_id: footer.file_index_to_stable_id, file_index_to_file: Default::default(), prev_cnums: footer.prev_cnums, - cnum_map: Once::new(), + cnum_map: OnceCell::new(), source_map: sess.source_map(), current_diagnostics: Default::default(), query_result_index: footer.query_result_index.into_iter().collect(), @@ -144,7 +141,7 @@ impl<'sess> OnDiskCache<'sess> { file_index_to_stable_id: Default::default(), file_index_to_file: Default::default(), prev_cnums: vec![], - cnum_map: Once::new(), + cnum_map: OnceCell::new(), source_map, current_diagnostics: Default::default(), query_result_index: Default::default(), @@ -370,14 +367,14 @@ impl<'sess> OnDiskCache<'sess> { { let pos = index.get(&dep_node_index).cloned()?; - // Initialize `cnum_map` using the value from the thread that finishes the closure first. - self.cnum_map.init_nonlocking_same(|| Self::compute_cnum_map(tcx, &self.prev_cnums[..])); + let cnum_map = + self.cnum_map.get_or_init(|| Self::compute_cnum_map(tcx, &self.prev_cnums[..])); let mut decoder = CacheDecoder { tcx, opaque: opaque::Decoder::new(&self.serialized_data[..], pos.to_usize()), source_map: self.source_map, - cnum_map: self.cnum_map.get(), + cnum_map, synthetic_syntax_contexts: &self.synthetic_syntax_contexts, file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, @@ -527,16 +524,39 @@ impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { let cache_key = ty::CReaderCacheKey { cnum: CrateNum::ReservedForIncrCompCache, pos: shorthand }; - if let Some(&ty) = tcx.rcache.borrow().get(&cache_key) { + if let Some(&ty) = tcx.ty_rcache.borrow().get(&cache_key) { return Ok(ty); } let ty = or_insert_with(self)?; // This may overwrite the entry, but it should overwrite with the same value. - tcx.rcache.borrow_mut().insert_same(cache_key, ty); + tcx.ty_rcache.borrow_mut().insert_same(cache_key, ty); Ok(ty) } + fn cached_predicate_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> Result, Self::Error> + where + F: FnOnce(&mut Self) -> Result, Self::Error>, + { + let tcx = self.tcx(); + + let cache_key = + ty::CReaderCacheKey { cnum: CrateNum::ReservedForIncrCompCache, pos: shorthand }; + + if let Some(&pred) = tcx.pred_rcache.borrow().get(&cache_key) { + return Ok(pred); + } + + let pred = or_insert_with(self)?; + // This may overwrite the entry, but it should overwrite with the same value. + tcx.pred_rcache.borrow_mut().insert_same(cache_key, pred); + Ok(pred) + } + fn with_position(&mut self, pos: usize, f: F) -> R where F: FnOnce(&mut Self) -> R, @@ -657,26 +677,7 @@ impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { #[inline] fn specialized_decode(&mut self) -> Result { - Ok(LocalDefId::from_def_id(DefId::decode(self)?)) - } -} - -impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { - fn specialized_decode(&mut self) -> Result { - // Load the `DefPathHash` which is what we encoded the `DefIndex` as. - let def_path_hash = DefPathHash::decode(self)?; - - // Use the `DefPathHash` to map to the current `DefId`. - let def_id = self.tcx().def_path_hash_to_def_id.as_ref().unwrap()[&def_path_hash]; - - debug_assert!(def_id.is_local()); - - // The `ItemLocalId` needs no remapping. - let local_id = hir::ItemLocalId::decode(self)?; - - // Reconstruct the `HirId` and look up the corresponding `NodeId` in the - // context of the current session. - Ok(hir::HirId { owner: def_id.index, local_id }) + Ok(DefId::decode(self)?.expect_local()) } } @@ -686,24 +687,6 @@ impl<'a, 'tcx> SpecializedDecoder for CacheDecoder<'a, 'tcx> { } } -impl<'a, 'tcx, T: Decodable> SpecializedDecoder> - for CacheDecoder<'a, 'tcx> -{ - #[inline] - fn specialized_decode(&mut self) -> Result, Self::Error> { - let discr = u8::decode(self)?; - - match discr { - TAG_CLEAR_CROSS_CRATE_CLEAR => Ok(mir::ClearCrossCrate::Clear), - TAG_CLEAR_CROSS_CRATE_SET => { - let val = T::decode(self)?; - Ok(mir::ClearCrossCrate::Set(val)) - } - _ => unreachable!(), - } - } -} - //- ENCODING ------------------------------------------------------------------- /// An encoder that can write the incr. comp. cache. @@ -847,47 +830,34 @@ where } } -impl<'a, 'tcx, E> SpecializedEncoder> for CacheEncoder<'a, 'tcx, E> +impl<'a, 'b, 'c, 'tcx, E> SpecializedEncoder<&'b ty::TyS<'c>> for CacheEncoder<'a, 'tcx, E> where E: 'a + TyEncoder, + &'b ty::TyS<'c>: UseSpecializedEncodable, { #[inline] - fn specialized_encode(&mut self, ty: &Ty<'tcx>) -> Result<(), Self::Error> { + fn specialized_encode(&mut self, ty: &&'b ty::TyS<'c>) -> Result<(), Self::Error> { + debug_assert!(self.tcx.lift(ty).is_some()); + let ty = unsafe { std::mem::transmute::<&&'b ty::TyS<'c>, &&'tcx ty::TyS<'tcx>>(ty) }; ty_codec::encode_with_shorthand(self, ty, |encoder| &mut encoder.type_shorthands) } } -impl<'a, 'tcx, E> SpecializedEncoder<&'tcx [(ty::Predicate<'tcx>, Span)]> - for CacheEncoder<'a, 'tcx, E> +impl<'a, 'b, 'tcx, E> SpecializedEncoder> for CacheEncoder<'a, 'tcx, E> where E: 'a + TyEncoder, { #[inline] - fn specialized_encode( - &mut self, - predicates: &&'tcx [(ty::Predicate<'tcx>, Span)], - ) -> Result<(), Self::Error> { - ty_codec::encode_spanned_predicates(self, predicates, |encoder| { + fn specialized_encode(&mut self, predicate: &ty::Predicate<'b>) -> Result<(), Self::Error> { + debug_assert!(self.tcx.lift(predicate).is_some()); + let predicate = + unsafe { std::mem::transmute::<&ty::Predicate<'b>, &ty::Predicate<'tcx>>(predicate) }; + ty_codec::encode_with_shorthand(self, predicate, |encoder| { &mut encoder.predicate_shorthands }) } } -impl<'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, -{ - #[inline] - fn specialized_encode(&mut self, id: &hir::HirId) -> Result<(), Self::Error> { - let hir::HirId { owner, local_id } = *id; - - let def_path_hash = self.tcx.hir().definitions().def_path_hash(owner); - - def_path_hash.encode(self)?; - local_id.encode(self) - } -} - impl<'a, 'tcx, E> SpecializedEncoder for CacheEncoder<'a, 'tcx, E> where E: 'a + TyEncoder, @@ -924,23 +894,6 @@ impl<'a, 'tcx> SpecializedEncoder for CacheEncoder<'a, 'tcx, opaque } } -impl<'a, 'tcx, E, T> SpecializedEncoder> for CacheEncoder<'a, 'tcx, E> -where - E: 'a + TyEncoder, - T: Encodable, -{ - #[inline] - fn specialized_encode(&mut self, val: &mir::ClearCrossCrate) -> Result<(), Self::Error> { - match *val { - mir::ClearCrossCrate::Clear => TAG_CLEAR_CROSS_CRATE_CLEAR.encode(self), - mir::ClearCrossCrate::Set(ref val) => { - TAG_CLEAR_CROSS_CRATE_SET.encode(self)?; - val.encode(self) - } - } - } -} - macro_rules! encoder_methods { ($($name:ident($ty:ty);)*) => { #[inline] @@ -956,6 +909,7 @@ where { type Error = E::Error; + #[inline] fn emit_unit(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -1028,7 +982,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> + super::QueryAccessors>, + Q::Value: Encodable, E: 'a + TyEncoder, { let _timer = tcx @@ -1041,7 +996,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 85% rename from src/librustc/ty/query/profiling_support.rs rename to src/librustc_middle/ty/query/profiling_support.rs index 99ada34d59ebe..3c44662441890 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::config::QueryAccessors; -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; @@ -112,30 +112,53 @@ impl IntoSelfProfilingString for T { } } -impl IntoSelfProfilingString for DefId { +impl IntoSelfProfilingString for T { fn to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_, '_>) -> StringId { + self.spec_to_self_profile_string(builder) + } +} + +#[rustc_specialization_trait] +pub trait SpecIntoSelfProfilingString: Debug { + fn spec_to_self_profile_string( + &self, + builder: &mut QueryKeyStringBuilder<'_, '_, '_>, + ) -> StringId; +} + +impl SpecIntoSelfProfilingString for DefId { + fn spec_to_self_profile_string( + &self, + builder: &mut QueryKeyStringBuilder<'_, '_, '_>, + ) -> StringId { builder.def_id_to_string_id(*self) } } -impl IntoSelfProfilingString for CrateNum { - fn to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_, '_>) -> StringId { +impl SpecIntoSelfProfilingString for CrateNum { + fn spec_to_self_profile_string( + &self, + builder: &mut QueryKeyStringBuilder<'_, '_, '_>, + ) -> StringId { builder.def_id_to_string_id(DefId { krate: *self, index: CRATE_DEF_INDEX }) } } -impl IntoSelfProfilingString for DefIndex { - fn to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_, '_>) -> StringId { +impl SpecIntoSelfProfilingString for DefIndex { + fn spec_to_self_profile_string( + &self, + builder: &mut QueryKeyStringBuilder<'_, '_, '_>, + ) -> StringId { builder.def_id_to_string_id(DefId { krate: LOCAL_CRATE, index: *self }) } } -impl IntoSelfProfilingString for (T0, T1) +impl SpecIntoSelfProfilingString for (T0, T1) where - T0: IntoSelfProfilingString + Debug, - T1: IntoSelfProfilingString + Debug, + T0: SpecIntoSelfProfilingString, + T1: SpecIntoSelfProfilingString, { - default fn to_self_profile_string( + fn spec_to_self_profile_string( &self, builder: &mut QueryKeyStringBuilder<'_, '_, '_>, ) -> StringId { @@ -157,13 +180,14 @@ where /// Allocate the self-profiling query strings for a single query cache. This /// method is called from `alloc_self_profile_query_strings` which knows all /// the queries via macro magic. -pub(super) fn alloc_self_profile_query_strings_for_query_cache<'tcx, Q>( +pub(super) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( tcx: TyCtxt<'tcx>, query_name: &'static str, - query_state: &QueryState<'tcx, Q>, + query_state: &QueryState, C>, string_cache: &mut QueryKeyStringCache, ) where - Q: QueryAccessors<'tcx>, + C: QueryCache, + C::Key: Debug + Clone, { tcx.prof.with_profiler(|profiler| { let event_id_builder = profiler.event_id_builder(); diff --git a/src/librustc/ty/query/stats.rs b/src/librustc_middle/ty/query/stats.rs similarity index 87% rename from src/librustc/ty/query/stats.rs rename to src/librustc_middle/ty/query/stats.rs index d257320d4eaf6..b496bf839ab9e 100644 --- a/src/librustc/ty/query/stats.rs +++ b/src/librustc_middle/ty/query/stats.rs @@ -1,8 +1,9 @@ -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; @@ -37,9 +38,9 @@ struct QueryStats { local_def_id_keys: Option, } -fn stats<'tcx, Q: QueryAccessors<'tcx>>( +fn stats( name: &'static str, - map: &QueryState<'tcx, Q>, + map: &QueryState, ) -> QueryStats { let mut stats = QueryStats { name, @@ -47,10 +48,10 @@ fn stats<'tcx, Q: QueryAccessors<'tcx>>( cache_hits: map.cache_hits.load(Ordering::Relaxed), #[cfg(not(debug_assertions))] cache_hits: 0, - key_size: mem::size_of::(), - key_type: type_name::(), - value_size: mem::size_of::(), - value_type: type_name::(), + key_size: mem::size_of::(), + key_type: type_name::(), + value_size: mem::size_of::(), + value_type: type_name::(), entry_count: map.iter_results(|results| results.count()), local_def_id_keys: None, }; @@ -125,7 +126,10 @@ macro_rules! print_stats { let mut queries = Vec::new(); $($( - queries.push(stats::>( + queries.push(stats::< + TyCtxt<'_>, + as QueryAccessors>>::Cache, + >( stringify!($name), &tcx.queries.$name, )); diff --git a/src/librustc_middle/ty/query/values.rs b/src/librustc_middle/ty/query/values.rs new file mode 100644 index 0000000000000..0a0ff101b5203 --- /dev/null +++ b/src/librustc_middle/ty/query/values.rs @@ -0,0 +1,40 @@ +use crate::ty::{self, AdtSizedConstraint, Ty, TyCtxt, TyS}; + +use rustc_span::symbol::Symbol; + +pub(super) trait Value<'tcx>: Sized { + fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self; +} + +impl<'tcx, T> Value<'tcx> for T { + default fn from_cycle_error(tcx: TyCtxt<'tcx>) -> T { + tcx.sess.abort_if_errors(); + bug!("Value::from_cycle_error called without errors"); + } +} + +impl<'tcx> Value<'tcx> for &'_ TyS<'_> { + fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self { + // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. + // FIXME: Represent the above fact in the trait system somehow. + unsafe { std::mem::transmute::, Ty<'_>>(tcx.ty_error()) } + } +} + +impl<'tcx> Value<'tcx> for ty::SymbolName { + fn from_cycle_error(_: TyCtxt<'tcx>) -> Self { + ty::SymbolName { name: Symbol::intern("") } + } +} + +impl<'tcx> Value<'tcx> for AdtSizedConstraint<'_> { + fn from_cycle_error(tcx: TyCtxt<'tcx>) -> Self { + // SAFETY: This is never called when `Self` is not `AdtSizedConstraint<'tcx>`. + // FIXME: Represent the above fact in the trait system somehow. + unsafe { + std::mem::transmute::, AdtSizedConstraint<'_>>( + AdtSizedConstraint(tcx.intern_type_list(&[tcx.ty_error()])), + ) + } + } +} diff --git a/src/librustc_middle/ty/relate.rs b/src/librustc_middle/ty/relate.rs new file mode 100644 index 0000000000000..14cddd11c438d --- /dev/null +++ b/src/librustc_middle/ty/relate.rs @@ -0,0 +1,798 @@ +//! Generalized type relating mechanism. +//! +//! A type relation `R` relates a pair of values `(A, B)`. `A and B` are usually +//! types or regions but can be other things. Examples of type relations are +//! subtyping, type equality, etc. + +use crate::mir::interpret::{get_slice_bytes, ConstValue}; +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; + +pub type RelateResult<'tcx, T> = Result>; + +#[derive(Clone, Debug)] +pub enum Cause { + ExistentialRegionBound, // relating an existential region bound +} + +pub trait TypeRelation<'tcx>: Sized { + fn tcx(&self) -> TyCtxt<'tcx>; + + fn param_env(&self) -> ty::ParamEnv<'tcx>; + + /// Returns a static string we can use for printouts. + fn tag(&self) -> &'static str; + + /// Returns `true` if the value `a` is the "expected" type in the + /// relation. Just affects error messages. + fn a_is_expected(&self) -> bool; + + fn with_cause(&mut self, _cause: Cause, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + f(self) + } + + /// Generic relation routine suitable for most anything. + fn relate>(&mut self, a: &T, b: &T) -> RelateResult<'tcx, T> { + Relate::relate(self, a, b) + } + + /// Relate the two substitutions for the given item. The default + /// is to look up the variance for the item and proceed + /// accordingly. + fn relate_item_substs( + &mut self, + item_def_id: DefId, + a_subst: SubstsRef<'tcx>, + b_subst: SubstsRef<'tcx>, + ) -> RelateResult<'tcx, SubstsRef<'tcx>> { + debug!( + "relate_item_substs(item_def_id={:?}, a_subst={:?}, b_subst={:?})", + item_def_id, a_subst, b_subst + ); + + let opt_variances = self.tcx().variances_of(item_def_id); + relate_substs(self, Some(opt_variances), a_subst, b_subst) + } + + /// Switch variance for the purpose of relating `a` and `b`. + fn relate_with_variance>( + &mut self, + variance: ty::Variance, + a: &T, + b: &T, + ) -> RelateResult<'tcx, T>; + + // Overridable relations. You shouldn't typically call these + // directly, instead call `relate()`, which in turn calls + // these. This is both more uniform but also allows us to add + // additional hooks for other types in the future if needed + // without making older code, which called `relate`, obsolete. + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>; + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>>; + + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>; + + fn binders( + &mut self, + a: &ty::Binder, + b: &ty::Binder, + ) -> RelateResult<'tcx, ty::Binder> + where + T: Relate<'tcx>; +} + +pub trait Relate<'tcx>: TypeFoldable<'tcx> { + fn relate>( + relation: &mut R, + a: &Self, + b: &Self, + ) -> RelateResult<'tcx, Self>; +} + +/////////////////////////////////////////////////////////////////////////// +// Relate impls + +impl<'tcx> Relate<'tcx> for ty::TypeAndMut<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::TypeAndMut<'tcx>, + b: &ty::TypeAndMut<'tcx>, + ) -> RelateResult<'tcx, ty::TypeAndMut<'tcx>> { + debug!("{}.mts({:?}, {:?})", relation.tag(), a, b); + if a.mutbl != b.mutbl { + Err(TypeError::Mutability) + } else { + let mutbl = a.mutbl; + let variance = match mutbl { + ast::Mutability::Not => ty::Covariant, + ast::Mutability::Mut => ty::Invariant, + }; + let ty = relation.relate_with_variance(variance, &a.ty, &b.ty)?; + Ok(ty::TypeAndMut { ty, mutbl }) + } + } +} + +pub fn relate_substs>( + relation: &mut R, + variances: Option<&[ty::Variance]>, + a_subst: SubstsRef<'tcx>, + b_subst: SubstsRef<'tcx>, +) -> RelateResult<'tcx, SubstsRef<'tcx>> { + let tcx = relation.tcx(); + + let params = a_subst.iter().zip(b_subst).enumerate().map(|(i, (a, b))| { + let variance = variances.map_or(ty::Invariant, |v| v[i]); + relation.relate_with_variance(variance, &a, &b) + }); + + Ok(tcx.mk_substs(params)?) +} + +impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::FnSig<'tcx>, + b: &ty::FnSig<'tcx>, + ) -> RelateResult<'tcx, ty::FnSig<'tcx>> { + let tcx = relation.tcx(); + + if a.c_variadic != b.c_variadic { + return Err(TypeError::VariadicMismatch(expected_found( + relation, + &a.c_variadic, + &b.c_variadic, + ))); + } + let unsafety = relation.relate(&a.unsafety, &b.unsafety)?; + let abi = relation.relate(&a.abi, &b.abi)?; + + if a.inputs().len() != b.inputs().len() { + return Err(TypeError::ArgCount); + } + + let inputs_and_output = a + .inputs() + .iter() + .cloned() + .zip(b.inputs().iter().cloned()) + .map(|x| (x, false)) + .chain(iter::once(((a.output(), b.output()), true))) + .map(|((a, b), is_output)| { + if is_output { + relation.relate(&a, &b) + } else { + relation.relate_with_variance(ty::Contravariant, &a, &b) + } + }); + Ok(ty::FnSig { + inputs_and_output: tcx.mk_type_list(inputs_and_output)?, + c_variadic: a.c_variadic, + unsafety, + abi, + }) + } +} + +impl<'tcx> Relate<'tcx> for ast::Unsafety { + fn relate>( + relation: &mut R, + a: &ast::Unsafety, + b: &ast::Unsafety, + ) -> RelateResult<'tcx, ast::Unsafety> { + if a != b { + Err(TypeError::UnsafetyMismatch(expected_found(relation, a, b))) + } else { + Ok(*a) + } + } +} + +impl<'tcx> Relate<'tcx> for abi::Abi { + fn relate>( + relation: &mut R, + a: &abi::Abi, + b: &abi::Abi, + ) -> RelateResult<'tcx, abi::Abi> { + if a == b { Ok(*a) } else { Err(TypeError::AbiMismatch(expected_found(relation, a, b))) } + } +} + +impl<'tcx> Relate<'tcx> for ty::ProjectionTy<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::ProjectionTy<'tcx>, + b: &ty::ProjectionTy<'tcx>, + ) -> RelateResult<'tcx, ty::ProjectionTy<'tcx>> { + if a.item_def_id != b.item_def_id { + Err(TypeError::ProjectionMismatched(expected_found( + relation, + &a.item_def_id, + &b.item_def_id, + ))) + } else { + let substs = relation.relate(&a.substs, &b.substs)?; + Ok(ty::ProjectionTy { item_def_id: a.item_def_id, substs: &substs }) + } + } +} + +impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::ExistentialProjection<'tcx>, + b: &ty::ExistentialProjection<'tcx>, + ) -> RelateResult<'tcx, ty::ExistentialProjection<'tcx>> { + if a.item_def_id != b.item_def_id { + Err(TypeError::ProjectionMismatched(expected_found( + relation, + &a.item_def_id, + &b.item_def_id, + ))) + } else { + let ty = relation.relate_with_variance(ty::Invariant, &a.ty, &b.ty)?; + let substs = relation.relate_with_variance(ty::Invariant, &a.substs, &b.substs)?; + Ok(ty::ExistentialProjection { item_def_id: a.item_def_id, substs, ty }) + } + } +} + +impl<'tcx> Relate<'tcx> for Vec> { + fn relate>( + relation: &mut R, + a: &Vec>, + b: &Vec>, + ) -> RelateResult<'tcx, Vec>> { + // To be compatible, `a` and `b` must be for precisely the + // same set of traits and item names. We always require that + // projection bounds lists are sorted by trait-def-id and item-name, + // so we can just iterate through the lists pairwise, so long as they are the + // same length. + if a.len() != b.len() { + Err(TypeError::ProjectionBoundsLength(expected_found(relation, &a.len(), &b.len()))) + } else { + a.iter().zip(b).map(|(a, b)| relation.relate(a, b)).collect() + } + } +} + +impl<'tcx> Relate<'tcx> for ty::TraitRef<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::TraitRef<'tcx>, + b: &ty::TraitRef<'tcx>, + ) -> RelateResult<'tcx, ty::TraitRef<'tcx>> { + // Different traits cannot be related. + if a.def_id != b.def_id { + Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id))) + } else { + let substs = relate_substs(relation, None, a.substs, b.substs)?; + Ok(ty::TraitRef { def_id: a.def_id, substs }) + } + } +} + +impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::ExistentialTraitRef<'tcx>, + b: &ty::ExistentialTraitRef<'tcx>, + ) -> RelateResult<'tcx, ty::ExistentialTraitRef<'tcx>> { + // Different traits cannot be related. + if a.def_id != b.def_id { + Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id))) + } else { + let substs = relate_substs(relation, None, a.substs, b.substs)?; + Ok(ty::ExistentialTraitRef { def_id: a.def_id, substs }) + } + } +} + +#[derive(Debug, Clone, TypeFoldable)] +struct GeneratorWitness<'tcx>(&'tcx ty::List>); + +impl<'tcx> Relate<'tcx> for GeneratorWitness<'tcx> { + fn relate>( + relation: &mut R, + a: &GeneratorWitness<'tcx>, + b: &GeneratorWitness<'tcx>, + ) -> RelateResult<'tcx, GeneratorWitness<'tcx>> { + assert_eq!(a.0.len(), b.0.len()); + let tcx = relation.tcx(); + let types = tcx.mk_type_list(a.0.iter().zip(b.0).map(|(a, b)| relation.relate(&a, &b)))?; + Ok(GeneratorWitness(types)) + } +} + +impl<'tcx> Relate<'tcx> for Ty<'tcx> { + fn relate>( + relation: &mut R, + a: &Ty<'tcx>, + b: &Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> { + relation.tys(a, b) + } +} + +/// The main "type relation" routine. Note that this does not handle +/// inference artifacts, so you should filter those out before calling +/// it. +pub fn super_relate_tys>( + relation: &mut R, + a: Ty<'tcx>, + b: Ty<'tcx>, +) -> RelateResult<'tcx, Ty<'tcx>> { + let tcx = relation.tcx(); + debug!("super_relate_tys: a={:?} b={:?}", a, b); + match (&a.kind, &b.kind) { + (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { + // The caller should handle these cases! + bug!("var types encountered in super_relate_tys") + } + + (ty::Bound(..), _) | (_, ty::Bound(..)) => { + bug!("bound types encountered in super_relate_tys") + } + + (&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(tcx.ty_error()), + + (&ty::Never, _) + | (&ty::Char, _) + | (&ty::Bool, _) + | (&ty::Int(_), _) + | (&ty::Uint(_), _) + | (&ty::Float(_), _) + | (&ty::Str, _) + if a == b => + { + Ok(a) + } + + (&ty::Param(ref a_p), &ty::Param(ref b_p)) if a_p.index == b_p.index => Ok(a), + + (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a), + + (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def => { + let substs = relation.relate_item_substs(a_def.did, a_substs, b_substs)?; + Ok(tcx.mk_adt(a_def, substs)) + } + + (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(tcx.mk_foreign(a_id)), + + (&ty::Dynamic(ref a_obj, ref a_region), &ty::Dynamic(ref b_obj, ref b_region)) => { + let region_bound = relation.with_cause(Cause::ExistentialRegionBound, |relation| { + relation.relate_with_variance(ty::Contravariant, a_region, b_region) + })?; + Ok(tcx.mk_dynamic(relation.relate(a_obj, b_obj)?, region_bound)) + } + + (&ty::Generator(a_id, a_substs, movability), &ty::Generator(b_id, b_substs, _)) + if a_id == b_id => + { + // All Generator types with the same id represent + // the (anonymous) type of the same generator expression. So + // all of their regions should be equated. + let substs = relation.relate(&a_substs, &b_substs)?; + Ok(tcx.mk_generator(a_id, substs, movability)) + } + + (&ty::GeneratorWitness(a_types), &ty::GeneratorWitness(b_types)) => { + // Wrap our types with a temporary GeneratorWitness struct + // inside the binder so we can related them + let a_types = a_types.map_bound(GeneratorWitness); + let b_types = b_types.map_bound(GeneratorWitness); + // Then remove the GeneratorWitness for the result + let types = relation.relate(&a_types, &b_types)?.map_bound(|witness| witness.0); + Ok(tcx.mk_generator_witness(types)) + } + + (&ty::Closure(a_id, a_substs), &ty::Closure(b_id, b_substs)) if a_id == b_id => { + // All Closure types with the same id represent + // the (anonymous) type of the same closure expression. So + // all of their regions should be equated. + let substs = relation.relate(&a_substs, &b_substs)?; + Ok(tcx.mk_closure(a_id, &substs)) + } + + (&ty::RawPtr(ref a_mt), &ty::RawPtr(ref b_mt)) => { + let mt = relation.relate(a_mt, b_mt)?; + Ok(tcx.mk_ptr(mt)) + } + + (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => { + let r = relation.relate_with_variance(ty::Contravariant, &a_r, &b_r)?; + let a_mt = ty::TypeAndMut { ty: a_ty, mutbl: a_mutbl }; + let b_mt = ty::TypeAndMut { ty: b_ty, mutbl: b_mutbl }; + let mt = relation.relate(&a_mt, &b_mt)?; + Ok(tcx.mk_ref(r, mt)) + } + + (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => { + 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. + let sz_a = sz_a.try_eval_usize(tcx, relation.param_env()); + let sz_b = sz_b.try_eval_usize(tcx, relation.param_env()); + match (sz_a, sz_b) { + (Some(sz_a_val), Some(sz_b_val)) => Err(TypeError::FixedArraySize( + expected_found(relation, &sz_a_val, &sz_b_val), + )), + _ => Err(err), + } + } + } + } + + (&ty::Slice(a_t), &ty::Slice(b_t)) => { + let t = relation.relate(&a_t, &b_t)?; + Ok(tcx.mk_slice(t)) + } + + (&ty::Tuple(as_), &ty::Tuple(bs)) => { + if as_.len() == bs.len() { + Ok(tcx.mk_tup( + as_.iter() + .zip(bs) + .map(|(a, b)| relation.relate(&a.expect_ty(), &b.expect_ty())), + )?) + } else if !(as_.is_empty() || bs.is_empty()) { + Err(TypeError::TupleSize(expected_found(relation, &as_.len(), &bs.len()))) + } else { + Err(TypeError::Sorts(expected_found(relation, &a, &b))) + } + } + + (&ty::FnDef(a_def_id, a_substs), &ty::FnDef(b_def_id, b_substs)) + if a_def_id == b_def_id => + { + let substs = relation.relate_item_substs(a_def_id, a_substs, b_substs)?; + Ok(tcx.mk_fn_def(a_def_id, substs)) + } + + (&ty::FnPtr(a_fty), &ty::FnPtr(b_fty)) => { + let fty = relation.relate(&a_fty, &b_fty)?; + Ok(tcx.mk_fn_ptr(fty)) + } + + // 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)?; + Ok(tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs)) + } + + (&ty::Opaque(a_def_id, a_substs), &ty::Opaque(b_def_id, b_substs)) + if a_def_id == b_def_id => + { + let substs = relate_substs(relation, None, a_substs, b_substs)?; + Ok(tcx.mk_opaque(a_def_id, substs)) + } + + _ => Err(TypeError::Sorts(expected_found(relation, &a, &b))), + } +} + +/// The main "const relation" routine. Note that this does not handle +/// inference artifacts, so you should filter those out before calling +/// it. +pub fn super_relate_consts>( + relation: &mut R, + 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>| x.eval(tcx, relation.param_env()).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. + 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(d), _) | (_, ty::ConstKind::Error(d)) => Ok(ty::ConstKind::Error(d)), + + (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => { + return Ok(a); + } + (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) if p1 == p2 => { + return Ok(a); + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => { + let new_val = match (a_val, b_val) { + (ConstValue::Scalar(a_val), ConstValue::Scalar(b_val)) if a.ty == b.ty => { + if a_val == b_val { + Ok(ConstValue::Scalar(a_val)) + } else if let ty::FnPtr(_) = a.ty.kind { + 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 { + Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) + } + } else { + Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) + } + } + + (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 { + Ok(a_val) + } else { + Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))) + } + } + + (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))), + }; + + new_val.map(ty::ConstKind::Value) + } + + // FIXME(const_generics): this is wrong, as it is a projection + ( + ty::ConstKind::Unevaluated(a_def_id, a_substs, a_promoted), + ty::ConstKind::Unevaluated(b_def_id, b_substs, b_promoted), + ) if a_def_id == b_def_id && a_promoted == b_promoted => { + let substs = + relation.relate_with_variance(ty::Variance::Invariant, &a_substs, &b_substs)?; + Ok(ty::ConstKind::Unevaluated(a_def_id, &substs, a_promoted)) + } + _ => Err(TypeError::ConstMismatch(expected_found(relation, &a, &b))), + }; + new_const_val.map(|val| tcx.mk_const(ty::Const { val, ty: a.ty })) +} + +impl<'tcx> Relate<'tcx> for &'tcx ty::List> { + fn relate>( + relation: &mut R, + a: &Self, + b: &Self, + ) -> RelateResult<'tcx, Self> { + if a.len() != b.len() { + return Err(TypeError::ExistentialMismatch(expected_found(relation, a, b))); + } + + let tcx = relation.tcx(); + let v = a.iter().zip(b.iter()).map(|(ep_a, ep_b)| { + use crate::ty::ExistentialPredicate::*; + match (ep_a, ep_b) { + (Trait(ref a), Trait(ref b)) => Ok(Trait(relation.relate(a, b)?)), + (Projection(ref a), Projection(ref b)) => Ok(Projection(relation.relate(a, b)?)), + (AutoTrait(ref a), AutoTrait(ref b)) if a == b => Ok(AutoTrait(*a)), + _ => Err(TypeError::ExistentialMismatch(expected_found(relation, a, b))), + } + }); + Ok(tcx.mk_existential_predicates(v)?) + } +} + +impl<'tcx> Relate<'tcx> for ty::ClosureSubsts<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::ClosureSubsts<'tcx>, + b: &ty::ClosureSubsts<'tcx>, + ) -> RelateResult<'tcx, ty::ClosureSubsts<'tcx>> { + let substs = relate_substs(relation, None, a.substs, b.substs)?; + Ok(ty::ClosureSubsts { substs }) + } +} + +impl<'tcx> Relate<'tcx> for ty::GeneratorSubsts<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::GeneratorSubsts<'tcx>, + b: &ty::GeneratorSubsts<'tcx>, + ) -> RelateResult<'tcx, ty::GeneratorSubsts<'tcx>> { + let substs = relate_substs(relation, None, a.substs, b.substs)?; + Ok(ty::GeneratorSubsts { substs }) + } +} + +impl<'tcx> Relate<'tcx> for SubstsRef<'tcx> { + fn relate>( + relation: &mut R, + a: &SubstsRef<'tcx>, + b: &SubstsRef<'tcx>, + ) -> RelateResult<'tcx, SubstsRef<'tcx>> { + relate_substs(relation, None, a, b) + } +} + +impl<'tcx> Relate<'tcx> for ty::Region<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::Region<'tcx>, + b: &ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + relation.regions(*a, *b) + } +} + +impl<'tcx> Relate<'tcx> for &'tcx ty::Const<'tcx> { + fn relate>( + relation: &mut R, + a: &&'tcx ty::Const<'tcx>, + b: &&'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + relation.consts(*a, *b) + } +} + +impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for ty::Binder { + fn relate>( + relation: &mut R, + a: &ty::Binder, + b: &ty::Binder, + ) -> RelateResult<'tcx, ty::Binder> { + relation.binders(a, b) + } +} + +impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for Rc { + fn relate>( + relation: &mut R, + a: &Rc, + b: &Rc, + ) -> RelateResult<'tcx, Rc> { + let a: &T = a; + let b: &T = b; + Ok(Rc::new(relation.relate(a, b)?)) + } +} + +impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for Box { + fn relate>( + relation: &mut R, + a: &Box, + b: &Box, + ) -> RelateResult<'tcx, Box> { + let a: &T = a; + let b: &T = b; + Ok(Box::new(relation.relate(a, b)?)) + } +} + +impl<'tcx> Relate<'tcx> for GenericArg<'tcx> { + fn relate>( + relation: &mut R, + a: &GenericArg<'tcx>, + b: &GenericArg<'tcx>, + ) -> RelateResult<'tcx, GenericArg<'tcx>> { + match (a.unpack(), b.unpack()) { + (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => { + Ok(relation.relate(&a_lt, &b_lt)?.into()) + } + (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => { + Ok(relation.relate(&a_ty, &b_ty)?.into()) + } + (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => { + Ok(relation.relate(&a_ct, &b_ct)?.into()) + } + (GenericArgKind::Lifetime(unpacked), x) => { + bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (GenericArgKind::Type(unpacked), x) => { + bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (GenericArgKind::Const(unpacked), x) => { + bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + } + } +} + +impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::TraitPredicate<'tcx>, + b: &ty::TraitPredicate<'tcx>, + ) -> RelateResult<'tcx, ty::TraitPredicate<'tcx>> { + Ok(ty::TraitPredicate { trait_ref: relation.relate(&a.trait_ref, &b.trait_ref)? }) + } +} + +impl<'tcx> Relate<'tcx> for ty::ProjectionPredicate<'tcx> { + fn relate>( + relation: &mut R, + a: &ty::ProjectionPredicate<'tcx>, + b: &ty::ProjectionPredicate<'tcx>, + ) -> RelateResult<'tcx, ty::ProjectionPredicate<'tcx>> { + Ok(ty::ProjectionPredicate { + projection_ty: relation.relate(&a.projection_ty, &b.projection_ty)?, + ty: relation.relate(&a.ty, &b.ty)?, + }) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Error handling + +pub fn expected_found(relation: &mut R, a: &T, b: &T) -> ExpectedFound +where + R: TypeRelation<'tcx>, + T: Clone, +{ + expected_found_bool(relation.a_is_expected(), a, b) +} + +pub fn expected_found_bool(a_is_expected: bool, a: &T, b: &T) -> ExpectedFound +where + T: Clone, +{ + let a = a.clone(); + let b = b.clone(); + if a_is_expected { + ExpectedFound { expected: a, found: b } + } else { + ExpectedFound { expected: b, found: a } + } +} 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..f736037b5c15a --- /dev/null +++ b/src/librustc_middle/ty/structural_impls.rs @@ -0,0 +1,1132 @@ +//! This module contains implements of the `Lift` and `TypeFoldable` +//! traits for various types in the Rust compiler. Most are written by +//! hand, though we've recently added some macros and proc-macros to help with the tedium. + +use crate::mir::interpret; +use crate::mir::ProjectionKind; +use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; +use crate::ty::print::{FmtPrinter, Printer}; +use crate::ty::{self, InferConst, Lift, Ty, TyCtxt}; +use rustc_hir as hir; +use rustc_hir::def::Namespace; +use rustc_hir::def_id::CRATE_DEF_INDEX; +use rustc_index::vec::{Idx, IndexVec}; + +use smallvec::SmallVec; +use std::fmt; +use std::rc::Rc; +use std::sync::Arc; + +impl fmt::Debug for ty::TraitDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.def_id, &[])?; + Ok(()) + }) + } +} + +impl fmt::Debug for ty::AdtDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ty::tls::with(|tcx| { + FmtPrinter::new(tcx, f, Namespace::TypeNS).print_def_path(self.did, &[])?; + Ok(()) + }) + } +} + +impl fmt::Debug for ty::UpvarId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let name = ty::tls::with(|tcx| tcx.hir().name(self.var_path.hir_id)); + write!(f, "UpvarId({:?};`{}`;{:?})", self.var_path.hir_id, name, self.closure_expr_id) + } +} + +impl fmt::Debug for ty::UpvarBorrow<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UpvarBorrow({:?}, {:?})", self.kind, self.region) + } +} + +impl fmt::Debug for ty::ExistentialTraitRef<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Debug for ty::adjustment::Adjustment<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?} -> {}", self.kind, self.target) + } +} + +impl fmt::Debug for ty::BoundRegion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::BrAnon(n) => write!(f, "BrAnon({:?})", n), + ty::BrNamed(did, name) => { + if did.index == CRATE_DEF_INDEX { + write!(f, "BrNamed({})", name) + } else { + write!(f, "BrNamed({:?}, {})", did, name) + } + } + ty::BrEnv => write!(f, "BrEnv"), + } + } +} + +impl fmt::Debug for ty::RegionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::ReEarlyBound(ref data) => write!(f, "ReEarlyBound({}, {})", data.index, data.name), + + ty::ReLateBound(binder_id, ref bound_region) => { + write!(f, "ReLateBound({:?}, {:?})", binder_id, bound_region) + } + + ty::ReFree(ref fr) => fr.fmt(f), + + ty::ReStatic => write!(f, "ReStatic"), + + ty::ReVar(ref vid) => vid.fmt(f), + + ty::RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder), + + ty::ReEmpty(ui) => write!(f, "ReEmpty({:?})", ui), + + ty::ReErased => write!(f, "ReErased"), + } + } +} + +impl fmt::Debug for ty::FreeRegion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ReFree({:?}, {:?})", self.scope, self.bound_region) + } +} + +impl fmt::Debug for ty::Variance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match *self { + ty::Covariant => "+", + ty::Contravariant => "-", + ty::Invariant => "o", + ty::Bivariant => "*", + }) + } +} + +impl fmt::Debug for ty::FnSig<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output()) + } +} + +impl fmt::Debug for ty::TyVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}t", self.index) + } +} + +impl<'tcx> fmt::Debug for ty::ConstVid<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}c", self.index) + } +} + +impl fmt::Debug for ty::IntVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}i", self.index) + } +} + +impl fmt::Debug for ty::FloatVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "_#{}f", self.index) + } +} + +impl fmt::Debug for ty::RegionVid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "'_#{}r", self.index()) + } +} + +impl fmt::Debug for ty::InferTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::TyVar(ref v) => v.fmt(f), + ty::IntVar(ref v) => v.fmt(f), + ty::FloatVar(ref v) => v.fmt(f), + ty::FreshTy(v) => write!(f, "FreshTy({:?})", v), + ty::FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), + ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v), + } + } +} + +impl fmt::Debug for ty::IntVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::IntType(ref v) => v.fmt(f), + ty::UintType(ref v) => v.fmt(f), + } + } +} + +impl fmt::Debug for ty::FloatVarValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Debug for ty::TraitRef<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Debug for Ty<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Debug for ty::ParamTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}/#{}", self.name, self.index) + } +} + +impl fmt::Debug for ty::ParamConst { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}/#{}", self.name, self.index) + } +} + +impl fmt::Debug for ty::TraitPredicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraitPredicate({:?})", self.trait_ref) + } +} + +impl fmt::Debug for ty::ProjectionPredicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_ty, self.ty) + } +} + +impl fmt::Debug for ty::Predicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.kind()) + } +} + +impl fmt::Debug for ty::PredicateKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + ty::PredicateKind::Trait(ref a, constness) => { + if let hir::Constness::Const = constness { + write!(f, "const ")?; + } + a.fmt(f) + } + ty::PredicateKind::Subtype(ref pair) => pair.fmt(f), + ty::PredicateKind::RegionOutlives(ref pair) => pair.fmt(f), + ty::PredicateKind::TypeOutlives(ref pair) => pair.fmt(f), + ty::PredicateKind::Projection(ref pair) => pair.fmt(f), + ty::PredicateKind::WellFormed(data) => write!(f, "WellFormed({:?})", data), + ty::PredicateKind::ObjectSafe(trait_def_id) => { + write!(f, "ObjectSafe({:?})", trait_def_id) + } + ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { + write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind) + } + ty::PredicateKind::ConstEvaluatable(def_id, substs) => { + write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs) + } + ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2), + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Atomic structs +// +// For things that don't carry any arena-allocated data (and are +// copy...), just add them to this list. + +CloneTypeFoldableAndLiftImpls! { + (), + bool, + usize, + ::rustc_target::abi::VariantIdx, + u64, + String, + crate::middle::region::Scope, + ::rustc_ast::ast::FloatTy, + ::rustc_ast::ast::InlineAsmOptions, + ::rustc_ast::ast::InlineAsmTemplatePiece, + ::rustc_ast::ast::NodeId, + ::rustc_span::symbol::Symbol, + ::rustc_hir::def::Res, + ::rustc_hir::def_id::DefId, + ::rustc_hir::LlvmInlineAsmInner, + ::rustc_hir::MatchSource, + ::rustc_hir::Mutability, + ::rustc_hir::Unsafety, + ::rustc_target::asm::InlineAsmRegOrRegClass, + ::rustc_target::spec::abi::Abi, + crate::mir::Local, + crate::mir::Promoted, + crate::traits::Reveal, + crate::ty::adjustment::AutoBorrowMutability, + crate::ty::AdtKind, + // Including `BoundRegion` is a *bit* dubious, but direct + // references to bound region appear in `ty::Error`, and aren't + // really meant to be folded. In general, we can only fold a fully + // general `Region`. + crate::ty::BoundRegion, + crate::ty::Placeholder, + crate::ty::ClosureKind, + crate::ty::FreeRegion, + crate::ty::InferTy, + crate::ty::IntVarValue, + crate::ty::ParamConst, + crate::ty::ParamTy, + crate::ty::adjustment::PointerCast, + crate::ty::RegionVid, + crate::ty::UniverseIndex, + crate::ty::Variance, + ::rustc_span::Span, +} + +/////////////////////////////////////////////////////////////////////////// +// Lift implementations + +// FIXME(eddyb) replace all the uses of `Option::map` with `?`. +impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) { + type Lifted = (A::Lifted, B::Lifted); + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.0).and_then(|a| tcx.lift(&self.1).map(|b| (a, b))) + } +} + +impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) { + type Lifted = (A::Lifted, B::Lifted, C::Lifted); + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.0) + .and_then(|a| tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { + type Lifted = Option; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + Some(ref x) => tcx.lift(x).map(Some), + None => Some(None), + } + } +} + +impl<'tcx, T: Lift<'tcx>, E: Lift<'tcx>> Lift<'tcx> for Result { + type Lifted = Result; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + Ok(ref x) => tcx.lift(x).map(Ok), + Err(ref e) => tcx.lift(e).map(Err), + } + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { + type Lifted = Box; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Box::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { + type Lifted = Rc; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Rc::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Arc { + type Lifted = Arc; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Arc::new) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] { + type Lifted = Vec; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + // type annotation needed to inform `projection_must_outlive` + let mut result: Vec<>::Lifted> = Vec::with_capacity(self.len()); + for x in self { + if let Some(value) = tcx.lift(x) { + result.push(value); + } else { + return None; + } + } + Some(result) + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Vec { + type Lifted = Vec; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self[..]) + } +} + +impl<'tcx, I: Idx, T: Lift<'tcx>> Lift<'tcx> for IndexVec { + type Lifted = IndexVec; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + self.iter().map(|e| tcx.lift(e)).collect() + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::TraitRef<'a> { + type Lifted = ty::TraitRef<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::TraitRef { def_id: self.def_id, substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialTraitRef<'a> { + type Lifted = ty::ExistentialTraitRef<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::ExistentialTraitRef { def_id: self.def_id, substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialPredicate<'a> { + type Lifted = ty::ExistentialPredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match self { + ty::ExistentialPredicate::Trait(x) => tcx.lift(x).map(ty::ExistentialPredicate::Trait), + ty::ExistentialPredicate::Projection(x) => { + tcx.lift(x).map(ty::ExistentialPredicate::Projection) + } + ty::ExistentialPredicate::AutoTrait(def_id) => { + Some(ty::ExistentialPredicate::AutoTrait(*def_id)) + } + } + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> { + type Lifted = ty::TraitPredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitPredicate { trait_ref }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> { + type Lifted = ty::SubtypePredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&(self.a, self.b)).map(|(a, b)| ty::SubtypePredicate { + a_is_expected: self.a_is_expected, + a, + b, + }) + } +} + +impl<'tcx, A: Copy + Lift<'tcx>, B: Copy + Lift<'tcx>> Lift<'tcx> for ty::OutlivesPredicate { + type Lifted = ty::OutlivesPredicate; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&(self.0, self.1)).map(|(a, b)| ty::OutlivesPredicate(a, b)) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionTy<'a> { + type Lifted = ty::ProjectionTy<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&self.substs) + .map(|substs| ty::ProjectionTy { item_def_id: self.item_def_id, substs }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionPredicate<'a> { + type Lifted = ty::ProjectionPredicate<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(&(self.projection_ty, self.ty)) + .map(|(projection_ty, ty)| ty::ProjectionPredicate { projection_ty, ty }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialProjection<'a> { + type Lifted = ty::ExistentialProjection<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&self.substs).map(|substs| ty::ExistentialProjection { + substs, + ty: tcx.lift(&self.ty).expect("type must lift when substs do"), + item_def_id: self.item_def_id, + }) + } +} + +impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> { + type Lifted = ty::PredicateKind<'tcx>; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + match *self { + ty::PredicateKind::Trait(ref binder, constness) => { + tcx.lift(binder).map(|binder| ty::PredicateKind::Trait(binder, constness)) + } + ty::PredicateKind::Subtype(ref binder) => { + tcx.lift(binder).map(ty::PredicateKind::Subtype) + } + ty::PredicateKind::RegionOutlives(ref binder) => { + tcx.lift(binder).map(ty::PredicateKind::RegionOutlives) + } + ty::PredicateKind::TypeOutlives(ref binder) => { + tcx.lift(binder).map(ty::PredicateKind::TypeOutlives) + } + ty::PredicateKind::Projection(ref binder) => { + tcx.lift(binder).map(ty::PredicateKind::Projection) + } + ty::PredicateKind::WellFormed(ty) => tcx.lift(&ty).map(ty::PredicateKind::WellFormed), + ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { + tcx.lift(&closure_substs).map(|closure_substs| { + ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) + }) + } + ty::PredicateKind::ObjectSafe(trait_def_id) => { + Some(ty::PredicateKind::ObjectSafe(trait_def_id)) + } + ty::PredicateKind::ConstEvaluatable(def_id, substs) => { + tcx.lift(&substs).map(|substs| ty::PredicateKind::ConstEvaluatable(def_id, substs)) + } + ty::PredicateKind::ConstEquate(c1, c2) => { + tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::PredicateKind::ConstEquate(c1, c2)) + } + } + } +} + +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder { + type Lifted = ty::Binder; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.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 ty::Predicate<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let new = ty::PredicateKind::super_fold_with(&self.inner.kind, folder); + if new != self.inner.kind { folder.tcx().mk_predicate(new) } else { *self } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + ty::PredicateKind::super_visit_with(&self.inner.kind, visitor) + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_predicate(*self) + } + + fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { + self.inner.outer_exclusive_binder > binder + } + + fn has_type_flags(&self, flags: ty::TypeFlags) -> bool { + self.inner.flags.intersects(flags) + } +} + +pub(super) trait PredicateVisitor<'tcx>: TypeVisitor<'tcx> { + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool; +} + +impl> PredicateVisitor<'tcx> for T { + default fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool { + predicate.super_visit_with(self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + fold_list(*self, folder, |tcx, v| tcx.intern_predicates(v)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|p| p.visit_with(visitor)) + } +} + +impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec { + fn super_fold_with>(&self, folder: &mut F) -> Self { + self.iter().map(|x| x.fold_with(folder)).collect() + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.iter().any(|t| t.visit_with(visitor)) + } +} + +impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + let ty = self.ty.fold_with(folder); + let val = self.val.fold_with(folder); + if ty != self.ty || val != self.val { + folder.tcx().mk_const(ty::Const { ty, val }) + } else { + *self + } + } + + fn fold_with>(&self, folder: &mut F) -> Self { + folder.fold_const(*self) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.ty.visit_with(visitor) || self.val.visit_with(visitor) + } + + fn visit_with>(&self, visitor: &mut V) -> bool { + visitor.visit_const(self) + } +} + +impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { + fn super_fold_with>(&self, folder: &mut F) -> Self { + match *self { + ty::ConstKind::Infer(ic) => ty::ConstKind::Infer(ic.fold_with(folder)), + ty::ConstKind::Param(p) => ty::ConstKind::Param(p.fold_with(folder)), + ty::ConstKind::Unevaluated(did, substs, promoted) => { + ty::ConstKind::Unevaluated(did, substs.fold_with(folder), promoted) + } + ty::ConstKind::Value(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(..) + | ty::ConstKind::Error(_) => *self, + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + match *self { + ty::ConstKind::Infer(ic) => ic.visit_with(visitor), + ty::ConstKind::Param(p) => p.visit_with(visitor), + ty::ConstKind::Unevaluated(_, substs, _) => substs.visit_with(visitor), + ty::ConstKind::Value(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Error(_) => false, + } + } +} + +impl<'tcx> TypeFoldable<'tcx> for InferConst<'tcx> { + fn super_fold_with>(&self, _folder: &mut F) -> Self { + *self + } + + fn super_visit_with>(&self, _visitor: &mut V) -> bool { + false + } +} + +// Does the equivalent of +// ``` +// let v = self.iter().map(|p| p.fold_with(folder)).collect::>(); +// folder.tcx().intern_*(&v) +// ``` +fn fold_list<'tcx, F, T>( + list: &'tcx ty::List, + folder: &mut F, + intern: impl FnOnce(TyCtxt<'tcx>, &[T]) -> &'tcx ty::List, +) -> &'tcx ty::List +where + F: TypeFolder<'tcx>, + T: TypeFoldable<'tcx> + PartialEq + Copy, +{ + let mut iter = list.iter(); + // Look for the first element that changed + if let Some((i, new_t)) = iter.by_ref().enumerate().find_map(|(i, t)| { + let new_t = t.fold_with(folder); + if new_t == t { None } else { Some((i, new_t)) } + }) { + // An element changed, prepare to intern the resulting list + let mut new_list = SmallVec::<[_; 8]>::with_capacity(list.len()); + new_list.extend_from_slice(&list[..i]); + new_list.push(new_t); + new_list.extend(iter.map(|t| t.fold_with(folder))); + intern(folder.tcx(), &new_list) + } else { + list + } +} diff --git a/src/librustc/ty/sty.rs b/src/librustc_middle/ty/sty.rs similarity index 82% rename from src/librustc/ty/sty.rs rename to src/librustc_middle/ty/sty.rs index 76b9aed831482..1d680c3563675 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -6,63 +6,40 @@ use self::InferTy::*; use self::TyKind::*; use crate::infer::canonical::Canonical; -use crate::middle::region; use crate::mir::interpret::ConstValue; -use crate::mir::interpret::Scalar; +use crate::mir::interpret::{LitToConstInput, Scalar}; use crate::mir::Promoted; -use crate::ty::layout::VariantIdx; -use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef}; +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; +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; use std::ops::Range; +use ty::util::IntTypeExt; -#[derive( - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Debug, - RustcEncodable, - RustcDecodable, - HashStable, - TypeFoldable, - Lift -)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable, TypeFoldable, Lift)] pub struct TypeAndMut<'tcx> { pub ty: Ty<'tcx>, pub mutbl: hir::Mutability, } -#[derive( - Clone, - PartialEq, - PartialOrd, - Eq, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - Copy, - HashStable -)] +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable, RustcDecodable, Copy)] +#[derive(HashStable)] /// A "free" region `fr` can be interpreted as "some region /// at least as big as the scope `fr.scope`". pub struct FreeRegion { @@ -70,18 +47,8 @@ pub struct FreeRegion { pub bound_region: BoundRegion, } -#[derive( - Clone, - PartialEq, - PartialOrd, - Eq, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - Copy, - HashStable -)] +#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable, RustcDecodable, Copy)] +#[derive(HashStable)] pub enum BoundRegion { /// An anonymous region parameter for a given fn (&T) BrAnon(u32), @@ -119,18 +86,8 @@ impl BoundRegion { /// N.B., if you change this, you'll probably want to change the corresponding /// AST structure in `librustc_ast/ast.rs` as well. -#[derive( - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - HashStable, - Debug -)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)] +#[derive(HashStable)] #[rustc_diagnostic_item = "TyKind"] pub enum TyKind<'tcx> { /// The primitive boolean type. Written as `bool`. @@ -224,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, @@ -251,24 +203,26 @@ pub enum TyKind<'tcx> { /// A placeholder for a type which could not be computed; this is /// propagated to avoid useless error messages. - Error, + Error(DelaySpanBugEmitted), } +/// A type that is not publicly constructable. This prevents people from making `TyKind::Error` +/// except through `tcx.err*()`. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[derive(RustcEncodable, RustcDecodable, HashStable)] +pub struct DelaySpanBugEmitted(pub(super) ()); + // `TyKind` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] static_assert_size!(TyKind<'_>, 24); /// A closure can be modeled as a struct that looks like: /// -/// struct Closure<'l0...'li, T0...Tj, CK, CS, U0...Uk> { -/// upvar0: U0, -/// ... -/// upvark: Uk -/// } +/// struct Closure<'l0...'li, T0...Tj, CK, CS, U>(...U); /// /// where: /// -/// - 'l0...'li and T0...Tj are the lifetime and type parameters +/// - 'l0...'li and T0...Tj are the generic parameters /// in scope on the function that defined the closure, /// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This /// is rather hackily encoded via a scalar type. See @@ -277,9 +231,9 @@ static_assert_size!(TyKind<'_>, 24); /// type. For example, `fn(u32, u32) -> u32` would mean that the closure /// implements `CK<(u32, u32), Output = u32>`, where `CK` is the trait /// specified above. -/// - U0...Uk are type parameters representing the types of its upvars -/// (borrowed, if appropriate; that is, if Ui represents a by-ref upvar, -/// and the up-var has the type `Foo`, then `Ui = &Foo`). +/// - U is a type parameter representing the types of its upvars, tupled up +/// (borrowed, if appropriate; that is, if an U field represents a by-ref upvar, +/// and the up-var has the type `Foo`, then that field of U will be `&Foo`). /// /// So, for example, given this function: /// @@ -289,9 +243,7 @@ static_assert_size!(TyKind<'_>, 24); /// /// the type of the closure would be something like: /// -/// struct Closure<'a, T, U0> { -/// data: U0 -/// } +/// struct Closure<'a, T, U>(...U); /// /// Note that the type of the upvar is not specified in the struct. /// You may wonder how the impl would then be able to use the upvar, @@ -299,7 +251,7 @@ static_assert_size!(TyKind<'_>, 24); /// (conceptually) not fully generic over Closure but rather tied to /// instances with the expected upvar types: /// -/// impl<'b, 'a, T> FnMut() for Closure<'a, T, &'b mut &'a mut T> { +/// impl<'b, 'a, T> FnMut() for Closure<'a, T, (&'b mut &'a mut T,)> { /// ... /// } /// @@ -308,7 +260,7 @@ static_assert_size!(TyKind<'_>, 24); /// (Here, I am assuming that `data` is mut-borrowed.) /// /// Now, the last question you may ask is: Why include the upvar types -/// as extra type parameters? The reason for this design is that the +/// in an extra type parameter? The reason for this design is that the /// upvar types can reference lifetimes that are internal to the /// creating function. In my example above, for example, the lifetime /// `'b` represents the scope of the closure itself; this is some @@ -360,7 +312,7 @@ static_assert_size!(TyKind<'_>, 24); #[derive(Copy, Clone, Debug, TypeFoldable)] pub struct ClosureSubsts<'tcx> { /// Lifetime and type parameters from the enclosing function, - /// concatenated with the types of the upvars. + /// concatenated with a tuple containing the types of the upvars. /// /// These are separated out because codegen wants to pass them around /// when monomorphizing. @@ -370,54 +322,52 @@ pub struct ClosureSubsts<'tcx> { /// Struct returned by `split()`. Note that these are subslices of the /// parent slice and not canonical substs themselves. struct SplitClosureSubsts<'tcx> { - closure_kind_ty: Ty<'tcx>, - closure_sig_ty: Ty<'tcx>, - upvar_kinds: &'tcx [GenericArg<'tcx>], + closure_kind_ty: GenericArg<'tcx>, + closure_sig_as_fn_ptr_ty: GenericArg<'tcx>, + tupled_upvars_ty: GenericArg<'tcx>, } impl<'tcx> ClosureSubsts<'tcx> { /// Divides the closure substs into their respective /// components. Single source of truth with respect to the /// ordering. - fn split(self, def_id: DefId, tcx: TyCtxt<'_>) -> SplitClosureSubsts<'tcx> { - let generics = tcx.generics_of(def_id); - let parent_len = generics.parent_count; - SplitClosureSubsts { - closure_kind_ty: self.substs.type_at(parent_len), - closure_sig_ty: self.substs.type_at(parent_len + 1), - upvar_kinds: &self.substs[parent_len + 2..], + fn split(self) -> SplitClosureSubsts<'tcx> { + match self.substs[..] { + [.., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => { + SplitClosureSubsts { closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty } + } + _ => bug!("closure substs missing synthetics"), } } + /// Returns `true` only if enough of the synthetic types are known to + /// allow using all of the methods on `ClosureSubsts` without panicking. + /// + /// Used primarily by `ty::print::pretty` to be able to handle closure + /// types that haven't had their synthetic types substituted in. + pub fn is_valid(self) -> bool { + self.substs.len() >= 3 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_)) + } + #[inline] - pub fn upvar_tys( - self, - def_id: DefId, - tcx: TyCtxt<'_>, - ) -> impl Iterator> + 'tcx { - let SplitClosureSubsts { upvar_kinds, .. } = self.split(def_id, tcx); - upvar_kinds.iter().map(|t| { - if let GenericArgKind::Type(ty) = t.unpack() { - ty - } else { - bug!("upvar should be type") - } - }) + pub fn upvar_tys(self) -> impl Iterator> + 'tcx { + self.split().tupled_upvars_ty.expect_ty().tuple_fields() } /// Returns the closure kind for this closure; may return a type /// variable during inference. To get the closure kind during - /// inference, use `infcx.closure_kind(def_id, substs)`. - pub fn kind_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { - self.split(def_id, tcx).closure_kind_ty + /// inference, use `infcx.closure_kind(substs)`. + pub fn kind_ty(self) -> Ty<'tcx> { + self.split().closure_kind_ty.expect_ty() } - /// Returns the type representing the closure signature for this - /// closure; may contain type variables during inference. To get - /// the closure signature during inference, use - /// `infcx.fn_sig(def_id)`. - pub fn sig_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { - self.split(def_id, tcx).closure_sig_ty + /// Returns the `fn` pointer type representing the closure signature for this + /// closure. + // FIXME(eddyb) this should be unnecessary, as the shallowly resolved + // type is known at the time of the creation of `ClosureSubsts`, + // see `rustc_typeck::check::closure`. + pub fn sig_as_fn_ptr_ty(self) -> Ty<'tcx> { + self.split().closure_sig_as_fn_ptr_ty.expect_ty() } /// Returns the closure kind for this closure; only usable outside @@ -425,20 +375,16 @@ impl<'tcx> ClosureSubsts<'tcx> { /// there are no type variables. /// /// If you have an inference context, use `infcx.closure_kind()`. - pub fn kind(self, def_id: DefId, tcx: TyCtxt<'tcx>) -> ty::ClosureKind { - self.split(def_id, tcx).closure_kind_ty.to_opt_closure_kind().unwrap() + pub fn kind(self) -> ty::ClosureKind { + self.kind_ty().to_opt_closure_kind().unwrap() } - /// Extracts the signature from the closure; only usable outside - /// of an inference context, because in that context we know that - /// there are no type variables. - /// - /// If you have an inference context, use `infcx.closure_sig()`. - pub fn sig(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> ty::PolyFnSig<'tcx> { - let ty = self.sig_ty(def_id, tcx); + /// Extracts the signature from the closure. + pub fn sig(self) -> ty::PolyFnSig<'tcx> { + let ty = self.sig_as_fn_ptr_ty(); match ty.kind { ty::FnPtr(sig) => sig, - _ => bug!("closure_sig_ty is not a fn-ptr: {:?}", ty.kind), + _ => bug!("closure_sig_as_fn_ptr_ty is not a fn-ptr: {:?}", ty.kind), } } } @@ -450,64 +396,59 @@ pub struct GeneratorSubsts<'tcx> { } struct SplitGeneratorSubsts<'tcx> { - resume_ty: Ty<'tcx>, - yield_ty: Ty<'tcx>, - return_ty: Ty<'tcx>, - witness: Ty<'tcx>, - upvar_kinds: &'tcx [GenericArg<'tcx>], + resume_ty: GenericArg<'tcx>, + yield_ty: GenericArg<'tcx>, + return_ty: GenericArg<'tcx>, + witness: GenericArg<'tcx>, + tupled_upvars_ty: GenericArg<'tcx>, } impl<'tcx> GeneratorSubsts<'tcx> { - fn split(self, def_id: DefId, tcx: TyCtxt<'_>) -> SplitGeneratorSubsts<'tcx> { - let generics = tcx.generics_of(def_id); - let parent_len = generics.parent_count; - SplitGeneratorSubsts { - resume_ty: self.substs.type_at(parent_len), - yield_ty: self.substs.type_at(parent_len + 1), - return_ty: self.substs.type_at(parent_len + 2), - witness: self.substs.type_at(parent_len + 3), - upvar_kinds: &self.substs[parent_len + 4..], + fn split(self) -> SplitGeneratorSubsts<'tcx> { + match self.substs[..] { + [.., resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty] => { + SplitGeneratorSubsts { resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty } + } + _ => bug!("generator substs missing synthetics"), } } + /// Returns `true` only if enough of the synthetic types are known to + /// allow using all of the methods on `GeneratorSubsts` without panicking. + /// + /// Used primarily by `ty::print::pretty` to be able to handle generator + /// types that haven't had their synthetic types substituted in. + pub fn is_valid(self) -> bool { + self.substs.len() >= 5 && matches!(self.split().tupled_upvars_ty.expect_ty().kind, Tuple(_)) + } + /// This describes the types that can be contained in a generator. /// It will be a type variable initially and unified in the last stages of typeck of a body. /// It contains a tuple of all the types that could end up on a generator frame. /// The state transformation MIR pass may only produce layouts which mention types /// in this tuple. Upvars are not counted here. - pub fn witness(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { - self.split(def_id, tcx).witness + pub fn witness(self) -> Ty<'tcx> { + self.split().witness.expect_ty() } #[inline] - pub fn upvar_tys( - self, - def_id: DefId, - tcx: TyCtxt<'_>, - ) -> impl Iterator> + 'tcx { - let SplitGeneratorSubsts { upvar_kinds, .. } = self.split(def_id, tcx); - upvar_kinds.iter().map(|t| { - if let GenericArgKind::Type(ty) = t.unpack() { - ty - } else { - bug!("upvar should be type") - } - }) + pub fn upvar_tys(self) -> impl Iterator> + 'tcx { + self.split().tupled_upvars_ty.expect_ty().tuple_fields() } /// Returns the type representing the resume type of the generator. - pub fn resume_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { - self.split(def_id, tcx).resume_ty + pub fn resume_ty(self) -> Ty<'tcx> { + self.split().resume_ty.expect_ty() } /// Returns the type representing the yield type of the generator. - pub fn yield_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { - self.split(def_id, tcx).yield_ty + pub fn yield_ty(self) -> Ty<'tcx> { + self.split().yield_ty.expect_ty() } /// Returns the type representing the return type of the generator. - pub fn return_ty(self, def_id: DefId, tcx: TyCtxt<'_>) -> Ty<'tcx> { - self.split(def_id, tcx).return_ty + pub fn return_ty(self) -> Ty<'tcx> { + self.split().return_ty.expect_ty() } /// Returns the "generator signature", which consists of its yield @@ -516,17 +457,17 @@ impl<'tcx> GeneratorSubsts<'tcx> { /// N.B., some bits of the code prefers to see this wrapped in a /// binder, but it never contains bound regions. Probably this /// function should be removed. - pub fn poly_sig(self, def_id: DefId, tcx: TyCtxt<'_>) -> PolyGenSig<'tcx> { - ty::Binder::dummy(self.sig(def_id, tcx)) + pub fn poly_sig(self) -> PolyGenSig<'tcx> { + ty::Binder::dummy(self.sig()) } /// Returns the "generator signature", which consists of its resume, yield /// and return types. - pub fn sig(self, def_id: DefId, tcx: TyCtxt<'_>) -> GenSig<'tcx> { + pub fn sig(self) -> GenSig<'tcx> { ty::GenSig { - resume_ty: self.resume_ty(def_id, tcx), - yield_ty: self.yield_ty(def_id, tcx), - return_ty: self.return_ty(def_id, tcx), + resume_ty: self.resume_ty(), + yield_ty: self.yield_ty(), + return_ty: self.return_ty(), } } } @@ -618,8 +559,8 @@ impl<'tcx> GeneratorSubsts<'tcx> { /// This is the types of the fields of a generator which are not stored in a /// variant. #[inline] - pub fn prefix_tys(self, def_id: DefId, tcx: TyCtxt<'tcx>) -> impl Iterator> { - self.upvar_tys(def_id, tcx) + pub fn prefix_tys(self) -> impl Iterator> { + self.upvar_tys() } } @@ -631,22 +572,12 @@ pub enum UpvarSubsts<'tcx> { impl<'tcx> UpvarSubsts<'tcx> { #[inline] - pub fn upvar_tys( - self, - def_id: DefId, - tcx: TyCtxt<'tcx>, - ) -> impl Iterator> + 'tcx { - let upvar_kinds = match self { - UpvarSubsts::Closure(substs) => substs.as_closure().split(def_id, tcx).upvar_kinds, - UpvarSubsts::Generator(substs) => substs.as_generator().split(def_id, tcx).upvar_kinds, + pub fn upvar_tys(self) -> impl Iterator> + 'tcx { + let tupled_upvars_ty = match self { + UpvarSubsts::Closure(substs) => substs.as_closure().split().tupled_upvars_ty, + UpvarSubsts::Generator(substs) => substs.as_generator().split().tupled_upvars_ty, }; - upvar_kinds.iter().map(|t| { - if let GenericArgKind::Type(ty) = t.unpack() { - ty - } else { - bug!("upvar should be type") - } - }) + tupled_upvars_ty.expect_ty().tuple_fields() } } @@ -687,15 +618,16 @@ impl<'tcx> Binder> { use crate::ty::ToPredicate; match *self.skip_binder() { ExistentialPredicate::Trait(tr) => { - Binder(tr).with_self_ty(tcx, self_ty).without_const().to_predicate() + Binder(tr).with_self_ty(tcx, self_ty).without_const().to_predicate(tcx) } ExistentialPredicate::Projection(p) => { - ty::Predicate::Projection(Binder(p.with_self_ty(tcx, self_ty))) + ty::PredicateKind::Projection(Binder(p.with_self_ty(tcx, self_ty))) + .to_predicate(tcx) } ExistentialPredicate::AutoTrait(did) => { let trait_ref = Binder(ty::TraitRef { def_id: did, substs: tcx.mk_substs_trait(self_ty, &[]) }); - trait_ref.without_const().to_predicate() + trait_ref.without_const().to_predicate(tcx) } } } @@ -744,7 +676,7 @@ impl<'tcx> List> { pub fn projection_bounds<'a>( &'a self, ) -> impl Iterator> + 'a { - self.iter().filter_map(|predicate| match *predicate { + self.iter().filter_map(|predicate| match predicate { ExistentialPredicate::Projection(projection) => Some(projection), _ => None, }) @@ -752,7 +684,7 @@ impl<'tcx> List> { #[inline] pub fn auto_traits<'a>(&'a self) -> impl Iterator + 'a { - self.iter().filter_map(|predicate| match *predicate { + self.iter().filter_map(|predicate| match predicate { ExistentialPredicate::AutoTrait(did) => Some(did), _ => None, }) @@ -783,7 +715,7 @@ impl<'tcx> Binder<&'tcx List>> { pub fn iter<'a>( &'a self, ) -> impl DoubleEndedIterator>> + 'tcx { - self.skip_binder().iter().cloned().map(Binder::bind) + self.skip_binder().iter().map(Binder::bind) } } @@ -798,10 +730,6 @@ impl<'tcx> Binder<&'tcx List>> { /// /// Trait references also appear in object types like `Foo`, but in /// that case the `Self` parameter is absent from the substitutions. -/// -/// Note that a `TraitRef` introduces a level of region binding, to -/// account for higher-ranked trait bounds like `T: for<'a> Foo<&'a U>` -/// or higher-ranked object types. #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] #[derive(HashStable, TypeFoldable)] pub struct TraitRef<'tcx> { @@ -825,14 +753,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, @@ -847,8 +767,8 @@ impl<'tcx> TraitRef<'tcx> { pub type PolyTraitRef<'tcx> = Binder>; impl<'tcx> PolyTraitRef<'tcx> { - pub fn self_ty(&self) -> Ty<'tcx> { - self.skip_binder().self_ty() + pub fn self_ty(&self) -> Binder> { + self.map_bound_ref(|tr| tr.self_ty()) } pub fn def_id(&self) -> DefId { @@ -876,14 +796,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>, @@ -1174,18 +1086,8 @@ impl<'tcx> PolyFnSig<'tcx> { pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder>>; -#[derive( - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - HashStable -)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] pub struct ParamTy { pub index: u32, pub name: Symbol, @@ -1209,18 +1111,8 @@ impl<'tcx> ParamTy { } } -#[derive( - Copy, - Clone, - Hash, - RustcEncodable, - RustcDecodable, - Eq, - PartialEq, - Ord, - PartialOrd, - HashStable -)] +#[derive(Copy, Clone, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq, Ord, PartialOrd)] +#[derive(HashStable)] pub struct ParamConst { pub index: u32, pub name: Symbol, @@ -1289,17 +1181,15 @@ rustc_index::newtype_index! { pub type Region<'tcx> = &'tcx RegionKind; -/// Representation of (lexical) regions. Note that the NLL checker -/// uses a distinct representation of regions. For this reason, it -/// internally replaces all the regions with inference variables -- -/// the index of the variable is then used to index into internal NLL -/// data structures. See `rustc_mir::borrow_check` module for more -/// information. +/// Representation of regions. Note that the NLL checker uses a distinct +/// representation of regions. For this reason, it internally replaces all the +/// regions with inference variables -- the index of the variable is then used +/// to index into internal NLL data structures. See `rustc_mir::borrow_check` +/// module for more information. /// /// ## The Region lattice within a given function /// -/// In general, the (lexical, and hence deprecated) region lattice -/// looks like +/// In general, the region lattice looks like /// /// ``` /// static ----------+-----...------+ (greatest) @@ -1307,7 +1197,6 @@ pub type Region<'tcx> = &'tcx RegionKind; /// early-bound and | | /// free regions | | /// | | | -/// scope regions | | /// | | | /// empty(root) placeholder(U1) | /// | / | @@ -1322,13 +1211,7 @@ pub type Region<'tcx> = &'tcx RegionKind; /// Early-bound/free regions are the named lifetimes in scope from the /// function declaration. They have relationships to one another /// determined based on the declared relationships from the -/// function. They all collectively outlive the scope regions. (See -/// `RegionRelations` type, and particularly -/// `crate::infer::outlives::free_region_map::FreeRegionMap`.) -/// -/// The scope regions are related to one another based on the AST -/// structure. (See `RegionRelations` type, and particularly the -/// `rustc::middle::region::ScopeTree`.) +/// function. /// /// Note that inference variables and bound regions are not included /// in this diagram. In the case of inference variables, they should @@ -1417,11 +1300,6 @@ pub enum RegionKind { /// region parameters. ReFree(FreeRegion), - /// A concrete region naming some statically determined scope - /// (e.g., an expression or sequence of statements) within the - /// current function. - ReScope(region::Scope), - /// Static data that has an "infinite" lifetime. Top in the region lattice. ReStatic, @@ -1442,12 +1320,6 @@ pub enum RegionKind { /// Erased region, used by trait selection, in MIR and during codegen. ReErased, - - /// These are regions bound in the "defining type" for a - /// closure. They are used ONLY as part of the - /// `ClosureRegionRequirements` that are produced by MIR borrowck. - /// See `ClosureRegionRequirements` for more details. - ReClosureBound(RegionVid), } impl<'tcx> rustc_serialize::UseSpecializedDecodable for Region<'tcx> {} @@ -1492,18 +1364,8 @@ impl Atom for RegionVid { } } -#[derive( - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - HashStable -)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] pub enum InferTy { TyVar(TyVid), IntVar(IntVid), @@ -1521,37 +1383,15 @@ rustc_index::newtype_index! { pub struct BoundVar { .. } } -#[derive( - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Debug, - RustcEncodable, - RustcDecodable, - HashStable -)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] pub struct BoundTy { pub var: BoundVar, pub kind: BoundTyKind, } -#[derive( - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Debug, - RustcEncodable, - RustcDecodable, - HashStable -)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable)] pub enum BoundTyKind { Anon, Param(Symbol), @@ -1612,7 +1452,7 @@ impl<'tcx> PolyExistentialProjection<'tcx> { } pub fn item_def_id(&self) -> DefId { - return self.skip_binder().item_def_id; + self.skip_binder().item_def_id } } @@ -1683,13 +1523,11 @@ impl RegionKind { RegionKind::ReEarlyBound(ebr) => ebr.has_name(), RegionKind::ReLateBound(_, br) => br.is_named(), RegionKind::ReFree(fr) => fr.bound_region.is_named(), - RegionKind::ReScope(..) => false, RegionKind::ReStatic => true, RegionKind::ReVar(..) => false, RegionKind::RePlaceholder(placeholder) => placeholder.name.is_named(), RegionKind::ReEmpty(_) => false, RegionKind::ReErased => false, - RegionKind::ReClosureBound(..) => false, } } @@ -1743,42 +1581,38 @@ impl RegionKind { } } - pub fn keep_in_local_tcx(&self) -> bool { - if let ty::ReVar(..) = self { true } else { false } - } - pub fn type_flags(&self) -> TypeFlags { let mut flags = TypeFlags::empty(); - if self.keep_in_local_tcx() { - flags = flags | TypeFlags::KEEP_IN_LOCAL_TCX; - } - match *self { ty::ReVar(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_INFER; } ty::RePlaceholder(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_PLACEHOLDER; } - ty::ReLateBound(..) => { - flags = flags | TypeFlags::HAS_RE_LATE_BOUND; - } ty::ReEarlyBound(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; flags = flags | TypeFlags::HAS_RE_PARAM; } - ty::ReEmpty(_) | ty::ReStatic | ty::ReFree { .. } | ty::ReScope { .. } => { + ty::ReFree { .. } => { flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_FREE_LOCAL_REGIONS; + } + ty::ReEmpty(_) | ty::ReStatic => { + flags = flags | TypeFlags::HAS_FREE_REGIONS; + } + ty::ReLateBound(..) => { + flags = flags | TypeFlags::HAS_RE_LATE_BOUND; } ty::ReErased => { flags = flags | TypeFlags::HAS_RE_ERASED; } - ty::ReClosureBound(..) => { - flags = flags | TypeFlags::HAS_FREE_REGIONS; - } } debug!("type_flags({:?}) = {:?}", self, flags); @@ -2000,8 +1834,8 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_unsafe_ptr(&self) -> bool { match self.kind { - RawPtr(_) => return true, - _ => return false, + RawPtr(_) => true, + _ => false, } } @@ -2011,24 +1845,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 { @@ -2051,8 +1867,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, } } @@ -2164,7 +1987,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn has_concrete_skeleton(&self) -> bool { match self.kind { - Param(_) | Infer(_) | Error => false, + Param(_) | Infer(_) | Error(_) => false, _ => true, } } @@ -2196,13 +2019,13 @@ impl<'tcx> TyS<'tcx> { match self.kind { FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs), FnPtr(f) => f, - Error => { + Error(_) => { // ignore errors (#54954) ty::Binder::dummy(FnSig::fake()) } - Closure(..) => { - bug!("to get the signature of a closure, use `closure_sig()` not `fn_sig()`",) - } + Closure(..) => bug!( + "to get the signature of a closure, use `substs.as_closure().sig()` not `fn_sig()`", + ), _ => bug!("Ty::fn_sig() called on non-fn type: {:?}", self), } } @@ -2273,7 +2096,12 @@ impl<'tcx> TyS<'tcx> { variant_index: VariantIdx, ) -> Option> { match self.kind { - TyKind::Adt(adt, _) => Some(adt.discriminant_for_variant(tcx, variant_index)), + TyKind::Adt(adt, _) if adt.variants.is_empty() => { + bug!("discriminant_for_variant called on zero variant enum"); + } + TyKind::Adt(adt, _) if adt.is_enum() => { + Some(adt.discriminant_for_variant(tcx, variant_index)) + } TyKind::Generator(def_id, substs, _) => { Some(substs.as_generator().discriminant_for_variant(def_id, tcx, variant_index)) } @@ -2281,28 +2109,15 @@ 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]>) { + /// Returns the type of the discriminant of this type. + pub fn discriminant_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { 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()); - } + ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx), + ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx), + _ => { + // This can only be `0`, for now, so `u8` will suffice. + tcx.types.u8 } - 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 => {} } } @@ -2331,7 +2146,7 @@ impl<'tcx> TyS<'tcx> { // closure type is not yet known Bound(..) | Infer(_) => None, - Error => Some(ty::ClosureKind::Fn), + Error(_) => Some(ty::ClosureKind::Fn), _ => bug!("cannot convert type `{:?}` to a closure kind", self), } @@ -2343,8 +2158,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 @@ -2359,7 +2173,7 @@ impl<'tcx> TyS<'tcx> { | ty::Array(..) | ty::Closure(..) | ty::Never - | ty::Error => true, + | ty::Error(_) => true, ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => false, @@ -2369,35 +2183,25 @@ 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) } } } + + /// Is this a zero-sized type? + pub fn is_zst(&'tcx self, tcx: TyCtxt<'tcx>, did: DefId) -> bool { + tcx.layout_of(tcx.param_env(did).and(self)).map(|layout| layout.is_zst()).unwrap_or(false) + } } /// Typed constant value. -#[derive( - Copy, - Clone, - Debug, - Hash, - RustcEncodable, - RustcDecodable, - Eq, - PartialEq, - Ord, - PartialOrd, - HashStable -)] +#[derive(Copy, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq, Ord, PartialOrd)] +#[derive(HashStable)] pub struct Const<'tcx> { pub ty: Ty<'tcx>, @@ -2408,17 +2212,93 @@ pub struct Const<'tcx> { static_assert_size!(Const<'_>, 48); impl<'tcx> Const<'tcx> { + /// Literals and const generic parameters are eagerly converted to a constant, everything else + /// becomes `Unevaluated`. + pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self { + debug!("Const::from_anon_const(id={:?})", def_id); + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + let body_id = match tcx.hir().get(hir_id) { + hir::Node::AnonConst(ac) => ac.body, + _ => span_bug!( + tcx.def_span(def_id.to_def_id()), + "from_anon_const can only process anonymous constants" + ), + }; + + let expr = &tcx.hir().body(body_id).value; + + let ty = tcx.type_of(def_id.to_def_id()); + + let lit_input = match expr.kind { + hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), + hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => match expr.kind { + hir::ExprKind::Lit(ref lit) => { + Some(LitToConstInput { lit: &lit.node, ty, neg: true }) + } + _ => None, + }, + _ => None, + }; + + if let Some(lit_input) = lit_input { + // If an error occurred, ignore that it's a literal and leave reporting the error up to + // mir. + if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) { + return c; + } else { + tcx.sess.delay_span_bug(expr.span, "Const::from_anon_const: couldn't lit_to_const"); + } + } + + // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments + // currently have to be wrapped in curly brackets, so it's necessary to special-case. + let expr = match &expr.kind { + hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { + block.expr.as_ref().unwrap() + } + _ => expr, + }; + + use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath}; + let val = match expr.kind { + ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => { + // Find the name and index of the const parameter by indexing the generics of + // the parent item and construct a `ParamConst`. + let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local()); + let item_id = tcx.hir().get_parent_node(hir_id); + let item_def_id = tcx.hir().local_def_id(item_id); + let generics = tcx.generics_of(item_def_id.to_def_id()); + let index = + generics.param_def_id_to_index[&tcx.hir().local_def_id(hir_id).to_def_id()]; + let name = tcx.hir().name(hir_id); + ty::ConstKind::Param(ty::ParamConst::new(index, name)) + } + _ => ty::ConstKind::Unevaluated( + def_id.to_def_id(), + InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), + None, + ), + }; + + tcx.mk_const(ty::Const { val, ty }) + } + #[inline] + /// Interns the given value as a constant. pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { tcx.mk_const(Self { val: ConstKind::Value(val), ty }) } #[inline] + /// Interns the given scalar as a constant. pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self { Self::from_value(tcx, ConstValue::Scalar(val), ty) } #[inline] + /// Creates a constant with the given integer value and interns it. pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx Self { let size = tcx .layout_of(ty) @@ -2428,21 +2308,27 @@ impl<'tcx> Const<'tcx> { } #[inline] + /// Creates an interned zst constant. pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { Self::from_scalar(tcx, Scalar::zst(), ty) } #[inline] + /// Creates an interned bool constant. pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> &'tcx Self { Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool)) } #[inline] + /// Creates an interned usize constant. pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> &'tcx Self { Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) } #[inline] + /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of + /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it + /// contains const generic parameters or pointers). pub fn try_eval_bits( &self, tcx: TyCtxt<'tcx>, @@ -2456,44 +2342,46 @@ impl<'tcx> Const<'tcx> { } #[inline] + /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the + /// unevaluated constant. pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> { - 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.const_error(self.ty), } - _ => self, + } else { + self } } @@ -2512,33 +2400,22 @@ impl<'tcx> Const<'tcx> { } #[inline] + /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type. pub fn eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 { self.try_eval_bits(tcx, param_env, ty) .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self)) } #[inline] + /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`. pub fn eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 { self.eval_bits(tcx, param_env, tcx.types.usize) as u64 } } -impl<'tcx> rustc_serialize::UseSpecializedDecodable for &'tcx Const<'tcx> {} - /// Represents a constant in Rust. -#[derive( - Copy, - Clone, - Debug, - Eq, - PartialEq, - PartialOrd, - Ord, - RustcEncodable, - RustcDecodable, - Hash, - HashStable -)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] +#[derive(HashStable)] pub enum ConstKind<'tcx> { /// A const generic parameter. Param(ParamConst), @@ -2558,6 +2435,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(DelaySpanBugEmitted), } #[cfg(target_arch = "x86_64")] @@ -2570,25 +2451,14 @@ 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 } } } /// An inference variable for a const, for use in const generics. -#[derive( - Copy, - Clone, - Debug, - Eq, - PartialEq, - PartialOrd, - Ord, - RustcEncodable, - RustcDecodable, - Hash, - HashStable -)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, RustcEncodable, RustcDecodable, Hash)] +#[derive(HashStable)] pub enum InferConst<'tcx> { /// Infer the value of the const. Var(ConstVid<'tcx>), diff --git a/src/librustc/ty/subst.rs b/src/librustc_middle/ty/subst.rs similarity index 93% rename from src/librustc/ty/subst.rs rename to src/librustc_middle/ty/subst.rs index a005581283550..fd31adae499fe 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 @@ -357,6 +333,17 @@ impl<'a, 'tcx> InternalSubsts<'tcx> { /// in a different item, with `target_substs` as the base for /// the target impl/trait, with the source child-specific /// parameters (e.g., method parameters) on top of that base. + /// + /// For example given: + /// + /// trait X { fn f(); } + /// impl X for U { fn f() {} } + /// + /// * If `self` is `[Self, S, T]`: the identity substs of `f` in the trait. + /// * If `source_ancestor` is the def_id of the trait. + /// * If `target_substs` is `[U]`, the substs for the impl. + /// * Then we will return `[U, T]`, the subst for `f` in the impl that + /// are needed for it to match the trait. pub fn rebase_onto( &self, tcx: TyCtxt<'tcx>, @@ -364,11 +351,11 @@ impl<'a, 'tcx> InternalSubsts<'tcx> { target_substs: SubstsRef<'tcx>, ) -> SubstsRef<'tcx> { let defs = tcx.generics_of(source_ancestor); - tcx.mk_substs(target_substs.iter().chain(&self[defs.params.len()..]).cloned()) + tcx.mk_substs(target_substs.iter().chain(self.iter().skip(defs.params.len()))) } pub fn truncate_to(&self, tcx: TyCtxt<'tcx>, generics: &ty::Generics) -> SubstsRef<'tcx> { - tcx.mk_substs(self.iter().take(generics.count()).cloned()) + tcx.mk_substs(self.iter().take(generics.count())) } } @@ -524,7 +511,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for SubstFolder<'a, 'tcx> { self.root_ty = None; } - return t1; + t1 } fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { @@ -623,12 +610,12 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { /// /// ``` /// type Func = fn(A); - /// type MetaFunc = for<'a> fn(Func<&'a int>) + /// type MetaFunc = for<'a> fn(Func<&'a i32>) /// ``` /// /// The type `MetaFunc`, when fully expanded, will be /// - /// for<'a> fn(fn(&'a int)) + /// for<'a> fn(fn(&'a i32)) /// ^~ ^~ ^~~ /// | | | /// | | DebruijnIndex of 2 @@ -637,7 +624,7 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { /// Here the `'a` lifetime is bound in the outer function, but appears as an argument of the /// inner one. Therefore, that appearance will have a DebruijnIndex of 2, because we must skip /// over the inner binder (remember that we count De Bruijn indices from 1). However, in the - /// definition of `MetaFunc`, the binder is not visible, so the type `&'a int` will have a + /// definition of `MetaFunc`, the binder is not visible, so the type `&'a i32` will have a /// De Bruijn index of 1. It's only during the substitution that we can see we must increase the /// depth by 1 to account for the binder that we passed through. /// @@ -645,18 +632,18 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { /// /// ``` /// type FuncTuple = (A,fn(A)); - /// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a int>) + /// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a i32>) /// ``` /// /// Here the final type will be: /// - /// for<'a> fn((&'a int, fn(&'a int))) + /// for<'a> fn((&'a i32, fn(&'a i32))) /// ^~~ ^~~ /// | | /// DebruijnIndex of 1 | /// DebruijnIndex of 2 /// - /// As indicated in the diagram, here the same type `&'a int` is substituted once, but in the + /// As indicated in the diagram, here the same type `&'a i32` is substituted once, but in the /// first case we do not increase the De Bruijn index and in the second case we do. The reason /// is that only in the second case have we passed through a fn binder. fn shift_vars_through_binders>(&self, val: T) -> T { diff --git a/src/librustc/ty/trait_def.rs b/src/librustc_middle/ty/trait_def.rs similarity index 77% rename from src/librustc/ty/trait_def.rs rename to src/librustc_middle/ty/trait_def.rs index 91a99ec43e6a9..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,10 +5,12 @@ 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; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_errors::ErrorReported; use rustc_macros::HashStable; use std::collections::BTreeMap; @@ -35,11 +36,33 @@ pub struct TraitDef { /// and thus `impl`s of it are allowed to overlap. pub is_marker: bool, + /// Used to determine whether the standard library is allowed to specialize + /// on this trait. + pub specialization_kind: TraitSpecializationKind, + /// The ICH of this trait's DefPath, cached here so it doesn't have to be /// recomputed all the time. pub def_path_hash: DefPathHash, } +/// Whether this trait is treated specially by the standard library +/// specialization lint. +#[derive(HashStable, PartialEq, Clone, Copy, RustcEncodable, RustcDecodable)] +pub enum TraitSpecializationKind { + /// The default. Specializing on this trait is not allowed. + None, + /// Specializing on this trait is allowed because it doesn't have any + /// methods. For example `Sized` or `FusedIterator`. + /// Applies to traits with the `rustc_unsafe_specialization_marker` + /// attribute. + Marker, + /// Specializing on this trait is allowed because all of the impls of this + /// trait are "always applicable". Always applicable means that if + /// `X<'x>: T<'y>` for any lifetimes, then `for<'a, 'b> X<'a>: T<'b>`. + /// Applies to traits with the `rustc_specialization_trait` attribute. + AlwaysApplicable, +} + #[derive(Default)] pub struct TraitImpls { blanket_impls: Vec, @@ -54,16 +77,25 @@ impl<'tcx> TraitDef { paren_sugar: bool, has_auto_impl: bool, is_marker: bool, + specialization_kind: TraitSpecializationKind, def_path_hash: DefPathHash, ) -> TraitDef { - TraitDef { def_id, unsafety, paren_sugar, has_auto_impl, is_marker, def_path_hash } + TraitDef { + def_id, + unsafety, + paren_sugar, + has_auto_impl, + is_marker, + specialization_kind, + def_path_hash, + } } pub fn ancestors( &self, tcx: TyCtxt<'tcx>, of_impl: DefId, - ) -> specialization_graph::Ancestors<'tcx> { + ) -> Result, ErrorReported> { specialization_graph::ancestors(tcx, self.def_id, of_impl) } } @@ -136,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() } } @@ -157,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; @@ -185,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 90% rename from src/librustc/ty/util.rs rename to src/librustc_middle/ty/util.rs index 69daa2da1fd0e..67ad7ee708267 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}; @@ -176,7 +176,7 @@ impl<'tcx> TyCtxt<'tcx> { if let ty::Adt(def, substs) = ty.kind { for field in def.all_fields() { let field_ty = field.ty(self, substs); - if let Error = field_ty.kind { + if let Error(_) = field_ty.kind { return true; } } @@ -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 @@ -415,7 +413,7 @@ impl<'tcx> TyCtxt<'tcx> { let result = item_substs .iter() .zip(impl_substs.iter()) - .filter(|&(_, &k)| { + .filter(|&(_, k)| { match k.unpack() { GenericArgKind::Lifetime(&ty::RegionKind::ReEarlyBound(ref ebr)) => { !impl_generics.region_param(ebr, self).pure_wrt_drop @@ -435,7 +433,7 @@ impl<'tcx> TyCtxt<'tcx> { } } }) - .map(|(&item_param, _)| item_param) + .map(|(item_param, _)| item_param) .collect(); debug!("destructor_constraint({:?}) = {:?}", def.did, result); result @@ -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 @@ -502,7 +500,7 @@ impl<'tcx> TyCtxt<'tcx> { ) -> Option>> { let closure_ty = self.mk_closure(closure_def_id, closure_substs); let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); - let closure_kind_ty = closure_substs.as_closure().kind_ty(closure_def_id, self); + let closure_kind_ty = closure_substs.as_closure().kind_ty(); let closure_kind = closure_kind_ty.to_opt_closure_kind()?; let env_ty = match closure_kind { ty::ClosureKind::Fn => self.mk_imm_ref(self.mk_region(env_region), closure_ty), @@ -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) @@ -678,11 +681,10 @@ impl<'tcx> ty::TyS<'tcx> { /// winds up being reported as an error during NLL borrow check. pub fn is_copy_modulo_regions( &'tcx self, - tcx: TyCtxt<'tcx>, + tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>, - span: Span, ) -> bool { - tcx.at(span).is_copy_raw(param_env.and(self)) + tcx_at.is_copy_raw(param_env.and(self)) } /// Checks whether values of this type `T` have a size known at @@ -702,13 +704,9 @@ impl<'tcx> ty::TyS<'tcx> { /// optimization as well as the rules around static values. Note /// that the `Freeze` trait is not exposed to end users and is /// effectively an implementation detail. - pub fn is_freeze( - &'tcx self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - span: Span, - ) -> bool { - self.is_trivially_freeze() || tcx.at(span).is_freeze_raw(param_env.and(self)) + // FIXME: use `TyCtxtAt` instead of separate `Span`. + pub fn is_freeze(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + self.is_trivially_freeze() || tcx_at.is_freeze_raw(param_env.and(self)) } /// Fast path helper for testing if a type is `Freeze`. @@ -727,7 +725,7 @@ impl<'tcx> ty::TyS<'tcx> { | ty::Ref(..) | ty::RawPtr(_) | ty::FnDef(..) - | ty::Error + | ty::Error(_) | ty::FnPtr(_) => true, ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_freeze), ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_freeze(), @@ -742,8 +740,7 @@ impl<'tcx> ty::TyS<'tcx> { | ty::Opaque(..) | ty::Param(_) | ty::Placeholder(_) - | ty::Projection(_) - | ty::UnnormalizedProjection(_) => false, + | ty::Projection(_) => false, } } @@ -776,6 +773,57 @@ impl<'tcx> ty::TyS<'tcx> { } } + /// Returns `true` if equality for this type is both reflexive and structural. + /// + /// Reflexive equality for a type is indicated by an `Eq` impl for that type. + /// + /// Primitive types (`u32`, `str`) have structural equality by definition. For composite data + /// types, equality for the type as a whole is structural when it is the same as equality + /// between all components (fields, array elements, etc.) of that type. For ADTs, structural + /// equality is indicated by an implementation of `PartialStructuralEq` and `StructuralEq` for + /// that type. + /// + /// This function is "shallow" because it may return `true` for a composite type whose fields + /// are not `StructuralEq`. For example, `[T; 4]` has structural equality regardless of `T` + /// because equality for arrays is determined by the equality of each array element. If you + /// want to know whether a given call to `PartialEq::eq` will proceed structurally all the way + /// down, you will need to use a type visitor. + #[inline] + pub fn is_structural_eq_shallow(&'tcx self, tcx: TyCtxt<'tcx>) -> bool { + match self.kind { + // Look for an impl of both `PartialStructuralEq` and `StructuralEq`. + Adt(..) => tcx.has_structural_eq_impls(self), + + // Primitive types that satisfy `Eq`. + Bool | Char | Int(_) | Uint(_) | Str | Never => true, + + // Composite types that satisfy `Eq` when all of their fields do. + // + // Because this function is "shallow", we return `true` for these composites regardless + // of the type(s) contained within. + Ref(..) | Array(..) | Slice(_) | Tuple(..) => true, + + // Raw pointers use bitwise comparison. + RawPtr(_) | FnPtr(_) => true, + + // Floating point numbers are not `Eq`. + Float(_) => false, + + // Conservatively return `false` for all others... + + // Anonymous function types + FnDef(..) | Closure(..) | Dynamic(..) | Generator(..) => false, + + // Generic or inferred types + // + // FIXME(ecstaticmorse): Maybe we should `bug` here? This should probably only be + // called for known, fully-monomorphized types. + Projection(_) | Opaque(..) | Param(_) | Bound(..) | Placeholder(_) | Infer(_) => false, + + Foreign(_) | GeneratorWitness(..) | Error(_) => false, + } + } + pub fn same_type(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.kind, &b.kind) { (&Adt(did_a, substs_a), &Adt(did_b, substs_b)) => { @@ -825,7 +873,15 @@ impl<'tcx> ty::TyS<'tcx> { // Find non representable fields with their spans fold_repr(def.all_fields().map(|field| { let ty = field.ty(tcx, substs); - let span = tcx.hir().span_if_local(field.did).unwrap_or(sp); + let span = match field + .did + .as_local() + .map(|id| tcx.hir().as_local_hir_id(id)) + .and_then(|id| tcx.hir().find(id)) + { + Some(hir::Node::Field(field)) => field.ty.span, + _ => sp, + }; match is_type_structurally_recursive( tcx, span, @@ -1047,10 +1103,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 +1130,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..633d4fda8a46d --- /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 i32 + /// iter.next(); // yields usize + /// ``` + pub fn skip_current_subtree(&mut self) { + self.stack.truncate(self.last_subtree); + } +} + +impl<'tcx> Iterator for TypeWalker<'tcx> { + type Item = GenericArg<'tcx>; + + fn next(&mut self) -> Option> { + debug!("next(): stack={:?}", self.stack); + 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().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().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().rev()); + } + ty::GeneratorWitness(ts) => { + stack.extend(ts.skip_binder().iter().rev().map(|ty| ty.into())); + } + ty::FnPtr(sig) => { + stack.push(sig.skip_binder().output().into()); + stack.extend(sig.skip_binder().inputs().iter().copied().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().rev()); + } + } + } + } +} diff --git a/src/librustc_middle/util/bug.rs b/src/librustc_middle/util/bug.rs new file mode 100644 index 0000000000000..9c3a97d8332f1 --- /dev/null +++ b/src/librustc_middle/util/bug.rs @@ -0,0 +1,52 @@ +// These functions are used by macro expansion for bug! and span_bug! + +use crate::ty::{tls, TyCtxt}; +use rustc_span::{MultiSpan, Span}; +use std::fmt; +use std::panic::Location; + +#[cold] +#[inline(never)] +#[track_caller] +pub fn bug_fmt(args: fmt::Arguments<'_>) -> ! { + // this wrapper mostly exists so I don't have to write a fully + // qualified path of None:: inside the bug!() macro definition + opt_span_bug_fmt(None::, args, Location::caller()); +} + +#[cold] +#[inline(never)] +#[track_caller] +pub fn span_bug_fmt>(span: S, args: fmt::Arguments<'_>) -> ! { + opt_span_bug_fmt(Some(span), args, Location::caller()); +} + +fn opt_span_bug_fmt>( + span: Option, + args: fmt::Arguments<'_>, + location: &Location<'_>, +) -> ! { + tls::with_opt(move |tcx| { + let msg = format!("{}: {}", location, args); + match (tcx, span) { + (Some(tcx), Some(span)) => tcx.sess.diagnostic().span_bug(span, &msg), + (Some(tcx), None) => tcx.sess.diagnostic().bug(&msg), + (None, _) => panic!(msg), + } + }); + unreachable!(); +} + +/// A query to trigger a `delay_span_bug`. Clearly, if one has a `tcx` one can already trigger a +/// `delay_span_bug`, so what is the point of this? It exists to help us test `delay_span_bug`'s +/// interactions with the query system and incremental. +pub fn trigger_delay_span_bug(tcx: TyCtxt<'_>, key: rustc_hir::def_id::DefId) { + tcx.sess.delay_span_bug( + tcx.def_span(key), + "delayed span bug triggered by #[rustc_error(delay_span_bug_from_inside_query)]", + ); +} + +pub fn provide(providers: &mut crate::ty::query::Providers<'_>) { + *providers = crate::ty::query::Providers { trigger_delay_span_bug, ..*providers }; +} 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 cad50d50f3cec..aebce78e4018b 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -11,13 +11,12 @@ doctest = false [dependencies] either = "1.5.0" -dot = { path = "../libgraphviz", package = "graphviz" } +rustc_graphviz = { path = "../librustc_graphviz" } 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" } @@ -26,7 +25,8 @@ rustc_index = { path = "../librustc_index" } rustc_infer = { path = "../librustc_infer" } rustc_lexer = { path = "../librustc_lexer" } rustc_macros = { path = "../librustc_macros" } -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } +rustc_session = { path = "../librustc_session" } rustc_target = { path = "../librustc_target" } rustc_trait_selection = { path = "../librustc_trait_selection" } rustc_ast = { path = "../librustc_ast" } diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs index 9d5cf3ec4bec0..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); } @@ -273,7 +273,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { assert_eq!(borrow_data.borrowed_place, *place); } - return self.super_rvalue(rvalue, location); + self.super_rvalue(rvalue, location) } } 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 e895eec5d52af..8d7944004c75e 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}; @@ -138,7 +138,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let move_msg = if move_spans.for_closure() { " into closure" } else { "" }; - if span == move_span { + if location == move_out.source { err.span_label( span, format!("value moved{} here, in previous iteration of loop", move_msg), @@ -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 { @@ -214,7 +214,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let generics = tcx.generics_of(self.mir_def_id); let param = generics.type_param(¶m_ty, tcx); if let Some(generics) = - tcx.hir().get_generics(tcx.closure_base_def_id(self.mir_def_id)) + tcx.hir().get_generics(tcx.closure_base_def_id(self.mir_def_id.to_def_id())) { suggest_constraining_type_param( tcx, @@ -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,48 +865,42 @@ 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) { - err.span_label( - drop_span, - format!( - "...but `{}` will be dropped here, when the {} returns", - name, - self.infcx - .tcx - .hir() - .opt_name(fn_hir_id) - .map(|name| format!("function `{}`", name)) - .unwrap_or_else(|| { - match &self - .infcx - .tcx - .typeck_tables_of(self.mir_def_id) - .node_type(fn_hir_id) - .kind - { - ty::Closure(..) => "enclosing closure", - ty::Generator(..) => "enclosing generator", - kind => bug!("expected closure or generator, found {:?}", kind), - } - .to_string() - }) - ), - ); + let fn_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id); + err.span_label( + drop_span, + format!( + "...but `{}` will be dropped here, when the {} returns", + name, + self.infcx + .tcx + .hir() + .opt_name(fn_hir_id) + .map(|name| format!("function `{}`", name)) + .unwrap_or_else(|| { + match &self + .infcx + .tcx + .typeck_tables_of(self.mir_def_id) + .node_type(fn_hir_id) + .kind + { + ty::Closure(..) => "enclosing closure", + ty::Generator(..) => "enclosing generator", + kind => bug!("expected closure or generator, found {:?}", kind), + } + .to_string() + }) + ), + ); - err.note( - "functions cannot return a borrow to data owned within the function's scope, \ - functions can only return borrows to data passed as arguments", - ); - err.note( - "to learn more, visit ", - ); - } else { - err.span_label( - drop_span, - format!("...but `{}` dropped here while still borrowed", name), - ); - } + err.note( + "functions cannot return a borrow to data owned within the function's scope, \ + functions can only return borrows to data passed as arguments", + ); + err.note( + "to learn more, visit ", + ); if let BorrowExplanation::MustBeValidFor { .. } = explanation { } else { @@ -986,7 +938,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>, ) { @@ -1137,7 +1089,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { opt_place_desc: Option<&String>, ) -> Option> { let return_kind = match category { - ConstraintCategory::Return => "return", + ConstraintCategory::Return(_) => "return", ConstraintCategory::Yield => "yield", _ => return None, }; @@ -1211,7 +1163,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 +1188,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 +1203,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) @@ -1275,7 +1230,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> DiagnosticBuilder<'cx> { let tcx = self.infcx.tcx; - let (_, escapes_from) = tcx.article_and_description(self.mir_def_id); + let (_, escapes_from) = tcx.article_and_description(self.mir_def_id.to_def_id()); let mut err = borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from); @@ -1303,8 +1258,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 +1356,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 +1368,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 +1395,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 +1421,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 +1438,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 +1463,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 +1497,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 +1505,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 +1530,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 +1549,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"), @@ -1604,14 +1565,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> Option> { // Define a fallback for when we can't match a closure. let fallback = || { - let is_closure = self.infcx.tcx.is_closure(self.mir_def_id); + let is_closure = self.infcx.tcx.is_closure(self.mir_def_id.to_def_id()); if is_closure { None } else { let ty = self.infcx.tcx.type_of(self.mir_def_id); match ty.kind { - ty::FnDef(_, _) | ty::FnPtr(_) => self - .annotate_fn_sig(self.mir_def_id, self.infcx.tcx.fn_sig(self.mir_def_id)), + ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig( + self.mir_def_id.to_def_id(), + self.infcx.tcx.fn_sig(self.mir_def_id), + ), _ => None, } } @@ -1682,10 +1645,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // If a closure captured our `target` and then assigned // into a place then we should annotate the closure in // case it ends up being assigned into the return place. - annotated_closure = self.annotate_fn_sig( - *def_id, - self.infcx.closure_sig(*def_id, *substs), - ); + annotated_closure = + self.annotate_fn_sig(*def_id, substs.as_closure().sig()); debug!( "annotate_argument_and_return_for_borrow: \ annotated_closure={:?} assigned_from_local={:?} \ @@ -1818,7 +1779,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 @@ -1852,7 +1813,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..ca8e54ea28649 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) => { @@ -361,11 +377,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.describe_field_from_ty(&ty, field, variant_index) } ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { - // `tcx.upvars(def_id)` returns an `Option`, which is `None` in case + // `tcx.upvars_mentioned(def_id)` returns an `Option`, which is `None` in case // the closure comes from another crate. But in that case we wouldn't // be borrowck'ing it, so we can just unwrap: - let (&var_id, _) = - self.infcx.tcx.upvars(def_id).unwrap().get_index(field.index()).unwrap(); + let (&var_id, _) = self + .infcx + .tcx + .upvars_mentioned(def_id) + .unwrap() + .get_index(field.index()) + .unwrap(); self.infcx.tcx.hir().name(var_id).to_string() } @@ -434,7 +455,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 +468,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 +490,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 +638,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 +668,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,11 +810,11 @@ 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 { - for (upvar, place) in self.infcx.tcx.upvars(def_id)?.values().zip(places) { + for (upvar, place) in self.infcx.tcx.upvars_mentioned(def_id)?.values().zip(places) { match place { Operand::Copy(place) | Operand::Move(place) if target_place == place.as_ref() => diff --git a/src/librustc_mir/borrow_check/diagnostics/move_errors.rs b/src/librustc_mir/borrow_check/diagnostics/move_errors.rs index 9451fee499d36..b49e4187fb810 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)); @@ -331,9 +331,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.cannot_move_out_of_interior_noncopy(span, ty, None) } ty::Closure(def_id, closure_substs) - if def_id == self.mir_def_id && upvar_field.is_some() => + if def_id.as_local() == Some(self.mir_def_id) && upvar_field.is_some() => { - let closure_kind_ty = closure_substs.as_closure().kind_ty(def_id, self.infcx.tcx); + let closure_kind_ty = closure_substs.as_closure().kind_ty(); let closure_kind = closure_kind_ty.to_opt_closure_kind(); let capture_description = match closure_kind { Some(ty::ClosureKind::Fn) => "captured variable in an `Fn` closure", @@ -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,26 +482,21 @@ 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) { if pat_snippet.starts_with('&') { let pat_snippet = pat_snippet[1..].trim_start(); - let suggestion; - let to_remove; - if pat_snippet.starts_with("mut") + let (suggestion, to_remove) = if pat_snippet.starts_with("mut") && pat_snippet["mut".len()..].starts_with(rustc_lexer::is_whitespace) { - suggestion = pat_snippet["mut".len()..].trim_start(); - to_remove = "&mut"; + (pat_snippet["mut".len()..].trim_start(), "&mut") } else { - suggestion = pat_snippet; - to_remove = "&"; - } + (pat_snippet, "&") + }; suggestions.push((pat_span, to_remove, suggestion.to_owned())); } } diff --git a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs index 5d22ef46c41b1..4d4b6fb9386db 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mutability_errors.rs @@ -1,9 +1,10 @@ -use rustc::mir::{self, ClearCrossCrate, Local, LocalInfo, Location, ReadOnlyBodyAndCache}; -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; @@ -21,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, @@ -57,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()); @@ -84,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 { @@ -103,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 @@ -119,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); @@ -136,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); @@ -168,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 @@ -195,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( @@ -214,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`. @@ -271,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)); @@ -338,26 +340,55 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { match self.local_names[local] { Some(name) if !local_decl.from_compiler_desugaring() => { - let suggestion = 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(_), - )) => Some(suggest_ampmut_self(self.infcx.tcx, local_decl)), + )) => { + let (span, suggestion) = + suggest_ampmut_self(self.infcx.tcx, local_decl); + 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, .. }, - ))) => Some(suggest_ampmut( - self.infcx.tcx, - self.body, - local, - local_decl, - opt_ty_info, - )), - - LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( + ))) => { + // check if the RHS is from desugaring + let locations = self.body.find_assignments(local); + let opt_assignment_rhs_span = locations + .first() + .map(|&location| self.body.source_info(location).span); + let opt_desugaring_kind = + opt_assignment_rhs_span.and_then(|span| span.desugaring_kind()); + match opt_desugaring_kind { + // on for loops, RHS points to the iterator part + Some(DesugaringKind::ForLoop) => Some(( + false, + opt_assignment_rhs_span.unwrap(), + format!( + "this iterator yields `{SIGIL}` {DESC}s", + SIGIL = pointer_sigil, + DESC = pointer_desc + ), + )), + // don't create labels for compiler-generated spans + Some(_) => None, + None => { + let (span, suggestion) = suggest_ampmut( + self.infcx.tcx, + local_decl, + opt_assignment_rhs_span, + *opt_ty_info, + ); + Some((true, span, suggestion)) + } + } + } + + box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( mir::VarBindingForm { binding_mode: ty::BindingMode::BindByReference(_), .. @@ -365,23 +396,32 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ))) => { let pattern_span = local_decl.source_info.span; suggest_ref_mut(self.infcx.tcx, pattern_span) - .map(|replacement| (pattern_span, replacement)) + .map(|replacement| (true, pattern_span, replacement)) } - LocalInfo::User(ClearCrossCrate::Clear) => { + box LocalInfo::User(ClearCrossCrate::Clear) => { bug!("saw cleared local state") } _ => unreachable!(), }; - if let Some((err_help_span, suggested_code)) = suggestion { - err.span_suggestion( - err_help_span, - &format!("consider changing this to be a mutable {}", pointer_desc), - suggested_code, - Applicability::MachineApplicable, - ); + match label { + Some((true, err_help_span, suggested_code)) => { + err.span_suggestion( + err_help_span, + &format!( + "consider changing this to be a mutable {}", + pointer_desc + ), + suggested_code, + Applicability::MachineApplicable, + ); + } + Some((false, err_label_span, message)) => { + err.span_label(err_label_span, &message); + } + None => {} } err.span_label( span, @@ -452,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); 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 item_id = hir.enclosing_body_owner(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::Method(sig, _), + 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. @@ -527,7 +565,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }) | hir::Node::ImplItem(hir::ImplItem { ident, - kind: hir::ImplItemKind::Method(sig, _), + kind: hir::ImplItemKind::Fn(sig, _), .. }) => { err.span_label(ident.span, ""); @@ -581,14 +619,11 @@ fn suggest_ampmut_self<'tcx>( // by trying (3.), then (2.) and finally falling back on (1.). fn suggest_ampmut<'tcx>( tcx: TyCtxt<'tcx>, - body: ReadOnlyBodyAndCache<'_, 'tcx>, - local: Local, local_decl: &mir::LocalDecl<'tcx>, + opt_assignment_rhs_span: Option, opt_ty_info: Option, ) -> (Span, String) { - let locations = body.find_assignments(local); - if !locations.is_empty() { - let assignment_rhs_span = body.source_info(locations[0]).span; + if let Some(assignment_rhs_span) = opt_assignment_rhs_span { if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) { if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(|c: char| -> bool { c.is_whitespace() })) @@ -656,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 f751a16cfce7c..f1923b9e81c66 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs @@ -1,13 +1,13 @@ //! 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_span::symbol::kw; +use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; +use rustc_middle::ty::{self, RegionVid, Ty}; +use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use crate::util::borrowck_errors; @@ -26,7 +26,7 @@ impl ConstraintDescription for ConstraintCategory { // Must end with a space. Allows for empty names to be provided. match self { ConstraintCategory::Assignment => "assignment ", - ConstraintCategory::Return => "returning this value ", + ConstraintCategory::Return(_) => "returning this value ", ConstraintCategory::Yield => "yielding this value ", ConstraintCategory::UseAsConst => "using this value as a constant ", ConstraintCategory::UseAsStatic => "using this value as a static ", @@ -37,6 +37,7 @@ impl ConstraintDescription for ConstraintCategory { ConstraintCategory::SizedBound => "proving this value is `Sized` ", ConstraintCategory::CopyBound => "copying this value ", ConstraintCategory::OpaqueType => "opaque type ", + ConstraintCategory::ClosureUpvar(_) => "closure capture ", ConstraintCategory::Boring | ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => "", @@ -135,11 +136,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fn is_closure_fn_mut(&self, fr: RegionVid) -> bool { if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) { if let ty::BoundRegion::BrEnv = free_region.bound_region { - if let DefiningTy::Closure(def_id, substs) = + if let DefiningTy::Closure(_, substs) = self.regioncx.universal_regions().defining_ty { - return substs.as_closure().kind(def_id, self.infcx.tcx) - == ty::ClosureKind::FnMut; + return substs.as_closure().kind() == ty::ClosureKind::FnMut; } } } @@ -163,10 +163,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let type_test_span = type_test.locations.span(&self.body); if let Some(lower_bound_region) = lower_bound_region { - let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id); self.infcx .construct_generic_bound_failure( - region_scope_tree, type_test_span, None, type_test.generic_kind, @@ -195,12 +193,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, member_region } => { - let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id); let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty); let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); unexpected_hidden_region_diagnostic( self.infcx.tcx, - Some(region_scope_tree), span, named_ty, named_region, @@ -284,8 +280,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { debug!("report_region_error: category={:?} {:?}", category, span); // Check if we can use one of the "nice region errors". if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { - let tables = self.infcx.tcx.typeck_tables_of(self.mir_def_id); - let nice = NiceRegionError::new_from_span(self.infcx, span, o, f, Some(tables)); + let nice = NiceRegionError::new_from_span(self.infcx, span, o, f); if let Some(diag) = nice.try_report_from_nll() { diag.buffer(&mut self.errors_buffer); return; @@ -312,8 +307,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; let diag = match (category, fr_is_local, outlived_fr_is_local) { - (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(fr) => { - self.report_fnmut_error(&errci) + (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => { + self.report_fnmut_error(&errci, kind) } (ConstraintCategory::Assignment, true, false) | (ConstraintCategory::CallArgument, true, false) => { @@ -353,7 +348,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// executing... /// = note: ...therefore, returned references to captured variables will escape the closure /// ``` - fn report_fnmut_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> { + fn report_fnmut_error( + &self, + errci: &ErrorConstraintInfo, + kind: ReturnConstraint, + ) -> DiagnosticBuilder<'tcx> { let ErrorConstraintInfo { outlived_fr, span, .. } = errci; let mut diag = self @@ -362,19 +361,39 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .sess .struct_span_err(*span, "captured variable cannot escape `FnMut` closure body"); - // We should check if the return type of this closure is in fact a closure - in that - // case, we can special case the error further. - let return_type_is_closure = - self.regioncx.universal_regions().unnormalized_output_ty.is_closure(); - let message = if return_type_is_closure { - "returns a closure that contains a reference to a captured variable, which then \ - escapes the closure body" - } else { - "returns a reference to a captured variable which escapes the closure body" + let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty; + if let ty::Opaque(def_id, _) = output_ty.kind { + output_ty = self.infcx.tcx.type_of(def_id) + }; + + debug!("report_fnmut_error: output_ty={:?}", output_ty); + + let message = match output_ty.kind { + ty::Closure(_, _) => { + "returns a closure that contains a reference to a captured variable, which then \ + escapes the closure body" + } + ty::Adt(def, _) if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did) => { + "returns an `async` block that contains a reference to a captured variable, which then \ + escapes the closure body" + } + _ => "returns a reference to a captured variable which escapes the closure body", }; diag.span_label(*span, message); + if let ReturnConstraint::ClosureUpvar(upvar) = kind { + let def_id = match self.regioncx.universal_regions().defining_ty { + DefiningTy::Closure(def_id, _) => def_id, + ty @ _ => bug!("unexpected DefiningTy {:?}", ty), + }; + + let upvar_def_span = self.infcx.tcx.hir().span(upvar); + let upvar_span = self.infcx.tcx.upvars_mentioned(def_id).unwrap()[&upvar].span; + diag.span_label(upvar_def_span, "variable defined here"); + diag.span_label(upvar_span, "variable captured here"); + } + match self.give_region_a_name(*outlived_fr).unwrap().source { RegionNameSource::NamedEarlyBoundRegion(fr_span) | RegionNameSource::NamedFreeRegion(fr_span) @@ -504,7 +523,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut diag = self.infcx.tcx.sess.struct_span_err(*span, "lifetime may not live long enough"); - let (_, mir_def_name) = self.infcx.tcx.article_and_description(self.mir_def_id); + let (_, mir_def_name) = self.infcx.tcx.article_and_description(self.mir_def_id.to_def_id()); let fr_name = self.give_region_a_name(*fr).unwrap(); fr_name.highlight_region_name(&mut diag); @@ -512,7 +531,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { outlived_fr_name.highlight_region_name(&mut diag); match (category, outlived_fr_is_local, fr_is_local) { - (ConstraintCategory::Return, true, _) => { + (ConstraintCategory::Return(_), true, _) => { diag.span_label( *span, format!( @@ -578,7 +597,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut found = false; for predicate in bounds.predicates { - if let ty::Predicate::TypeOutlives(binder) = predicate { + if let ty::PredicateKind::TypeOutlives(binder) = predicate.kind() { if let ty::OutlivesPredicate(_, ty::RegionKind::ReStatic) = binder.skip_binder() { diff --git a/src/librustc_mir/borrow_check/diagnostics/region_name.rs b/src/librustc_mir/borrow_check/diagnostics/region_name.rs index 01ace74287622..2240eb81e1fa7 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,15 +237,10 @@ 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); let def_ty = self.regioncx.universal_regions().defining_ty; - if let DefiningTy::Closure(def_id, substs) = def_ty { + if let DefiningTy::Closure(_, substs) = def_ty { let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) = tcx.hir().expect_expr(mir_hir_id).kind { @@ -255,7 +250,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { }; let region_name = self.synthesize_region_name(); - let closure_kind_ty = substs.as_closure().kind_ty(def_id, tcx); + let closure_kind_ty = substs.as_closure().kind_ty(); let note = match closure_kind_ty.to_opt_closure_kind() { Some(ty::ClosureKind::Fn) => { "closure implements `Fn`, so references to captured variables \ @@ -288,12 +283,10 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { }, ty::ReLateBound(..) - | ty::ReScope(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReEmpty(_) - | ty::ReErased - | ty::ReClosureBound(..) => None, + | ty::ReErased => None, } } @@ -301,7 +294,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` /// ``` @@ -329,7 +322,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); 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 { @@ -348,7 +341,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// that has no type annotation. /// For example, we might produce an annotation like this: /// - /// ``` + /// ```text /// | foo(|a, b| b) /// | - - /// | | | @@ -397,7 +390,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` /// ``` @@ -500,7 +493,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } } - return None; + None } /// We've found an enum/struct/union type with the substitutions @@ -578,9 +571,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. @@ -601,7 +597,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>` /// ``` @@ -637,7 +633,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); let (return_span, mir_description) = match tcx.hir().get(mir_hir_id) { hir::Node::Expr(hir::Expr { @@ -651,7 +647,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { if gen_move.is_some() { " of generator" } else { " of closure" }, ), hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Method(method_sig, _), + kind: hir::ImplItemKind::Fn(method_sig, _), .. }) => (method_sig.decl.output.span(), ""), _ => (self.body.span, ""), @@ -689,7 +685,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); 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 5f3585ce8b119..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; @@ -35,7 +35,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Search the upvars (if any) to find one that references fr. Return its index. crate fn get_upvar_index_for_region(&self, tcx: TyCtxt<'tcx>, fr: RegionVid) -> Option { let upvar_index = - self.universal_regions().defining_ty.upvar_tys(tcx).position(|upvar_ty| { + self.universal_regions().defining_ty.upvar_tys().position(|upvar_ty| { debug!("get_upvar_index_for_region: upvar_ty={:?}", upvar_ty); tcx.any_free_region_meets(&upvar_ty, |r| { let r = r.to_region_vid(); @@ -44,7 +44,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { }) })?; - let upvar_ty = self.universal_regions().defining_ty.upvar_tys(tcx).nth(upvar_index); + let upvar_ty = self.universal_regions().defining_ty.upvar_tys().nth(upvar_index); debug!( "get_upvar_index_for_region: found {:?} in upvar {} which has type {:?}", 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..fd8f17718e795 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, Terminator, 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, ); @@ -112,48 +112,49 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.super_statement(statement, location); } - fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, location: Location) { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { self.check_activations(location); - match kind { + match &terminator.kind { TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { self.consume_operand(location, discr); } - TerminatorKind::Drop { location: ref drop_place, target: _, unwind: _ } => { + TerminatorKind::Drop { place: 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, + place: 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: _, + fn_span: _, } => { self.consume_operand(location, func); 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,33 +167,62 @@ 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: _, + line_spans: _, + 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 { def_id: _ } => {} + } + } + } TerminatorKind::Goto { target: _ } | TerminatorKind::Abort | TerminatorKind::Unreachable - | TerminatorKind::FalseEdges { real_target: _, imaginary_target: _ } + | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => { // no data used, thus irrelevant to borrowck } } - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } } @@ -201,7 +231,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 +246,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 +254,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { LocalMutationIsAllowed::No, ); } - Operand::Move(ref place) => { + Operand::Move(place) => { self.access_place( location, place, @@ -239,7 +269,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 +288,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, @@ -272,6 +302,8 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); } + Rvalue::ThreadLocalRef(_) => {} + Rvalue::Use(ref operand) | Rvalue::Repeat(ref operand, _) | Rvalue::UnaryOp(_ /*un_op*/, ref operand) @@ -279,7 +311,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 +345,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 +357,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 +391,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 +412,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 +446,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 a61d00b0120cb..03b663eb75056 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -1,25 +1,25 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. -use rustc::lint::builtin::MUTABLE_BORROW_RESERVATION_CONFLICT; -use rustc::lint::builtin::UNUSED_MUT; -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_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::LocalDefId; +use rustc_hir::{HirId, Node}; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; +use rustc_middle::mir::{ + traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, + PlaceRef, +}; +use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; +use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; +use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, RegionVid, TyCtxt}; +use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; +use rustc_span::{Span, Symbol, DUMMY_SP}; use either::Either; use smallvec::SmallVec; @@ -28,17 +28,14 @@ use std::collections::BTreeMap; use std::mem; use std::rc::Rc; -use rustc_ast::ast::Name; -use rustc_span::{Span, DUMMY_SP}; - 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}; @@ -51,6 +48,7 @@ use self::path_utils::*; mod borrow_set; mod constraint_generation; mod constraints; +mod def_use; mod diagnostics; mod facts; mod invalidation; @@ -76,7 +74,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, @@ -92,9 +90,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(); @@ -103,20 +101,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 { @@ -138,12 +136,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) + .closure_captures + .get(&def_id.to_def_id()) .into_iter() .flat_map(|v| v.values()) .map(|upvar_id| { @@ -171,27 +169,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(); + 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); @@ -217,18 +216,25 @@ fn do_mir_borrowck<'a, 'tcx>( &mut flow_inits, &mdpe.move_data, &borrow_set, + &upvars, ); // Dump MIR results into a file, if that is enabled. This let us // write unit-tests, as well as helping with debugging. - nll::dump_mir_results(infcx, MirSource::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, @@ -243,13 +249,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) { @@ -260,6 +266,41 @@ 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, + 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 { @@ -297,13 +338,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, ); @@ -311,8 +350,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 { @@ -419,8 +457,8 @@ fn do_mir_borrowck<'a, 'tcx>( crate struct MirBorrowckCtxt<'cx, 'tcx> { crate infcx: &'cx InferCtxt<'cx, 'tcx>, - body: ReadOnlyBodyAndCache<'cx, 'tcx>, - mir_def_id: DefId, + body: &'cx Body<'tcx>, + mir_def_id: LocalDefId, move_data: &'cx MoveData<'tcx>, /// Map from MIR `Location` to `LocationIndex`; created @@ -494,7 +532,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. @@ -512,10 +550,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>, @@ -526,11 +564,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 @@ -550,17 +588,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, @@ -574,7 +612,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, @@ -595,7 +633,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, @@ -604,7 +642,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>, @@ -619,11 +657,11 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { self.consume_operand(loc, (discr, span), flow_state); } - TerminatorKind::Drop { location: ref drop_place, target: _, unwind: _ } => { + TerminatorKind::Drop { place: ref drop_place, target: _, unwind: _ } => { let tcx = self.infcx.tcx; // Compute the type with accurate region information. - let drop_place_ty = drop_place.ty(*self.body, self.infcx.tcx); + 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; @@ -641,14 +679,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, + place: drop_place, value: ref new_value, target: _, unwind: _, @@ -662,43 +700,86 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt ref destination, cleanup: _, from_hir_call: _, + fn_span: _, } => { self.consume_operand(loc, (func, span), flow_state); for arg in args { self.consume_operand(loc, (arg, span), flow_state); } - if let Some((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: _, + line_spans: _, + destination: _, + } => { + for op in operands { + match *op { + InlineAsmOperand::In { reg: _, ref value } + | InlineAsmOperand::Const { ref value } => { + self.consume_operand(loc, (value, span), flow_state); + } + InlineAsmOperand::Out { reg: _, late: _, place, .. } => { + if let Some(place) = place { + self.mutate_place( + loc, + (place, span), + Shallow(None), + JustWrite, + flow_state, + ); + } + } + InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { + self.consume_operand(loc, (in_value, span), flow_state); + if let Some(out_place) = out_place { + self.mutate_place( + loc, + (out_place, span), + Shallow(None), + JustWrite, + flow_state, + ); + } + } + InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } + TerminatorKind::Goto { target: _ } | TerminatorKind::Abort | TerminatorKind::Unreachable | TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdges { real_target: _, imaginary_target: _ } + | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => { // no data used, thus irrelevant to borrowck } } } - fn visit_terminator_exit( + fn visit_terminator_after_primary_effect( &mut self, flow_state: &Flows<'cx, 'tcx>, term: &'cx Terminator<'tcx>, @@ -735,11 +816,12 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt | TerminatorKind::Call { .. } | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::FalseEdges { real_target: _, imaginary_target: _ } + | TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ } | TerminatorKind::FalseUnwind { real_target: _, unwind: _ } | TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Unreachable => {} + | TerminatorKind::Unreachable + | TerminatorKind::InlineAsm { .. } => {} } } } @@ -875,7 +957,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 @@ -887,7 +969,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>, @@ -908,7 +990,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=`{:?}`", @@ -936,14 +1018,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>, @@ -956,7 +1038,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. @@ -995,19 +1076,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)); @@ -1028,12 +1108,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!( @@ -1046,13 +1126,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!( @@ -1060,7 +1140,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!( @@ -1103,7 +1183,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>, @@ -1153,7 +1233,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))) @@ -1191,7 +1271,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - Rvalue::AddressOf(mutability, ref place) => { + Rvalue::AddressOf(mutability, place) => { let access_kind = match mutability { Mutability::Mut => ( Deep, @@ -1218,6 +1298,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } + Rvalue::ThreadLocalRef(_) => {} + Rvalue::Use(ref operand) | Rvalue::Repeat(ref operand, _) | Rvalue::UnaryOp(_ /*un_op*/, ref operand) @@ -1225,7 +1307,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, @@ -1267,7 +1349,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()]); @@ -1286,7 +1368,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); @@ -1300,7 +1382,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() { @@ -1339,8 +1421,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 { @@ -1364,7 +1445,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( @@ -1383,7 +1464,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, @@ -1414,7 +1495,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 @@ -1468,7 +1549,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, @@ -1494,7 +1575,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, @@ -1509,7 +1590,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); @@ -1733,7 +1814,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); @@ -1906,7 +1987,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>, @@ -1921,10 +2002,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, @@ -1954,14 +2037,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(), @@ -1985,11 +2072,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; } @@ -2157,10 +2248,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 @@ -2217,30 +2309,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// be `self` in the current MIR, because that is the only time we directly access the fields /// of a closure type. pub fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option { - let mut place_projection = place_ref.projection; - let mut by_ref = false; - - if let [proj_base @ .., ProjectionElem::Deref] = place_projection { - place_projection = proj_base; - by_ref = true; - } - - match place_projection { - [base @ .., ProjectionElem::Field(field, _ty)] => { - let tcx = self.infcx.tcx; - let base_ty = Place::ty_from(place_ref.local, base, self.body(), tcx).ty; - - if (base_ty.is_closure() || base_ty.is_generator()) - && (!by_ref || self.upvars[field.index()].by_ref) - { - Some(*field) - } else { - None - } - } - - _ => None, - } + path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body()) } } diff --git a/src/librustc_mir/borrow_check/nll.rs b/src/librustc_mir/borrow_check/nll.rs index ba1b322524e87..ea68364be37a3 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; @@ -39,6 +39,7 @@ use crate::borrow_check::{ renumber, type_check::{self, MirTypeckRegionConstraints, MirTypeckResults}, universal_regions::UniversalRegions, + Upvar, }; crate type PoloniusOutput = Output; @@ -58,10 +59,10 @@ crate struct NllOutput<'tcx> { /// `compute_regions`. pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, - def_id: DefId, + def_id: LocalDefId, 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); @@ -71,7 +72,7 @@ pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>( // Replace all remaining regions with fresh inference variables. renumber::renumber_mir(infcx, body, promoted); - let source = MirSource::item(def_id); + let source = MirSource::item(def_id.to_def_id()); mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, body, |_, _| Ok(())); universal_regions @@ -157,15 +158,16 @@ 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>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, + upvars: &[Upvar], ) -> NllOutput<'tcx> { let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default()); @@ -188,6 +190,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( flow_inits, move_data, elements, + upvars, ); if let Some(all_facts) = &mut all_facts { @@ -272,7 +275,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.hir().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 +295,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`. @@ -314,10 +317,10 @@ pub(super) fn dump_mir_results<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, source: MirSource<'tcx>, body: &Body<'tcx>, - regioncx: &RegionInferenceContext<'_>, + regioncx: &RegionInferenceContext<'tcx>, 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; } @@ -325,7 +328,7 @@ pub(super) fn dump_mir_results<'a, 'tcx>( match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => { - regioncx.dump_mir(out)?; + regioncx.dump_mir(infcx.tcx, out)?; writeln!(out, "|")?; if let Some(closure_region_requirements) = closure_region_requirements { diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index deec6f386ffb3..934729553a73b 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -1,11 +1,12 @@ use crate::borrow_check::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation}; use crate::borrow_check::places_conflict; use crate::borrow_check::AccessDepth; +use crate::borrow_check::Upvar; 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, Field, Location, Place, PlaceRef, ProjectionElem}; +use rustc_middle::ty::TyCtxt; /// Returns `true` if the borrow represented by `kind` is /// allowed to be split into separate Reservation and @@ -27,7 +28,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 +49,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,8 +131,43 @@ 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() } + +/// If `place` is a field projection, and the field is being projected from a closure type, +/// then returns the index of the field being projected. Note that this closure will always +/// be `self` in the current MIR, because that is the only time we directly access the fields +/// of a closure type. +pub(crate) fn is_upvar_field_projection( + tcx: TyCtxt<'tcx>, + upvars: &[Upvar], + place_ref: PlaceRef<'tcx>, + body: &Body<'tcx>, +) -> Option { + let mut place_projection = place_ref.projection; + let mut by_ref = false; + + if let [proj_base @ .., ProjectionElem::Deref] = place_projection { + place_projection = proj_base; + by_ref = true; + } + + match place_projection { + [base @ .., ProjectionElem::Field(field, _ty)] => { + let base_ty = Place::ty_from(place_ref.local, base, body, tcx).ty; + + if (base_ty.is_closure() || base_ty.is_generator()) + && (!by_ref || upvars[field.index()].by_ref) + { + Some(*field) + } else { + None + } + } + + _ => None, + } +} diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs index 01c44d0d905b6..cadf1ebf1b774 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> { @@ -47,7 +47,7 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { for (i, elem) in self.projection.iter().enumerate() { let proj_base = &self.projection[..i]; - if *elem == ProjectionElem::Deref { + if elem == ProjectionElem::Deref { let ty = Place::ty_from(self.local, proj_base, body, tcx).ty; match ty.kind { ty::Ref(_, _, hir::Mutability::Not) if i == 0 => { diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index 767ffa50fedb4..246e4826e0e76 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, @@ -138,7 +138,7 @@ fn place_components_conflict<'tcx>( } // loop invariant: borrow_c is always either equal to access_c or disjoint from it. - for (i, (borrow_c, access_c)) in + for (i, (borrow_c, &access_c)) in borrow_place.projection.iter().zip(access_place.projection.iter()).enumerate() { debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c); @@ -313,8 +313,8 @@ fn place_projection_conflict<'tcx>( body: &Body<'tcx>, pi1_local: Local, pi1_proj_base: &[PlaceElem<'tcx>], - pi1_elem: &PlaceElem<'tcx>, - pi2_elem: &PlaceElem<'tcx>, + pi1_elem: PlaceElem<'tcx>, + pi2_elem: PlaceElem<'tcx>, bias: PlaceConflictBias, ) -> Overlap { match (pi1_elem, pi2_elem) { @@ -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 { @@ -444,7 +449,7 @@ fn place_projection_conflict<'tcx>( // element (like -1 in Python) and `min_length` the first. // Therefore, `min_length - offset_from_end` gives the minimal possible // offset from the beginning - if *offset_from_begin >= *min_length - *offset_from_end { + if offset_from_begin >= min_length - offset_from_end { debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-FE"); Overlap::EqualOrDisjoint } else { @@ -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/dump_mir.rs b/src/librustc_mir/borrow_check/region_infer/dump_mir.rs index 369e540231168..d6e48deb031ac 100644 --- a/src/librustc_mir/borrow_check/region_infer/dump_mir.rs +++ b/src/librustc_mir/borrow_check/region_infer/dump_mir.rs @@ -4,7 +4,9 @@ //! context internal state. use super::{OutlivesConstraint, RegionInferenceContext}; +use crate::borrow_check::type_check::Locations; use rustc_infer::infer::NLLRegionVariableOrigin; +use rustc_middle::ty::TyCtxt; use std::io::{self, Write}; // Room for "'_#NNNNr" before things get misaligned. @@ -14,7 +16,7 @@ const REGION_WIDTH: usize = 8; impl<'tcx> RegionInferenceContext<'tcx> { /// Write out our state into the `.mir` files. - pub(crate) fn dump_mir(&self, out: &mut dyn Write) -> io::Result<()> { + pub(crate) fn dump_mir(&self, tcx: TyCtxt<'tcx>, out: &mut dyn Write) -> io::Result<()> { writeln!(out, "| Free Region Mapping")?; for region in self.regions() { @@ -48,7 +50,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { writeln!(out, "|")?; writeln!(out, "| Inference Constraints")?; - self.for_each_constraint(&mut |msg| writeln!(out, "| {}", msg))?; + self.for_each_constraint(tcx, &mut |msg| writeln!(out, "| {}", msg))?; Ok(()) } @@ -59,6 +61,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// inference resulted in the values that it did when debugging. fn for_each_constraint( &self, + tcx: TyCtxt<'tcx>, with_msg: &mut dyn FnMut(&str) -> io::Result<()>, ) -> io::Result<()> { for region in self.definitions.indices() { @@ -72,7 +75,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraints.sort(); for constraint in &constraints { let OutlivesConstraint { sup, sub, locations, category } = constraint; - with_msg(&format!("{:?}: {:?} due to {:?} at {:?}", sup, sub, category, locations,))?; + let (name, arg) = match locations { + Locations::All(span) => ("All", tcx.sess.source_map().span_to_string(*span)), + Locations::Single(loc) => ("Single", format!("{:?}", loc)), + }; + with_msg(&format!("{:?}: {:?} due to {:?} at {}({})", sup, sub, category, name, arg))?; } Ok(()) diff --git a/src/librustc_mir/borrow_check/region_infer/graphviz.rs b/src/librustc_mir/borrow_check/region_infer/graphviz.rs index 39b396ba4e7d3..a272e922a504e 100644 --- a/src/librustc_mir/borrow_check/region_infer/graphviz.rs +++ b/src/librustc_mir/borrow_check/region_infer/graphviz.rs @@ -1,5 +1,5 @@ //! This module provides linkage between RegionInferenceContext and -//! libgraphviz traits, specialized to attaching borrowck analysis +//! librustc_graphviz traits, specialized to attaching borrowck analysis //! data to rendered labels. use std::borrow::Cow; @@ -7,6 +7,7 @@ use std::io::{self, Write}; use super::*; use crate::borrow_check::constraints::OutlivesConstraint; +use rustc_graphviz as dot; impl<'tcx> RegionInferenceContext<'tcx> { /// Write out the region constraint graph. diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/src/librustc_mir/borrow_check/region_infer/mod.rs index fe96b3e34a2a8..3e459bd52f757 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, ReturnConstraint, +}; +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 @@ -940,8 +985,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// inference variables with some region from the closure /// signature -- this is not always possible, so this is a /// fallible process. Presuming we do find a suitable region, we - /// will represent it with a `ReClosureBound`, which is a - /// `RegionKind` variant that can be allocated in the gcx. + /// will use it's *external name*, which will be a `RegionKind` + /// variant that can be used in query responses such as + /// `ReEarlyBound`. fn try_promote_type_test_subject( &self, infcx: &InferCtxt<'_, 'tcx>, @@ -991,18 +1037,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { // find an equivalent. let upper_bound = self.non_local_universal_upper_bound(region_vid); if self.region_contains(region_vid, upper_bound) { - tcx.mk_region(ty::ReClosureBound(upper_bound)) + self.definitions[upper_bound].external_name.unwrap_or(r) } else { - // In the case of a failure, use a `ReVar` - // result. This will cause the `lift` later on to - // fail. + // In the case of a failure, use a `ReVar` result. This will + // 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; } @@ -1257,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 } } @@ -1359,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 } } @@ -1632,9 +1680,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { universe1.cannot_name(placeholder.universe) } - NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential { .. } => { - false - } + NLLRegionVariableOrigin::RootEmptyRegion + | NLLRegionVariableOrigin::FreeRegion + | NLLRegionVariableOrigin::Existential { .. } => false, } } @@ -1772,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!( @@ -1793,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) @@ -1814,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(), } } @@ -1944,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, }; @@ -1960,7 +2017,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { | ConstraintCategory::BoringNoLocation | ConstraintCategory::Internal => false, ConstraintCategory::TypeAnnotation - | ConstraintCategory::Return + | ConstraintCategory::Return(_) | ConstraintCategory::Yield => true, _ => constraint_sup_scc != target_scc, } @@ -1985,7 +2042,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let Some(i) = best_choice { if let Some(next) = categorized_path.get(i + 1) { - if categorized_path[i].0 == ConstraintCategory::Return + if matches!(categorized_path[i].0, ConstraintCategory::Return(_)) && next.0 == ConstraintCategory::OpaqueType { // The return expression is being influenced by the return type being @@ -1993,6 +2050,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { return *next; } } + + if categorized_path[i].0 == ConstraintCategory::Return(ReturnConstraint::Normal) { + let field = categorized_path.iter().find_map(|p| { + if let ConstraintCategory::ClosureUpvar(f) = p.0 { Some(f) } else { None } + }); + + if let Some(field) = field { + categorized_path[i].0 = + ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); + } + } + return categorized_path[i]; } @@ -2029,15 +2098,6 @@ pub trait ClosureRegionRequirementsExt<'tcx> { closure_def_id: DefId, closure_substs: SubstsRef<'tcx>, ) -> Vec>; - - fn subst_closure_mapping( - &self, - tcx: TyCtxt<'tcx>, - closure_mapping: &IndexVec>, - value: &T, - ) -> T - where - T: TypeFoldable<'tcx>; } impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx> { @@ -2094,7 +2154,6 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx } ClosureOutlivesSubject::Ty(ty) => { - let ty = self.subst_closure_mapping(tcx, closure_mapping, &ty); debug!( "apply_requirements: ty={:?} \ outlived_region={:?} \ @@ -2107,22 +2166,4 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx }) .collect() } - - fn subst_closure_mapping( - &self, - tcx: TyCtxt<'tcx>, - closure_mapping: &IndexVec>, - value: &T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - tcx.fold_regions(value, &mut false, |r, _depth| { - if let ty::ReClosureBound(vid) = r { - closure_mapping[*vid] - } else { - bug!("subst_closure_mapping: encountered non-closure bound free region {:?}", r) - } - }) - } } 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..5df033b48c1f9 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,12 +64,16 @@ 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); + let new_ty = self.renumber_regions(&ty); - if new_ty != *ty { - return Some(PlaceElem::Field(*field, new_ty)); + if new_ty != ty { + return Some(PlaceElem::Field(field, new_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..beee31812563e 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; @@ -259,7 +259,13 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { .param_env .and(type_op::normalize::Normalize::new(ty)) .fully_perform(self.infcx) - .unwrap_or_else(|_| bug!("failed to normalize {:?}", ty)); + .unwrap_or_else(|_| { + self.infcx + .tcx + .sess + .delay_span_bug(DUMMY_SP, &format!("failed to normalize {:?}", ty)); + (self.infcx.tcx.ty_error(), None) + }); let constraints2 = self.add_implied_bounds(ty); normalized_inputs_and_output.push(ty); constraints1.into_iter().chain(constraints2) 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 37cf77b7095c6..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,44 +33,49 @@ 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!( + "equate_inputs_and_outputs: normalized_input_tys = {:?}, local_decls = {:?}", + normalized_input_tys, body.local_decls + ); + // Equate expected input tys with those in the MIR. for (&normalized_input_ty, argument_index) in normalized_input_tys.iter().zip(0..) { // In MIR, argument N is stored in local N+1. let local = Local::new(argument_index + 1); - debug!("equate_inputs_and_outputs: normalized_input_ty = {:?}", normalized_input_ty); - let mir_input_ty = body.local_decls[local].ty; let mir_input_span = body.local_decls[local].source_info.span; self.equate_normalized_input_or_output( @@ -117,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, ) { @@ -140,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 d98abc57bfd49..0e35cafb9f3e9 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, + ToPredicate, Ty, TyCtxt, UserType, UserTypeAnnotationIndex, +}; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi::VariantIdx; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::opaque_types::{GenerateMemberConstraints, InferCtxtExt}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; @@ -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, @@ -51,6 +55,7 @@ use crate::borrow_check::{ location::LocationTable, member_constraints::MemberConstraintSet, nll::ToRegionVid, + path_utils, region_infer::values::{ LivenessValues, PlaceholderIndex, PlaceholderIndices, RegionValueElements, }, @@ -58,6 +63,7 @@ use crate::borrow_check::{ renumber, type_check::free_region_relations::{CreateResult, UniversalRegionRelations}, universal_regions::{DefiningTy, UniversalRegions}, + Upvar, }; macro_rules! span_mirbug { @@ -105,26 +111,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>, @@ -132,6 +134,7 @@ pub(crate) fn type_check<'mir, 'tcx>( flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, elements: &Rc, + upvars: &[Upvar], ) -> MirTypeckResults<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { @@ -162,6 +165,7 @@ pub(crate) fn type_check<'mir, 'tcx>( borrow_set, all_facts, constraints: &mut constraints, + upvars, }; let opaque_type_values = type_check_internal( @@ -188,10 +192,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 +204,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 +213,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 +270,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 +324,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 +345,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 +406,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 +458,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, @@ -491,7 +498,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { if place_ty.variant_index.is_none() { if place_ty.ty.references_error() { assert!(self.errors_reported); - return PlaceTy::from_ty(self.tcx().types.err); + return PlaceTy::from_ty(self.tcx().ty_error()); } } place_ty = self.sanitize_projection(place_ty, elem, place, location) @@ -500,7 +507,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 +532,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 +566,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 @@ -578,7 +581,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { for constraint in constraints.outlives().iter() { let mut constraint = *constraint; constraint.locations = locations; - if let ConstraintCategory::Return + if let ConstraintCategory::Return(_) | ConstraintCategory::UseAsConst | ConstraintCategory::UseAsStatic = constraint.category { @@ -612,14 +615,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { fn sanitize_projection( &mut self, base: PlaceTy<'tcx>, - pi: &PlaceElem<'tcx>, + pi: PlaceElem<'tcx>, place: &Place<'tcx>, location: Location, ) -> PlaceTy<'tcx> { debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); let tcx = self.tcx(); let base_ty = base.ty; - match *pi { + match pi { ProjectionElem::Deref => { let deref_ty = base_ty.builtin_deref(true); PlaceTy::from_ty(deref_ty.map(|t| t.ty).unwrap_or_else(|| { @@ -690,6 +693,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, @@ -721,7 +725,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { fn error(&mut self) -> Ty<'tcx> { self.errors_reported = true; - self.tcx().types.err + self.tcx().ty_error() } fn field_ty( @@ -757,21 +761,21 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { ty::Adt(adt_def, substs) if !adt_def.is_enum() => { (&adt_def.variants[VariantIdx::new(0)], substs) } - ty::Closure(def_id, substs) => { - return match substs.as_closure().upvar_tys(def_id, tcx).nth(field.index()) { + ty::Closure(_, substs) => { + return match substs.as_closure().upvar_tys().nth(field.index()) { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: substs.as_closure().upvar_tys(def_id, tcx).count(), + field_count: substs.as_closure().upvar_tys().count(), }), }; } - ty::Generator(def_id, substs, _) => { + ty::Generator(_, substs, _) => { // Only prefix fields (upvars and current state) are // accessible without a variant index. - return match substs.as_generator().prefix_tys(def_id, tcx).nth(field.index()) { + return match substs.as_generator().prefix_tys().nth(field.index()) { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: substs.as_generator().prefix_tys(def_id, tcx).count(), + field_count: substs.as_generator().prefix_tys().count(), }), }; } @@ -812,7 +816,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)>, @@ -827,6 +831,7 @@ struct BorrowCheckContext<'a, 'tcx> { all_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, + upvars: &'a [Upvar], } crate struct MirTypeckResults<'tcx> { @@ -960,7 +965,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>, @@ -1016,7 +1021,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } self.prove_predicate( - ty::Predicate::WellFormed(inferred_ty), + ty::PredicateKind::WellFormed(inferred_ty.into()).to_predicate(self.tcx()), Locations::All(span), ConstraintCategory::TypeAnnotation, ); @@ -1053,7 +1058,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 +1144,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 +1238,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); @@ -1244,7 +1250,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { |infcx| { let mut obligations = ObligationAccumulator::default(); - let dummy_body_id = ObligationCause::dummy().body_id; + let dummy_body_id = hir::CRATE_HIR_ID; let (output_ty, opaque_type_map) = obligations.add(infcx.instantiate_opaque_types( anon_owner_def_id, @@ -1267,7 +1273,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { obligations.obligations.push(traits::Obligation::new( ObligationCause::dummy(), param_env, - ty::Predicate::WellFormed(revealed_ty), + ty::PredicateKind::WellFormed(revealed_ty.into()).to_predicate(infcx.tcx), )); obligations.add( infcx @@ -1396,12 +1402,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 { @@ -1424,7 +1425,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::UseAsConst } } else { - ConstraintCategory::Return + ConstraintCategory::Return(ReturnConstraint::Normal) } } Some(l) if !body.local_decls[l].is_user_variable() => { @@ -1433,9 +1434,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 +1474,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 +1485,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 +1507,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 +1530,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { StatementKind::FakeRead(..) | StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - | StatementKind::InlineAsm { .. } + | StatementKind::LlvmInlineAsm { .. } | StatementKind::Retag { .. } | StatementKind::Nop => {} } @@ -1551,13 +1552,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } - | TerminatorKind::FalseEdges { .. } - | TerminatorKind::FalseUnwind { .. } => { + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => { // no checks needed for these } - TerminatorKind::DropAndReplace { ref location, ref value, target: _, unwind: _ } => { - let place_ty = location.ty(body, tcx).ty; + TerminatorKind::DropAndReplace { ref place, ref value, target: _, unwind: _ } => { + let place_ty = place.ty(body, tcx).ty; let rv_ty = value.ty(body, tcx); let locations = term_location.to_locations(); @@ -1615,7 +1617,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_call_dest(body, term, &sig, destination, term_location); self.prove_predicates( - sig.inputs_and_output.iter().map(|ty| ty::Predicate::WellFormed(ty)), + sig.inputs_and_output.iter().map(|ty| ty::PredicateKind::WellFormed(ty.into())), term_location.to_locations(), ConstraintCategory::Boring, ); @@ -1706,7 +1708,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::UseAsConst } } else { - ConstraintCategory::Return + ConstraintCategory::Return(ReturnConstraint::Normal) } } Some(l) if !body.local_decls[l].is_user_variable() => { @@ -1760,6 +1762,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 { @@ -1845,7 +1848,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.assert_iscleanup(body, block_data, cleanup, true); } } - TerminatorKind::FalseEdges { real_target, imaginary_target } => { + TerminatorKind::FalseEdge { real_target, imaginary_target } => { self.assert_iscleanup(body, block_data, real_target, is_cleanup); self.assert_iscleanup(body, block_data, imaginary_target, is_cleanup); } @@ -1858,6 +1861,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); + } + } } } @@ -1946,22 +1954,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) } } - AggregateKind::Closure(def_id, substs) => { - match substs.as_closure().upvar_tys(def_id, tcx).nth(field_index) { + AggregateKind::Closure(_, substs) => { + match substs.as_closure().upvar_tys().nth(field_index) { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: substs.as_closure().upvar_tys(def_id, tcx).count(), + field_count: substs.as_closure().upvar_tys().count(), }), } } - AggregateKind::Generator(def_id, substs, _) => { + AggregateKind::Generator(_, substs, _) => { // It doesn't make sense to look at a field beyond the prefix; // these require a variant index, and are not initialized in // aggregate rvalues. - match substs.as_generator().prefix_tys(def_id, tcx).nth(field_index) { + match substs.as_generator().prefix_tys().nth(field_index) { Some(ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: substs.as_generator().prefix_tys(def_id, tcx).count(), + field_count: substs.as_generator().prefix_tys().count(), }), } } @@ -1972,12 +1980,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 { @@ -1986,21 +1989,28 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } Rvalue::Repeat(operand, len) => { - if *len > 1 { + // If the length cannot be evaluated we must assume that the length can be larger + // than 1. + // If the length is larger than 1, the repeat expression will need to copy the + // element, so we require the `Copy` trait. + if len.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) { if let Operand::Move(_) = operand { // While this is located in `nll::typeck` this error is not an NLL error, it's // a required check to make sure that repeated elements implement `Copy`. let span = body.source_info(location).span; - let ty = operand.ty(*body, tcx); + 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); @@ -2008,19 +2018,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { &traits::Obligation::new( ObligationCause::new( span, - self.tcx().hir().def_index_to_hir_id(self.mir_def_id.index), + 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::PredicateKind::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, &[]), ), }), hir::Constness::NotConst, - ), + ) + .to_predicate(self.tcx()), ), &traits::SelectionError::Unimplemented, false, @@ -2039,7 +2053,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, &[]), }; @@ -2053,7 +2067,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 @@ -2082,13 +2096,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => { - let sig = match op.ty(*body, tcx).kind { - ty::Closure(def_id, substs) => { - substs.as_closure().sig_ty(def_id, tcx).fn_sig(tcx) - } + 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, @@ -2108,7 +2120,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 @@ -2139,8 +2151,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( @@ -2151,7 +2166,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, @@ -2199,7 +2214,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 { @@ -2259,27 +2274,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(_))) => (), } } } @@ -2289,47 +2300,65 @@ 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) + ), } } Rvalue::AddressOf(..) + | Rvalue::ThreadLocalRef(..) | Rvalue::Use(..) | Rvalue::Len(..) | Rvalue::BinaryOp(..) @@ -2345,6 +2374,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option { match rvalue { Rvalue::Use(_) + | Rvalue::ThreadLocalRef(_) | Rvalue::Repeat(..) | Rvalue::Ref(..) | Rvalue::AddressOf(..) @@ -2398,6 +2428,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, @@ -2463,6 +2494,19 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); let mut cursor = borrowed_place.projection.as_ref(); + let tcx = self.infcx.tcx; + let field = path_utils::is_upvar_field_projection( + tcx, + &self.borrowck_context.upvars, + borrowed_place.as_ref(), + body, + ); + let category = if let Some(field) = field { + ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id) + } else { + ConstraintCategory::Boring + }; + while let [proj_base @ .., elem] = cursor { cursor = proj_base; @@ -2470,7 +2514,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { match elem { ProjectionElem::Deref => { - let tcx = self.infcx.tcx; let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty; debug!("add_reborrow_constraint - base_ty = {:?}", base_ty); @@ -2480,7 +2523,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { sup: ref_region.to_region_vid(), sub: borrow_region.to_region_vid(), locations: location.to_locations(), - category: ConstraintCategory::Boring, + category, }); match mutbl { @@ -2575,7 +2618,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(), @@ -2590,14 +2633,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 @@ -2659,7 +2706,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { category: ConstraintCategory, ) { self.prove_predicates( - Some(ty::Predicate::Trait( + Some(ty::PredicateKind::Trait( trait_ref.to_poly_trait_ref().to_poly_trait_predicate(), hir::Constness::NotConst, )), @@ -2681,11 +2728,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn prove_predicates( &mut self, - predicates: impl IntoIterator>, + predicates: impl IntoIterator>, locations: Locations, category: ConstraintCategory, ) { for predicate in predicates { + let predicate = predicate.to_predicate(self.tcx()); debug!("prove_predicates(predicate={:?}, locations={:?})", predicate, locations,); self.prove_predicate(predicate, locations, category); @@ -2711,7 +2759,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..285d9ed64691a 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}; @@ -26,7 +25,7 @@ pub(super) fn relate_types<'tcx>( category: ConstraintCategory, borrowck_context: Option<&mut BorrowCheckContext<'_, 'tcx>>, ) -> Fallible<()> { - debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations); + debug!("relate_types(a={:?}, v={:?}, b={:?}, locations={:?})", a, v, b, locations); TypeRelating::new( infcx, NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category), @@ -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 67b00e9ffdd5f..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`. @@ -108,13 +115,11 @@ impl<'tcx> DefiningTy<'tcx> { /// not a closure or generator, there are no upvars, and hence it /// will be an empty list. The order of types in this list will /// match up with the upvar order in the HIR, typesystem, and MIR. - pub fn upvar_tys(self, tcx: TyCtxt<'tcx>) -> impl Iterator> + 'tcx { + pub fn upvar_tys(self) -> impl Iterator> + 'tcx { match self { - DefiningTy::Closure(def_id, substs) => { - Either::Left(substs.as_closure().upvar_tys(def_id, tcx)) - } - DefiningTy::Generator(def_id, substs, _) => { - Either::Right(Either::Left(substs.as_generator().upvar_tys(def_id, tcx))) + DefiningTy::Closure(_, substs) => Either::Left(substs.as_closure().upvar_tys()), + DefiningTy::Generator(_, substs, _) => { + Either::Right(Either::Left(substs.as_generator().upvar_tys())) } DefiningTy::FnDef(..) | DefiningTy::Const(..) => { Either::Right(Either::Right(iter::empty())) @@ -222,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 @@ -318,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 @@ -470,16 +480,20 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { debug!("build: local regions = {}..{}", first_local_index, num_universals); let yield_ty = match defining_ty { - DefiningTy::Generator(def_id, substs, _) => { - Some(substs.as_generator().yield_ty(def_id, self.infcx.tcx)) - } + DefiningTy::Generator(_, substs, _) => Some(substs.as_generator().yield_ty()), _ => 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, @@ -501,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) }; @@ -580,7 +594,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { match defining_ty { DefiningTy::Closure(def_id, substs) => { assert_eq!(self.mir_def_id, def_id); - let closure_sig = substs.as_closure().sig_ty(def_id, tcx).fn_sig(tcx); + let closure_sig = substs.as_closure().sig(); let inputs_and_output = closure_sig.inputs_and_output(); let closure_ty = tcx.closure_env_ty(def_id, substs).unwrap(); ty::Binder::fuse(closure_ty, inputs_and_output, |closure_ty, inputs_and_output| { @@ -604,8 +618,8 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { DefiningTy::Generator(def_id, substs, movability) => { assert_eq!(self.mir_def_id, def_id); - let resume_ty = substs.as_generator().resume_ty(def_id, tcx); - let output = substs.as_generator().return_ty(def_id, tcx); + let resume_ty = substs.as_generator().resume_ty(); + let output = substs.as_generator().return_ty(); let generator_ty = tcx.mk_generator(def_id, substs, movability); let inputs_and_output = self.infcx.tcx.intern_type_list(&[generator_ty, resume_ty, output]); @@ -774,14 +788,14 @@ fn for_each_late_bound_region_defined_on<'tcx>( fn_def_id: DefId, mut f: impl FnMut(ty::Region<'tcx>), ) { - if let Some(late_bounds) = tcx.is_late_bound_map(fn_def_id.index) { + if let Some(late_bounds) = tcx.is_late_bound_map(fn_def_id.expect_local()) { for late_bound in late_bounds.iter() { - let hir_id = HirId { owner: fn_def_id.index, local_id: *late_bound }; + let hir_id = HirId { owner: fn_def_id.expect_local(), local_id: *late_bound }; let name = tcx.hir().name(hir_id); 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..e027056842db9 100644 --- a/src/librustc_mir/borrow_check/used_muts.rs +++ b/src/librustc_mir/borrow_check/used_muts.rs @@ -1,5 +1,7 @@ -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, Terminator, TerminatorKind, +}; use rustc_data_structures::fx::FxHashSet; @@ -32,7 +34,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 +53,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 @@ -62,31 +64,32 @@ impl GatherUsedMutsVisitor<'_, '_, '_> { } impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tcx> { - fn visit_terminator_kind(&mut self, kind: &TerminatorKind<'tcx>, _location: Location) { - debug!("visit_terminator_kind: kind={:?}", kind); - match &kind { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + debug!("visit_terminator: terminator={:?}", terminator); + match &terminator.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); + TerminatorKind::DropAndReplace { place, .. } => { + self.remove_never_initialized_mut_locals(*place); } _ => {} } + + self.super_terminator(terminator, location); } - fn visit_statement(&mut self, statement: &Statement<'tcx>, _location: Location) { - match &statement.kind { - StatementKind::Assign(box (into, _)) => { - debug!( - "visit_statement: statement={:?} local={:?} \ + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + 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); } + + self.super_statement(statement, location); } fn visit_local(&mut self, local: &Local, place_context: PlaceContext, location: Location) { diff --git a/src/librustc_mir/const_eval/error.rs b/src/librustc_mir/const_eval/error.rs index 63ad9ec8cae9e..5deae94fe0c8e 100644 --- a/src/librustc_mir/const_eval/error.rs +++ b/src/librustc_mir/const_eval/error.rs @@ -1,27 +1,28 @@ use std::error::Error; use std::fmt; -use rustc::mir::AssertKind; -use rustc_span::Symbol; +use rustc_middle::mir::AssertKind; +use rustc_span::{Span, Symbol}; use super::InterpCx; -use crate::interpret::{ConstEvalErr, InterpError, InterpErrorInfo, Machine}; +use crate::interpret::{ConstEvalErr, InterpErrorInfo, Machine}; /// The CTFE machine has some custom error kinds. #[derive(Clone, Debug)] pub enum ConstEvalErrKind { NeedsRfc(String), ConstAccessesStatic, + ModifiedGlobal, AssertFailure(AssertKind), Panic { msg: Symbol, line: u32, col: u32, file: Symbol }, } // The errors become `MachineStop` with plain strings when being raised. -// `ConstEvalErr` (in `librustc/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> { - InterpError::MachineStop(Box::new(self.to_string())).into() + err_machine_stop!(self.to_string()).into() } } @@ -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,12 @@ 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>, + span: Option, ) -> ConstEvalErr<'tcx> { error.print_backtrace(); - let stacktrace = ecx.generate_stacktrace(None); - ConstEvalErr { error: error.kind, stacktrace, span: ecx.tcx.span } + let stacktrace = ecx.generate_stacktrace(); + ConstEvalErr { error: error.kind, stacktrace, span: span.unwrap_or_else(|| ecx.cur_span()) } } diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs index ffbff00cf3760..d62300b3f5541 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 { @@ -26,7 +27,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( body: &'mir mir::Body<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { debug!("eval_body_using_ecx: {:?}, {:?}", cid, ecx.param_env); - let tcx = ecx.tcx.tcx; + let tcx = *ecx.tcx; let layout = ecx.layout_of(body.return_ty().subst(tcx, cid.instance.substs))?; assert!(!layout.is_unsized()); let ret = ecx.allocate(layout, MemoryKind::Stack); @@ -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) @@ -81,15 +81,16 @@ fn eval_body_using_ecx<'mir, 'tcx>( /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. pub(super) fn mk_eval_cx<'mir, 'tcx>( tcx: TyCtxt<'tcx>, - span: Span, + root_span: Span, param_env: ty::ParamEnv<'tcx>, can_access_statics: bool, ) -> CompileTimeEvalContext<'mir, 'tcx> { debug!("mk_eval_cx: {:?}", param_env); InterpCx::new( - tcx.at(span), + tcx, + root_span, param_env, - CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()), + CompileTimeInterpreter::new(tcx.sess.const_eval_limit()), MemoryExtra { can_access_statics }, ) } @@ -100,14 +101,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, @@ -122,7 +123,7 @@ pub(super) fn op_to_const<'tcx>( } else { // It is guaranteed that any non-slice scalar pair is actually ByRef here. // When we come back from raw const eval, we are always by-ref. The only way our op here is - // by-val is if we are in const_field, i.e., if this is (a field of) something that we + // by-val is if we are in destructure_const, i.e., if this is (a field of) something that we // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or // structs containing such. op.try_as_mplace(ecx) @@ -130,7 +131,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 +148,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).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 +177,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 +194,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 +204,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 { @@ -209,21 +213,18 @@ 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| { + let err = error_to_const_error(&ecx, error, None); + err.struct_error(ecx.tcx, "it is undefined behavior to use this value", |mut diag| { diag.note(note_on_undefined_behavior_error()); diag.emit(); - }) { - 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 +258,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,35 +290,37 @@ 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); - let span = tcx.def_span(cid.instance.def_id()); let mut ecx = InterpCx::new( - tcx.at(span), + tcx, + tcx.def_span(cid.instance.def_id()), key.param_env, - CompileTimeInterpreter::new(*tcx.sess.const_eval_limit.get()), + CompileTimeInterpreter::new(tcx.sess.const_eval_limit()), MemoryExtra { can_access_statics: is_static }, ); let res = ecx.load_mir(cid.instance.def, cid.promoted); - 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 }) - }) + res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) + .map(|place| RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty }) .map_err(|error| { - let err = error_to_const_error(&ecx, error); + let err = error_to_const_error(&ecx, error, None); // errors in statics are always emitted as fatal errors if is_static { // Ensure that if the above error was either `TooGeneric` or `Reported` // an error must be reported. - let v = err.report_as_error(ecx.tcx, "could not evaluate static initializer"); + let v = err.report_as_error( + ecx.tcx.at(ecx.cur_span()), + "could not evaluate static initializer", + ); // If this is `Reveal:All`, then we need to make sure an error is reported but if // this is `Reveal::UserFacing`, then it's expected that we could get a @@ -342,8 +345,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,20 +369,23 @@ 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), ) } // anything else (array lengths, enum initializers, constant patterns) are // reported as hard errors } else { - err.report_as_error(ecx.tcx, "evaluation of constant value failed") + err.report_as_error( + ecx.tcx.at(ecx.cur_span()), + "evaluation of constant value failed", + ) } } } } else { // use of broken constant from other crate - err.report_as_error(ecx.tcx, "could not evaluate constant") + err.report_as_error(ecx.tcx.at(ecx.cur_span()), "could not evaluate constant") } }) } diff --git a/src/librustc_mir/const_eval/fn_queries.rs b/src/librustc_mir/const_eval/fn_queries.rs index 27efcd508414a..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, LocalDefId::from_def_id(parent_id)) - } 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); @@ -171,7 +159,7 @@ fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool { pub fn provide(providers: &mut Providers<'_>) { *providers = Providers { is_const_fn_raw, - is_const_impl_raw: |tcx, def_id| is_const_impl_raw(tcx, LocalDefId::from_def_id(def_id)), + is_const_impl_raw: |tcx, def_id| is_const_impl_raw(tcx, def_id.expect_local()), is_promotable_const_fn, const_fn_is_allowed_fn_ptr, ..*providers diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs index bb661d3d2a30a..dc13126df0e4c 100644 --- a/src/librustc_mir/const_eval/machine.rs +++ b/src/librustc_mir/const_eval/machine.rs @@ -1,20 +1,21 @@ -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::convert::TryFrom; 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_session::Limit; use rustc_span::symbol::Symbol; use crate::interpret::{ - self, snapshot, 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::*; @@ -56,7 +57,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { self.return_to_block(ret.map(|r| r.1))?; self.dump_place(*dest); - return Ok(true); + Ok(true) } /// "Intercept" a function call to a panic-related function @@ -64,7 +65,6 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { /// 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> { @@ -77,7 +77,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { 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,40 +86,32 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { } } -/// The number of steps between loop detector snapshots. -/// Should be a power of two for performance reasons. -const DETECTOR_SNAPSHOT_PERIOD: isize = 256; - -// Extra machine state for CTFE, and the Machine instance +/// Extra machine state for CTFE, and the Machine instance pub struct CompileTimeInterpreter<'mir, 'tcx> { - /// When this value is negative, it indicates the number of interpreter - /// steps *until* the loop detector is enabled. When it is positive, it is - /// the number of steps after the detector has been enabled modulo the loop - /// detector period. - pub(super) steps_since_detector_enabled: isize, - - pub(super) is_detector_enabled: bool, + /// 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, - /// Extra state to detect loops. - pub(super) loop_detector: snapshot::InfiniteLoopDetector<'mir, 'tcx>, + /// 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<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> { - pub(super) fn new(const_eval_limit: usize) -> Self { - let steps_until_detector_enabled = - isize::try_from(const_eval_limit).unwrap_or(std::isize::MAX); - - CompileTimeInterpreter { - loop_detector: Default::default(), - steps_since_detector_enabled: -steps_until_detector_enabled, - is_detector_enabled: const_eval_limit != 0, - } + pub(super) fn new(const_eval_limit: Limit) -> Self { + CompileTimeInterpreter { steps_remaining: const_eval_limit.0, stack: Vec::new() } } } @@ -185,30 +177,12 @@ impl interpret::MayLeak for ! { } impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> { - type MemoryKinds = !; - type PointerTag = (); - type ExtraFnVal = !; + 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)>, @@ -230,7 +204,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } 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) @@ -238,9 +212,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } // 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(ref path)) = err.kind { + if let err_unsup!(NoMirFor(did)) = err.kind { + let path = ecx.tcx.def_path_str(did); return Err(ConstEvalErrKind::NeedsRfc(format!( "calling extern function `{}`", path @@ -252,25 +227,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, })) } - 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 @@ -283,7 +247,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 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 } => { @@ -322,22 +286,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 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>, @@ -346,41 +294,68 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - if !ecx.machine.is_detector_enabled { + // The step limit has already been hit in a previous call to `before_terminator`. + if ecx.machine.steps_remaining == 0 { return Ok(()); } - { - let steps = &mut ecx.machine.steps_since_detector_enabled; - - *steps += 1; - if *steps < 0 { - return Ok(()); - } - - *steps %= DETECTOR_SNAPSHOT_PERIOD; - if *steps != 0 { - return Ok(()); - } + ecx.machine.steps_remaining -= 1; + if ecx.machine.steps_remaining == 0 { + throw_exhaust!(StepLimitReached) } - let span = ecx.frame().span; - ecx.machine.loop_detector.observe_and_analyze(*ecx.tcx, span, &ecx.memory, &ecx.stack[..]) + Ok(()) + } + + #[inline(always)] + fn stack( + ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] { + &ecx.machine.stack } #[inline(always)] - fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { - Ok(()) + fn stack_mut( + ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec> { + &mut ecx.machine.stack } - fn before_access_static( + 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..ed992a5983954 100644 --- a/src/librustc_mir/const_eval/mod.rs +++ b/src/librustc_mir/const_eval/mod.rs @@ -1,8 +1,9 @@ // 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 crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx}; @@ -17,32 +18,6 @@ pub use eval_queries::*; pub use fn_queries::*; pub use machine::*; -/// Extracts a field of a (variant of a) const. -// this function uses `unwrap` copiously, because an already validated constant must have valid -// fields and can thus never fail outside of compiler bugs -pub(crate) fn const_field<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - variant: Option, - field: mir::Field, - value: &'tcx ty::Const<'tcx>, -) -> ConstValue<'tcx> { - trace!("const_field: {:?}, {:?}", field, value); - let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); - // get the operand again - let op = ecx.eval_const_to_op(value, None).unwrap(); - // downcast - let down = match variant { - None => op, - Some(variant) => ecx.operand_downcast(op, variant).unwrap(), - }; - // then project - let field = ecx.operand_field(down, field.index() as u64).unwrap(); - // and finally move back to the const world, always normalizing because - // this is not called for statics. - op_to_const(&ecx, field) -} - pub(crate) fn const_caller_location( tcx: TyCtxt<'tcx>, (file, line, col): (Symbol, u32, u32), @@ -51,12 +26,14 @@ 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) } -// this function uses `unwrap` copiously, because an already validated constant -// must have valid fields and can thus never fail outside of compiler bugs +/// This function uses `unwrap` copiously, because an already validated constant +/// must have valid fields and can thus never fail outside of compiler bugs. However, it is +/// invoked from the pretty printer, where it can receive enums with no variants and e.g. +/// `read_discriminant` needs to be able to handle that. pub(crate) fn destructure_const<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -66,16 +43,21 @@ pub(crate) fn destructure_const<'tcx>( let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false); let op = ecx.eval_const_to_op(val, None).unwrap(); - let variant = ecx.read_discriminant(op).unwrap().1; - - 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, + // We go to `usize` as we cannot allocate anything bigger anyway. + let (field_count, variant, down) = match val.ty.kind { + ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op), + ty::Adt(def, _) if def.variants.is_empty() => { + return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) }; + } + ty::Adt(def, _) => { + let variant = ecx.read_discriminant(op).unwrap().1; + let down = ecx.operand_downcast(op, variant).unwrap(); + (def.variants[variant].fields.len(), Some(variant), down) + } + ty::Tuple(substs) => (substs.len(), None, op), _ => bug!("cannot destructure constant {:?}", val), }; - let down = ecx.operand_downcast(op, variant).unwrap(); let fields_iter = (0..field_count).map(|i| { let field_op = ecx.operand_field(down, i).unwrap(); let val = op_to_const(&ecx, field_op); 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..6dd06743e2d5b 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}; @@ -12,12 +12,12 @@ pub fn move_path_children_matching<'tcx, F>( mut cond: F, ) -> Option where - F: FnMut(&mir::PlaceElem<'tcx>) -> bool, + F: FnMut(mir::PlaceElem<'tcx>) -> bool, { let mut next_child = move_data.move_paths[path].first_child; while let Some(child_index) = next_child { let move_path_children = &move_data.move_paths[child_index]; - if let Some(elem) = move_path_children.place.projection.last() { + if let Some(&elem) = move_path_children.place.projection.last() { if cond(elem) { return Some(child_index); } @@ -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..4512ae96c0833 --- /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, place: _ } + | DropAndReplace { target, unwind, value: _, place: _ } + | 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); + } + + FalseEdge { 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: _, fn_span: _ } => { + 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: _, line_spans: _, 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..243b3679f2984 --- /dev/null +++ b/src/librustc_mir/dataflow/framework/engine.rs @@ -0,0 +1,411 @@ +//! 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_graphviz as dot; +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..896616a2175ac --- /dev/null +++ b/src/librustc_mir/dataflow/framework/graphviz.rs @@ -0,0 +1,740 @@ +//! A helpful diagram for debugging dataflow problems. + +use std::cell::RefCell; +use std::{io, ops, str}; + +use rustc_graphviz as dot; +use rustc_hir::def_id::DefId; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::{self, BasicBlock, Body, Location}; + +use super::{Analysis, Direction, GenKillSet, Results, ResultsRefCursor}; +use crate::util::graphviz_safe_def_name; + +pub struct Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + body: &'a Body<'tcx>, + def_id: DefId, + + // This must be behind a `RefCell` because `dot::Labeller` takes `&self`. + block_formatter: RefCell>, +} + +impl Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + pub fn new( + body: &'a Body<'tcx>, + def_id: DefId, + results: &'a Results<'tcx, A>, + state_formatter: &'a mut dyn StateFormatter<'tcx, A>, + ) -> Self { + let block_formatter = BlockFormatter { + bg: Background::Light, + results: ResultsRefCursor::new(body, results), + state_formatter, + }; + + Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) } + } +} + +/// A pair of a basic block and an index into that basic blocks `successors`. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct CfgEdge { + source: BasicBlock, + index: usize, +} + +fn dataflow_successors(body: &Body<'tcx>, bb: BasicBlock) -> Vec { + body[bb] + .terminator() + .successors() + .enumerate() + .map(|(index, _)| CfgEdge { source: bb, index }) + .collect() +} + +impl dot::Labeller<'_> for Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + type Node = BasicBlock; + type Edge = CfgEdge; + + fn graph_id(&self) -> dot::Id<'_> { + let name = graphviz_safe_def_name(self.def_id); + dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap() + } + + fn node_id(&self, n: &Self::Node) -> dot::Id<'_> { + dot::Id::new(format!("bb_{}", n.index())).unwrap() + } + + fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { + let mut label = Vec::new(); + self.block_formatter.borrow_mut().write_node_label(&mut label, self.body, *block).unwrap(); + dot::LabelText::html(String::from_utf8(label).unwrap()) + } + + fn node_shape(&self, _n: &Self::Node) -> Option> { + Some(dot::LabelText::label("none")) + } + + fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> { + let label = &self.body[e.source].terminator().kind.fmt_successor_labels()[e.index]; + dot::LabelText::label(label.clone()) + } +} + +impl dot::GraphWalk<'a> for Formatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + type Node = BasicBlock; + type Edge = CfgEdge; + + fn nodes(&self) -> dot::Nodes<'_, Self::Node> { + self.body.basic_blocks().indices().collect::>().into() + } + + fn edges(&self) -> dot::Edges<'_, Self::Edge> { + self.body + .basic_blocks() + .indices() + .flat_map(|bb| dataflow_successors(self.body, bb)) + .collect::>() + .into() + } + + fn source(&self, edge: &Self::Edge) -> Self::Node { + edge.source + } + + fn target(&self, edge: &Self::Edge) -> Self::Node { + self.body[edge.source].terminator().successors().nth(edge.index).copied().unwrap() + } +} + +struct BlockFormatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + results: ResultsRefCursor<'a, 'a, 'tcx, A>, + bg: Background, + state_formatter: &'a mut dyn StateFormatter<'tcx, A>, +} + +impl BlockFormatter<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + const HEADER_COLOR: &'static str = "#a0a0a0"; + + fn num_state_columns(&self) -> usize { + std::cmp::max(1, self.state_formatter.column_names().len()) + } + + fn toggle_background(&mut self) -> Background { + let bg = self.bg; + self.bg = !bg; + bg + } + + fn write_node_label( + &mut self, + w: &mut impl io::Write, + body: &'a Body<'tcx>, + block: BasicBlock, + ) -> io::Result<()> { + // Sample output: + // +-+-----------------------------------------------+ + // A | bb4 | + // +-+----------------------------------+------------+ + // B | MIR | STATE | + // +-+----------------------------------+------------+ + // C | | (on entry) | {_0,_2,_3} | + // +-+----------------------------------+------------+ + // D |0| StorageLive(_7) | | + // +-+----------------------------------+------------+ + // |1| StorageLive(_8) | | + // +-+----------------------------------+------------+ + // |2| _8 = &mut _1 | +_8 | + // +-+----------------------------------+------------+ + // E |T| _4 = const Foo::twiddle(move _2) | -_2 | + // +-+----------------------------------+------------+ + // F | | (on unwind) | {_0,_3,_8} | + // +-+----------------------------------+------------+ + // | | (on successful return) | +_4 | + // +-+----------------------------------+------------+ + + // N.B., Some attributes (`align`, `balign`) are repeated on parent elements and their + // children. This is because `xdot` seemed to have a hard time correctly propagating + // attributes. Make sure to test the output before trying to remove the redundancy. + // Notably, `align` was found to have no effect when applied only to . + + let table_fmt = concat!( + " border=\"1\"", + " cellborder=\"1\"", + " cellspacing=\"0\"", + " cellpadding=\"3\"", + " sides=\"rb\"", + ); + write!(w, r#""#, fmt = table_fmt)?; + + // A + B: Block header + if self.state_formatter.column_names().is_empty() { + self.write_block_header_simple(w, block)?; + } else { + self.write_block_header_with_state_columns(w, block)?; + } + + // C: State at start of block + self.bg = Background::Light; + self.results.seek_to_block_start(block); + let block_entry_state = self.results.get().clone(); + + self.write_row_with_full_state(w, "", "(on start)")?; + + // D: Statement transfer functions + for (i, statement) in body[block].statements.iter().enumerate() { + let location = Location { block, statement_index: i }; + let statement_str = format!("{:?}", statement); + self.write_row_for_location(w, &i.to_string(), &statement_str, location)?; + } + + // E: Terminator transfer function + let terminator = body[block].terminator(); + let terminator_loc = body.terminator_loc(block); + let mut terminator_str = String::new(); + terminator.kind.fmt_head(&mut terminator_str).unwrap(); + + self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?; + + // F: State at end of block + + // Write the full dataflow state immediately after the terminator if it differs from the + // state at block entry. + self.results.seek_to_block_end(block); + if self.results.get() != &block_entry_state || A::Direction::is_backward() { + let after_terminator_name = match terminator.kind { + mir::TerminatorKind::Call { destination: Some(_), .. } => "(on unwind)", + _ => "(on end)", + }; + + self.write_row_with_full_state(w, "", after_terminator_name)?; + } + + // Write any changes caused by terminator-specific effects + let num_state_columns = self.num_state_columns(); + match terminator.kind { + mir::TerminatorKind::Call { + destination: Some((return_place, _)), + ref func, + ref args, + .. + } => { + self.write_row(w, "", "(on successful return)", |this, w, fmt| { + write!( + w, + r#"") + })?; + } + + mir::TerminatorKind::Yield { resume, resume_arg, .. } => { + self.write_row(w, "", "(on yield resume)", |this, w, fmt| { + write!( + w, + r#"") + })?; + } + + _ => {} + }; + + write!(w, "
"#, + colspan = num_state_columns, + fmt = fmt, + )?; + + let state_on_unwind = this.results.get().clone(); + this.results.apply_custom_effect(|analysis, state| { + analysis.apply_call_return_effect(state, block, func, args, return_place); + }); + + write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?; + write!(w, ""#, + colspan = num_state_columns, + fmt = fmt, + )?; + + let state_on_generator_drop = this.results.get().clone(); + this.results.apply_custom_effect(|analysis, state| { + analysis.apply_yield_resume_effect(state, resume, resume_arg); + }); + + write_diff( + w, + this.results.analysis(), + &state_on_generator_drop, + this.results.get(), + )?; + write!(w, "
") + } + + fn write_block_header_simple( + &mut self, + w: &mut impl io::Write, + block: BasicBlock, + ) -> io::Result<()> { + // +-------------------------------------------------+ + // A | bb4 | + // +-----------------------------------+-------------+ + // B | MIR | STATE | + // +-+---------------------------------+-------------+ + // | | ... | | + + // A + write!( + w, + concat!("", r#"bb{block_id}"#, "",), + block_id = block.index(), + )?; + + // B + write!( + w, + concat!( + "", + r#"MIR"#, + r#"STATE"#, + "", + ), + fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR), + ) + } + + fn write_block_header_with_state_columns( + &mut self, + w: &mut impl io::Write, + block: BasicBlock, + ) -> io::Result<()> { + // +------------------------------------+-------------+ + // A | bb4 | STATE | + // +------------------------------------+------+------+ + // B | MIR | GEN | KILL | + // +-+----------------------------------+------+------+ + // | | ... | | | + + let state_column_names = self.state_formatter.column_names(); + + // A + write!( + w, + concat!( + "", + r#"bb{block_id}"#, + r#"STATE"#, + "", + ), + fmt = "sides=\"tl\"", + num_state_cols = state_column_names.len(), + block_id = block.index(), + )?; + + // B + let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR); + write!(w, concat!("", r#"MIR"#,), fmt = fmt,)?; + + for name in state_column_names { + write!(w, "{name}", fmt = fmt, name = name)?; + } + + write!(w, "") + } + + /// Write a row with the given index and MIR, using the function argument to fill in the + /// "STATE" column(s). + fn write_row( + &mut self, + w: &mut W, + i: &str, + mir: &str, + f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>, + ) -> io::Result<()> { + let bg = self.toggle_background(); + let valign = if mir.starts_with("(on ") && mir != "(on entry)" { "bottom" } else { "top" }; + + let fmt = format!("valign=\"{}\" sides=\"tl\" {}", valign, bg.attr()); + + write!( + w, + concat!( + "", + r#"{i}"#, + r#"{mir}"#, + ), + i = i, + fmt = fmt, + mir = dot::escape_html(mir), + )?; + + f(self, w, &fmt)?; + write!(w, "") + } + + fn write_row_with_full_state( + &mut self, + w: &mut impl io::Write, + i: &str, + mir: &str, + ) -> io::Result<()> { + self.write_row(w, i, mir, |this, w, fmt| { + let state = this.results.get(); + let analysis = this.results.analysis(); + + write!( + w, + r#"{{"#, + colspan = this.num_state_columns(), + fmt = fmt, + )?; + pretty_print_state_elems(w, analysis, state.iter(), ", ", LIMIT_30_ALIGN_1)?; + write!(w, "}}") + }) + } + + fn write_row_for_location( + &mut self, + w: &mut impl io::Write, + i: &str, + mir: &str, + location: Location, + ) -> io::Result<()> { + self.write_row(w, i, mir, |this, w, fmt| { + this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location) + }) + } +} + +/// Controls what gets printed under the `STATE` header. +pub trait StateFormatter<'tcx, A> +where + A: Analysis<'tcx>, +{ + /// The columns that will get printed under `STATE`. + fn column_names(&self) -> &[&str]; + + fn write_state_for_location( + &mut self, + w: &mut dyn io::Write, + fmt: &str, + results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, + location: Location, + ) -> io::Result<()>; +} + +/// Prints a single column containing the state vector immediately *after* each statement. +pub struct SimpleDiff<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + prev_state: ResultsRefCursor<'a, 'a, 'tcx, A>, +} + +impl
SimpleDiff<'a, 'tcx, A> +where + A: Analysis<'tcx>, +{ + pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>) -> Self { + SimpleDiff { prev_state: ResultsRefCursor::new(body, results) } + } +} + +impl StateFormatter<'tcx, A> for SimpleDiff<'_, 'tcx, A> +where + A: Analysis<'tcx>, +{ + fn column_names(&self) -> &[&str] { + &[] + } + + fn write_state_for_location( + &mut self, + mut w: &mut dyn io::Write, + fmt: &str, + results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, + location: Location, + ) -> io::Result<()> { + if A::Direction::is_forward() { + if location.statement_index == 0 { + self.prev_state.seek_to_block_start(location.block); + } else { + self.prev_state.seek_after_primary_effect(Location { + statement_index: location.statement_index - 1, + ..location + }); + } + } else { + if location == results.body().terminator_loc(location.block) { + self.prev_state.seek_to_block_end(location.block); + } else { + self.prev_state.seek_after_primary_effect(location.successor_within_block()); + } + } + + write!(w, r#""#, fmt = fmt)?; + results.seek_after_primary_effect(location); + let curr_state = results.get(); + write_diff(&mut w, results.analysis(), self.prev_state.get(), curr_state)?; + write!(w, "") + } +} + +/// Prints two state columns, one containing only the "before" effect of each statement and one +/// containing the full effect. +pub struct TwoPhaseDiff { + prev_state: BitSet, + prev_loc: Location, +} + +impl TwoPhaseDiff { + pub fn new(bits_per_block: usize) -> Self { + TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START } + } +} + +impl StateFormatter<'tcx, A> for TwoPhaseDiff +where + A: Analysis<'tcx>, +{ + fn column_names(&self) -> &[&str] { + &["BEFORE", " AFTER"] + } + + fn write_state_for_location( + &mut self, + mut w: &mut dyn io::Write, + fmt: &str, + results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, + location: Location, + ) -> io::Result<()> { + if location.statement_index == 0 { + results.seek_to_block_entry(location.block); + self.prev_state.overwrite(results.get()); + } else { + // Ensure that we are visiting statements in order, so `prev_state` is correct. + assert_eq!(self.prev_loc.successor_within_block(), location); + } + + self.prev_loc = location; + + // Before + + write!(w, r#""#, fmt = fmt)?; + results.seek_before_primary_effect(location); + let curr_state = results.get(); + write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; + self.prev_state.overwrite(curr_state); + write!(w, "")?; + + // After + + write!(w, r#""#, fmt = fmt)?; + results.seek_after_primary_effect(location); + let curr_state = results.get(); + write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?; + self.prev_state.overwrite(curr_state); + write!(w, "") + } +} + +/// Prints the gen/kill set for the entire block. +pub struct BlockTransferFunc<'a, 'tcx, T: Idx> { + body: &'a mir::Body<'tcx>, + trans_for_block: IndexVec>, +} + +impl BlockTransferFunc<'mir, 'tcx, T> { + pub fn new( + body: &'mir mir::Body<'tcx>, + trans_for_block: IndexVec>, + ) -> Self { + BlockTransferFunc { body, trans_for_block } + } +} + +impl StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx> +where + A: Analysis<'tcx>, +{ + fn column_names(&self) -> &[&str] { + &["GEN", "KILL"] + } + + fn write_state_for_location( + &mut self, + mut w: &mut dyn io::Write, + fmt: &str, + results: &mut ResultsRefCursor<'_, '_, 'tcx, A>, + location: Location, + ) -> io::Result<()> { + // Only print a single row. + if location.statement_index != 0 { + return Ok(()); + } + + let block_trans = &self.trans_for_block[location.block]; + let rowspan = self.body.basic_blocks()[location.block].statements.len(); + + for set in &[&block_trans.gen, &block_trans.kill] { + write!( + w, + r#""#, + fmt = fmt, + rowspan = rowspan + )?; + + pretty_print_state_elems(&mut w, results.analysis(), set.iter(), BR_LEFT, None)?; + write!(w, "")?; + } + + Ok(()) + } +} + +/// Writes two lines, one containing the added bits and one the removed bits. +fn write_diff>( + w: &mut impl io::Write, + analysis: &A, + from: &BitSet, + to: &BitSet, +) -> io::Result<()> { + assert_eq!(from.domain_size(), to.domain_size()); + let len = from.domain_size(); + + let mut set = HybridBitSet::new_empty(len); + let mut clear = HybridBitSet::new_empty(len); + + // FIXME: Implement a lazy iterator over the symmetric difference of two bitsets. + for i in (0..len).map(A::Idx::new) { + match (from.contains(i), to.contains(i)) { + (false, true) => set.insert(i), + (true, false) => clear.insert(i), + _ => continue, + }; + } + + if !set.is_empty() { + write!(w, r#"+"#)?; + pretty_print_state_elems(w, analysis, set.iter(), ", ", LIMIT_30_ALIGN_1)?; + write!(w, r#""#)?; + } + + if !set.is_empty() && !clear.is_empty() { + write!(w, "{}", BR_LEFT)?; + } + + if !clear.is_empty() { + write!(w, r#"-"#)?; + pretty_print_state_elems(w, analysis, clear.iter(), ", ", LIMIT_30_ALIGN_1)?; + write!(w, r#""#)?; + } + + Ok(()) +} + +const BR_LEFT: &str = r#"
"#; +const BR_LEFT_SPACE: &str = r#"
"#; + +/// Line break policy that breaks at 40 characters and starts the next line with a single space. +const LIMIT_30_ALIGN_1: Option = Some(LineBreak { sequence: BR_LEFT_SPACE, limit: 30 }); + +struct LineBreak { + sequence: &'static str, + limit: usize, +} + +/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given +/// separator (`sep`). +/// +/// Optionally, it will break lines using the given character sequence (usually `
`) and +/// character limit. +fn pretty_print_state_elems
( + w: &mut impl io::Write, + analysis: &A, + elems: impl Iterator, + sep: &str, + line_break: Option, +) -> io::Result +where + A: Analysis<'tcx>, +{ + let sep_width = sep.chars().count(); + + let mut buf = Vec::new(); + + let mut first = true; + let mut curr_line_width = 0; + let mut line_break_inserted = false; + + for idx in elems { + buf.clear(); + analysis.pretty_print_idx(&mut buf, idx)?; + let idx_str = + str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8"); + let escaped = dot::escape_html(idx_str); + let escaped_width = escaped.chars().count(); + + if first { + first = false; + } else { + write!(w, "{}", sep)?; + curr_line_width += sep_width; + + if let Some(line_break) = &line_break { + if curr_line_width + sep_width + escaped_width > line_break.limit { + write!(w, "{}", line_break.sequence)?; + line_break_inserted = true; + curr_line_width = 0; + } + } + } + + write!(w, "{}", escaped)?; + curr_line_width += escaped_width; + } + + Ok(line_break_inserted) +} + +/// The background color used for zebra-striping the table. +#[derive(Clone, Copy)] +enum Background { + Light, + Dark, +} + +impl Background { + fn attr(self) -> &'static str { + match self { + Self::Dark => "bgcolor=\"#f0f0f0\"", + Self::Light => "", + } + } +} + +impl ops::Not for Background { + type Output = Self; + + fn not(self) -> Self { + match self { + Self::Light => Self::Dark, + Self::Dark => Self::Light, + } + } +} diff --git a/src/librustc_mir/dataflow/framework/mod.rs b/src/librustc_mir/dataflow/framework/mod.rs 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..9349f5133a55d --- /dev/null +++ b/src/librustc_mir/dataflow/framework/tests.rs @@ -0,0 +1,325 @@ +//! A test for the logic that updates the state in a `ResultsCursor` during seek. + +use std::marker::PhantomData; + +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::{self, BasicBlock, Location}; +use rustc_middle::ty; +use rustc_span::DUMMY_SP; + +use super::*; +use crate::dataflow::BottomValue; + +/// Creates a `mir::Body` with a few disconnected basic blocks. +/// +/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not +/// important. +fn mock_body() -> mir::Body<'static> { + let source_info = mir::SourceInfo::outermost(DUMMY_SP); + + let mut blocks = IndexVec::new(); + let mut block = |n, kind| { + let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop }; + + blocks.push(mir::BasicBlockData { + statements: std::iter::repeat(&nop).cloned().take(n).collect(), + terminator: Some(mir::Terminator { source_info, kind }), + is_cleanup: false, + }) + }; + + let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() }; + + block(4, mir::TerminatorKind::Return); + block(1, mir::TerminatorKind::Return); + block( + 2, + mir::TerminatorKind::Call { + func: mir::Operand::Copy(dummy_place.clone()), + args: vec![], + destination: Some((dummy_place.clone(), mir::START_BLOCK)), + cleanup: None, + from_hir_call: false, + fn_span: DUMMY_SP, + }, + ); + block(3, mir::TerminatorKind::Return); + block(0, mir::TerminatorKind::Return); + block( + 4, + mir::TerminatorKind::Call { + func: mir::Operand::Copy(dummy_place.clone()), + args: vec![], + destination: Some((dummy_place.clone(), mir::START_BLOCK)), + cleanup: None, + from_hir_call: false, + fn_span: DUMMY_SP, + }, + ); + + mir::Body::new_cfg_only(blocks) +} + +/// A dataflow analysis whose state is unique at every possible `SeekTarget`. +/// +/// Uniqueness is achieved by having a *locally* unique effect before and after each statement and +/// terminator (see `effect_at_target`) while ensuring that the entry set for each block is +/// *globally* unique (see `mock_entry_set`). +/// +/// For example, a `BasicBlock` with ID `2` and a `Call` terminator has the following state at each +/// location ("+x" indicates that "x" is added to the state). +/// +/// | Location | Before | After | +/// |------------------------|-------------------|--------| +/// | (on_entry) | {102} || +/// | statement 0 | +0 | +1 | +/// | statement 1 | +2 | +3 | +/// | `Call` terminator | +4 | +5 | +/// | (on unwind) | {102,0,1,2,3,4,5} || +/// +/// The `102` in the block's entry set is derived from the basic block index and ensures that the +/// expected state is unique across all basic blocks. Remember, it is generated by +/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint. +struct MockAnalysis<'tcx, D> { + body: &'tcx mir::Body<'tcx>, + dir: PhantomData, +} + +impl MockAnalysis<'tcx, D> { + const BASIC_BLOCK_OFFSET: usize = 100; + + /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to + /// avoid colliding with the statement/terminator effects. + fn mock_entry_set(&self, bb: BasicBlock) -> BitSet { + let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); + ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index()); + ret + } + + fn mock_entry_sets(&self) -> IndexVec> { + let empty = BitSet::new_empty(self.bits_per_block(self.body)); + let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks()); + + for (bb, _) in self.body.basic_blocks().iter_enumerated() { + ret[bb] = self.mock_entry_set(bb); + } + + ret + } + + /// Returns the index that should be added to the dataflow state at the given target. + fn effect(&self, loc: EffectIndex) -> usize { + let idx = match loc.effect { + Effect::Before => loc.statement_index * 2, + Effect::Primary => loc.statement_index * 2 + 1, + }; + + assert!(idx < Self::BASIC_BLOCK_OFFSET, "Too many statements in basic block"); + idx + } + + /// Returns the expected state at the given `SeekTarget`. + /// + /// This is the union of index of the target basic block, the index assigned to the + /// target statement or terminator, and the indices of all preceding statements in the target + /// basic block. + /// + /// For example, the expected state when calling + /// `seek_before_primary_effect(Location { block: 2, statement_index: 2 })` + /// would be `[102, 0, 1, 2, 3, 4]`. + fn expected_state_at_target(&self, target: SeekTarget) -> BitSet { + let block = target.block(); + let mut ret = BitSet::new_empty(self.bits_per_block(self.body)); + ret.insert(Self::BASIC_BLOCK_OFFSET + block.index()); + + let target = match target { + SeekTarget::BlockEntry { .. } => return ret, + SeekTarget::Before(loc) => Effect::Before.at_index(loc.statement_index), + SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index), + }; + + let mut pos = if D::is_forward() { + Effect::Before.at_index(0) + } else { + Effect::Before.at_index(self.body[block].statements.len()) + }; + + loop { + ret.insert(self.effect(pos)); + + if pos == target { + return ret; + } + + if D::is_forward() { + pos = pos.next_in_forward_order(); + } else { + pos = pos.next_in_backward_order(); + } + } + } +} + +impl BottomValue for MockAnalysis<'tcx, D> { + const BOTTOM_VALUE: bool = false; +} + +impl AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { + type Idx = usize; + type Direction = D; + + const NAME: &'static str = "mock"; + + fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { + Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len() + } + + fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet) { + unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint"); + } +} + +impl Analysis<'tcx> for MockAnalysis<'tcx, D> { + fn apply_statement_effect( + &self, + state: &mut BitSet, + _statement: &mir::Statement<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Primary.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_before_statement_effect( + &self, + state: &mut BitSet, + _statement: &mir::Statement<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Before.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_terminator_effect( + &self, + state: &mut BitSet, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Primary.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_before_terminator_effect( + &self, + state: &mut BitSet, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + let idx = self.effect(Effect::Before.at_index(location.statement_index)); + assert!(state.insert(idx)); + } + + fn apply_call_return_effect( + &self, + _state: &mut BitSet, + _block: BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + _return_place: mir::Place<'tcx>, + ) { + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum SeekTarget { + BlockEntry(BasicBlock), + Before(Location), + After(Location), +} + +impl SeekTarget { + fn block(&self) -> BasicBlock { + use SeekTarget::*; + + match *self { + BlockEntry(block) => block, + Before(loc) | After(loc) => loc.block, + } + } + + /// An iterator over all possible `SeekTarget`s in a given block in order, starting with + /// `BlockEntry`. + fn iter_in_block(body: &mir::Body<'_>, block: BasicBlock) -> impl Iterator { + let statements_and_terminator = (0..=body[block].statements.len()) + .flat_map(|i| (0..2).map(move |j| (i, j))) + .map(move |(i, kind)| { + let loc = Location { block, statement_index: i }; + match kind { + 0 => SeekTarget::Before(loc), + 1 => SeekTarget::After(loc), + _ => unreachable!(), + } + }); + + std::iter::once(SeekTarget::BlockEntry(block)).chain(statements_and_terminator) + } +} + +fn test_cursor(analysis: MockAnalysis<'tcx, D>) { + let body = analysis.body; + + let mut cursor = + Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); + + let every_target = || { + body.basic_blocks() + .iter_enumerated() + .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb)) + }; + + let mut seek_to_target = |targ| { + use SeekTarget::*; + + match targ { + BlockEntry(block) => cursor.seek_to_block_entry(block), + Before(loc) => cursor.seek_before_primary_effect(loc), + After(loc) => cursor.seek_after_primary_effect(loc), + } + + assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ)); + }; + + // Seek *to* every possible `SeekTarget` *from* every possible `SeekTarget`. + // + // By resetting the cursor to `from` each time it changes, we end up checking some edges twice. + // What we really want is an Eulerian cycle for the complete digraph over all possible + // `SeekTarget`s, but it's not worth spending the time to compute it. + for from in every_target() { + seek_to_target(from); + + for to in every_target() { + dbg!(from); + dbg!(to); + seek_to_target(to); + seek_to_target(from); + } + } +} + +#[test] +fn backward_cursor() { + let body = mock_body(); + let body = &body; + let analysis = MockAnalysis { body, dir: PhantomData:: }; + test_cursor(analysis) +} + +#[test] +fn forward_cursor() { + let body = mock_body(); + let body = &body; + let analysis = MockAnalysis { body, dir: PhantomData:: }; + test_cursor(analysis) +} diff --git a/src/librustc_mir/dataflow/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 36decf7f5a9c3..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(|i| A::Idx::new(i)) { - 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..a3fc51cad656b 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,19 +160,20 @@ 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); } } mir::Rvalue::Cast(..) | mir::Rvalue::Use(..) + | mir::Rvalue::ThreadLocalRef(..) | mir::Rvalue::Repeat(..) | mir::Rvalue::Len(..) | mir::Rvalue::BinaryOp(..) @@ -188,8 +189,8 @@ where self.super_terminator(terminator, location); match terminator.kind { - mir::TerminatorKind::Drop { location: dropped_place, .. } - | mir::TerminatorKind::DropAndReplace { location: dropped_place, .. } => { + mir::TerminatorKind::Drop { place: dropped_place, .. } + | mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => { // See documentation for `unsound_ignore_borrow_on_drop` for an explanation. if !self.ignore_borrow_on_drop { self.trans.gen(dropped_place.local); @@ -199,10 +200,11 @@ where TerminatorKind::Abort | TerminatorKind::Assert { .. } | TerminatorKind::Call { .. } - | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } + | TerminatorKind::InlineAsm { .. } | TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::SwitchInt { .. } @@ -230,25 +232,25 @@ 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 { - !place.ty(self.body, self.tcx).ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) + fn shared_borrow_allows_mutation(&self, place: Place<'tcx>) -> bool { + !place.ty(self.body, self.tcx).ty.is_freeze(self.tcx.at(DUMMY_SP), self.param_env) } } 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 +258,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 +267,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/init_locals.rs b/src/librustc_mir/dataflow/impls/init_locals.rs new file mode 100644 index 0000000000000..01cb794a2e085 --- /dev/null +++ b/src/librustc_mir/dataflow/impls/init_locals.rs @@ -0,0 +1,115 @@ +//! A less precise version of `MaybeInitializedPlaces` whose domain is entire locals. +//! +//! A local will be maybe initialized if *any* projections of that local might be initialized. + +use crate::dataflow::{self, BottomValue, GenKill}; + +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::visit::{PlaceContext, Visitor}; +use rustc_middle::mir::{self, BasicBlock, Local, Location}; + +pub struct MaybeInitializedLocals; + +impl BottomValue for MaybeInitializedLocals { + /// bottom = uninit + const BOTTOM_VALUE: bool = false; +} + +impl dataflow::AnalysisDomain<'tcx> for MaybeInitializedLocals { + type Idx = Local; + + const NAME: &'static str = "maybe_init_locals"; + + fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize { + body.local_decls.len() + } + + fn initialize_start_block(&self, body: &mir::Body<'tcx>, entry_set: &mut BitSet) { + // Function arguments are initialized to begin with. + for arg in body.args_iter() { + entry_set.insert(arg); + } + } +} + +impl dataflow::GenKillAnalysis<'tcx> for MaybeInitializedLocals { + fn statement_effect( + &self, + trans: &mut impl GenKill, + statement: &mir::Statement<'tcx>, + loc: Location, + ) { + TransferFunction { trans }.visit_statement(statement, loc) + } + + fn terminator_effect( + &self, + trans: &mut impl GenKill, + terminator: &mir::Terminator<'tcx>, + loc: Location, + ) { + TransferFunction { trans }.visit_terminator(terminator, loc) + } + + fn call_return_effect( + &self, + trans: &mut impl GenKill, + _block: BasicBlock, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + return_place: mir::Place<'tcx>, + ) { + trans.gen(return_place.local) + } + + /// See `Analysis::apply_yield_resume_effect`. + fn yield_resume_effect( + &self, + trans: &mut impl GenKill, + _resume_block: BasicBlock, + resume_place: mir::Place<'tcx>, + ) { + trans.gen(resume_place.local) + } +} + +struct TransferFunction<'a, T> { + trans: &'a mut T, +} + +impl Visitor<'tcx> for TransferFunction<'a, T> +where + T: GenKill, +{ + fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) { + use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, NonUseContext}; + match context { + // These are handled specially in `call_return_effect` and `yield_resume_effect`. + PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => {} + + // Otherwise, when a place is mutated, we must consider it possibly initialized. + PlaceContext::MutatingUse(_) => self.trans.gen(local), + + // If the local is moved out of, or if it gets marked `StorageDead`, consider it no + // longer initialized. + PlaceContext::NonUse(NonUseContext::StorageDead) + | PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) => self.trans.kill(local), + + // All other uses do not affect this analysis. + PlaceContext::NonUse( + NonUseContext::StorageLive + | NonUseContext::AscribeUserTy + | NonUseContext::VarDebugInfo, + ) + | PlaceContext::NonMutatingUse( + NonMutatingUseContext::Inspect + | NonMutatingUseContext::Copy + | NonMutatingUseContext::SharedBorrow + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::UniqueBorrow + | NonMutatingUseContext::AddressOf + | NonMutatingUseContext::Projection, + ) => {} + } + } +} 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..d5def0389126a 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,16 @@ use super::on_lookup_result_bits; use crate::dataflow::drop_flag_effects; mod borrowed_locals; +pub(super) mod borrows; +mod init_locals; +mod liveness; mod storage_liveness; -pub use self::borrowed_locals::*; -pub use self::storage_liveness::*; - -pub(super) mod borrows; +pub use self::borrowed_locals::{MaybeBorrowedLocals, MaybeMutBorrowedLocals}; +pub use self::borrows::Borrows; +pub use self::init_locals::MaybeInitializedLocals; +pub use self::liveness::MaybeLiveLocals; +pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive}; /// `MaybeInitializedPlaces` tracks all places that might be /// initialized upon reaching a particular point in the control flow @@ -324,7 +327,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 +346,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 +429,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 +498,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 +548,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 +589,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..cd04493c092e0 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, .. } @@ -178,7 +207,7 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::Goto { .. } @@ -212,10 +241,11 @@ impl<'mir, 'tcx> dataflow::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseEdge { .. } | 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 b4e33b9502e69..ae1328dbd12c7 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -1,38 +1,19 @@ use rustc_ast::ast::{self, MetaItem}; -use rustc_ast_pretty::pprust; +use rustc_middle::ty; use rustc_span::symbol::{sym, Symbol}; -use rustc_data_structures::work_queue::WorkQueue; -use rustc_index::bit_set::{BitSet, HybridBitSet}; -use rustc_index::vec::Idx; - -use rustc::mir::traversal; -use rustc::mir::{self, BasicBlock, BasicBlockData, Body, Location, Statement, Terminator}; -use rustc::session::Session; -use rustc::ty::{self, TyCtxt}; -use rustc_hir::def_id::DefId; - -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 { @@ -42,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,813 +40,5 @@ pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Opti } } } - return 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; - } - } - return 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); - } - } + None } 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 7c449ad119796..7c8aa1db71ff8 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 }, )); } - _ => {} - }, + } + _ => {} }; @@ -175,7 +176,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { fn add_move_path( &mut self, base: MovePathIndex, - elem: &PlaceElem<'tcx>, + elem: PlaceElem<'tcx>, mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>, ) -> MovePathIndex { let MoveDataBuilder { @@ -184,18 +185,17 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { .. } = self.builder; *rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || { - let path = MoveDataBuilder::new_move_path( + MoveDataBuilder::new_move_path( move_paths, path_map, init_path_map, Some(base), mk_place(*tcx), - ); - path + ) }) } - 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); @@ -279,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); @@ -308,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!( @@ -324,6 +324,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { match *rvalue { + Rvalue::ThreadLocalRef(_) => {} // not-a-move Rvalue::Use(ref operand) | Rvalue::Repeat(ref operand, _) | Rvalue::Cast(_, ref operand, _) @@ -364,12 +365,12 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { | TerminatorKind::Resume | TerminatorKind::Abort | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Unreachable => {} TerminatorKind::Return => { - self.gather_move(&Place::return_place()); + self.gather_move(Place::return_place()); } TerminatorKind::Assert { ref cond, .. } => { @@ -380,19 +381,19 @@ 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: _ } => { - self.gather_move(location); + TerminatorKind::Drop { place, target: _, unwind: _ } => { + self.gather_move(place); } - TerminatorKind::DropAndReplace { ref location, ref value, .. } => { - self.create_move_path(location); + TerminatorKind::DropAndReplace { place, ref value, .. } => { + self.create_move_path(place); self.gather_operand(value); - self.gather_init(location.as_ref(), InitKind::Deep); + self.gather_init(place.as_ref(), InitKind::Deep); } TerminatorKind::Call { ref func, @@ -400,30 +401,62 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { ref destination, cleanup: _, from_hir_call: _, + fn_span: _, } => { self.gather_operand(func); 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: _, + line_spans: _, + destination: _, + } => { + for op in operands { + match *op { + InlineAsmOperand::In { reg: _, ref value } + | InlineAsmOperand::Const { ref value } => { + self.gather_operand(value); + } + InlineAsmOperand::Out { reg: _, late: _, place, .. } => { + if let Some(place) = place { + self.create_move_path(place); + self.gather_init(place.as_ref(), InitKind::Deep); + } + } + InlineAsmOperand::InOut { reg: _, late: _, ref in_value, out_place } => { + self.gather_operand(in_value); + if let Some(out_place) = out_place { + self.create_move_path(out_place); + self.gather_init(out_place.as_ref(), InitKind::Deep); + } + } + InlineAsmOperand::SymFn { value: _ } + | InlineAsmOperand::SymStatic { def_id: _ } => {} + } + } + } } } fn gather_operand(&mut self, operand: &Operand<'tcx>) { match *operand { Operand::Constant(..) | Operand::Copy(..) => {} // not-a-move - Operand::Move(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 }] = @@ -434,7 +467,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); @@ -458,22 +491,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 3cbb0667ff392..60cf21552e9e9 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -1,37 +1,47 @@ -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 std::convert::TryFrom; -use rustc::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; -use rustc::mir::CastKind; use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::{Float, FloatConvert}; +use rustc_ast::ast::FloatTy; +use rustc_attr as attr; +use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; +use rustc_middle::mir::CastKind; +use rustc_middle::ty::adjustment::PointerCast; +use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; +use rustc_span::symbol::sym; +use rustc_target::abi::{Integer, LayoutOf, Variants}; -use super::{FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy}; +use super::{truncate, 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, + cast_kind: CastKind, + cast_ty: Ty<'tcx>, dest: PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { - use rustc::mir::CastKind::*; - match kind { + use rustc_middle::mir::CastKind::*; + // FIXME: In which cases should we trigger UB when the source is uninit? + match cast_kind { Pointer(PointerCast::Unsize) => { - self.unsize_into(src, dest)?; + let cast_ty = self.layout_of(cast_ty)?; + self.unsize_into(src, cast_ty, dest)?; } - Misc - | Pointer(PointerCast::MutToConstPointer) - | Pointer(PointerCast::ArrayToPointer) => { + Misc => { let src = self.read_immediate(src)?; - let res = self.cast_immediate(src, dest.layout)?; + let res = self.misc_cast(src, cast_ty)?; self.write_immediate(res, dest)?; } + Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => { + // These are NOPs, but can be wide pointers. + let v = self.read_immediate(src)?; + self.write_immediate(*v, dest)?; + } + Pointer(PointerCast::ReifyFnPointer) => { // The src operand does not matter, just its type match src.layout.ty.kind { @@ -42,7 +52,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } if self.tcx.has_attr(def_id, sym::rustc_args_required_const) { - bug!("reifying a fn ptr that requires const arguments"); + span_bug!( + self.cur_span(), + "reifying a fn ptr that requires const arguments" + ); } let instance = ty::Instance::resolve_for_fn_ptr( @@ -56,18 +69,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); self.write_scalar(fn_ptr, dest)?; } - _ => bug!("reify fn pointer on {:?}", src.layout.ty), + _ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty), } } Pointer(PointerCast::UnsafeFnPointer) => { let src = self.read_immediate(src)?; - match dest.layout.ty.kind { + match cast_ty.kind { ty::FnPtr(_) => { // No change to value self.write_immediate(*src, dest)?; } - _ => bug!("fn to unsafe fn cast on {:?}", dest.layout.ty), + _ => span_bug!(self.cur_span(), "fn to unsafe fn cast on {:?}", cast_ty), } } @@ -89,32 +102,28 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); self.write_scalar(fn_ptr, dest)?; } - _ => bug!("closure fn pointer on {:?}", src.layout.ty), + _ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty), } } } Ok(()) } - fn cast_immediate( + fn misc_cast( &self, src: ImmTy<'tcx, M::PointerTag>, - dest_layout: TyLayout<'tcx>, + cast_ty: Ty<'tcx>, ) -> InterpResult<'tcx, Immediate> { - use rustc::ty::TyKind::*; - trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, dest_layout.ty); + use rustc_middle::ty::TyKind::*; + trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, cast_ty); match src.layout.ty.kind { // Floating point Float(FloatTy::F32) => { - return Ok(self - .cast_from_float(src.to_scalar()?.to_f32()?, dest_layout.ty)? - .into()); + return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, cast_ty).into()); } Float(FloatTy::F64) => { - return Ok(self - .cast_from_float(src.to_scalar()?.to_f64()?, dest_layout.ty)? - .into()); + return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into()); } // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that // are represented as integers. @@ -129,117 +138,121 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ), } + // # First handle non-scalar source values. + // 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()); - return Ok(Scalar::from_uint(discr.val, dest_layout.size).into()); + let discr_layout = self.layout_of(discr.ty)?; + return Ok(self.cast_from_scalar(discr.val, discr_layout, cast_ty).into()); } } - layout::Variants::Multiple { .. } => {} - } - - // Handle casting the metadata away from a fat pointer. - if src.layout.ty.is_unsafe_ptr() - && dest_layout.ty.is_unsafe_ptr() - && dest_layout.size != src.layout.size - { - assert_eq!(src.layout.size, 2 * self.memory.pointer_size()); - assert_eq!(dest_layout.size, self.memory.pointer_size()); - assert!(dest_layout.ty.is_unsafe_ptr()); - match *src { - Immediate::ScalarPair(data, _) => return Ok(data.into()), - Immediate::Scalar(..) => bug!( - "{:?} input to a fat-to-thin cast ({:?} -> {:?})", - *src, - src.layout.ty, - dest_layout.ty - ), - }; + Variants::Multiple { .. } => {} } // Handle casting any ptr to raw ptr (might be a fat ptr). - if src.layout.ty.is_any_ptr() && dest_layout.ty.is_unsafe_ptr() { - // The only possible size-unequal case was handled above. - assert_eq!(src.layout.size, dest_layout.size); - return Ok(*src); + if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() { + let dest_layout = self.layout_of(cast_ty)?; + if dest_layout.size == src.layout.size { + // Thin or fat pointer that just hast the ptr kind of target type changed. + return Ok(*src); + } else { + // Casting the metadata away from a fat ptr. + assert_eq!(src.layout.size, 2 * self.memory.pointer_size()); + assert_eq!(dest_layout.size, self.memory.pointer_size()); + assert!(src.layout.ty.is_unsafe_ptr()); + return match *src { + Immediate::ScalarPair(data, _) => Ok(data.into()), + Immediate::Scalar(..) => span_bug!( + self.cur_span(), + "{:?} input to a fat-to-thin cast ({:?} -> {:?})", + *src, + src.layout.ty, + cast_ty + ), + }; + } } + // # The remaining source values are scalar. + // For all remaining casts, we either // (a) cast a raw ptr to usize, or // (b) cast from an integer-like (including bool, char, enums). // In both cases we want the bits. let bits = self.force_bits(src.to_scalar()?, src.layout.size)?; - Ok(self.cast_from_int(bits, src.layout, dest_layout)?.into()) + Ok(self.cast_from_scalar(bits, src.layout, cast_ty).into()) } - fn cast_from_int( + pub(super) fn cast_from_scalar( &self, - v: u128, // raw bits - src_layout: TyLayout<'tcx>, - dest_layout: TyLayout<'tcx>, - ) -> InterpResult<'tcx, Scalar> { + v: u128, // raw bits (there is no ScalarTy so we separate data+layout) + src_layout: TyAndLayout<'tcx>, + cast_ty: Ty<'tcx>, + ) -> Scalar { // Let's make sure v is sign-extended *if* it has a signed type. - let signed = src_layout.abi.is_signed(); + let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`. let v = if signed { self.sign_extend(v, src_layout) } else { v }; - trace!("cast_from_int: {}, {}, {}", v, src_layout.ty, dest_layout.ty); - use rustc::ty::TyKind::*; - match dest_layout.ty.kind { + trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty); + use rustc_middle::ty::TyKind::*; + match cast_ty.kind { Int(_) | Uint(_) | RawPtr(_) => { - let v = self.truncate(v, dest_layout); - Ok(Scalar::from_uint(v, dest_layout.size)) + let size = match cast_ty.kind { + Int(t) => Integer::from_attr(self, attr::IntType::SignedInt(t)).size(), + Uint(t) => Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(), + RawPtr(_) => self.pointer_size(), + _ => bug!(), + }; + let v = truncate(v, size); + Scalar::from_uint(v, size) } - Float(FloatTy::F32) if signed => { - Ok(Scalar::from_f32(Single::from_i128(v as i128).value)) - } - Float(FloatTy::F64) if signed => { - Ok(Scalar::from_f64(Double::from_i128(v as i128).value)) - } - Float(FloatTy::F32) => Ok(Scalar::from_f32(Single::from_u128(v).value)), - Float(FloatTy::F64) => Ok(Scalar::from_f64(Double::from_u128(v).value)), + Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value), + Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value), + Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value), + Float(FloatTy::F64) => Scalar::from_f64(Double::from_u128(v).value), Char => { // `u8` to `char` cast - assert_eq!(v as u8 as u128, v); - Ok(Scalar::from_uint(v, Size::from_bytes(4))) + Scalar::from_u32(u8::try_from(v).unwrap().into()) } // Casts to bool are not permitted by rustc, no need to handle them here. - _ => bug!("invalid int to {:?} cast", dest_layout.ty), + _ => span_bug!(self.cur_span(), "invalid int to {:?} cast", cast_ty), } } - fn cast_from_float( - &self, - f: F, - dest_ty: Ty<'tcx>, - ) -> InterpResult<'tcx, Scalar> + fn cast_from_float(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar 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 size = Integer::from_attr(self, attr::IntType::UnsignedInt(t)).size(); + // `to_u128` is a saturating cast, which is what we need + // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). + let v = f.to_u128(size.bits_usize()).value; // This should already fit the bit width - Ok(Scalar::from_uint(v, Size::from_bits(width as u64))) + Scalar::from_uint(v, size) } // 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 size = Integer::from_attr(self, attr::IntType::SignedInt(t)).size(); + // `to_i128` is a saturating cast, which is what we need + // (https://doc.rust-lang.org/nightly/nightly-rustc/rustc_apfloat/trait.Float.html#method.to_i128_r). + let v = f.to_i128(size.bits_usize()).value; + Scalar::from_int(v, size) } // float -> f32 - Float(FloatTy::F32) => Ok(Scalar::from_f32(f.convert(&mut false).value)), + Float(FloatTy::F32) => Scalar::from_f32(f.convert(&mut false).value), // float -> f64 - Float(FloatTy::F64) => Ok(Scalar::from_f64(f.convert(&mut false).value)), + Float(FloatTy::F64) => Scalar::from_f64(f.convert(&mut false).value), // That's it. - _ => bug!("invalid float to {:?} cast", dest_ty), + _ => span_bug!(self.cur_span(), "invalid float to {:?} cast", dest_ty), } } @@ -249,21 +262,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { dest: PlaceTy<'tcx, M::PointerTag>, // The pointee types source_ty: Ty<'tcx>, - dest_ty: Ty<'tcx>, + cast_ty: Ty<'tcx>, ) -> InterpResult<'tcx> { // A -> A conversion let (src_pointee_ty, dest_pointee_ty) = - self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, dest_ty, self.param_env); + self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env); match (&src_pointee_ty.kind, &dest_pointee_ty.kind) { (&ty::Array(_, length), &ty::Slice(_)) => { let ptr = self.read_immediate(src)?.to_scalar()?; // u64 cast is from usize to u64, which is always good - let val = Immediate::new_slice( - ptr, - length.eval_usize(self.tcx.tcx, self.param_env), - self, - ); + let val = + Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self); self.write_immediate(val, dest) } (&ty::Dynamic(..), &ty::Dynamic(..)) => { @@ -281,33 +291,40 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.write_immediate(val, dest) } - _ => bug!("invalid unsizing {:?} -> {:?}", src.layout.ty, dest.layout.ty), + _ => { + span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty) + } } } fn unsize_into( &mut self, src: OpTy<'tcx, M::PointerTag>, + cast_ty: TyAndLayout<'tcx>, dest: PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx> { - trace!("Unsizing {:?} 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::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: d, .. })) => { - self.unsize_into_ptr(src, dest, s, d) + trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty); + match (&src.layout.ty.kind, &cast_ty.ty.kind) { + (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) + | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { + self.unsize_into_ptr(src, dest, s, c) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); if def_a.is_box() || def_b.is_box() { if !def_a.is_box() || !def_b.is_box() { - bug!("invalid unsizing between {:?} -> {:?}", src.layout, dest.layout); + span_bug!( + self.cur_span(), + "invalid unsizing between {:?} -> {:?}", + src.layout.ty, + cast_ty.ty + ); } return self.unsize_into_ptr( src, dest, src.layout.ty.boxed_ty(), - dest.layout.ty.boxed_ty(), + cast_ty.ty.boxed_ty(), ); } @@ -315,20 +332,26 @@ 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)?; - if dst_field.layout.is_zst() { + let cast_ty_field = cast_ty.field(self, i)?; + if cast_ty_field.is_zst() { continue; } - let src_field = self.operand_field(src, i as u64)?; - if src_field.layout.ty == dst_field.layout.ty { + let src_field = self.operand_field(src, i)?; + let dst_field = self.place_field(dest, i)?; + if src_field.layout.ty == cast_ty_field.ty { self.copy_op(src_field, dst_field)?; } else { - self.unsize_into(src_field, dst_field)?; + self.unsize_into(src_field, cast_ty_field, dst_field)?; } } Ok(()) } - _ => bug!("unsize_into: invalid conversion: {:?} -> {:?}", src.layout, dest.layout), + _ => span_bug!( + self.cur_span(), + "unsize_into: invalid conversion: {:?} -> {:?}", + src.layout, + dest.layout + ), } } } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 482c143a73ee2..95e193b625354 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -2,33 +2,39 @@ 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. + /// The span in this is the "root" of the evaluation, i.e., the const + /// we are evaluating (if this is CTFE). pub tcx: TyCtxtAt<'tcx>, /// Bounds in scope for polymorphic evaluations. @@ -37,9 +43,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 +60,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 +83,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 @@ -98,10 +93,10 @@ pub enum StackPopCleanup { /// Jump to the next block in the caller, or cause UB if None (that's a function /// that may never return). Also store layout of return place so /// we can validate it at that layout. - /// `ret` stores the block we jump to on a normal return, while 'unwind' - /// stores the block used for cleanup during unwinding + /// `ret` stores the block we jump to on a normal return, while `unwind` + /// stores the block used for cleanup during unwinding. Goto { ret: Option, unwind: Option }, - /// Just do nohing: Used by Main and for the box_alloc hook in miri. + /// Just do nothing: Used by Main and for the `box_alloc` hook in miri. /// `cleanup` says whether locals are deallocated. Static computation /// wants them leaked to intern what they need (and just throw away /// the entire `ecx` when it is done). @@ -110,16 +105,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,13 +127,13 @@ 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> { pub fn access(&self) -> InterpResult<'tcx, Operand> { match self.value { - LocalValue::Dead => throw_unsup!(DeadLocal), + LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Uninitialized => { bug!("The type checker should prevent reading from a never-written local") } @@ -152,31 +147,40 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { &mut self, ) -> InterpResult<'tcx, Result<&mut LocalValue, MemPlace>> { match self.value { - LocalValue::Dead => throw_unsup!(DeadLocal), + 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, } } } 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 - } else { - block.terminator().source_info - } - }) + pub fn current_source_info(&self) -> Option<&mir::SourceInfo> { + self.loc.map(|loc| self.body.source_info(loc)) } } 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 } } @@ -200,35 +204,113 @@ where } } -impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> LayoutOf for InterpCx<'mir, 'tcx, M> { +impl<'mir, 'tcx: 'mir, 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>, + tcx: TyCtxt<'tcx>, + root_span: Span, param_env: ty::ParamEnv<'tcx>, machine: M, memory_extra: M::MemoryExtra, ) -> Self { InterpCx { machine, - tcx, + tcx: tcx.at(root_span), param_env, memory: Memory::new(tcx, memory_extra), - stack: Vec::new(), vtables: FxHashMap::default(), } } + #[inline(always)] + pub fn cur_span(&self) -> Span { + self.stack() + .last() + .and_then(|f| f.current_source_info()) + .map(|si| si.span) + .unwrap_or(self.tcx.span) + } + #[inline(always)] pub fn force_ptr( &self, @@ -253,29 +335,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 fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] { - &self.stack + pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] { + M::stack(self) } #[inline(always)] - pub fn cur_frame(&self) -> usize { - assert!(!self.stack.is_empty()); - self.stack.len() - 1 + pub(crate) fn stack_mut( + &mut self, + ) -> &mut Vec> { + M::stack_mut(self) + } + + #[inline(always)] + 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 +374,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) } @@ -301,32 +391,33 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline] pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { - ty.is_freeze(*self.tcx, self.param_env, DUMMY_SP) + ty.is_freeze(self.tcx, self.param_env) } pub fn load_mir( &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(self.tcx.def_path_str(def_id))) + throw_unsup!(NoMirFor(def_id)) } } _ => Ok(self.tcx.instance_mir(instance)), @@ -335,15 +426,25 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Call this on things you got out of the MIR (so it is as generic as the current /// stack frame), to bring it into the proper environment for this interpreter. + pub(super) fn subst_from_current_frame_and_normalize_erasing_regions>( + &self, + value: T, + ) -> T { + self.subst_from_frame_and_normalize_erasing_regions(self.frame(), value) + } + + /// Call this on things you got out of the MIR (so it is as generic as the provided + /// stack frame), to bring it into the proper environment for this interpreter. pub(super) fn subst_from_frame_and_normalize_erasing_regions>( &self, + frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, value: T, ) -> T { - self.tcx.subst_and_normalize_erasing_regions( - self.frame().instance.substs, - self.param_env, - &value, - ) + if let Some(substs) = frame.instance.substs_for_mir_body() { + self.tcx.subst_and_normalize_erasing_regions(substs, self.param_env, &value) + } else { + self.tcx.normalize_erasing_regions(self.param_env, value) + } } /// The `substs` are assumed to already be in our interpreter "universe" (param_env). @@ -355,27 +456,29 @@ 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.tcx.subst_and_normalize_erasing_regions( - frame.instance.substs, - self.param_env, - &local_ty, - ); + let local_ty = + self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty); self.layout_of(local_ty) })?; if let Some(state) = frame.locals.get(local) { @@ -394,7 +497,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))); @@ -406,6 +509,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); @@ -432,7 +536,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if sized_size == Size::ZERO { return Ok(None); } else { - bug!("Fields cannot be extern types, unless they are at offset 0") + span_bug!( + self.cur_span(), + "Fields cannot be extern types, unless they are at offset 0" + ) } } }; @@ -445,7 +552,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). @@ -456,7 +563,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let size = size.align_to(align); // Check if this brought us over the size limit. - if size.bytes() >= self.tcx.data_layout().obj_size_bound() { + if size.bytes() >= self.tcx.data_layout.obj_size_bound() { throw_ub!(InvalidMeta("total size is bigger than largest supported object")); } Ok(Some((size, align))) @@ -472,7 +579,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let elem = layout.field(self, 0)?; // Make sure the slice is not too big. - let size = elem.size.checked_mul(len, &*self.tcx).ok_or_else(|| { + let size = elem.size.checked_mul(len, self).ok_or_else(|| { err_ub!(InvalidMeta("slice is bigger than largest supported object")) })?; Ok(Some((size, elem.align.abi))) @@ -480,7 +587,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::Foreign(_) => Ok(None), - _ => bug!("size_and_align_of::<{:?}> not supported", layout.ty), + _ => span_bug!(self.cur_span(), "size_and_align_of::<{:?}> not supported", layout.ty), } } #[inline] @@ -494,66 +601,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.tcx.sess.recursion_limit().value_within_limit(self.stack().len()) { throw_exhaust!(StackFrameLimitReached) } else { Ok(()) @@ -563,9 +663,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. @@ -574,7 +672,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// If `target` is `None`, that indicates the function cannot return, so we raise UB. pub fn return_to_block(&mut self, target: Option) -> InterpResult<'tcx> { if let Some(target) = target { - Ok(self.go_to_block(target)) + self.go_to_block(target); + Ok(()) } else { throw_ub!(Unreachable) } @@ -586,9 +685,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 @@ -607,7 +704,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 ); @@ -615,14 +712,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? @@ -637,7 +746,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. @@ -645,50 +754,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 ); @@ -783,6 +873,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) } @@ -796,12 +889,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 { @@ -822,16 +915,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); } } @@ -850,33 +943,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 } } @@ -888,14 +969,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 9b13db2289e7e..dffbc969c21b8 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. - let frozen = ty.map_or(true, |ty| ty.is_freeze(ecx.tcx.tcx, ecx.param_env, ecx.tcx.span)); + 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, ecx.param_env)); // 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,91 @@ 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, 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,74 +287,87 @@ 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), Constant, Promoted, - 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::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), - )?; + ); - while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() { - let interned = InternVisitor { + ref_tracking.track((ret, base_intern_mode), || ()); + + 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_unsup!(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, + &format!( + "error during interning should later cause validation failure: {}", + error + ), + ); + // Some errors shouldn't come up because creating them causes + // an allocation, which we should avoid. When that happens, + // dedicated error variants should be introduced instead. + assert!( + !error.kind.allocates(), + "interning encountered allocating error: {}", + error + ); } } } @@ -359,43 +388,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"); - } + InternKind::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_unsup!(ValidationFailure("encountered dangling pointer in final constant".into())) - } 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 37a60bcc8368d..6ac1e6be03674 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -2,20 +2,21 @@ //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE //! and miri. -use rustc::mir::{ +use std::convert::TryFrom; + +use rustc_hir::def_id::DefId; +use rustc_middle::mir::{ self, - interpret::{ConstValue, GlobalId, InterpResult, Scalar}, + interpret::{uabs, 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::{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}; +use super::{CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy}; mod caller_location; mod type_name; @@ -29,11 +30,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 +73,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 +96,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 +116,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,8 +134,13 @@ 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, - _ => throw_unsup!(TypeNotPrimitive(ty)), + Abi::Scalar(ref scalar) => scalar.value, + _ => span_bug!( + self.cur_span(), + "{} called on invalid type {:?}", + intrinsic_name, + ty + ), }; let (nonzero, intrinsic_name) = match intrinsic_name { sym::cttz_nonzero => (true, sym::cttz), @@ -220,7 +225,7 @@ 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)?; + self.write_scalar(discr_val, dest)?; } sym::unchecked_shl | sym::unchecked_shr @@ -246,9 +251,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let layout = self.layout_of(substs.type_at(0))?; let r_val = self.force_bits(r.to_scalar()?, layout.size)?; if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name { - throw_ub_format!("Overflowing shift by {} in `{}`", r_val, intrinsic_name); + throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name); } else { - throw_ub_format!("Overflow executing `{}`", intrinsic_name); + throw_ub_format!("overflow executing `{}`", intrinsic_name); } } self.write_scalar(val, dest)?; @@ -261,7 +266,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 { @@ -273,9 +278,30 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = Scalar::from_uint(truncated_bits, layout.size); self.write_scalar(result, dest)?; } + sym::offset => { + let ptr = self.read_scalar(args[0])?.not_undef()?; + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; + let pointee_ty = substs.type_at(0); + + let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; + self.write_scalar(offset_ptr, dest)?; + } + sym::arith_offset => { + let ptr = self.read_scalar(args[0])?.not_undef()?; + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; + let pointee_ty = substs.type_at(0); + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); + let offset_bytes = offset_count.wrapping_mul(pointee_size); + let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self); + self.write_scalar(offset_ptr, dest)?; + } + sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { + // FIXME: return `true` for at least some comparisons where we can reliably + // determine the result of runtime (in)equality tests at compile-time. + self.write_scalar(Scalar::from_bool(false), dest)?; + } sym::ptr_offset_from => { - let 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 +318,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 +338,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) = @@ -330,7 +357,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let index = u64::from(self.read_scalar(args[1])?.to_u32()?); let elem = args[2]; let input = args[0]; - let (len, e_ty) = input.layout.ty.simd_size_and_type(self.tcx.tcx); + let (len, e_ty) = input.layout.ty.simd_size_and_type(*self.tcx); assert!( index < len, "Index `{}` must be in bounds of vector type `{}`: `[0, {})`", @@ -350,14 +377,14 @@ 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)?; } } sym::simd_extract => { let index = u64::from(self.read_scalar(args[1])?.to_u32()?); - let (len, e_ty) = args[0].layout.ty.simd_size_and_type(self.tcx.tcx); + let (len, e_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx); assert!( index < len, "index `{}` is out-of-bounds of vector type `{}` with length `{}`", @@ -370,8 +397,10 @@ 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)?; } + // FIXME(#73156): Handle source code coverage in const eval + sym::count_code_region => (), _ => return Ok(false), } @@ -403,4 +432,36 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // `Rem` says this is all right, so we can let `Div` do its job. self.binop_ignore_overflow(BinOp::Div, a, b, dest) } + + /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its + /// allocation. For integer pointers, we consider each of them their own tiny allocation of size + /// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value. + pub fn ptr_offset_inbounds( + &self, + ptr: Scalar, + pointee_ty: Ty<'tcx>, + offset_count: i64, + ) -> InterpResult<'tcx, Scalar> { + // We cannot overflow i64 as a type's size must be <= isize::MAX. + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); + // The computed offset, in bytes, cannot overflow an isize. + let offset_bytes = + offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; + // The offset being in bounds cannot rely on "wrapping around" the address space. + // So, first rule out overflows in the pointer arithmetic. + let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?; + // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the + // memory between these pointers must be accessible. Note that we do not require the + // pointers to be properly aligned (unlike a read/write operation). + let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr }; + let size: u64 = uabs(offset_bytes); + // This call handles checking for integer/NULL pointers. + self.memory.check_ptr_access_align( + min_ptr, + Size::from_bytes(size), + None, + CheckInAllocMsg::InboundsTest, + )?; + Ok(offset_ptr) + } } diff --git a/src/librustc_mir/interpret/intrinsics/caller_location.rs b/src/librustc_mir/interpret/intrinsics/caller_location.rs index 566601f0cae28..9adef8c43c7c8 100644 --- a/src/librustc_mir/interpret/intrinsics/caller_location.rs +++ b/src/librustc_mir/interpret/intrinsics/caller_location.rs @@ -1,5 +1,8 @@ -use rustc::middle::lang_items::PanicLocationLangItem; -use rustc::ty::subst::Subst; +use std::convert::TryFrom; + +use rustc_hir::lang_items::PanicLocationLangItem; +use rustc_middle::mir::TerminatorKind; +use rustc_middle::ty::subst::Subst; use rustc_span::{Span, Symbol}; use rustc_target::abi::LayoutOf; @@ -8,20 +11,43 @@ 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; + /// frame which is not `#[track_caller]`. + crate fn find_closest_untracked_caller_location(&self) -> Span { + let frame = self + .stack() + .iter() + .rev() + // Find first non-`#[track_caller]` frame. + .find(|frame| { + debug!( + "find_closest_untracked_caller_location: checking frame {:?}", + frame.instance + ); + !frame.instance.def.requires_caller_location(*self.tcx) + }) + // Assert that there is always such a frame. + .unwrap(); + // Assert that the frame we look at is actually executing code currently + // (`current_source_info` is None when we are unwinding and the frame does + // not require cleanup). + let loc = frame.loc.unwrap(); + // If this is a `Call` terminator, use the `fn_span` instead. + let block = &frame.body.basic_blocks()[loc.block]; + if loc.statement_index == block.statements.len() { + debug!( + "find_closest_untracked_caller_location:: got terminator {:?} ({:?})", + block.terminator(), + block.terminator().kind + ); + if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind { + return fn_span; } - caller_span = Some(next_caller.span); } - - caller_span + // This is a different terminator (such as `Drop`) or not a terminator at all + // (such as `box`). Use the normal span. + frame.body.source_info(loc).span } /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. @@ -39,7 +65,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let loc_ty = self .tcx .type_of(self.tcx.require_lang_item(PanicLocationLangItem, None)) - .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter())); + .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_erased.into()].iter())); let loc_layout = self.layout_of(loc_ty).unwrap(); let location = self.allocate(loc_layout, MemoryKind::CallerLocation); @@ -59,8 +85,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 cd8bf7085d1b1..379117f3b846a 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> { @@ -50,7 +50,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | ty::Dynamic(_, _) => self.pretty_print_type(ty), // Placeholders (all printed as `_` to uniformize them). - ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error => { + ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => { write!(self, "_")?; Ok(self) } @@ -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("::"); @@ -157,6 +155,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { } } } + impl PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> { fn region_should_not_be_omitted(&self, _region: ty::Region<'_>) -> bool { false @@ -191,7 +190,8 @@ impl PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> { impl Write for AbsolutePathPrinter<'_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { - Ok(self.path.push_str(s)) + self.path.push_str(s); + Ok(()) } } diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 0e70e54ad85af..b5dc40d955191 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)>, @@ -171,7 +172,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Called to evaluate `Abort` MIR terminator. fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, !> { - throw_unsup_format!("aborting execution is not supported"); + throw_unsup_format!("aborting execution is not supported") } /// Called for all binary operations where the LHS has pointer type. @@ -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 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 gets popped - fn stack_pop( + /// 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 @@ -286,8 +345,10 @@ pub trait Machine<'mir, 'tcx>: Sized { int: u64, ) -> InterpResult<'tcx, Pointer> { Err((if int == 0 { - err_unsup!(InvalidNullPointerUsage) + // This is UB, seriously. + err_ub!(DanglingIntPointer(0, CheckInAllocMsg::InboundsTest)) } else { + // This is just something we cannot support during const-eval. err_unsup!(ReadBytesAsPointer) }) .into()) @@ -297,4 +358,75 @@ pub trait Machine<'mir, 'tcx>: Sized { _mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer, ) -> InterpResult<'tcx, u64>; + + fn thread_local_alloc_id( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + did: DefId, + ) -> InterpResult<'tcx, AllocId> { + throw_unsup!(ThreadLocalStatic(did)) + } +} + +// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines +// (CTFE and ConstProp) use the same instance. Here, we share that code. +pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { + type PointerTag = (); + type ExtraFnVal = !; + + type MemoryKind = !; + type MemoryMap = rustc_data_structures::fx::FxHashMap, Allocation)>; + const GLOBAL_KIND: Option = None; // no copying of globals from `tcx` to machine memory + + type AllocExtra = (); + type FrameExtra = (); + + #[inline(always)] + fn enforce_alignment(_memory_extra: &Self::MemoryExtra) -> bool { + // We do not check for alignment to avoid having to carry an `Align` + // in `ConstValue::ByRef`. + false + } + + #[inline(always)] + fn enforce_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { + false // for now, we don't enforce validity + } + + #[inline(always)] + fn call_extra_fn( + _ecx: &mut InterpCx<$mir, $tcx, Self>, + fn_val: !, + _args: &[OpTy<$tcx>], + _ret: Option<(PlaceTy<$tcx>, mir::BasicBlock)>, + _unwind: Option, + ) -> InterpResult<$tcx> { + match fn_val {} + } + + #[inline(always)] + fn init_allocation_extra<'b>( + _memory_extra: &Self::MemoryExtra, + _id: AllocId, + alloc: Cow<'b, Allocation>, + _kind: Option>, + ) -> (Cow<'b, Allocation>, Self::PointerTag) { + // We do not use a tag so we can just cheaply forward the allocation + (alloc, ()) + } + + #[inline(always)] + fn tag_global_base_pointer( + _memory_extra: &Self::MemoryExtra, + _id: AllocId, + ) -> Self::PointerTag { + () + } + + #[inline(always)] + fn init_frame_extra( + _ecx: &mut InterpCx<$mir, $tcx, Self>, + frame: Frame<$mir, $tcx>, + ) -> InterpResult<$tcx, Frame<$mir, $tcx>> { + Ok(frame) + } } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 82a467c7ba92c..8af1a8ac608ac 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, Instance, ParamEnv, TyCtxt}; +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, @@ -102,7 +115,7 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> { pub extra: M::MemoryExtra, /// Lets us implement `HasDataLayout`, which is awfully convenient. - pub tcx: TyCtxtAt<'tcx>, + pub tcx: TyCtxt<'tcx>, } impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for Memory<'mir, 'tcx, M> { @@ -112,27 +125,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for Memory<'mir, 'tcx, M> } } -// FIXME: Really we shouldn't clone memory, ever. Snapshot machinery should instead -// carefully copy only the reachable parts. -impl<'mir, 'tcx, M> Clone for Memory<'mir, 'tcx, M> -where - M: Machine<'mir, 'tcx, PointerTag = (), AllocExtra = ()>, - M::MemoryExtra: Copy, - M::MemoryMap: AllocMap, Allocation)>, -{ - fn clone(&self) -> Self { - Memory { - alloc_map: self.alloc_map.clone(), - extra_fn_ptr_map: self.extra_fn_ptr_map.clone(), - dead_alloc_map: self.dead_alloc_map.clone(), - extra: self.extra, - tcx: self.tcx, - } - } -} - impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { - pub fn new(tcx: TyCtxtAt<'tcx>, extra: M::MemoryExtra) -> Self { + pub fn new(tcx: TyCtxt<'tcx>, extra: M::MemoryExtra) -> Self { Memory { alloc_map: M::MemoryMap::default(), extra_fn_ptr_map: FxHashMap::default(), @@ -149,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( @@ -159,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) @@ -193,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())); @@ -212,10 +206,13 @@ 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_unsup!(ReallocateNonBasePtr) + throw_ub_format!( + "reallocating {:?} which does not point to the beginning of an object", + ptr + ); } // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc". @@ -231,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) @@ -246,42 +243,50 @@ 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); if ptr.offset.bytes() != 0 { - throw_unsup!(DeallocateNonBasePtr) + throw_ub_format!( + "deallocating {:?} which does not point to the beginning of an object", + ptr + ); } + 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) { - Some(GlobalAlloc::Function(..)) => err_unsup!(DeallocatedWrongMemoryKind( - "function".to_string(), - format!("{:?}", kind), - )), - Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => err_unsup!( - DeallocatedWrongMemoryKind("static".to_string(), format!("{:?}", kind)) - ), - None => err_unsup!(DoubleFree), + // 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(..) | GlobalAlloc::Memory(..)) => { + err_ub_format!("deallocating static memory") + } + None => err_ub!(PointerUseAfterFree(ptr.alloc_id)), } .into()); } }; if alloc_kind != kind { - throw_unsup!(DeallocatedWrongMemoryKind( - format!("{:?}", alloc_kind), - format!("{:?}", kind), - )) + throw_ub_format!( + "deallocating {} memory using {} deallocation operation", + alloc_kind, + kind + ); } if let Some((size, align)) = old_size_and_align { if size != alloc.size || align != alloc.align { - let bytes = alloc.size; - throw_unsup!(IncorrectAllocationInformation(size, bytes, align, alloc.align)) + throw_ub_format!( + "incorrect layout on deallocation: allocation has size {} and alignment {}, but gave size {} and alignment {}", + alloc.size.bytes(), + alloc.align.bytes(), + size.bytes(), + align.bytes(), + ) } } @@ -318,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, @@ -338,7 +343,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } else { // The biggest power of two through which `offset` is divisible. let offset_pow2 = 1 << offset.trailing_zeros(); - throw_unsup!(AlignmentCheckFailed { + throw_ub!(AlignmentCheckFailed { has: Align::from_bytes(offset_pow2).unwrap(), required: align, }) @@ -356,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_unsup!(InvalidNullPointerUsage) + throw_ub!(DanglingIntPointer(0, msg)) } // Must be aligned. if let Some(align) = align { @@ -375,7 +380,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // It is sufficient to check this for the end pointer. The addition // checks for overflow. let end_ptr = ptr.offset(size, self)?; - end_ptr.check_inbounds_alloc(allocation_size, msg)?; + if end_ptr.offset > allocation_size { + // equal is okay! + throw_ub!(PointerOutOfBounds { ptr: end_ptr.erase_tag(), msg, allocation_size }) + } // Test align. Check this last; if both bounds and alignment are violated // we want the error to be about the bounds. if let Some(align) = align { @@ -385,7 +393,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // got picked we might be aligned even if this check fails. // We instead have to fall back to converting to an integer and checking // the "real" alignment. - throw_unsup!(AlignmentCheckFailed { has: alloc_align, required: align }); + throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align }); } check_offset_align(ptr.offset.bytes(), align)?; } @@ -402,80 +410,84 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let (size, _align) = self .get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead) .expect("alloc info with MaybeDead cannot fail"); - ptr.check_inbounds_alloc(size, CheckInAllocMsg::NullPointerTest).is_err() + // If the pointer is out-of-bounds, it may be null. + // Note that one-past-the-end (offset == size) is still inbounds, and never null. + ptr.offset > size } } /// 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>, + tcx: TyCtxt<'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), - Some(GlobalAlloc::Function(..)) => throw_unsup!(DerefFunctionPointer), - None => throw_unsup!(DanglingPointerDeref), + 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. + assert!(!tcx.is_thread_local_static(def_id)); + // Notice that every static has two `AllocId` that will resolve to the same + // thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID, + // 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); - throw_unsup!(ReadForeignStatic) + trace!("get_global_alloc: foreign item {:?}", def_id); + throw_unsup!(ReadForeignStatic(def_id)) } - trace!("get_static_alloc: Need to compute {:?}", def_id); - let instance = Instance::mono(tcx.tcx, def_id); + trace!("get_global_alloc: Need to compute {:?}", def_id); + let instance = Instance::mono(tcx, def_id); let gid = GlobalId { instance, promoted: None }; - // use the raw query here to break validation cycles. Later uses of the static - // will call the full query anyway + // 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, @@ -483,10 +495,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 @@ -495,8 +508,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)) @@ -511,7 +524,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, @@ -520,16 +533,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_unsup!(ModifiedConstantMemory) - } - match M::STATIC_KIND { - Some(kind) => Ok((MemoryKind::Machine(kind), alloc.into_owned())), - None => throw_unsup!(ModifiedStatic), + throw_ub!(WriteToReadOnly(id)) } + 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`) @@ -538,7 +552,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Ok(a) => { let a = &mut a.1; if a.mutability == Mutability::Not { - throw_unsup!(ModifiedConstantMemory) + throw_ub!(WriteToReadOnly(id)) } Ok(a) } @@ -558,7 +572,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)); } @@ -568,7 +582,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if self.get_fn_alloc(id).is_some() { return if let AllocCheck::Dereferenceable = liveness { // The caller requested no function pointers. - throw_unsup!(DerefFunctionPointer) + throw_ub!(DerefFunctionPointer(id)) } else { Ok((Size::ZERO, Align::from_bytes(1).unwrap())) }; @@ -577,9 +591,9 @@ 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)) => { + assert!(!self.tcx.is_thread_local_static(did)); // Use size and align of the type. let ty = self.tcx.type_of(did); let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap(); @@ -596,12 +610,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if let AllocCheck::MaybeDead = liveness { // Deallocated pointers are allowed, we should be able to find // them in the map. - Ok(*self.dead_alloc_map.get(&id).expect( - "deallocated pointers should all be recorded in \ - `dead_alloc_map`", - )) + Ok(*self + .dead_alloc_map + .get(&id) + .expect("deallocated pointers should all be recorded in `dead_alloc_map`")) } else { - throw_unsup!(DanglingPointerDeref) + throw_ub!(PointerUseAfterFree(id)) } } } @@ -613,7 +627,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, } @@ -626,10 +640,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { let ptr = self.force_ptr(ptr)?; // We definitely need a pointer value. if ptr.offset.bytes() != 0 { - throw_unsup!(InvalidFunctionPointer) + throw_ub!(InvalidFunctionPointer(ptr.erase_tag())) } let id = M::canonical_alloc_id(self, ptr.alloc_id); - self.get_fn_alloc(id).ok_or_else(|| err_unsup!(ExecuteMemory).into()) + self.get_fn_alloc(id).ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into()) } pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { @@ -644,128 +658,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: TyCtxt<'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, 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 @@ -833,17 +809,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; + let tcx = self.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; + 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, @@ -872,16 +888,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let relocations = self.get_raw(src.alloc_id)?.prepare_relocation_copy(self, src, size, dest, length); - 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()); + let tcx = self.tcx; // 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 @@ -902,7 +915,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(()); } @@ -913,9 +926,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) { @@ -926,16 +939,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(), ); } } @@ -975,7 +988,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 c3fd968276577..d46010d98a5aa 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -9,29 +9,22 @@ mod memory; mod operand; mod operator; mod place; -pub(crate) mod snapshot; // for const_eval mod step; mod terminator; 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 22b1a7b7137d9..38948ee53846a 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -1,20 +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 rustc::ty::layout::{ - self, HasDataLayout, IntegerExt, LayoutOf, PrimitiveExt, Size, TyLayout, VariantIdx, -}; -use rustc::{mir, ty}; - -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_ast::ast; +use rustc_errors::ErrorReported; +use rustc_hir::def::Namespace; use rustc_macros::HashStable; +use rustc_middle::ty::layout::{PrimitiveExt, TyAndLayout}; +use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Printer}; +use rustc_middle::ty::Ty; +use rustc_middle::{mir, ty}; +use rustc_target::abi::{Abi, HasDataLayout, LayoutOf, Size, TagEncoding}; +use rustc_target::abi::{VariantIdx, Variants}; + +use super::{ + from_known_layout, mir_assign_valid_types, ConstValue, GlobalId, InterpCx, InterpResult, + MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, +}; /// An `Immediate` represents a single immediate self-contained Rust value. /// @@ -24,14 +27,14 @@ use rustc_macros::HashStable; /// 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) } } @@ -52,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 { @@ -63,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"), @@ -88,51 +88,48 @@ 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>, } -// `Tag: Copy` because some methods on `Scalar` consume them by value impl std::fmt::Display for ImmTy<'tcx, Tag> { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.imm { - // We cannot use `to_bits_or_ptr` as we do not have a `tcx`. - // So we use `is_bits` and circumvent a bunch of sanity checking -- but - // this is anyway only for printing. - Immediate::Scalar(ScalarMaybeUndef::Scalar(s)) if s.is_ptr() => { - fmt.write_str("{pointer}") + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// Helper function for printing a scalar to a FmtPrinter + fn p<'a, 'tcx, F: std::fmt::Write, Tag>( + cx: FmtPrinter<'a, 'tcx, F>, + s: ScalarMaybeUninit, + ty: Ty<'tcx>, + ) -> Result, std::fmt::Error> { + match s { + ScalarMaybeUninit::Scalar(s) => { + cx.pretty_print_const_scalar(s.erase_tag(), ty, true) + } + ScalarMaybeUninit::Uninit => cx.typed_value( + |mut this| { + this.write_str("{undef ")?; + Ok(this) + }, + |this| this.print_type(ty), + " ", + ), } - Immediate::Scalar(ScalarMaybeUndef::Scalar(s)) => { - let s = s.assert_bits(self.layout.size); - match self.layout.ty.kind { - ty::Int(_) => { - return write!(fmt, "{}", super::sign_extend(s, self.layout.size) as i128,); - } - ty::Uint(_) => return write!(fmt, "{}", s), - ty::Bool if s == 0 => return fmt.write_str("false"), - ty::Bool if s == 1 => return fmt.write_str("true"), - ty::Char => { - if let Some(c) = u32::try_from(s).ok().and_then(std::char::from_u32) { - return write!(fmt, "{}", c); - } - } - ty::Float(ast::FloatTy::F32) => { - if let Ok(u) = u32::try_from(s) { - return write!(fmt, "{}", f32::from_bits(u)); - } - } - ty::Float(ast::FloatTy::F64) => { - if let Ok(u) = u64::try_from(s) { - return write!(fmt, "{}", f64::from_bits(u)); - } + } + ty::tls::with(|tcx| { + match self.imm { + Immediate::Scalar(s) => { + if let Some(ty) = tcx.lift(&self.layout.ty) { + let cx = FmtPrinter::new(tcx, f, Namespace::ValueNS); + p(cx, s, ty)?; + return Ok(()); } - _ => {} + write!(f, "{}: {}", s.erase_tag(), self.layout.ty) + } + Immediate::ScalarPair(a, b) => { + // FIXME(oli-obk): at least print tuples and slices nicely + write!(f, "({}, {}): {}", a.erase_tag(), b.erase_tag(), self.layout.ty,) } - write!(fmt, "{:x}", s) } - Immediate::Scalar(ScalarMaybeUndef::Undef) => fmt.write_str("{undef}"), - Immediate::ScalarPair(..) => fmt.write_str("{wide pointer or tuple}"), - } + }) } } @@ -148,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> { @@ -183,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] @@ -261,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(), @@ -269,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. @@ -288,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), @@ -327,7 +311,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Ok(imm) = self.try_read_immediate(op)? { Ok(imm) } else { - bug!("primitive read failed for type: {:?}", op.layout.ty); + span_bug!(self.cur_span(), "primitive read failed for type: {:?}", op.layout.ty); } } @@ -335,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_unsup!(ValidationFailure(err.to_string())))?; + 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) } @@ -352,18 +335,17 @@ 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) => { - // The easy case + // We can reuse the mplace field computation logic for indirect operands. let field = self.mplace_field(mplace, field)?; return Ok(field.into()); } Err(value) => value, }; - let field = field.try_into().unwrap(); let field_layout = op.layout.field(self, field)?; if field_layout.is_zst() { let immediate = Scalar::zst().into(); @@ -378,13 +360,31 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = if offset.bytes() == 0 { a } else { b }; Immediate::from(val) } - Immediate::Scalar(val) => { - bug!("field access on non aggregate {:#?}, {:#?}", val, op.layout) - } + Immediate::Scalar(val) => span_bug!( + self.cur_span(), + "field access on non aggregate {:#?}, {:#?}", + val, + op.layout + ), }; Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }) } + pub fn operand_index( + &self, + op: OpTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + if let Ok(index) = usize::try_from(index) { + // We can just treat this as a field. + self.operand_field(op, index) + } else { + // Indexing into a big array. This must be an mplace. + let mplace = op.assert_mem_place(self); + Ok(self.mplace_index(mplace, index)?.into()) + } + } + pub fn operand_downcast( &self, op: OpTy<'tcx, M::PointerTag>, @@ -403,11 +403,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn operand_projection( &self, base: OpTy<'tcx, M::PointerTag>, - proj_elem: &mir::PlaceElem<'tcx>, + proj_elem: mir::PlaceElem<'tcx>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - use rustc::mir::ProjectionElem::*; - Ok(match *proj_elem { - Field(field, _) => self.operand_field(base, field.index() as u64)?, + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.operand_field(base, field.index())?, Downcast(_, variant) => self.operand_downcast(base, variant)?, Deref => self.deref_operand(base)?.into(), Subslice { .. } | ConstantIndex { .. } | Index(_) => { @@ -424,9 +424,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 @@ -437,7 +436,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, @@ -445,7 +446,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 }) } @@ -454,19 +457,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_unsup!(ReadFromReturnPointer), - 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 @@ -474,6 +472,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .try_fold(base_op, |op, elem| self.operand_projection(op, elem))?; trace!("eval_place_to_op: got {:?}", *op); + // Sanity-check the type we ended up with. + debug_assert!(mir_assign_valid_types( + *self.tcx, + self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( + place.ty(&self.frame().body.local_decls, *self.tcx).ty + ))?, + op.layout, + )); Ok(op) } @@ -483,15 +489,16 @@ 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 = self.subst_from_frame_and_normalize_erasing_regions(constant.literal); + let val = + self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal); self.eval_const_to_op(val, layout)? } }; @@ -514,15 +521,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,23 +539,31 @@ 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(..) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(..) => { - bug!("eval_const_to_op: Unexpected ConstKind {:?}", val) + span_bug!(self.cur_span(), "eval_const_to_op: Unexpected ConstKind {:?}", val) } ty::ConstKind::Value(val_val) => val_val, }; + // This call allows the machine to create fresh allocation ids for + // thread-local statics (see the `adjust_global_const` function + // documentation). + let val_val = M::adjust_global_const(self, val_val)?; // Other cases need layout. - let layout = from_known_layout(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 +571,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, )) } @@ -571,106 +587,110 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Read discriminant, return the runtime value as well as the variant index. pub fn read_discriminant( &self, - rval: OpTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, (u128, VariantIdx)> { - trace!("read_discriminant_value {:#?}", rval.layout); - - let (discr_layout, discr_kind, discr_index) = match rval.layout.variants { - layout::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); - return Ok((discr_val, index)); + op: OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, (Scalar, VariantIdx)> { + trace!("read_discriminant_value {:#?}", op.layout); + // Get type and layout of the discriminant. + let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; + trace!("discriminant type: {:?}", discr_layout.ty); + + // We use "discriminant" to refer to the value associated with a particular enum variant. + // This is not to be confused with its "variant index", which is just determining its position in the + // declared list of variants -- they can differ with explicitly assigned discriminants. + // We use "tag" to refer to how the discriminant is encoded in memory, which can be either + // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). + let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants { + Variants::Single { index } => { + let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) { + Some(discr) => { + // This type actually has discriminants. + assert_eq!(discr.ty, discr_layout.ty); + Scalar::from_uint(discr.val, discr_layout.size) + } + None => { + // On a type without actual discriminants, variant is 0. + assert_eq!(index.as_u32(), 0); + Scalar::from_uint(index.as_u32(), discr_layout.size) + } + }; + return Ok((discr, index)); + } + Variants::Multiple { ref tag, ref tag_encoding, tag_field, .. } => { + (tag, tag_encoding, tag_field) } - layout::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_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 => { - let bits_discr = raw_discr - .not_undef() - .and_then(|raw_discr| self.force_bits(raw_discr, discr_val.layout.size)) - .map_err(|_| err_ub!(InvalidDiscriminant(raw_discr.erase_tag())))?; - let real_discr = if discr_val.layout.ty.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; - // and then zeroing with the typeck discriminant type - let discr_ty = rval - .layout - .ty - .ty_adt_def() - .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) - } else { - bits_discr - }; - // Make sure we catch invalid discriminants - let index = match rval.layout.ty.kind { + // There are *three* layouts that come into play here: + // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for + // the `Scalar` we return. + // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type, + // and used to interpret the value we read from the tag field. + // For the return value, a cast to `discr_layout` is performed. + // - The field storing the tag has a layout, which is very similar to `tag_layout` but + // may be a pointer. This is `tag_val.layout`; we just use it for sanity checks. + + // Get layout for tag. + let tag_layout = self.layout_of(tag_scalar_layout.value.to_int_ty(*self.tcx))?; + + // Read tag and sanity-check `tag_layout`. + let tag_val = self.read_immediate(self.operand_field(op, tag_field)?)?; + assert_eq!(tag_layout.size, tag_val.layout.size); + assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed()); + let tag_val = tag_val.to_scalar()?; + trace!("tag value: {:?}", tag_val); + + // Figure out which discriminant and variant this corresponds to. + Ok(match *tag_encoding { + TagEncoding::Direct => { + let tag_bits = self + .force_bits(tag_val, tag_layout.size) + .map_err(|_| err_ub!(InvalidTag(tag_val.erase_tag())))?; + // Cast bits from tag layout to discriminant layout. + let discr_val = self.cast_from_scalar(tag_bits, tag_layout, discr_layout.ty); + let discr_bits = discr_val.assert_bits(discr_layout.size); + // Convert discriminant to variant index, and catch invalid discriminants. + let index = match op.layout.ty.kind { ty::Adt(adt, _) => { - adt.discriminants(self.tcx.tcx).find(|(_, var)| var.val == real_discr) + adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits) } ty::Generator(def_id, substs, _) => { let substs = substs.as_generator(); substs - .discriminants(def_id, self.tcx.tcx) - .find(|(_, var)| var.val == real_discr) + .discriminants(def_id, *self.tcx) + .find(|(_, var)| var.val == discr_bits) } - _ => bug!("tagged layout for non-adt non-generator"), + _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"), } - .ok_or_else(|| err_ub!(InvalidDiscriminant(raw_discr.erase_tag())))?; - (real_discr, index.0) + .ok_or_else(|| err_ub!(InvalidTag(tag_val.erase_tag())))?; + // Return the cast value, and the index. + (discr_val, index.0) } - layout::DiscriminantKind::Niche { - dataful_variant, - ref niche_variants, - niche_start, - } => { + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start } => { + // Compute the variant this niche value/"tag" corresponds to. With niche layout, + // discriminant (encoded in niche/tag) and variant index are the same. let variants_start = niche_variants.start().as_u32(); let variants_end = niche_variants.end().as_u32(); - let raw_discr = raw_discr - .not_undef() - .map_err(|_| err_ub!(InvalidDiscriminant(ScalarMaybeUndef::Undef)))?; - match raw_discr.to_bits_or_ptr(discr_val.layout.size, self) { + let variant = match tag_val.to_bits_or_ptr(tag_layout.size, self) { Err(ptr) => { // The niche must be just 0 (which an inbounds pointer value never is) let ptr_valid = niche_start == 0 && variants_start == variants_end && !self.memory.ptr_may_be_null(ptr); if !ptr_valid { - throw_ub!(InvalidDiscriminant(raw_discr.erase_tag().into())) + throw_ub!(InvalidTag(tag_val.erase_tag())) } - (dataful_variant.as_u32() as u128, dataful_variant) + dataful_variant } - Ok(raw_discr) => { + Ok(tag_bits) => { // We need to use machine arithmetic to get the relative variant idx: - // variant_index_relative = discr_val - niche_start_val - let discr_layout = - self.layout_of(discr_layout.value.to_int_ty(*self.tcx))?; - let discr_val = ImmTy::from_uint(raw_discr, discr_layout); - let niche_start_val = ImmTy::from_uint(niche_start, discr_layout); + // variant_index_relative = tag_val - niche_start_val + let tag_val = ImmTy::from_uint(tag_bits, tag_layout); + let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); let variant_index_relative_val = - self.binary_op(mir::BinOp::Sub, discr_val, niche_start_val)?; + self.binary_op(mir::BinOp::Sub, tag_val, niche_start_val)?; let variant_index_relative = variant_index_relative_val .to_scalar()? - .assert_bits(discr_val.layout.size); + .assert_bits(tag_val.layout.size); // Check if this is in the range that indicates an actual discriminant. if variant_index_relative <= u128::from(variants_end - variants_start) { let variant_index_relative = u32::try_from(variant_index_relative) @@ -679,20 +699,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let variant_index = variants_start .checked_add(variant_index_relative) .expect("overflow computing absolute variant idx"); - let variants_len = rval + let variants_len = op .layout .ty .ty_adt_def() .expect("tagged layout for non adt") .variants .len(); - assert!((variant_index as usize) < variants_len); - (u128::from(variant_index), VariantIdx::from_u32(variant_index)) + assert!(usize::try_from(variant_index).unwrap() < variants_len); + VariantIdx::from_u32(variant_index) } else { - (u128::from(dataful_variant.as_u32()), dataful_variant) + dataful_variant } } - } + }; + // Compute the size of the scalar we need to return. + // No need to cast, because the variant index directly serves as discriminant and is + // encoded in the tag. + (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant) } }) } diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index f2ee5e047a88e..607122935347e 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, @@ -62,9 +61,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Le => l <= r, Gt => l > r, Ge => l >= r, - _ => bug!("Invalid operation on char: {:?}", bin_op), + _ => span_bug!(self.cur_span(), "Invalid operation on char: {:?}", bin_op), }; - return (Scalar::from_bool(res), false, self.tcx.types.bool); + (Scalar::from_bool(res), false, self.tcx.types.bool) } fn binary_bool_op( @@ -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, @@ -85,9 +84,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { BitAnd => l & r, BitOr => l | r, BitXor => l ^ r, - _ => bug!("Invalid operation on bool: {:?}", bin_op), + _ => span_bug!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op), }; - return (Scalar::from_bool(res), false, self.tcx.types.bool); + (Scalar::from_bool(res), false, self.tcx.types.bool) } fn binary_float_op>>( @@ -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), @@ -111,9 +110,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Mul => ((l * r).value.into(), ty), Div => ((l / r).value.into(), ty), Rem => ((l % r).value.into(), ty), - _ => bug!("invalid float op: `{:?}`", bin_op), + _ => span_bug!(self.cur_span(), "invalid float op: `{:?}`", bin_op), }; - return (val, false, ty); + (val, false, ty) } fn binary_int_op( @@ -121,42 +120,42 @@ 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 if left_layout.ty != right_layout.ty { - bug!( + span_bug!( + self.cur_span(), "invalid asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, l, @@ -193,21 +192,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; @@ -256,7 +252,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )); } - _ => bug!( + _ => span_bug!( + self.cur_span(), "invalid binary op {:?}: {:?}, {:?} (both {:?})", bin_op, l, @@ -338,7 +335,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { M::binary_ptr_op(self, bin_op, left, right) } - _ => bug!("Invalid MIR: bad LHS type for binop: {:?}", left.layout.ty), + _ => span_bug!( + self.cur_span(), + "Invalid MIR: bad LHS type for binop: {:?}", + left.layout.ty + ), } } @@ -361,7 +362,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()?; @@ -372,7 +373,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = val.to_bool()?; let res = match un_op { Not => !val, - _ => bug!("Invalid bool op {:?}", un_op), + _ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op), }; Ok((Scalar::from_bool(res), false, self.tcx.types.bool)) } @@ -380,7 +381,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let res = match (un_op, fty) { (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?), (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?), - _ => bug!("Invalid float op {:?}", un_op), + _ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op), }; Ok((res, false, layout.ty)) } diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index a4815b9696ebb..396aec0a8f89f 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, FieldsShape, TagEncoding}; +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 } } @@ -212,16 +203,14 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { if self.layout.is_unsized() { // We need to consult `meta` metadata match self.layout.ty.kind { - ty::Slice(..) | ty::Str => { - return self.mplace.meta.unwrap_meta().to_machine_usize(cx); - } + ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_machine_usize(cx), _ => bug!("len not supported on unsized type {:?}", self.layout.ty), } } else { // Go through the layout. There are lots of types that support a length, // e.g., SIMD types. match self.layout.fields { - layout::FieldPlacement::Array { count, .. } => Ok(count), + FieldsShape::Array { count, .. } => Ok(count), _ => bug!("len not supported on sized type {:?}", self.layout.ty), } } @@ -250,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)), } } @@ -263,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 { @@ -286,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. @@ -336,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 @@ -361,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(); @@ -387,51 +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) => { - // This is a narrow bug-fix for rust-lang/rust#69191: if we are - // trying to access absent field of uninhabited variant, then - // signal UB (but don't ICE the compiler). - // FIXME temporary hack to work around incoherence between - // layout computation and MIR building - if field >= count as u64 && base.layout.abi == layout::Abi::Uninhabited { - throw_ub!(Unreachable); - } - 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() { @@ -447,7 +404,10 @@ where // to get some code to work that probably ought to work. field_layout.align.abi } - None => bug!("Cannot compute offset for extern type field at non-0 offset"), + None => span_bug!( + self.cur_span(), + "cannot compute offset for extern type field at non-0 offset" + ), }; (base.meta, offset.align_to(align)) } else { @@ -461,6 +421,36 @@ 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) + } + _ => span_bug!( + self.cur_span(), + "`mplace_index` called on non-array type {:?}", + base.layout.ty + ), + } + } + // Iterates over all fields of an array. Much more efficient than doing the // same by repeatedly calling `mplace_array`. pub(super) fn mplace_array_fields( @@ -470,12 +460,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, - _ => bug!("mplace_array_fields: expected an array layout"), + FieldsShape::Array { stride, .. } => stride, + _ => span_bug!(self.cur_span(), "mplace_array_fields: expected an array layout"), }; let layout = base.layout.field(self, 0)?; let dl = &self.tcx.data_layout; - 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( @@ -487,11 +478,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 }; @@ -499,21 +490,25 @@ 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, - _ => bug!("Unexpected layout of index access: {:#?}", base.layout), + FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked + _ => { + span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout) + } }; // Compute meta and new layout - let inner_len = actual_to - 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), + _ => { + span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty) + } }; let layout = self.layout_of(ty)?; base.offset(from_offset, meta, layout, self) @@ -533,11 +528,11 @@ where pub(super) fn mplace_projection( &self, base: MPlaceTy<'tcx, M::PointerTag>, - proj_elem: &mir::PlaceElem<'tcx>, + proj_elem: mir::PlaceElem<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - use rustc::mir::ProjectionElem::*; - Ok(match *proj_elem { - Field(field, _) => self.mplace_field(base, field.index() as u64)?, + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.mplace_field(base, field.index())?, Downcast(_, variant) => self.mplace_downcast(base, variant)?, Deref => self.deref_operand(base.into())?, @@ -545,26 +540,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 } => { @@ -580,7 +578,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. @@ -588,6 +586,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>, @@ -609,11 +616,11 @@ where pub fn place_projection( &mut self, base: PlaceTy<'tcx, M::PointerTag>, - proj_elem: &mir::ProjectionElem>, + &proj_elem: &mir::ProjectionElem>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - use rustc::mir::ProjectionElem::*; - Ok(match *proj_elem { - Field(field, _) => self.place_field(base, field.index() as u64)?, + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.place_field(base, field.index())?, Downcast(_, variant) => self.place_downcast(base, variant)?, Deref => self.deref_operand(self.place_to_op(base)?)?.into(), // For the other variants, we have to force an allocation. @@ -629,42 +636,27 @@ 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_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() { - place_ty = self.place_projection(place_ty, elem)? + place_ty = self.place_projection(place_ty, &elem)? } self.dump_place(place_ty.place); + // Sanity-check the type we ended up with. + debug_assert!(mir_assign_valid_types( + *self.tcx, + self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( + place.ty(&self.frame().body.local_decls, *self.tcx).ty + ))?, + place_ty.layout, + )); Ok(place_ty) } @@ -672,7 +664,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) @@ -724,19 +716,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? } @@ -748,7 +740,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)); @@ -787,20 +779,22 @@ where None => return Ok(()), // zero-sized access }; - let tcx = &*self.tcx; + let tcx = *self.tcx; // FIXME: We should check that there are dest.layout.size many bytes available in // memory. The code below is not sufficient, with enough padding it might not // cover all the bytes! match value { Immediate::Scalar(scalar) => { match dest.layout.abi { - layout::Abi::Scalar(_) => {} // fine - _ => { - bug!("write_immediate_to_mplace: invalid Scalar layout: {:#?}", dest.layout) - } + Abi::Scalar(_) => {} // fine + _ => span_bug!( + self.cur_span(), + "write_immediate_to_mplace: invalid Scalar layout: {:#?}", + dest.layout + ), } self.memory.get_raw_mut(ptr.alloc_id)?.write_scalar( - tcx, + &tcx, ptr, scalar, dest.layout.size, @@ -811,8 +805,9 @@ 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), - _ => bug!( + Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value), + _ => span_bug!( + self.cur_span(), "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}", dest.layout ), @@ -825,8 +820,8 @@ where // but that does not work: We could be a newtype around a pair, then the // fields do not match the `ScalarPair` components. - self.memory.get_raw_mut(ptr.alloc_id)?.write_scalar(tcx, ptr, a_val, a_size)?; - self.memory.get_raw_mut(b_ptr.alloc_id)?.write_scalar(tcx, b_ptr, b_val, b_size) + self.memory.get_raw_mut(ptr.alloc_id)?.write_scalar(&tcx, ptr, a_val, a_size)?; + self.memory.get_raw_mut(b_ptr.alloc_id)?.write_scalar(&tcx, b_ptr, b_val, b_size) } } } @@ -860,12 +855,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, src.layout, dest.layout) { + span_bug!( + self.cur_span(), + "type mismatch when copying!\nsrc: {:?},\ndest: {:?}", + src.layout.ty, + dest.layout.ty, + ); + } // Let us see if the layout is simple so we take a shortcut, avoid force_allocation. let src = match self.try_read_immediate(src)? { @@ -915,7 +912,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, src.layout, dest.layout) { // Fast path: Just use normal `copy_op` return self.copy_op(src, dest); } @@ -926,7 +923,11 @@ where // most likey we *are* running `typeck` right now. Investigate whether we can bail out // on `typeck_tables().has_errors` at all const eval entry points. debug!("Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}", src, dest); - throw_unsup!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty)); + self.tcx.sess.delay_span_bug( + self.cur_span(), + "size-changing transmute, should have been caught by transmute checking", + ); + throw_inval!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty)); } // Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want // to avoid that here. @@ -972,14 +973,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)? @@ -995,7 +997,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)) } @@ -1018,8 +1020,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) @@ -1029,10 +1031,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(), @@ -1043,7 +1045,8 @@ where MPlaceTy { mplace, layout } } - pub fn write_discriminant_index( + /// Writes the discriminant of the given variant. + pub fn write_discriminant( &mut self, variant_index: VariantIdx, dest: PlaceTy<'tcx, M::PointerTag>, @@ -1055,17 +1058,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, - discr: ref discr_layout, - discr_index, + Variants::Multiple { + tag_encoding: TagEncoding::Direct, + tag: ref tag_layout, + tag_field, .. } => { // 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; @@ -1073,21 +1076,21 @@ where // raw discriminants for enums are isize or bigger during // their computation, but the in-memory tag is the smallest possible // representation - let size = discr_layout.value.size(self); - let discr_val = truncate(discr_val, size); + let size = tag_layout.value.size(self); + let tag_val = truncate(discr_val, size); - let discr_dest = self.place_field(dest, discr_index as u64)?; - self.write_scalar(Scalar::from_uint(discr_val, size), discr_dest)?; + let tag_dest = self.place_field(dest, tag_field)?; + self.write_scalar(Scalar::from_uint(tag_val, size), tag_dest)?; } - layout::Variants::Multiple { - discr_kind: - layout::DiscriminantKind::Niche { dataful_variant, ref niche_variants, niche_start }, - discr: ref discr_layout, - discr_index, + Variants::Multiple { + tag_encoding: + TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, + tag: ref tag_layout, + tag_field, .. } => { // No need to validate that the discriminant here because the - // `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(); @@ -1096,19 +1099,19 @@ where .checked_sub(variants_start) .expect("overflow computing relative variant idx"); // We need to use machine arithmetic when taking into account `niche_start`: - // discr_val = variant_index_relative + niche_start_val - let discr_layout = self.layout_of(discr_layout.value.to_int_ty(*self.tcx))?; - let niche_start_val = ImmTy::from_uint(niche_start, discr_layout); + // tag_val = variant_index_relative + niche_start_val + let tag_layout = self.layout_of(tag_layout.value.to_int_ty(*self.tcx))?; + let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); let variant_index_relative_val = - ImmTy::from_uint(variant_index_relative, discr_layout); - let discr_val = self.binary_op( + ImmTy::from_uint(variant_index_relative, tag_layout); + let tag_val = self.binary_op( mir::BinOp::Add, variant_index_relative_val, niche_start_val, )?; // Write result. - let niche_dest = self.place_field(dest, discr_index as u64)?; - self.write_immediate(*discr_val, niche_dest)?; + let niche_dest = self.place_field(dest, tag_field)?; + self.write_immediate(*tag_val, niche_dest)?; } } } @@ -1121,8 +1124,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/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs deleted file mode 100644 index ee45179fd8b31..0000000000000 --- a/src/librustc_mir/interpret/snapshot.rs +++ /dev/null @@ -1,420 +0,0 @@ -//! This module contains the machinery necessary to detect infinite loops -//! during const-evaluation by taking snapshots of the state of the interpreter -//! at regular intervals. - -// This lives in `interpret` because it needs access to all sots of private state. However, -// it is not used by the general miri engine, just by CTFE. - -use std::hash::{Hash, Hasher}; - -use rustc::ich::StableHashingContextProvider; -use rustc::mir; -use rustc::mir::interpret::{ - AllocId, Allocation, InterpResult, Pointer, Relocations, Scalar, UndefMask, -}; - -use rustc::ty::layout::{Align, Size}; -use rustc::ty::{self, TyCtxt}; -use rustc_ast::ast::Mutability; -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_index::vec::IndexVec; -use rustc_macros::HashStable; -use rustc_span::source_map::Span; - -use super::eval_context::{LocalState, StackPopCleanup}; -use super::{ - Frame, Immediate, LocalValue, MemPlace, MemPlaceMeta, Memory, Operand, Place, ScalarMaybeUndef, -}; -use crate::const_eval::CompileTimeInterpreter; - -#[derive(Default)] -pub(crate) struct InfiniteLoopDetector<'mir, 'tcx> { - /// The set of all `InterpSnapshot` *hashes* observed by this detector. - /// - /// When a collision occurs in this table, we store the full snapshot in - /// `snapshots`. - hashes: FxHashSet, - - /// The set of all `InterpSnapshot`s observed by this detector. - /// - /// An `InterpSnapshot` will only be fully cloned once it has caused a - /// collision in `hashes`. As a result, the detector must observe at least - /// *two* full cycles of an infinite loop before it triggers. - snapshots: FxHashSet>, -} - -impl<'mir, 'tcx> InfiniteLoopDetector<'mir, 'tcx> { - pub fn observe_and_analyze( - &mut self, - tcx: TyCtxt<'tcx>, - span: Span, - memory: &Memory<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, - stack: &[Frame<'mir, 'tcx>], - ) -> InterpResult<'tcx, ()> { - // Compute stack's hash before copying anything - let mut hcx = tcx.get_stable_hashing_context(); - let mut hasher = StableHasher::new(); - stack.hash_stable(&mut hcx, &mut hasher); - let hash = hasher.finish::(); - - // Check if we know that hash already - if self.hashes.is_empty() { - // FIXME(#49980): make this warning a lint - tcx.sess.span_warn( - span, - "Constant evaluating a complex constant, this might take some time", - ); - } - if self.hashes.insert(hash) { - // No collision - return Ok(()); - } - - // We need to make a full copy. NOW things that to get really expensive. - info!("snapshotting the state of the interpreter"); - - if self.snapshots.insert(InterpSnapshot::new(memory, stack)) { - // Spurious collision or first cycle - return Ok(()); - } - - // Second cycle - throw_exhaust!(InfiniteLoop) - } -} - -trait SnapshotContext<'a> { - fn resolve(&'a self, id: &AllocId) -> Option<&'a Allocation>; -} - -/// Taking a snapshot of the evaluation context produces a view of -/// the state of the interpreter that is invariant to `AllocId`s. -trait Snapshot<'a, Ctx: SnapshotContext<'a>> { - type Item; - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item; -} - -macro_rules! __impl_snapshot_field { - ($field:ident, $ctx:expr) => { - $field.snapshot($ctx) - }; - ($field:ident, $ctx:expr, $delegate:expr) => { - $delegate - }; -} - -// This assumes the type has two type parameters, first for the tag (set to `()`), -// then for the id -macro_rules! impl_snapshot_for { - (enum $enum_name:ident { - $( $variant:ident $( ( $($field:ident $(-> $delegate:expr)?),* ) )? ),* $(,)? - }) => { - - impl<'a, Ctx> self::Snapshot<'a, Ctx> for $enum_name - where Ctx: self::SnapshotContext<'a>, - { - type Item = $enum_name<(), AllocIdSnapshot<'a>>; - - #[inline] - fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item { - match *self { - $( - $enum_name::$variant $( ( $(ref $field),* ) )? => { - $enum_name::$variant $( - ( $( __impl_snapshot_field!($field, __ctx $(, $delegate)?) ),* ) - )? - } - )* - } - } - } - }; - - (struct $struct_name:ident { $($field:ident $(-> $delegate:expr)?),* $(,)? }) => { - impl<'a, Ctx> self::Snapshot<'a, Ctx> for $struct_name - where Ctx: self::SnapshotContext<'a>, - { - type Item = $struct_name<(), AllocIdSnapshot<'a>>; - - #[inline] - fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item { - let $struct_name { - $(ref $field),* - } = *self; - - $struct_name { - $( $field: __impl_snapshot_field!($field, __ctx $(, $delegate)?) ),* - } - } - } - }; -} - -impl<'a, Ctx, T> Snapshot<'a, Ctx> for Option -where - Ctx: SnapshotContext<'a>, - T: Snapshot<'a, Ctx>, -{ - type Item = Option<>::Item>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - match self { - Some(x) => Some(x.snapshot(ctx)), - None => None, - } - } -} - -#[derive(Eq, PartialEq)] -struct AllocIdSnapshot<'a>(Option>); - -impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId -where - Ctx: SnapshotContext<'a>, -{ - type Item = AllocIdSnapshot<'a>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - AllocIdSnapshot(ctx.resolve(self).map(|alloc| alloc.snapshot(ctx))) - } -} - -impl_snapshot_for!(struct Pointer { - alloc_id, - offset -> *offset, // just copy offset verbatim - tag -> *tag, // just copy tag -}); - -impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar -where - Ctx: SnapshotContext<'a>, -{ - type Item = Scalar<(), AllocIdSnapshot<'a>>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - match self { - Scalar::Ptr(p) => Scalar::Ptr(p.snapshot(ctx)), - Scalar::Raw { size, data } => Scalar::Raw { data: *data, size: *size }, - } - } -} - -impl_snapshot_for!( - enum ScalarMaybeUndef { - Scalar(s), - Undef, - } -); - -impl_snapshot_for!( - enum MemPlaceMeta { - Meta(s), - None, - Poison, - } -); - -impl_snapshot_for!(struct MemPlace { - ptr, - meta, - align -> *align, // just copy alignment verbatim -}); - -impl<'a, Ctx> Snapshot<'a, Ctx> for Place -where - Ctx: SnapshotContext<'a>, -{ - type Item = Place<(), AllocIdSnapshot<'a>>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - match self { - Place::Ptr(p) => Place::Ptr(p.snapshot(ctx)), - - Place::Local { frame, local } => Place::Local { frame: *frame, local: *local }, - } - } -} - -impl_snapshot_for!( - enum Immediate { - Scalar(s), - ScalarPair(s, t), - } -); - -impl_snapshot_for!( - enum Operand { - Immediate(v), - Indirect(m), - } -); - -impl_snapshot_for!( - enum LocalValue { - Dead, - Uninitialized, - Live(v), - } -); - -impl<'a, Ctx> Snapshot<'a, Ctx> for Relocations -where - Ctx: SnapshotContext<'a>, -{ - type Item = Relocations<(), AllocIdSnapshot<'a>>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - Relocations::from_presorted( - self.iter().map(|(size, ((), id))| (*size, ((), id.snapshot(ctx)))).collect(), - ) - } -} - -#[derive(Eq, PartialEq)] -struct AllocationSnapshot<'a> { - bytes: &'a [u8], - relocations: Relocations<(), AllocIdSnapshot<'a>>, - undef_mask: &'a UndefMask, - align: &'a Align, - size: &'a Size, - mutability: &'a Mutability, -} - -impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation -where - Ctx: SnapshotContext<'a>, -{ - type Item = AllocationSnapshot<'a>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - let Allocation { size, align, mutability, extra: (), .. } = self; - - let all_bytes = 0..self.len(); - // This 'inspect' is okay since following access respects undef and relocations. This does - // influence interpreter exeuction, but only to detect the error of cycles in evaluation - // dependencies. - let bytes = self.inspect_with_undef_and_ptr_outside_interpreter(all_bytes); - - let undef_mask = self.undef_mask(); - let relocations = self.relocations(); - - AllocationSnapshot { - bytes, - undef_mask, - align, - size, - mutability, - relocations: relocations.snapshot(ctx), - } - } -} - -#[derive(Eq, PartialEq)] -struct FrameSnapshot<'a, 'tcx> { - instance: ty::Instance<'tcx>, - span: Span, - return_to_block: &'a StackPopCleanup, - return_place: Option>>, - locals: IndexVec>>, - block: Option, - stmt: usize, -} - -impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx> -where - Ctx: SnapshotContext<'a>, -{ - type Item = FrameSnapshot<'a, 'tcx>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - let Frame { - body: _, - instance, - span, - return_to_block, - return_place, - locals, - block, - stmt, - extra: _, - } = self; - - FrameSnapshot { - instance: *instance, - span: *span, - return_to_block, - block: *block, - stmt: *stmt, - return_place: return_place.map(|r| r.snapshot(ctx)), - locals: locals.iter().map(|local| local.snapshot(ctx)).collect(), - } - } -} - -impl<'a, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a LocalState<'tcx> -where - Ctx: SnapshotContext<'a>, -{ - type Item = LocalValue<(), AllocIdSnapshot<'a>>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - let LocalState { value, layout: _ } = self; - value.snapshot(ctx) - } -} - -impl<'b, 'mir, 'tcx> SnapshotContext<'b> - for Memory<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> -{ - fn resolve(&'b self, id: &AllocId) -> Option<&'b Allocation> { - self.get_raw(*id).ok() - } -} - -/// The virtual machine state during const-evaluation at a given point in time. -/// We assume the `CompileTimeInterpreter` has no interesting extra state that -/// is worth considering here. -#[derive(HashStable)] -struct InterpSnapshot<'mir, 'tcx> { - // Not hashing memory: Avoid hashing memory all the time during execution - #[stable_hasher(ignore)] - memory: Memory<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, - stack: Vec>, -} - -impl InterpSnapshot<'mir, 'tcx> { - fn new( - memory: &Memory<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, - stack: &[Frame<'mir, 'tcx>], - ) -> Self { - InterpSnapshot { memory: memory.clone(), stack: stack.into() } - } - - // Used to compare two snapshots - fn snapshot(&'b self) -> Vec> { - // Start with the stack, iterate and recursively snapshot - self.stack.iter().map(|frame| frame.snapshot(&self.memory)).collect() - } -} - -impl<'mir, 'tcx> Hash for InterpSnapshot<'mir, 'tcx> { - fn hash(&self, state: &mut H) { - // Implement in terms of hash stable, so that k1 == k2 -> hash(k1) == hash(k2) - let mut hcx = self.memory.tcx.get_stable_hashing_context(); - let mut hasher = StableHasher::new(); - self.hash_stable(&mut hcx, &mut hasher); - hasher.finish::().hash(state) - } -} - -impl<'mir, 'tcx> Eq for InterpSnapshot<'mir, 'tcx> {} - -impl<'mir, 'tcx> PartialEq for InterpSnapshot<'mir, 'tcx> { - fn eq(&self, other: &Self) -> bool { - // FIXME: This looks to be a *ridiculously expensive* comparison operation. - // Doesn't this make tons of copies? Either `snapshot` is very badly named, - // or it does! - self.snapshot() == other.snapshot() - } -} diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index f298a6677d6dc..18f9bbd2e3150 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,7 +69,7 @@ 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) } @@ -79,31 +77,29 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> { info!("{:?}", stmt); - 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(*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 +108,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 +120,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,12 +134,18 @@ 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 { + ThreadLocalRef(did) => { + let id = M::thread_local_alloc_id(self, did)?; + let val = Scalar::Ptr(self.tag_global_base_pointer(id.into())); + self.write_scalar(val, dest)?; + } + Use(ref operand) => { // Avoid recomputing the layout let op = self.eval_operand(operand, Some(dest.layout))?; @@ -177,7 +179,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Aggregate(ref kind, ref operands) => { let (dest, active_field_index) = match **kind { mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => { - self.write_discriminant_index(variant_index, dest)?; + self.write_discriminant(variant_index, dest)?; if adt_def.is_enum() { (self.place_downcast(dest, variant_index)?, active_field_index) } else { @@ -192,7 +194,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 +226,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 { @@ -248,26 +249,25 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } NullaryOp(mir::NullOp::SizeOf, ty) => { - let ty = self.subst_from_frame_and_normalize_erasing_regions(ty); + let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty); let layout = self.layout_of(ty)?; assert!( !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, _) => { + Cast(cast_kind, ref operand, cast_ty) => { let src = self.eval_operand(operand, None)?; - self.cast(src, kind, dest)?; + let cast_ty = self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty); + self.cast(src, cast_kind, cast_ty, 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; - self.write_scalar(Scalar::from_uint(discr_val, size), dest)?; + self.write_scalar(discr_val, dest)?; } } @@ -278,18 +278,11 @@ 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.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 473ed9d13ecfb..4681079a22ddf 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -1,34 +1,36 @@ 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)? } Goto { target } => self.go_to_block(target), - SwitchInt { ref discr, ref values, ref targets, .. } => { + SwitchInt { ref discr, ref values, ref targets, switch_ty } => { let discr = self.read_immediate(self.eval_operand(discr, None)?)?; trace!("SwitchInt({:?})", *discr); + assert_eq!(discr.layout.ty, switch_ty); // 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 +51,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, from_hir_call: _, fn_span: _ } => { + 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 +66,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 - let place = self.eval_place(location)?; + Drop { place, target, unwind } => { + let place = self.eval_place(place)?; let ty = place.layout.ty; - trace!("TerminatorKind::drop: {:?}, type {}", location, ty); + trace!("TerminatorKind::drop: {:?}, type {}", place, 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 } => { @@ -119,12 +124,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // These should never occur for MIR we actually run. DropAndReplace { .. } - | FalseEdges { .. } + | FalseEdge { .. } | 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 +142,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 +158,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, @@ -172,13 +182,19 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("Skipping callee ZST"); return Ok(()); } - let caller_arg = caller_arg.next().ok_or_else(|| err_unsup!(FunctionArgCountMismatch))?; + let caller_arg = caller_arg.next().ok_or_else(|| { + err_ub_format!("calling a function with fewer arguments than it requires") + })?; if rust_abi { assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out"); } // Now, check if !Self::check_argument_compat(rust_abi, caller_arg.layout, callee_arg.layout) { - throw_unsup!(FunctionArgMismatch(caller_arg.layout.ty, callee_arg.layout.ty)) + throw_ub_format!( + "calling a function with argument of type {:?} passing data of type {:?}", + callee_arg.layout.ty, + caller_arg.layout.ty + ) } // We allow some transmutes here self.copy_op_transmute(caller_arg, callee_arg) @@ -188,7 +204,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)>, @@ -211,7 +226,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::FnDef(..) => instance_ty.fn_sig(*self.tcx).abi(), ty::Closure(..) => Abi::RustCall, ty::Generator(..) => Abi::Rust, - _ => bug!("unexpected callee ty: {:?}", instance_ty), + _ => span_bug!(self.cur_span(), "unexpected callee ty: {:?}", instance_ty), } }; let normalize_abi = |abi| match abi { @@ -223,14 +238,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { abi => abi, }; if normalize_abi(caller_abi) != normalize_abi(callee_abi) { - throw_unsup!(FunctionAbiMismatch(caller_abi, callee_abi)) + throw_ub_format!( + "calling a function with ABI {:?} using caller ABI {:?}", + callee_abi, + caller_abi + ) } } match instance.def { ty::InstanceDef::Intrinsic(..) => { assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic); - return M::call_intrinsic(self, span, instance, args, ret, unwind); + M::call_intrinsic(self, instance, args, ret, unwind) } ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) @@ -240,129 +259,126 @@ 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 }, )?; - // We want to pop this frame again in case there was an error, to put - // the blame in the right location. Until the 2018 edition is used in - // the compiler, we have to do this with an immediately invoked function. - let res = - (|| { - trace!( - "caller ABI: {:?}, args: {:#?}", - caller_abi, - args.iter() - .map(|arg| (arg.layout.ty, format!("{:?}", **arg))) - .collect::>() - ); - trace!( - "spread_arg: {:?}, locals: {:#?}", - body.spread_arg, - body.args_iter() - .map(|local| ( - local, - self.layout_of_local(self.frame(), local, None).unwrap().ty - )) - .collect::>() - ); - - // Figure out how to pass which arguments. - // The Rust ABI is special: ZST get skipped. - let rust_abi = match caller_abi { - Abi::Rust | Abi::RustCall => true, - _ => false, + // If an error is raised here, pop the frame again to get an accurate backtrace. + // To this end, we wrap it all in a `try` block. + let res: InterpResult<'tcx> = try { + trace!( + "caller ABI: {:?}, args: {:#?}", + caller_abi, + args.iter() + .map(|arg| (arg.layout.ty, format!("{:?}", **arg))) + .collect::>() + ); + trace!( + "spread_arg: {:?}, locals: {:#?}", + body.spread_arg, + body.args_iter() + .map(|local| ( + local, + self.layout_of_local(self.frame(), local, None).unwrap().ty + )) + .collect::>() + ); + + // Figure out how to pass which arguments. + // The Rust ABI is special: ZST get skipped. + let rust_abi = match caller_abi { + Abi::Rust | Abi::RustCall => true, + _ => false, + }; + // We have two iterators: Where the arguments come from, + // and where they go to. + + // For where they come from: If the ABI is RustCall, we untuple the + // last incoming argument. These two iterators do not have the same type, + // so to keep the code paths uniform we accept an allocation + // (for RustCall ABI only). + let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> = + if caller_abi == Abi::RustCall && !args.is_empty() { + // Untuple + let (&untuple_arg, args) = args.split_last().unwrap(); + trace!("eval_fn_call: Will pass last argument by untupling"); + Cow::from( + args.iter() + .map(|&a| Ok(a)) + .chain( + (0..untuple_arg.layout.fields.count()) + .map(|i| self.operand_field(untuple_arg, i)), + ) + .collect::>>>( + )?, + ) + } else { + // Plain arg passing + Cow::from(args) }; - // We have two iterators: Where the arguments come from, - // and where they go to. - - // For where they come from: If the ABI is RustCall, we untuple the - // last incoming argument. These two iterators do not have the same type, - // so to keep the code paths uniform we accept an allocation - // (for RustCall ABI only). - let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> = - if caller_abi == Abi::RustCall && !args.is_empty() { - // Untuple - let (&untuple_arg, args) = args.split_last().unwrap(); - trace!("eval_fn_call: Will pass last argument by untupling"); - Cow::from(args.iter().map(|&a| Ok(a)) - .chain((0..untuple_arg.layout.fields.count()) - .map(|i| self.operand_field(untuple_arg, i as u64)) - ) - .collect::>>>()?) - } else { - // Plain arg passing - Cow::from(args) - }; - // Skip ZSTs - let mut caller_iter = caller_args - .iter() - .filter(|op| !rust_abi || !op.layout.is_zst()) - .copied(); - - // Now we have to spread them out across the callee's locals, - // taking into account the `spread_arg`. If we could write - // this is a single iterator (that handles `spread_arg`), then - // `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))?; - 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)?; - self.pass_argument(rust_abi, &mut caller_iter, dest)?; - } - } else { - // Normal argument + // Skip ZSTs + let mut caller_iter = + caller_args.iter().filter(|op| !rust_abi || !op.layout.is_zst()).copied(); + + // Now we have to spread them out across the callee's locals, + // taking into account the `spread_arg`. If we could write + // this is a single iterator (that handles `spread_arg`), then + // `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))?; + 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)?; self.pass_argument(rust_abi, &mut caller_iter, dest)?; } + } else { + // Normal argument + self.pass_argument(rust_abi, &mut caller_iter, dest)?; } - // Now we should have no more caller args - if caller_iter.next().is_some() { - trace!("Caller has passed too many args"); - throw_unsup!(FunctionArgCountMismatch) + } + // Now we should have no more caller args + if caller_iter.next().is_some() { + throw_ub_format!("calling a function with more arguments than it expected") + } + // Don't forget to check the return type! + if let Some((caller_ret, _)) = ret { + let callee_ret = self.eval_place(mir::Place::return_place())?; + if !Self::check_argument_compat( + rust_abi, + caller_ret.layout, + callee_ret.layout, + ) { + throw_ub_format!( + "calling a function with return type {:?} passing \ + return place of type {:?}", + callee_ret.layout.ty, + caller_ret.layout.ty + ) } - // Don't forget to check the return type! - if let Some((caller_ret, _)) = ret { - let callee_ret = self.eval_place(&mir::Place::return_place())?; - if !Self::check_argument_compat( - rust_abi, - caller_ret.layout, - callee_ret.layout, - ) { - throw_unsup!(FunctionRetMismatch( - caller_ret.layout.ty, - callee_ret.layout.ty - )) - } - } else { - let local = mir::RETURN_PLACE; - let callee_layout = self.layout_of_local(self.frame(), local, None)?; - if !callee_layout.abi.is_uninhabited() { - throw_unsup!(FunctionRetMismatch( - self.tcx.types.never, - callee_layout.ty - )) - } + } else { + let local = mir::RETURN_PLACE; + let callee_layout = self.layout_of_local(self.frame(), local, None)?; + if !callee_layout.abi.is_uninhabited() { + throw_ub_format!("calling a returning function without a return place") } - Ok(()) - })(); + } + }; match res { Err(err) => { - self.stack.pop(); + self.stack_mut().pop(); Err(err) } - Ok(v) => Ok(v), + Ok(()) => Ok(()), } } // cannot use the shim here, because that will only result in infinite recursion @@ -384,7 +400,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 @@ -394,10 +410,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) } } } @@ -406,7 +422,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> { @@ -424,17 +439,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..a1d124bb7602e 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, 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. /// @@ -47,44 +49,44 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let size = layout.size.bytes(); let align = layout.align.abi.bytes(); + let tcx = *self.tcx; let ptr_size = self.pointer_size(); - let ptr_align = self.tcx.data_layout.pointer_align.abi; + let ptr_align = tcx.data_layout.pointer_align.abi; // ///////////////////////////////////////////////////////////////////////////////////////// // If you touch this code, be sure to also make the corresponding changes to // `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, ); - let tcx = &*self.tcx; - let drop = Instance::resolve_drop_in_place(*tcx, ty); + let drop = Instance::resolve_drop_in_place(tcx, ty); let drop = self.memory.create_fn_alloc(FnVal::Instance(drop)); // No need to do any alignment checks on the memory accesses below, because we know the // allocation is correctly aligned as we created it above. Also we're only offsetting by // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`. let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?; - vtable_alloc.write_ptr_sized(tcx, vtable, drop.into())?; + vtable_alloc.write_ptr_sized(&tcx, vtable, drop.into())?; - let size_ptr = vtable.offset(ptr_size, tcx)?; - vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?; - let align_ptr = vtable.offset(ptr_size * 2, tcx)?; - vtable_alloc.write_ptr_sized(tcx, align_ptr, Scalar::from_uint(align, ptr_size).into())?; + let size_ptr = vtable.offset(ptr_size, &tcx)?; + vtable_alloc.write_ptr_sized(&tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?; + let align_ptr = vtable.offset(ptr_size * 2, &tcx)?; + vtable_alloc.write_ptr_sized(&tcx, align_ptr, Scalar::from_uint(align, ptr_size).into())?; for (i, method) in methods.iter().enumerate() { if let Some((def_id, substs)) = *method { // resolve for vtable: insert shims where needed let instance = - ty::Instance::resolve_for_vtable(*tcx, self.param_env, def_id, substs) + ty::Instance::resolve_for_vtable(tcx, self.param_env, def_id, substs) .ok_or_else(|| err_inval!(TooGeneric))?; let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); // We cannot use `vtable_allic` as we are creating fn ptrs in this loop. - let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?; + let method_ptr = vtable.offset(ptr_size * (3 + i as u64), &tcx)?; self.memory.get_raw_mut(vtable.alloc_id)?.write_ptr_sized( - tcx, + &tcx, method_ptr, fn_ptr.into(), )?; @@ -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,12 +166,12 @@ 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() { + if size >= self.tcx.data_layout.obj_size_bound() { throw_ub_format!( "invalid vtable: \ size is bigger than largest supported object" diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 05bb010959b32..3bb9ba3712058 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); + ($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_); } - write!(&mut msg, ", but expected {}", $details).unwrap(); - throw_unsup!(ValidationFailure(msg)) - }}; - ($what:expr, $where:expr) => {{ - let mut msg = format!("encountered {}", $what); - let where_ = &$where; - if !where_.is_empty() { - msg.push_str(" at "); - write_path(&mut msg, where_); - } - throw_unsup!(ValidationFailure(msg)) + $( + 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) => {{ - 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) => {{ + ($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), + // 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,12 +204,12 @@ 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, .. } => { - if discr_index == field { + Variants::Multiple { tag_field, .. } => { + if tag_field == field { return match layout.ty.kind { ty::Adt(def, ..) if def.is_enum() => PathElem::EnumTag, ty::Generator(..) => PathElem::GeneratorTag, @@ -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.closure_captures.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,88 +363,82 @@ 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_unsup!(InvalidNullPointerUsage) => { - throw_validation_failure!(format_args!("a NULL {}", kind), self.path) - } - err_unsup!(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_unsup!(PointerOutOfBounds { .. }) | err_unsup!(DanglingPointerDeref) => { - throw_validation_failure!( - format_args!("a dangling {} (not entirely in bounds)", 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 { + assert!(!self.ecx.tcx.is_thread_local_static(did)); + // 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! @@ -437,12 +474,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 scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" }, + ); Ok(true) } ty::Float(_) | ty::Int(_) | ty::Uint(_) => { @@ -453,10 +498,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 { @@ -467,11 +510,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)?; @@ -490,14 +534,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) @@ -515,13 +561,12 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M | ty::Generator(..) => Ok(false), // Some types only occur during typechecking, they have no layout. // We should not see them here and we could not check them anyway. - ty::Error + ty::Error(_) | ty::Infer(..) | ty::Placeholder(..) | ty::Bound(..) | ty::Param(..) | ty::Opaque(..) - | ty::UnnormalizedProjection(..) | ty::Projection(..) | ty::GeneratorWitness(..) => bug!("Encountered invalid type {:?}", ty), } @@ -530,7 +575,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; @@ -547,35 +592,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) - ) + } ) } } @@ -585,16 +628,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>; @@ -632,10 +674,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(()) } @@ -651,19 +694,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!(InvalidTag(val)) => + { "{}", val } expected { "a valid enum tag" }, + 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 @@ -676,22 +714,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. } } @@ -707,10 +744,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) @@ -738,7 +776,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)?; @@ -763,18 +801,22 @@ 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_unsup!(ReadUndefBytes(offset)) => { - // Some byte was undefined, determine which + err_ub!(InvalidUninitBytes(Some(access))) => { + // Some byte was uninitialized, determine which // element that byte belongs to so we can // provide an index. - let i = (offset.bytes() / layout.size.bytes()) as usize; + let i = usize::try_from( + access.uninit_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), } } @@ -795,7 +837,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>, @@ -817,11 +859,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Run it. match visitor.visit_value(op) { Ok(()) => Ok(()), - Err(err) if matches!(err.kind, err_unsup!(ValidationFailure { .. })) => Err(err), - Err(err) if cfg!(debug_assertions) => { - bug!("Unexpected error during validation: {}", err) + // Pass through validation failures. + Err(err) if matches!(err.kind, err_ub!(ValidationFailure { .. })) => Err(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); } - Err(err) => Err(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 284dd74ce99d4..928d5bf88f2fc 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -9,24 +9,32 @@ Rust MIR: a lowered representation of Rust. #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] +#![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; @@ -37,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); @@ -48,10 +56,6 @@ pub fn provide(providers: &mut Providers<'_>) { providers.const_eval_validated = const_eval::const_eval_validated_provider; providers.const_eval_raw = const_eval::const_eval_raw_provider; providers.const_caller_location = const_eval::const_caller_location; - providers.const_field = |tcx, param_env_and_value| { - let (param_env, (value, field)) = param_env_and_value.into_parts(); - const_eval::const_field(tcx, param_env, None, field, value) - }; providers.destructure_const = |tcx, param_env_and_value| { let (param_env, value) = param_env_and_value.into_parts(); const_eval::destructure_const(tcx, param_env, value) diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 862a7ef1e73c0..f9b3c319c1f66 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -1,7 +1,7 @@ //! Mono Item Collection //! ==================== //! -//! This module is responsible for discovering all items that will contribute to +//! This module is responsible for discovering all items that will contribute //! to code generation of the crate. The important part here is that it not only //! needs to find syntax-level items (functions, structs, etc) but also all //! their monomorphized instantiations. Every non-generic, non-const function @@ -79,7 +79,7 @@ //! function or method call (represented by a CALL terminator in MIR). But //! calls are not the only thing that might introduce a reference between two //! function mono items, and as we will see below, they are just a -//! specialized of the form described next, and consequently will don't get any +//! specialization of the form described next, and consequently will not get any //! special treatment in the algorithm. //! //! #### Taking a reference to a function or method @@ -158,7 +158,7 @@ //! - Eager mode is meant to be used in conjunction with incremental compilation //! where a stable set of mono items is more important than a minimal //! one. Thus, eager mode will instantiate drop-glue for every drop-able type -//! in the crate, even of no drop call for that type exists (yet). It will +//! in the crate, even if no drop call for that type exists (yet). It will //! also instantiate default implementations of trait methods, something that //! otherwise is only done on demand. //! @@ -176,24 +176,26 @@ 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::session::config::EntryFnType; -use rustc::ty::adjustment::{CustomCoerceUnsized, PointerCast}; -use rustc::ty::print::obsolete::DefPathBasedNames; -use rustc::ty::subst::{InternalSubsts, SubstsRef}; -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, FatalError}; 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 rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP}; use smallvec::SmallVec; use std::iter; @@ -293,7 +295,13 @@ pub fn collect_crate_mono_items( tcx.sess.time("monomorphization_collector_graph_walk", || { par_iter(roots).for_each(|root| { let mut recursion_depths = DefIdMap::default(); - collect_items_rec(tcx, root, visited, &mut recursion_depths, inlining_map); + collect_items_rec( + tcx, + dummy_spanned(root), + visited, + &mut recursion_depths, + inlining_map, + ); }); }); } @@ -322,29 +330,30 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec( tcx: TyCtxt<'tcx>, - starting_point: MonoItem<'tcx>, + starting_point: Spanned>, visited: MTRef<'_, MTLock>>>, recursion_depths: &mut DefIdMap, inlining_map: MTRef<'_, MTLock>>, ) { - if !visited.lock_mut().insert(starting_point.clone()) { + if !visited.lock_mut().insert(starting_point.node) { // We've been here already, no need to search again. return; } - debug!("BEGIN collect_items_rec({})", starting_point.to_string(tcx, true)); + debug!("BEGIN collect_items_rec({})", starting_point.node.to_string(tcx, true)); let mut neighbors = Vec::new(); let recursion_depth_reset; - match starting_point { + match starting_point.node { MonoItem::Static(def_id) => { let instance = Instance::mono(tcx, def_id); @@ -352,7 +361,7 @@ fn collect_items_rec<'tcx>( debug_assert!(should_monomorphize_locally(tcx, &instance)); let ty = instance.monomorphic_ty(tcx); - visit_drop_use(tcx, ty, true, &mut neighbors); + visit_drop_use(tcx, ty, true, starting_point.span, &mut neighbors); recursion_depth_reset = None; @@ -365,17 +374,20 @@ fn collect_items_rec<'tcx>( debug_assert!(should_monomorphize_locally(tcx, &instance)); // Keep track of the monomorphization recursion depth - recursion_depth_reset = Some(check_recursion_limit(tcx, instance, recursion_depths)); + recursion_depth_reset = + Some(check_recursion_limit(tcx, instance, starting_point.span, 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; } } - record_accesses(tcx, starting_point, &neighbors[..], inlining_map); + record_accesses(tcx, starting_point.node, neighbors.iter().map(|i| &i.node), inlining_map); for neighbour in neighbors { collect_items_rec(tcx, neighbour, visited, recursion_depths, inlining_map); @@ -385,13 +397,13 @@ fn collect_items_rec<'tcx>( recursion_depths.insert(def_id, depth); } - debug!("END collect_items_rec({})", starting_point.to_string(tcx, true)); + debug!("END collect_items_rec({})", starting_point.node.to_string(tcx, true)); } -fn record_accesses<'tcx>( +fn record_accesses<'a, 'tcx: 'a>( tcx: TyCtxt<'tcx>, caller: MonoItem<'tcx>, - callees: &[MonoItem<'tcx>], + callees: impl Iterator>, inlining_map: MTRef<'_, MTLock>>, ) { let is_inlining_candidate = |mono_item: &MonoItem<'tcx>| { @@ -402,7 +414,7 @@ fn record_accesses<'tcx>( // FIXME: Call `is_inlining_candidate` when pushing to `neighbors` in `collect_items_rec` // instead to avoid creating this `SmallVec`. let accesses: SmallVec<[_; 128]> = - callees.iter().map(|mono_item| (*mono_item, is_inlining_candidate(mono_item))).collect(); + callees.map(|mono_item| (*mono_item, is_inlining_candidate(mono_item))).collect(); inlining_map.lock_mut().record_accesses(caller, &accesses); } @@ -410,6 +422,7 @@ fn record_accesses<'tcx>( fn check_recursion_limit<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, + span: Span, recursion_depths: &mut DefIdMap, ) -> (DefId, usize) { let def_id = instance.def_id(); @@ -427,13 +440,15 @@ fn check_recursion_limit<'tcx>( // Code that needs to instantiate the same function recursively // more than the recursion limit is assumed to be causing an // infinite expansion. - if adjusted_recursion_depth > *tcx.sess.recursion_limit.get() { + if !tcx.sess.recursion_limit().value_within_limit(adjusted_recursion_depth) { let error = format!("reached the recursion limit while instantiating `{}`", instance); - if let Some(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); - } + let mut err = tcx.sess.struct_span_fatal(span, &error); + err.span_note( + tcx.def_span(def_id), + &format!("`{}` defined here", tcx.def_path_str(def_id)), + ); + err.emit(); + FatalError.raise(); } recursion_depths.insert(def_id, recursion_depth + 1); @@ -442,9 +457,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 @@ -452,12 +474,7 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { // which means that rustc basically hangs. // // 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 !tcx.sess.type_length_limit().value_within_limit(type_length) { // 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. @@ -492,14 +509,30 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { struct MirNeighborCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, - output: &'a mut Vec>, - param_substs: SubstsRef<'tcx>, + output: &'a mut Vec>>, + instance: Instance<'tcx>, +} + +impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { + pub fn monomorphize(&self, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + debug!("monomorphize: self.instance={:?}", self.instance); + if let Some(substs) = self.instance.substs_for_mir_body() { + self.tcx.subst_and_normalize_erasing_regions(substs, ty::ParamEnv::reveal_all(), &value) + } else { + self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), value) + } + } } impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { debug!("visiting rvalue {:?}", *rvalue); + let span = self.body.source_info(location).span; + match *rvalue { // When doing an cast from a regular pointer to a fat pointer, we // have to instantiate all methods of the trait being cast to, so we @@ -509,17 +542,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { ref operand, target_ty, ) => { - let target_ty = self.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &target_ty, - ); + let target_ty = self.monomorphize(target_ty); let source_ty = operand.ty(self.body, self.tcx); - let source_ty = self.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &source_ty, - ); + let source_ty = self.monomorphize(source_ty); let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.tcx, source_ty, target_ty); // This could also be a different Unsize instruction, like @@ -530,6 +555,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.tcx, target_ty, source_ty, + span, self.output, ); } @@ -540,12 +566,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { _, ) => { let fn_ty = operand.ty(self.body, self.tcx); - let fn_ty = self.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &fn_ty, - ); - visit_fn_use(self.tcx, fn_ty, false, &mut self.output); + let fn_ty = self.monomorphize(fn_ty); + visit_fn_use(self.tcx, fn_ty, false, span, &mut self.output); } mir::Rvalue::Cast( mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)), @@ -553,11 +575,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { _, ) => { let source_ty = operand.ty(self.body, self.tcx); - let source_ty = self.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &source_ty, - ); + let source_ty = self.monomorphize(source_ty); match source_ty.kind { ty::Closure(def_id, substs) => { let instance = Instance::resolve_closure( @@ -567,7 +585,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { ty::ClosureKind::FnOnce, ); if should_monomorphize_locally(self.tcx, &instance) { - self.output.push(create_fn_mono_item(instance)); + self.output.push(create_fn_mono_item(instance, span)); } } _ => bug!(), @@ -575,13 +593,19 @@ 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)); + self.output.push(create_fn_mono_item(instance, span)); + } + } + mir::Rvalue::ThreadLocalRef(def_id) => { + assert!(self.tcx.is_thread_local_static(def_id)); + let instance = Instance::mono(self.tcx, def_id); + if should_monomorphize_locally(self.tcx, &instance) { + trace!("collecting thread-local static {:?}", def_id); + self.output.push(respan(span, MonoItem::Static(def_id))); } } _ => { /* not interesting */ } @@ -593,34 +617,61 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) { debug!("visiting const {:?} @ {:?}", *constant, location); - collect_const(self.tcx, *constant, self.param_substs, self.output); + let substituted_constant = self.monomorphize(*constant); + let param_env = ty::ParamEnv::reveal_all(); + + match substituted_constant.val { + ty::ConstKind::Value(val) => collect_const_value(self.tcx, val, self.output), + 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(ErrorReported) | ErrorHandled::Linted) => {} + Err(ErrorHandled::TooGeneric) => span_bug!( + self.tcx.def_span(def_id), + "collection encountered polymorphic constant", + ), + } + } + _ => {} + } self.super_const(constant); } - fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) { - debug!("visiting terminator {:?} @ {:?}", kind, location); + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + debug!("visiting terminator {:?} @ {:?}", terminator, location); + let source = self.body.source_info(location).span; let tcx = self.tcx; - match *kind { + match terminator.kind { mir::TerminatorKind::Call { ref func, .. } => { let callee_ty = func.ty(self.body, tcx); - let callee_ty = tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &callee_ty, - ); - visit_fn_use(self.tcx, callee_ty, true, &mut self.output); + let callee_ty = self.monomorphize(callee_ty); + visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output); } - mir::TerminatorKind::Drop { ref location, .. } - | mir::TerminatorKind::DropAndReplace { ref location, .. } => { - let ty = location.ty(self.body, self.tcx).ty; - let ty = tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - &ty, - ); - visit_drop_use(self.tcx, ty, true, self.output); + mir::TerminatorKind::Drop { ref place, .. } + | mir::TerminatorKind::DropAndReplace { ref place, .. } => { + let ty = place.ty(self.body, self.tcx).ty; + let ty = self.monomorphize(ty); + visit_drop_use(self.tcx, ty, true, source, self.output); + } + mir::TerminatorKind::InlineAsm { ref operands, .. } => { + for op in operands { + match *op { + mir::InlineAsmOperand::SymFn { ref value } => { + let fn_ty = self.monomorphize(value.literal.ty); + visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output); + } + mir::InlineAsmOperand::SymStatic { def_id } => { + let instance = Instance::mono(self.tcx, def_id); + if should_monomorphize_locally(self.tcx, &instance) { + trace!("collecting asm sym static {:?}", def_id); + self.output.push(respan(source, MonoItem::Static(def_id))); + } + } + _ => {} + } + } } mir::TerminatorKind::Goto { .. } | mir::TerminatorKind::SwitchInt { .. } @@ -631,14 +682,14 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { | mir::TerminatorKind::Assert { .. } => {} mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Yield { .. } - | mir::TerminatorKind::FalseEdges { .. } + | mir::TerminatorKind::FalseEdge { .. } | mir::TerminatorKind::FalseUnwind { .. } => bug!(), } - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } - fn visit_place_base( + fn visit_local( &mut self, _place_local: &Local, _context: mir::visit::PlaceContext, @@ -651,23 +702,28 @@ fn visit_drop_use<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, is_direct_call: bool, - output: &mut Vec>, + source: Span, + output: &mut Vec>>, ) { let instance = Instance::resolve_drop_in_place(tcx, ty); - visit_instance_use(tcx, instance, is_direct_call, output); + visit_instance_use(tcx, instance, is_direct_call, source, output); } fn visit_fn_use<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, is_direct_call: bool, - output: &mut Vec>, + source: Span, + 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(); - visit_instance_use(tcx, instance, is_direct_call, output); + 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, source, output); } } @@ -675,7 +731,8 @@ fn visit_instance_use<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, is_direct_call: bool, - output: &mut Vec>, + source: Span, + output: &mut Vec>>, ) { debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); if !should_monomorphize_locally(tcx, &instance) { @@ -691,7 +748,7 @@ fn visit_instance_use<'tcx>( ty::InstanceDef::DropGlue(_, None) => { // Don't need to emit noop drop glue if we are calling directly. if !is_direct_call { - output.push(create_fn_mono_item(instance)); + output.push(create_fn_mono_item(instance, source)); } } ty::InstanceDef::DropGlue(_, Some(_)) @@ -701,7 +758,7 @@ fn visit_instance_use<'tcx>( | ty::InstanceDef::Item(..) | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::CloneShim(..) => { - output.push(create_fn_mono_item(instance)); + output.push(create_fn_mono_item(instance, source)); } } } @@ -745,7 +802,7 @@ fn should_monomorphize_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx bug!("cannot create local mono-item for {:?}", def_id) } - return true; + true } /// For a given pair of source and target type that occur in an unsizing coercion, @@ -793,7 +850,6 @@ fn find_vtable_types_for_unsizing<'tcx>( let ptr_vtable = |inner_source: Ty<'tcx>, inner_target: Ty<'tcx>| { let param_env = ty::ParamEnv::reveal_all(); let type_has_metadata = |ty: Ty<'tcx>| -> bool { - use rustc_span::DUMMY_SP; if ty.is_sized(tcx.at(DUMMY_SP), param_env) { return false; } @@ -812,8 +868,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) } @@ -848,9 +903,9 @@ fn find_vtable_types_for_unsizing<'tcx>( } } -fn create_fn_mono_item(instance: Instance<'_>) -> MonoItem<'_> { +fn create_fn_mono_item(instance: Instance<'_>, source: Span) -> Spanned> { debug!("create_fn_mono_item(instance={})", instance); - MonoItem::Fn(instance) + respan(source, MonoItem::Fn(instance)) } /// Creates a `MonoItem` for each method that is referenced by the vtable for @@ -859,7 +914,8 @@ fn create_mono_items_for_vtable_methods<'tcx>( tcx: TyCtxt<'tcx>, trait_ty: Ty<'tcx>, impl_ty: Ty<'tcx>, - output: &mut Vec>, + source: Span, + output: &mut Vec>>, ) { assert!( !trait_ty.needs_subst() @@ -889,12 +945,12 @@ fn create_mono_items_for_vtable_methods<'tcx>( .unwrap() }) .filter(|&instance| should_monomorphize_locally(tcx, &instance)) - .map(|instance| create_fn_mono_item(instance)); + .map(|item| create_fn_mono_item(item, source)); output.extend(methods); } // Also add the destructor. - visit_drop_use(tcx, impl_ty, false, output); + visit_drop_use(tcx, impl_ty, false, source, output); } } @@ -905,8 +961,8 @@ fn create_mono_items_for_vtable_methods<'tcx>( struct RootCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, mode: MonoItemCollectionMode, - output: &'a mut Vec>, - entry_fn: Option<(DefId, EntryFnType)>, + output: &'a mut Vec>>, + entry_fn: Option<(LocalDefId, EntryFnType)>, } impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { @@ -940,9 +996,9 @@ 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); - visit_drop_use(self.tcx, ty, true, self.output); + let ty = Instance::new(def_id.to_def_id(), InternalSubsts::empty()) + .monomorphic_ty(self.tcx); + visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output); } } } @@ -951,12 +1007,12 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { "RootCollector: ItemKind::GlobalAsm({})", def_id_to_string(self.tcx, self.tcx.hir().local_def_id(item.hir_id)) ); - self.output.push(MonoItem::GlobalAsm(item.hir_id)); + self.output.push(dummy_spanned(MonoItem::GlobalAsm(item.hir_id))); } hir::ItemKind::Static(..) => { let def_id = self.tcx.hir().local_def_id(item.hir_id); debug!("RootCollector: ItemKind::Static({})", def_id_to_string(self.tcx, def_id)); - self.output.push(MonoItem::Static(def_id)); + self.output.push(dummy_spanned(MonoItem::Static(def_id.to_def_id()))); } hir::ItemKind::Const(..) => { // const items only generate mono items if they are @@ -965,7 +1021,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); } } @@ -982,18 +1038,15 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { } fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) { - match ii.kind { - hir::ImplItemKind::Method(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, @@ -1011,12 +1064,12 @@ 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); - self.output.push(create_fn_mono_item(instance)); + let instance = Instance::mono(self.tcx, def_id.to_def_id()); + self.output.push(create_fn_mono_item(instance, DUMMY_SP)); } } @@ -1050,13 +1103,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)); + self.output.push(create_fn_mono_item(start_instance, DUMMY_SP)); } } -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) } @@ -1064,7 +1118,7 @@ fn item_requires_monomorphization(tcx: TyCtxt<'_>, def_id: DefId) -> bool { fn create_mono_items_for_default_impls<'tcx>( tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>, - output: &mut Vec>, + output: &mut Vec>>, ) { match item.kind { hir::ItemKind::Impl { ref generics, ref items, .. } => { @@ -1088,9 +1142,9 @@ fn create_mono_items_for_default_impls<'tcx>( let param_env = ty::ParamEnv::reveal_all(); let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref); let overridden_methods: FxHashSet<_> = - items.iter().map(|iiref| iiref.ident.modern()).collect(); + items.iter().map(|iiref| iiref.ident.normalize_to_macros_2_0()).collect(); for method in tcx.provided_trait_methods(trait_ref.def_id) { - if overridden_methods.contains(&method.ident.modern()) { + if overridden_methods.contains(&method.ident.normalize_to_macros_2_0()) { continue; } @@ -1105,11 +1159,13 @@ 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) + let mono_item = create_fn_mono_item(instance, DUMMY_SP); + if mono_item.node.is_instantiable(tcx) + && should_monomorphize_locally(tcx, &instance) { output.push(mono_item); } @@ -1121,29 +1177,34 @@ 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)) => { +fn collect_miri<'tcx>( + tcx: TyCtxt<'tcx>, + alloc_id: AllocId, + output: &mut Vec>>, +) { + match tcx.global_alloc(alloc_id) { + GlobalAlloc::Static(def_id) => { + assert!(!tcx.is_thread_local_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)); + output.push(dummy_spanned(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)); + output.push(create_fn_mono_item(fn_instance, DUMMY_SP)); } } - None => bug!("alloc id without corresponding allocation: {}", alloc_id), } } @@ -1151,53 +1212,25 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut Vec( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, - output: &mut Vec>, + output: &mut Vec>>, ) { debug!("collect_neighbours: {:?}", instance.def_id()); let body = tcx.instance_mir(instance.def); - MirNeighborCollector { tcx, body: &body, output, param_substs: instance.substs } - .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 } -fn collect_const<'tcx>( - tcx: TyCtxt<'tcx>, - constant: &'tcx ty::Const<'tcx>, - param_substs: SubstsRef<'tcx>, - output: &mut Vec>, -) { - debug!("visiting const {:?}", constant); - - let param_env = ty::ParamEnv::reveal_all(); - let substituted_constant = - tcx.subst_and_normalize_erasing_regions(param_substs, param_env, &constant); - - match substituted_constant.val { - ty::ConstKind::Value(val) => collect_const_value(tcx, val, output), - ty::ConstKind::Unevaluated(def_id, substs, promoted) => { - match tcx.const_eval_resolve(param_env, def_id, substs, promoted, None) { - Ok(val) => collect_const_value(tcx, val, output), - Err(ErrorHandled::Reported) => {} - Err(ErrorHandled::TooGeneric) => { - span_bug!(tcx.def_span(def_id), "collection encountered polymorphic constant",) - } - } - } - _ => {} - } -} - fn collect_const_value<'tcx>( tcx: TyCtxt<'tcx>, value: ConstValue<'tcx>, - output: &mut Vec>, + output: &mut Vec>>, ) { match value { ConstValue::Scalar(Scalar::Ptr(ptr)) => collect_miri(tcx, ptr.alloc_id, output), diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs index 7177bf726d403..76c1c465a8be0 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,11 +20,12 @@ 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, .. })) => { - tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap() - } - vtable => { - bug!("invalid `CoerceUnsized` vtable: {:?}", vtable); + Ok(traits::ImplSourceUserDefined(traits::ImplSourceUserDefinedData { + impl_def_id, + .. + })) => tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap(), + impl_source => { + bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); } } } diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 9b81d69ce694c..a945c1d626a9a 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 }; @@ -480,6 +470,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 +481,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 +748,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 +772,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 +882,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 +920,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 +943,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 +991,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 +1006,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 1f7db2861a2eb..8327affd982ed 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -1,14 +1,15 @@ -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}; 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}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use rustc_target::spec::abi::Abi; use std::fmt; @@ -26,23 +27,24 @@ 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 { ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance), - ty::InstanceDef::VtableShim(def_id) => build_call_shim( - tcx, - instance, - Some(Adjustment::DerefMove), - CallKind::Direct(def_id), - None, - ), + ty::InstanceDef::VtableShim(def_id) => { + build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id), None) + } ty::InstanceDef::FnPtrShim(def_id, ty) => { + // FIXME(eddyb) support generating shims for a "shallow type", + // e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic + // `Foo` or `[String]` etc. + assert!(!ty.needs_subst()); + 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, @@ -54,7 +56,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx let sig = tcx.erase_late_bound_regions(&ty.fn_sig(tcx)); let arg_tys = sig.inputs(); - build_call_shim(tcx, instance, Some(adjustment), CallKind::Indirect, Some(arg_tys)) + build_call_shim(tcx, instance, Some(adjustment), CallKind::Indirect(ty), Some(arg_tys)) } // We are generating a call back to our def-id, which the // codegen backend knows to turn to an actual call, be it @@ -65,11 +67,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; @@ -81,17 +83,21 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx None, ) } - ty::InstanceDef::DropGlue(def_id, ty) => build_drop_shim(tcx, def_id, ty), + ty::InstanceDef::DropGlue(def_id, ty) => { + // FIXME(eddyb) support generating shims for a "shallow type", + // e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic + // `Foo` or `[String]` etc. + assert!(!ty.needs_subst()); + + build_drop_shim(tcx, def_id, ty) + } ty::InstanceDef::CloneShim(def_id, ty) => { - let name = tcx.item_name(def_id); - if name == sym::clone { - build_clone_shim(tcx, def_id, ty) - } else if name == sym::clone_from { - debug!("make_shim({:?}: using default trait implementation", instance); - return tcx.optimized_mir(def_id); - } else { - bug!("builtin clone shim {:?} not supported", instance) - } + // FIXME(eddyb) support generating shims for a "shallow type", + // e.g. `Foo<_>` or `[_]` instead of requiring a fully monomorphic + // `Foo` or `[String]` etc. + assert!(!ty.needs_subst()); + + build_clone_shim(tcx, def_id, ty) } ty::InstanceDef::Virtual(..) => { bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance) @@ -108,62 +114,57 @@ 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)] enum Adjustment { + /// Pass the receiver as-is. Identity, + + /// We get passed `&[mut] self` and call the target with `*self`. + /// + /// This either copies `self` (if `Self: Copy`, eg. for function items), or moves out of it + /// (for `VtableShim`, which effectively is passed `&own Self`). Deref, - DerefMove, + + /// We get passed `self: Self` and call the target with `&mut self`. + /// + /// In this case we need to ensure that the `Self` is dropped after the call, as the callee + /// won't do it for us. RefMut, } #[derive(Copy, Clone, Debug, PartialEq)] -enum CallKind { - Indirect, - Direct(DefId), -} +enum CallKind<'tcx> { + /// Call the `FnPtr` that was passed as the receiver. + Indirect(Ty<'tcx>), -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, - } + /// Call a known `FnDef`. + Direct(DefId), } 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 @@ -181,7 +182,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); @@ -195,9 +196,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. @@ -221,7 +220,7 @@ fn build_drop_shim<'tcx>( elaborate_drops::elaborate_drop( &mut elaborator, source_info, - &dropee, + dropee, (), return_block, elaborate_drops::Unwind::To(resume_block), @@ -287,7 +286,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> { @@ -311,17 +321,13 @@ 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); let mut builder = CloneShimBuilder::new(tcx, def_id, self_ty); - let is_copy = self_ty.is_copy_modulo_regions(tcx, param_env, builder.span); + let is_copy = self_ty.is_copy_modulo_regions(tcx.at(builder.span), param_env); let dest = Place::return_place(); let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0))); @@ -332,14 +338,14 @@ fn build_clone_shim<'tcx>( let len = len.eval_usize(tcx, param_env); builder.array_shim(dest, src, ty, len) } - ty::Closure(def_id, substs) => { - builder.tuple_like_shim(dest, src, substs.as_closure().upvar_tys(def_id, tcx)) + ty::Closure(_, substs) => { + builder.tuple_like_shim(dest, src, substs.as_closure().upvar_tys()) } ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()), _ => 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> { @@ -376,7 +382,7 @@ impl CloneShimBuilder<'tcx> { } fn source_info(&self) -> SourceInfo { - SourceInfo { span: self.span, scope: OUTERMOST_SOURCE_SCOPE } + SourceInfo::outermost(self.span) } fn block( @@ -416,7 +422,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( @@ -459,6 +469,7 @@ impl CloneShimBuilder<'tcx> { destination: Some((dest, next)), cleanup: Some(cleanup), from_hir_call: true, + fn_span: self.span, }, false, ); @@ -500,7 +511,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 @@ -529,7 +540,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)); @@ -555,7 +566,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))), @@ -580,7 +591,7 @@ impl CloneShimBuilder<'tcx> { self.block( vec![], TerminatorKind::Drop { - location: self.tcx.mk_place_index(dest, beg), + place: self.tcx.mk_place_index(dest, beg), target: BasicBlock::new(8), unwind: None, }, @@ -611,9 +622,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); @@ -624,7 +635,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() { @@ -632,7 +643,7 @@ impl CloneShimBuilder<'tcx> { self.block( vec![], TerminatorKind::Drop { - location: previous_field, + place: previous_field, target: previous_cleanup, unwind: None, }, @@ -660,9 +671,9 @@ fn build_call_shim<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>, rcvr_adjustment: Option, - call_kind: CallKind, + call_kind: CallKind<'tcx>, untuple_args: Option<&[Ty<'tcx>]>, -) -> BodyAndCache<'tcx> { +) -> Body<'tcx> { debug!( "build_call_shim(instance={:?}, rcvr_adjustment={:?}, \ call_kind={:?}, untuple_args={:?})", @@ -673,6 +684,29 @@ fn build_call_shim<'tcx>( let sig = tcx.fn_sig(def_id); let mut sig = tcx.erase_late_bound_regions(&sig); + if let CallKind::Indirect(fnty) = call_kind { + // `sig` determines our local decls, and thus the callee type in the `Call` terminator. This + // can only be an `FnDef` or `FnPtr`, but currently will be `Self` since the types come from + // the implemented `FnX` trait. + + // Apply the opposite adjustment to the MIR input. + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + + // Initial signature is `fn(&? Self, Args) -> Self::Output` where `Args` is a tuple of the + // fn arguments. `Self` may be passed via (im)mutable reference or by-value. + assert_eq!(inputs_and_output.len(), 3); + + // `Self` is always the original fn type `ty`. The MIR call terminator is only defined for + // `FnDef` and `FnPtr` callees, not the `Self` type param. + let self_arg = &mut inputs_and_output[0]; + *self_arg = match rcvr_adjustment.unwrap() { + Adjustment::Identity => fnty, + Adjustment::Deref => tcx.mk_imm_ptr(fnty), + Adjustment::RefMut => tcx.mk_mut_ptr(fnty), + }; + sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output); + } + // FIXME(eddyb) avoid having this snippet both here and in // `Instance::fn_sig` (introduce `InstanceDef::fn_sig`?). if let ty::InstanceDef::VtableShim(..) = instance { @@ -689,7 +723,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()); @@ -699,18 +733,19 @@ fn build_call_shim<'tcx>( let rcvr = rcvr_adjustment.map(|rcvr_adjustment| match rcvr_adjustment { Adjustment::Identity => Operand::Move(rcvr_place()), - Adjustment::Deref => Operand::Copy(tcx.mk_place_deref(rcvr_place())), - Adjustment::DerefMove => Operand::Move(tcx.mk_place_deref(rcvr_place())), + Adjustment::Deref => 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, @@ -724,7 +759,10 @@ fn build_call_shim<'tcx>( }); let (callee, mut args) = match call_kind { - CallKind::Indirect => (rcvr.unwrap(), vec![]), + // `FnPtr` call has no receiver. Args are untupled below. + CallKind::Indirect(_) => (rcvr.unwrap(), vec![]), + + // `FnDef` call with optional receiver. CallKind::Direct(def_id) => { let ty = tcx.type_of(def_id); ( @@ -785,6 +823,7 @@ fn build_call_shim<'tcx>( None }, from_hir_call: true, + fn_span: span, }, false, ); @@ -794,11 +833,7 @@ fn build_call_shim<'tcx>( block( &mut blocks, vec![], - TerminatorKind::Drop { - location: rcvr_place(), - target: BasicBlock::new(2), - unwind: None, - }, + TerminatorKind::Drop { place: rcvr_place(), target: BasicBlock::new(2), unwind: None }, false, ); } @@ -809,11 +844,7 @@ fn build_call_shim<'tcx>( block( &mut blocks, vec![], - TerminatorKind::Drop { - location: rcvr_place(), - target: BasicBlock::new(4), - unwind: None, - }, + TerminatorKind::Drop { place: rcvr_place(), target: BasicBlock::new(4), unwind: None }, true, ); @@ -826,10 +857,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 = @@ -850,7 +882,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) @@ -896,7 +928,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..a02d0f655600d 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,8 +64,8 @@ fn add_moves_for_packed_drops_patch<'tcx>( let terminator = data.terminator(); match terminator.kind { - TerminatorKind::Drop { ref location, .. } - if util::is_disaligned(tcx, body, param_env, location) => + TerminatorKind::Drop { place, .. } + if util::is_disaligned(tcx, body, param_env, place) => { add_move_for_packed_drop(tcx, body, &mut patch, terminator, loc, data.is_cleanup); } @@ -92,13 +88,13 @@ fn add_move_for_packed_drop<'tcx>( is_cleanup: bool, ) { debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc); - let (location, target, unwind) = match terminator.kind { - TerminatorKind::Drop { ref location, target, unwind } => (location, target, unwind), + let (place, target, unwind) = match terminator.kind { + TerminatorKind::Drop { ref place, target, unwind } => (place, target, unwind), _ => unreachable!(), }; let source_info = terminator.source_info; - let ty = location.ty(body, tcx).ty; + let ty = place.ty(body, tcx).ty; let temp = patch.new_temp(ty, terminator.source_info.span); let storage_dead_block = patch.new_block(BasicBlockData { @@ -108,9 +104,9 @@ fn add_move_for_packed_drop<'tcx>( }); patch.add_statement(loc, StatementKind::StorageLive(temp)); - patch.add_assign(loc, Place::from(temp), Rvalue::Use(Operand::Move(*location))); + patch.add_assign(loc, Place::from(temp), Rvalue::Use(Operand::Move(*place))); patch.patch_terminator( loc.block, - TerminatorKind::Drop { location: Place::from(temp), target: storage_dead_block, unwind }, + TerminatorKind::Drop { place: Place::from(temp), target: storage_dead_block, unwind }, ); } 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..e4aa88e3c20a7 100644 --- a/src/librustc_mir/transform/check_consts/mod.rs +++ b/src/librustc_mir/transform/check_consts/mod.rs @@ -4,109 +4,53 @@ //! 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 post_drop_elaboration; 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 d06a2aa44f231..676688daf1c6d 100644 --- a/src/librustc_mir/transform/check_consts/ops.rs +++ b/src/librustc_mir/transform/check_consts/ops.rs @@ -1,19 +1,33 @@ //! Concrete error types for all operations which may be invalid in a certain const context. -use rustc::session::config::nightly_options; -use rustc::session::parse::feature_err; 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; + +/// Emits an error if `op` is not allowed in the given const context. +pub fn non_const(ccx: &ConstCx<'_, '_>, op: O, span: Span) { + debug!("illegal_op: op={:?}", op); + + if op.is_allowed_in_item(ccx) { + return; + } + + if ccx.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { + ccx.tcx.sess.miri_unleashed_feature(span, O::feature_gate()); + return; + } + + op.emit_error(ccx, span); +} /// An operation that is not *always* allowed in a const context. pub trait NonConstOp: std::fmt::Debug { - /// Whether this operation can be evaluated by miri. - const IS_SUPPORTED_IN_MIRI: bool = true; - /// Returns the `Symbol` corresponding to the feature gate that would enable this operation, /// or `None` if such a feature gate does not exist. fn feature_gate() -> Option { @@ -27,19 +41,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 +70,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 +85,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 +121,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,24 +149,31 @@ 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 LiveDrop; +pub struct InlineAsm; +impl NonConstOp for InlineAsm {} + +#[derive(Debug)] +pub struct LiveDrop(pub Option); impl NonConstOp for LiveDrop { - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { - struct_span_err!( - item.tcx.sess, + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + let mut diagnostic = struct_span_err!( + ccx.tcx.sess, span, E0493, "destructors cannot be evaluated at compile-time" - ) - .span_label(span, format!("{}s cannot evaluate destructors", item.const_kind())) - .emit(); + ); + diagnostic.span_label(span, format!("{}s cannot evaluate destructors", ccx.const_kind())); + if let Some(span) = self.0 { + diagnostic.span_label(span, "value is dropped here"); + } + diagnostic.emit(); } } @@ -181,18 +184,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 \ @@ -205,23 +208,35 @@ impl NonConstOp for CellBorrow { #[derive(Debug)] pub struct MutBorrow; impl NonConstOp for MutBorrow { + fn is_allowed_in_item(&self, ccx: &ConstCx<'_, '_>) -> bool { + // Forbid everywhere except in const fn + ccx.const_kind() == hir::ConstContext::ConstFn + && ccx.tcx.features().enabled(Self::feature_gate().unwrap()) + } + fn feature_gate() -> Option { Some(sym::const_mut_refs) } - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { - let mut err = feature_err( - &item.tcx.sess.parse_sess, - sym::const_mut_refs, - span, - &format!( - "references in {}s may only refer \ - to immutable values", - item.const_kind() - ), - ); - err.span_label(span, format!("{}s require immutable values", item.const_kind())); - if item.tcx.sess.teach(&err.get_code().unwrap()) { + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn { + feature_err( + &ccx.tcx.sess.parse_sess, + sym::const_mut_refs, + span, + &format!("mutable references are not allowed in {}s", ccx.const_kind()), + ) + } else { + struct_span_err!( + ccx.tcx.sess, + span, + E0764, + "mutable references are not allowed in {}s", + ccx.const_kind(), + ) + }; + err.span_label(span, "`&mut` is only allowed in `const fn`".to_string()); + 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 +259,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 +285,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(); } @@ -284,18 +299,16 @@ impl NonConstOp for Panic { #[derive(Debug)] pub struct RawPtrComparison; impl NonConstOp for RawPtrComparison { - fn feature_gate() -> Option { - Some(sym::const_compare_raw_pointers) - } - - fn emit_error(&self, item: &Item<'_, '_>, span: Span) { - feature_err( - &item.tcx.sess.parse_sess, - sym::const_compare_raw_pointers, - span, - &format!("comparing raw pointers inside {}", item.const_kind()), - ) - .emit(); + fn emit_error(&self, ccx: &ConstCx<'_, '_>, span: Span) { + let mut err = ccx + .tcx + .sess + .struct_span_err(span, "pointers cannot be reliably compared during const eval."); + err.note( + "see issue #53020 \ + for more information", + ); + err.emit(); } } @@ -306,12 +319,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 +337,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 +352,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.", @@ -369,11 +382,9 @@ impl NonConstOp for StaticAccess { #[derive(Debug)] 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 +397,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/post_drop_elaboration.rs b/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs new file mode 100644 index 0000000000000..1fd907f89fe18 --- /dev/null +++ b/src/librustc_mir/transform/check_consts/post_drop_elaboration.rs @@ -0,0 +1,119 @@ +use rustc_hir::def_id::LocalDefId; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{self, BasicBlock, Location}; +use rustc_middle::ty::TyCtxt; +use rustc_span::Span; + +use super::ops; +use super::qualifs::{NeedsDrop, Qualif}; +use super::validation::Qualifs; +use super::ConstCx; + +/// Returns `true` if we should use the more precise live drop checker that runs after drop +/// elaboration. +pub fn checking_enabled(tcx: TyCtxt<'tcx>) -> bool { + tcx.features().const_precise_live_drops +} + +/// Look for live drops in a const context. +/// +/// This is separate from the rest of the const checking logic because it must run after drop +/// elaboration. +pub fn check_live_drops(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &mir::Body<'tcx>) { + let const_kind = tcx.hir().body_const_context(def_id); + if const_kind.is_none() { + return; + } + + if !checking_enabled(tcx) { + return; + } + + let ccx = ConstCx { + body, + tcx, + def_id: def_id.to_def_id(), + const_kind, + param_env: tcx.param_env(def_id), + }; + + let mut visitor = CheckLiveDrops { ccx: &ccx, qualifs: Qualifs::default() }; + + visitor.visit_body(body); +} + +struct CheckLiveDrops<'mir, 'tcx> { + ccx: &'mir ConstCx<'mir, 'tcx>, + qualifs: Qualifs<'mir, 'tcx>, +} + +// So we can access `body` and `tcx`. +impl std::ops::Deref for CheckLiveDrops<'mir, 'tcx> { + type Target = ConstCx<'mir, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.ccx + } +} + +impl CheckLiveDrops<'mir, 'tcx> { + fn check_live_drop(&self, span: Span) { + ops::non_const(self.ccx, ops::LiveDrop(None), span); + } +} + +impl Visitor<'tcx> for CheckLiveDrops<'mir, 'tcx> { + fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &mir::BasicBlockData<'tcx>) { + trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup); + + // Ignore drop terminators in cleanup blocks. + if block.is_cleanup { + return; + } + + self.super_basic_block_data(bb, block); + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + trace!("visit_terminator: terminator={:?} location={:?}", terminator, location); + + match &terminator.kind { + mir::TerminatorKind::Drop { place: dropped_place, .. } => { + let dropped_ty = dropped_place.ty(self.body, self.tcx).ty; + if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) { + return; + } + + if dropped_place.is_indirect() { + self.check_live_drop(terminator.source_info.span); + return; + } + + if self.qualifs.needs_drop(self.ccx, dropped_place.local, location) { + // Use the span where the dropped local was declared for the error. + let span = self.body.local_decls[dropped_place.local].source_info.span; + self.check_live_drop(span); + } + } + + mir::TerminatorKind::DropAndReplace { .. } => span_bug!( + terminator.source_info.span, + "`DropAndReplace` should be removed by drop elaboration", + ), + + mir::TerminatorKind::Abort + | mir::TerminatorKind::Call { .. } + | mir::TerminatorKind::Assert { .. } + | mir::TerminatorKind::FalseEdge { .. } + | mir::TerminatorKind::FalseUnwind { .. } + | mir::TerminatorKind::GeneratorDrop + | mir::TerminatorKind::Goto { .. } + | mir::TerminatorKind::InlineAsm { .. } + | mir::TerminatorKind::Resume + | mir::TerminatorKind::Return + | mir::TerminatorKind::SwitchInt { .. } + | mir::TerminatorKind::Unreachable + | mir::TerminatorKind::Yield { .. } => {} + } + } +} diff --git a/src/librustc_mir/transform/check_consts/qualifs.rs b/src/librustc_mir/transform/check_consts/qualifs.rs index baff8383c20a4..e2893e81a2ce6 100644 --- a/src/librustc_mir/transform/check_consts/qualifs.rs +++ b/src/librustc_mir/transform/check_consts/qualifs.rs @@ -1,24 +1,33 @@ -//! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator. +//! Structural const qualification. +//! +//! See the `Qualif` trait for more info. -use rustc::mir::*; -use rustc::ty::{self, Ty}; +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), } } /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some -/// code for promotion or prevent it from evaluating at compile time. So `return true` means -/// "I found something bad, no reason to go on searching". `false` is only returned if we -/// definitely cannot find anything bad anywhere. +/// code for promotion or prevent it from evaluating at compile time. /// -/// The default implementations proceed structurally. +/// Normally, we would determine what qualifications apply to each type and error when an illegal +/// operation is performed on such a type. However, this was found to be too imprecise, especially +/// in the presence of `enum`s. If only a single variant of an enum has a certain qualification, we +/// needn't reject code unless it actually constructs and operates on the qualifed variant. +/// +/// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a +/// type-based one). Qualifications propagate structurally across variables: If a local (or a +/// projection of a local) is assigned a qualifed value, that local itself becomes qualifed. pub trait Qualif { /// The name of the file used to debug the dataflow analysis that computes this qualif. const ANALYSIS_NAME: &'static str; @@ -26,157 +35,31 @@ pub trait Qualif { /// Whether this `Qualif` is cleared when a local is moved from. const IS_CLEARED_ON_MOVE: bool = false; + /// Extracts the field of `ConstQualifs` that corresponds to this `Qualif`. fn in_qualifs(qualifs: &ConstQualifs) -> bool; - /// Return the qualification that is (conservatively) correct for any value - /// of the type. - fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool; - - fn in_projection_structurally( + /// Returns `true` if *any* value of the given type could possibly have this `Qualif`. + /// + /// This function determines `Qualif`s when we cannot do a value-based analysis. Since qualif + /// propagation is context-insenstive, this includes function arguments and values returned + /// from a call to another function. + /// + /// It also determines the `Qualif`s for primitive types. + fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool; + + /// Returns `true` if this `Qualif` is inherent to the given struct or enum. + /// + /// By default, `Qualif`s propagate into ADTs in a structural way: An ADT only becomes + /// qualified if part of it is assigned a value with that `Qualif`. However, some ADTs *always* + /// have a certain `Qualif`, regardless of whether their fields have it. For example, a type + /// 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>, - per_local: &mut impl FnMut(Local) -> bool, - place: PlaceRef<'tcx>, - ) -> bool { - if let [proj_base @ .., elem] = place.projection { - let base_qualif = Self::in_place( - cx, - per_local, - PlaceRef { local: place.local, projection: proj_base }, - ); - let qualif = base_qualif - && Self::in_any_value_of_ty( - cx, - Place::ty_from(place.local, proj_base, *cx.body, cx.tcx) - .projection_ty(cx.tcx, elem) - .ty, - ); - match elem { - ProjectionElem::Deref - | ProjectionElem::Subslice { .. } - | ProjectionElem::Field(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Downcast(..) => qualif, - - ProjectionElem::Index(local) => qualif || per_local(*local), - } - } else { - bug!("This should be called if projection is not empty"); - } - } - - fn in_projection( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - place: PlaceRef<'tcx>, - ) -> bool { - Self::in_projection_structurally(cx, per_local, place) - } - - fn in_place( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - place: PlaceRef<'tcx>, - ) -> bool { - match place { - PlaceRef { local, projection: [] } => per_local(local), - PlaceRef { local: _, projection: [.., _] } => Self::in_projection(cx, per_local, place), - } - } - - fn in_operand( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - operand: &Operand<'tcx>, - ) -> bool { - match *operand { - Operand::Copy(ref place) | Operand::Move(ref place) => { - Self::in_place(cx, per_local, place.as_ref()) - } - - Operand::Constant(ref constant) => { - // Check the qualifs of the value of `const` items. - if let ty::ConstKind::Unevaluated(def_id, _, promoted) = constant.literal.val { - assert!(promoted.is_none()); - // Don't peek inside trait associated constants. - if cx.tcx.trait_of_item(def_id).is_none() { - let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id); - if !Self::in_qualifs(&qualifs) { - return false; - } - - // Just in case the type is more specific than - // the definition, e.g., impl associated const - // with type parameters, take it into account. - } - } - // Otherwise use the qualifs of the type. - Self::in_any_value_of_ty(cx, constant.literal.ty) - } - } - } - - fn in_rvalue_structurally( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - rvalue: &Rvalue<'tcx>, - ) -> bool { - match *rvalue { - Rvalue::NullaryOp(..) => false, - - Rvalue::Discriminant(ref place) | Rvalue::Len(ref place) => { - Self::in_place(cx, per_local, place.as_ref()) - } - - Rvalue::Use(ref operand) - | Rvalue::Repeat(ref operand, _) - | Rvalue::UnaryOp(_, ref operand) - | Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, per_local, operand), - - Rvalue::BinaryOp(_, ref lhs, ref rhs) - | Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => { - Self::in_operand(cx, per_local, lhs) || Self::in_operand(cx, per_local, rhs) - } - - Rvalue::Ref(_, _, ref place) | Rvalue::AddressOf(_, ref place) => { - // Special-case reborrows to be more like a copy of the reference. - if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { - let base_ty = Place::ty_from(place.local, proj_base, *cx.body, cx.tcx).ty; - if let ty::Ref(..) = base_ty.kind { - return Self::in_place( - cx, - per_local, - PlaceRef { local: place.local, projection: proj_base }, - ); - } - } - - Self::in_place(cx, per_local, place.as_ref()) - } - - Rvalue::Aggregate(_, ref operands) => { - operands.iter().any(|o| Self::in_operand(cx, per_local, o)) - } - } - } - - fn in_rvalue( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - rvalue: &Rvalue<'tcx>, - ) -> bool { - Self::in_rvalue_structurally(cx, per_local, rvalue) - } - - fn in_call( - cx: &ConstCx<'_, 'tcx>, - _per_local: &mut impl FnMut(Local) -> bool, - _callee: &Operand<'tcx>, - _args: &[Operand<'tcx>], - return_ty: Ty<'tcx>, - ) -> bool { - // Be conservative about the returned value of a const fn. - Self::in_any_value_of_ty(cx, return_ty) - } + adt: &'tcx AdtDef, + substs: SubstsRef<'tcx>, + ) -> bool; } /// Constant containing interior mutability (`UnsafeCell`). @@ -194,29 +77,13 @@ impl Qualif for HasMutInterior { } fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { - !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) + !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) } - fn in_rvalue( - cx: &ConstCx<'_, 'tcx>, - per_local: &mut impl FnMut(Local) -> bool, - rvalue: &Rvalue<'tcx>, - ) -> bool { - match *rvalue { - Rvalue::Aggregate(ref kind, _) => { - if let AggregateKind::Adt(def, ..) = **kind { - if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() { - let ty = rvalue.ty(*cx.body, cx.tcx); - assert_eq!(Self::in_any_value_of_ty(cx, ty), true); - return true; - } - } - } - - _ => {} - } - - Self::in_rvalue_structurally(cx, per_local, rvalue) + 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() } } @@ -238,19 +105,159 @@ impl Qualif for NeedsDrop { ty.needs_drop(cx.tcx, cx.param_env) } - fn in_rvalue( + 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>, - per_local: &mut impl FnMut(Local) -> bool, - rvalue: &Rvalue<'tcx>, + adt: &'tcx AdtDef, + substs: SubstsRef<'tcx>, ) -> bool { - if let Rvalue::Aggregate(ref kind, _) = *rvalue { - if let AggregateKind::Adt(def, ..) = **kind { - if def.has_dtor(cx.tcx) { + let ty = cx.tcx.mk_ty(ty::Adt(adt, substs)); + !ty.is_structural_eq_shallow(cx.tcx) + } +} + +// FIXME: Use `mir::visit::Visitor` for the `in_*` functions if/when it supports early return. + +/// Returns `true` if this `Rvalue` contains qualif `Q`. +pub fn in_rvalue(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, rvalue: &Rvalue<'tcx>) -> bool +where + Q: Qualif, + F: FnMut(Local) -> bool, +{ + match rvalue { + Rvalue::ThreadLocalRef(_) | 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()) + } + + Rvalue::Use(operand) + | Rvalue::Repeat(operand, _) + | Rvalue::UnaryOp(_, operand) + | Rvalue::Cast(_, operand, _) => in_operand::(cx, in_local, operand), + + Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { + in_operand::(cx, in_local, lhs) || in_operand::(cx, in_local, rhs) + } + + 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; + if let ty::Ref(..) = base_ty.kind { + return in_place::( + cx, + in_local, + PlaceRef { local: place.local, projection: proj_base }, + ); + } + } + + in_place::(cx, in_local, place.as_ref()) + } + + Rvalue::Aggregate(kind, operands) => { + // Return early if we know that the struct or enum being constructed is always + // qualified. + if let AggregateKind::Adt(def, _, substs, ..) = **kind { + if Q::in_adt_inherently(cx, def, substs) { return true; } } + + // Otherwise, proceed structurally... + operands.iter().any(|o| in_operand::(cx, in_local, o)) + } + } +} + +/// Returns `true` if this `Place` contains qualif `Q`. +pub fn in_place(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, place: PlaceRef<'tcx>) -> bool +where + Q: Qualif, + F: FnMut(Local) -> bool, +{ + let mut projection = place.projection; + while let &[ref proj_base @ .., proj_elem] = projection { + match proj_elem { + ProjectionElem::Index(index) if in_local(index) => return true, + + ProjectionElem::Deref + | ProjectionElem::Field(_, _) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(_, _) + | ProjectionElem::Index(_) => {} + } + + 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; + } + + projection = proj_base; + } + + assert!(projection.is_empty()); + in_local(place.local) +} + +/// Returns `true` if this `Operand` contains qualif `Q`. +pub fn in_operand(cx: &ConstCx<'_, 'tcx>, in_local: &mut F, operand: &Operand<'tcx>) -> bool +where + Q: Qualif, + F: FnMut(Local) -> bool, +{ + let constant = match operand { + Operand::Copy(place) | Operand::Move(place) => { + return in_place::(cx, in_local, place.as_ref()); } - Self::in_rvalue_structurally(cx, per_local, rvalue) + Operand::Constant(c) => c, + }; + + // Check the qualifs of the value of `const` items. + if let ty::ConstKind::Unevaluated(def_id, _, promoted) = constant.literal.val { + assert!(promoted.is_none()); + // Don't peek inside trait associated constants. + if cx.tcx.trait_of_item(def_id).is_none() { + let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id); + if !Q::in_qualifs(&qualifs) { + return false; + } + + // Just in case the type is more specific than + // the definition, e.g., impl associated const + // with type parameters, take it into account. + } } + // Otherwise use the qualifs of the type. + Q::in_any_value_of_ty(cx, constant.literal.ty) } diff --git a/src/librustc_mir/transform/check_consts/resolver.rs b/src/librustc_mir/transform/check_consts/resolver.rs index 3e14cc6d32a67..b8104292aab23 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::{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); } } @@ -66,20 +66,17 @@ where fn apply_call_return_effect( &mut self, _block: BasicBlock, - func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], - return_place: &mir::Place<'tcx>, + _func: &mir::Operand<'tcx>, + _args: &[mir::Operand<'tcx>], + return_place: mir::Place<'tcx>, ) { - let return_ty = return_place.ty(*self.item.body, self.item.tcx).ty; - let qualif = Q::in_call( - self.item, - &mut |l| self.qualifs_per_local.contains(l), - func, - args, - return_ty, - ); + // 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.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); } } } @@ -110,7 +107,11 @@ where rvalue: &mir::Rvalue<'tcx>, location: Location, ) { - let qualif = Q::in_rvalue(self.item, &mut |l| self.qualifs_per_local.contains(l), rvalue); + let qualif = qualifs::in_rvalue::( + self.ccx, + &mut |l| self.qualifs_per_local.contains(l), + rvalue, + ); if !place.is_indirect() { self.assign_qualif_direct(place, qualif); } @@ -120,27 +121,31 @@ where self.super_assign(place, rvalue, location); } - fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location: Location) { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { // The effect of assignment to the return place in `TerminatorKind::Call` is not applied // here; that occurs in `apply_call_return_effect`. - if let mir::TerminatorKind::DropAndReplace { value, location: dest, .. } = kind { - let qualif = - Q::in_operand(self.item, &mut |l| self.qualifs_per_local.contains(l), value); - if !dest.is_indirect() { - self.assign_qualif_direct(dest, qualif); + if let mir::TerminatorKind::DropAndReplace { value, place, .. } = &terminator.kind { + let qualif = qualifs::in_operand::( + self.ccx, + &mut |l| self.qualifs_per_local.contains(l), + value, + ); + + if !place.is_indirect() { + self.assign_qualif_direct(place, qualif); } } // We need to assign qualifs to the dropped location before visiting the operand that // replaces it since qualifs can be cleared on move. - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } } /// 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, } @@ -148,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; } @@ -209,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 bfd97fcff3f28..d263bf12e8868 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, -} +type QualifResults<'mir, 'tcx, Q> = + dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; -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 } - } +#[derive(Default)] +pub struct Qualifs<'mir, 'tcx> { + has_mut_interior: Option>, + needs_drop: Option>, + indirectly_mutable: Option>, } -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>, -} - -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> { + pub 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) { + pub 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) { + pub 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,63 +215,44 @@ 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) - } - - /// Emits an error at the given `span` if an expression cannot be evaluated in the current - /// context. - pub fn check_op_spanned(&mut self, op: O, span: Span) - where - O: NonConstOp, - { - trace!("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 is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { - self.tcx.sess.span_warn(span, "skipping const checks"); - return; - } - - op.emit_error(self, span); + self.qualifs.in_return_place(self.ccx) } /// Emits an error if an expression cannot be evaluated in the current context. pub fn check_op(&mut self, op: impl NonConstOp) { - let span = self.span; - self.check_op_spanned(op, span) + ops::non_const(self.ccx, op, self.span); + } + + /// Emits an error at the given `span` if an expression cannot be evaluated in the current + /// context. + pub fn check_op_spanned(&mut self, op: impl NonConstOp, span: Span) { + ops::non_const(self.ccx, op, span); } 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 { - self.check_op_spanned(ops::ThreadLocalAccess, span) - } else { - self.check_op_spanned(ops::StaticAccess, span) - } + assert!( + !self.tcx.is_thread_local_static(def_id), + "tls access is checked in `Rvalue::ThreadLocalRef" + ); + self.check_op_spanned(ops::StaticAccess, span) } } -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 +273,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 +289,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; } @@ -300,6 +313,8 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { self.super_rvalue(rvalue, location); match *rvalue { + Rvalue::ThreadLocalRef(_) => self.check_op(ops::ThreadLocalAccess), + Rvalue::Use(_) | Rvalue::Repeat(..) | Rvalue::UnaryOp(UnOp::Neg, _) @@ -313,10 +328,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 +358,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 = HasMutInterior::in_place( - &self.item, - &mut |local| self.qualifs.has_mut_interior(local, location), + let borrowed_place_has_mut_interior = qualifs::in_place::( + &self.ccx, + &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location), place.as_ref(), ); @@ -356,19 +372,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 +403,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 { @@ -411,7 +415,7 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { &mut self, place_local: Local, proj_base: &[PlaceElem<'tcx>], - elem: &PlaceElem<'tcx>, + elem: PlaceElem<'tcx>, context: PlaceContext, location: Location, ) { @@ -429,12 +433,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 +454,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 +467,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { _ => {} } } - - ProjectionElem::Downcast(..) => { - self.check_op(ops::Downcast); - } } } @@ -481,27 +482,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 +522,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 +536,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; @@ -550,14 +560,20 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> { // Forbid all `Drop` terminators unless the place being dropped is a local with no // projections that cannot be `NeedsDrop`. - TerminatorKind::Drop { location: dropped_place, .. } - | TerminatorKind::DropAndReplace { location: dropped_place, .. } => { + TerminatorKind::Drop { place: dropped_place, .. } + | TerminatorKind::DropAndReplace { place: dropped_place, .. } => { + // If we are checking live drops after drop-elaboration, don't emit duplicate + // errors here. + if super::post_drop_elaboration::checking_enabled(self.tcx) { + return; + } + let mut err_span = self.span; // 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,17 +582,36 @@ 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 }; if needs_drop { - self.check_op_spanned(ops::LiveDrop, err_span); + self.check_op_spanned( + ops::LiveDrop(Some(terminator.source_info.span)), + err_span, + ); } } - _ => {} + 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::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Return + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Unreachable + | TerminatorKind::Yield { .. } => {} } } } @@ -591,8 +626,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 +636,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 +680,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_packed_ref.rs b/src/librustc_mir/transform/check_packed_ref.rs new file mode 100644 index 0000000000000..043b2d0d1703e --- /dev/null +++ b/src/librustc_mir/transform/check_packed_ref.rs @@ -0,0 +1,66 @@ +use rustc_middle::mir::visit::{PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::lint::builtin::UNALIGNED_REFERENCES; + +use crate::transform::{MirPass, MirSource}; +use crate::util; + +pub struct CheckPackedRef; + +impl<'tcx> MirPass<'tcx> for CheckPackedRef { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + let param_env = tcx.param_env(src.instance.def_id()); + let source_info = SourceInfo::outermost(body.span); + let mut checker = PackedRefChecker { body, tcx, param_env, source_info }; + checker.visit_body(&body); + } +} + +struct PackedRefChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_info: SourceInfo, +} + +impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + // Make sure we know where in the MIR we are. + self.source_info = terminator.source_info; + self.super_terminator(terminator, location); + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + // Make sure we know where in the MIR we are. + self.source_info = statement.source_info; + self.super_statement(statement, location); + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { + 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.tcx.struct_span_lint_hir( + UNALIGNED_REFERENCES, + lint_root, + source_info.span, + |lint| { + lint.build("reference to packed field is unaligned") + .note( + "fields of packed structs are not properly aligned, and creating \ + a misaligned reference is undefined behavior (even if that \ + reference is never dereferenced)", + ) + .emit() + }, + ); + } + } + } +} diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 21788593259c2..b8f725e967ddb 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -1,25 +1,27 @@ -use rustc::hir::map::Map; -use rustc::lint::builtin::{SAFE_PACKED_BORROWS, UNUSED_UNSAFE}; -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::hir_id::HirId; 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, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; +use rustc_session::lint::Level; use rustc_span::symbol::{sym, Symbol}; use std::ops::Bound; -use crate::const_eval::{is_const_fn, is_min_const_fn}; +use crate::const_eval::is_min_const_fn; use crate::util; pub struct UnsafetyChecker<'a, 'tcx> { body: &'a Body<'tcx>, + body_did: LocalDefId, const_context: bool, min_const_fn: bool, violations: Vec, @@ -36,6 +38,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 { @@ -45,10 +48,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(), @@ -72,7 +76,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { | TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable - | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } => { // safe (at least as emitted during MIR construction) } @@ -88,7 +92,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); } @@ -107,7 +121,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, @@ -133,7 +147,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); } }, @@ -147,7 +161,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", @@ -157,62 +171,52 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { _ => {} } } - // raw pointer and fn pointer operations are unsafe as it is not clear whether one - // pointer would be "less" or "equal" to another, because we cannot know where llvm - // or the linker will place various statics in memory. Without this information the - // result of a comparison of addresses would differ between runtime and compile-time. - Rvalue::BinaryOp(_, ref lhs, _) - if self.const_context && self.tcx.features().const_compare_raw_pointers => - { - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind { - self.require_unsafe( - "pointer operation", - "operations on pointers in constants", - UnsafetyViolationKind::General, - ); - } - } _ => {} } self.super_rvalue(rvalue, location); } 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) { + 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, + ); + } } 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; + if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { 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), + UnsafetyViolationKind::BorrowPacked, ); } } - let old_source_info = self.source_info; + let 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", @@ -263,9 +267,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { ), }; if !elem_ty.is_copy_modulo_regions( - self.tcx, + self.tcx.at(self.source_info.span), self.param_env, - self.source_info.span, ) { self.require_unsafe( "assignment to non-`Copy` union field", @@ -289,7 +292,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { } _ => {} } - self.source_info = old_source_info; + self.source_info = source_info; } } } @@ -302,9 +305,15 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { kind: UnsafetyViolationKind, ) { let source_info = self.source_info; + let lint_root = self.body.source_scopes[self.source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; self.register_violations( &[UnsafetyViolation { source_info, + lint_root, description: Symbol::intern(description), details: Symbol::intern(details), kind, @@ -331,7 +340,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { match violation.kind { UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {} - UnsafetyViolationKind::BorrowPacked(_) => { + UnsafetyViolationKind::BorrowPacked => { if self.min_const_fn { // const fns don't need to be backwards compatible and can // emit these violations as a hard error instead of a backwards @@ -339,6 +348,10 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { violation.kind = UnsafetyViolationKind::General; } } + UnsafetyViolationKind::UnsafeFn + | UnsafetyViolationKind::UnsafeFnBorrowPacked => { + bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") + } } if !self.violations.contains(&violation) { self.violations.push(violation) @@ -346,7 +359,23 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { } false } - // `unsafe` function bodies allow unsafe without additional unsafe blocks + // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s + Safety::FnUnsafe if self.tcx.features().unsafe_block_in_unsafe_fn => { + for violation in violations { + let mut violation = *violation; + + if violation.kind == UnsafetyViolationKind::BorrowPacked { + violation.kind = UnsafetyViolationKind::UnsafeFnBorrowPacked; + } else { + violation.kind = UnsafetyViolationKind::UnsafeFn; + } + if !self.violations.contains(&violation) { + self.violations.push(violation) + } + } + false + } + // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585) Safety::BuiltinUnsafe | Safety::FnUnsafe => true, Safety::ExplicitUnsafe(hir_id) => { // mark unsafe block as used if there are any unsafe operations inside @@ -361,7 +390,7 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { UnsafetyViolationKind::GeneralAndConstFn => {} // these things are forbidden in const fns UnsafetyViolationKind::General - | UnsafetyViolationKind::BorrowPacked(_) => { + | UnsafetyViolationKind::BorrowPacked => { let mut violation = *violation; // const fns don't need to be backwards compatible and can // emit these violations as a hard error instead of a backwards @@ -371,6 +400,10 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { self.violations.push(violation) } } + UnsafetyViolationKind::UnsafeFn + | UnsafetyViolationKind::UnsafeFnBorrowPacked => bug!( + "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context" + ), } } } @@ -383,7 +416,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(); @@ -397,48 +430,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.at(self.source_info.span), self.param_env) + { + ( + "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<'_>) { @@ -451,7 +498,7 @@ struct UnusedUnsafeVisitor<'a> { } impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None @@ -468,12 +515,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, @@ -489,7 +535,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 @@ -498,17 +544,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 => { + (tcx.is_const_fn_raw(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,11 +563,8 @@ 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)); +fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) { + let lint_hir_id = tcx.hir().as_local_hir_id(def_id); tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| { // FIXME: when we make this a hard error, this should have its @@ -553,9 +596,12 @@ fn is_enclosed( kind: hir::ItemKind::Fn(ref sig, _, _), .. })) = tcx.hir().find(parent_id) { - match sig.header.unsafety { - hir::Unsafety::Unsafe => Some(("fn".to_string(), parent_id)), - hir::Unsafety::Normal => None, + if sig.header.unsafety == hir::Unsafety::Unsafe + && !tcx.features().unsafe_block_in_unsafe_fn + { + Some(("fn".to_string(), parent_id)) + } else { + None } } else { is_enclosed(tcx, used_unsafe, parent_id) @@ -566,14 +612,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), ); } @@ -597,43 +643,52 @@ fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { } } -pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { +pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { debug!("check_unsafety({:?})", def_id); // closures are handled by their parent fn. - if tcx.is_closure(def_id) { + if tcx.is_closure(def_id.to_def_id()) { return; } let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id); - for &UnsafetyViolation { source_info, description, details, kind } in violations.iter() { + for &UnsafetyViolation { source_info, lint_root, description, details, kind } in + violations.iter() + { // Report an error. + let unsafe_fn_msg = + if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" }; + match kind { UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => { + // once struct_span_err!( tcx.sess, source_info.span, E0133, - "{} is unsafe and requires unsafe function or block", - description + "{} is unsafe and requires unsafe{} block", + description, + unsafe_fn_msg, ) .span_label(source_info.span, &*description.as_str()) .note(&details.as_str()) .emit(); } - 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); + UnsafetyViolationKind::BorrowPacked => { + if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id.to_def_id()) { + // If a method is defined in the local crate, + // the impl containing that method should also be. + tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local()); } else { tcx.struct_span_lint_hir( SAFE_PACKED_BORROWS, - lint_hir_id, + lint_root, source_info.span, |lint| { lint.build(&format!( - "{} is unsafe and requires unsafe function or block (error E0133)", - description + "{} is unsafe and requires unsafe{} block (error E0133)", + description, unsafe_fn_msg, )) .note(&details.as_str()) .emit() @@ -641,16 +696,69 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: DefId) { ) } } + UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir( + UNSAFE_OP_IN_UNSAFE_FN, + lint_root, + source_info.span, + |lint| { + lint.build(&format!( + "{} is unsafe and requires unsafe block (error E0133)", + description, + )) + .span_label(source_info.span, &*description.as_str()) + .note(&details.as_str()) + .emit(); + }, + ), + UnsafetyViolationKind::UnsafeFnBorrowPacked => { + // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions + // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`, + // a safe packed borrow should emit a warning *but not an error* in an unsafe function, + // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`. + // + // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with + // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow + // should only issue a warning for the sake of backwards compatibility. + // + // The solution those 2 expectations is to always take the minimum of both lints. + // This prevent any new errors (unless both lints are explicitely set to `deny`). + let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0 + <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0 + { + SAFE_PACKED_BORROWS + } else { + UNSAFE_OP_IN_UNSAFE_FN + }; + tcx.struct_span_lint_hir(&lint, lint_root, source_info.span, |lint| { + lint.build(&format!( + "{} is unsafe and requires unsafe block (error E0133)", + description, + )) + .span_label(source_info.span, &*description.as_str()) + .note(&details.as_str()) + .emit(); + }) + } } } - 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); + } +} + +fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool { + tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow } diff --git a/src/librustc_mir/transform/cleanup_post_borrowck.rs b/src/librustc_mir/transform/cleanup_post_borrowck.rs index 5288b6b370ddb..3f3d247a8294f 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,9 +31,14 @@ 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(); + + for decl in &mut body.local_decls { + decl.user_ty = None; + } } } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 289b198d2c949..eb614170baae5 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -1,64 +1,73 @@ //! Propagates constants for early reporting of statically known //! assertion failures -use std::borrow::Cow; use std::cell::Cell; -use rustc::lint; -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_span::Span; +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::{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, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx, LocalState, + LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer, + ScalarMaybeUninit, StackPopCleanup, }; use crate::transform::{MirPass, MirSource}; /// The maximum number of bytes that we'll allocate space for a return value. const MAX_ALLOC_LIMIT: u64 = 1024; +/// Macro for machine-specific `InterpError` without allocation. +/// (These will never be shown to the user, but they help diagnose ICEs.) +macro_rules! throw_machine_stop_str { + ($($tt:tt)*) => {{ + // 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; + // 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_middle::mir::interpret::MachineStopType for Zst {} + throw_machine_stop!(Zst) + }}; +} + 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 { @@ -105,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; @@ -133,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)>, @@ -174,37 +174,26 @@ 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)>, _unwind: Option, ) -> InterpResult<'tcx> { - throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp")); + throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp") } 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"); + bug!("panics terminators are not evaluated in ConstProp") } fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> { - throw_unsup!(ConstPropUnsupported("ptr-to-int casts aren't supported in ConstProp")); + throw_unsup!(ReadPointerAsBytes) } fn binary_ptr_op( @@ -214,33 +203,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { _right: ImmTy<'tcx>, ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { // We can't do this because aliasing of memory can differ between const eval and llvm - throw_unsup!(ConstPropUnsupported( - "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 { - () + throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp") } fn box_alloc( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _dest: PlaceTy<'tcx>, ) -> InterpResult<'tcx> { - throw_unsup!(ConstPropUnsupported("can't const prop `box` keyword")); + throw_machine_stop_str!("can't const prop heap allocations") } fn access_local( @@ -251,34 +221,49 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { let l = &frame.locals[local]; if l.value == LocalValue::Uninitialized { - throw_unsup!(ConstPropUnsupported("tried to access an uninitialized local")); + throw_machine_stop_str!("tried to access an uninitialized local") } 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_unsup!(ConstPropUnsupported("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>, @@ -286,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)) } } @@ -317,24 +303,17 @@ 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>, ) -> ConstPropagator<'mir, 'tcx> { let def_id = source.def_id(); let substs = &InternalSubsts::identity_for_item(tcx, def_id); - let mut param_env = tcx.param_env(def_id); - - // If we're evaluating inside a monomorphic function, then use `Reveal::All` because - // we want to see the same instances that codegen will see. This allows us to `resolve()` - // specializations. - if !substs.needs_subst() { - param_env = param_env.with_reveal_all(); - } + 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, span, param_env, ConstPropMachine::new(), ()); let can_const_prop = CanConstProp::check(body); let ret = ecx @@ -349,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 }, @@ -366,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) }; } @@ -400,27 +378,24 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { where F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, { - let r = match f(self) { + match f(self) { Ok(val) => Some(val), Err(error) => { // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, // dedicated error variants should be introduced instead. - // Only test this in debug builds though to avoid disruptions. - debug_assert!( + assert!( !error.kind.allocates(), "const-prop encountered allocating error: {}", error ); None } - }; - r + } } + /// 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; @@ -429,7 +404,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { match self.ecx.eval_const_to_op(c.literal, None) { Ok(op) => Some(op), Err(error) => { - let err = error_to_const_error(&self.ecx, error); + let tcx = self.ecx.tcx.at(c.span); + let err = error_to_const_error(&self.ecx, error, Some(c.span)); if let Some(lint_root) = self.lint_root(source_info) { let lint_only = match c.literal.val { // Promoteds must lint and not error as the user didn't ask for them @@ -441,32 +417,30 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { if lint_only { // Out of backwards compatibility we cannot report hard errors in unused // generic functions using associated constants of the generic parameters. - err.report_as_lint( - self.ecx.tcx, - "erroneous constant used", - lint_root, - Some(c.span), - ); + err.report_as_lint(tcx, "erroneous constant used", lint_root, Some(c.span)); } else { - err.report_as_error(self.ecx.tcx, "erroneous constant used"); + err.report_as_error(tcx, "erroneous constant used"); } } else { - err.report_as_error(self.ecx.tcx, "erroneous constant used"); + err.report_as_error(tcx, "erroneous constant used"); } None } } } - 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), } } @@ -483,7 +457,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { err.span_label(source_info.span, format!("{:?}", panic)); err.emit() }); - return None; + None } fn check_unary_op( @@ -560,20 +534,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 @@ -605,8 +574,16 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } // Do not try creating references (#67862) - Rvalue::Ref(_, _, place_ref) => { - trace!("skipping Ref({:?})", place_ref); + Rvalue::AddressOf(_, place) | Rvalue::Ref(_, _, place) => { + trace!("skipping AddressOf | Ref for {:?}", place); + + // This may be creating mutable references or immutable references to cells. + // If that happens, the pointed to value could be mutated via that reference. + // Since we aren't tracking references, the const propagator loses track of what + // value the local has right now. + // Thus, all locals that have their reference taken + // must not take part in propagation. + Self::remove_const(&mut self.ecx, place.local); return None; } @@ -614,6 +591,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)?; @@ -621,11 +603,12 @@ 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, user_ty: None, - literal: self.tcx.mk_const(*ty::Const::from_scalar(self.tcx, scalar, ty)), + literal: ty::Const::from_scalar(self.tcx, scalar, ty), })) } @@ -635,6 +618,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, @@ -652,7 +642,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, @@ -660,12 +650,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 { @@ -675,7 +666,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) { @@ -702,6 +693,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; @@ -710,19 +702,13 @@ 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"); - true - } _ => false, } } @@ -733,56 +719,88 @@ 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. + /// The `Local` cannot be part of propagation at all. Any statement + /// referencing it either for reading or writing will not get propagated. NoPropagation, } 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 => {} + ConstPropMode::NoPropagation => {} + ConstPropMode::OnlyPropagateInto => {} + other @ ConstPropMode::FullConstProp => { + trace!( + "local {:?} can't be propagated because of multiple assignments", + local, + ); + *other = ConstPropMode::OnlyPropagateInto; + } + } } } // Reading constants is allowed an arbitrary number of times @@ -790,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; } @@ -805,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); @@ -815,34 +852,65 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { trace!("visit_statement: {:?}", statement); let source_info = statement.source_info; 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) { + // 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, source_info); + if can_const_prop == ConstPropMode::FullConstProp + || can_const_prop == ConstPropMode::OnlyInsideOwnBlock + { + trace!("propagated into {:?}", place); } } } - if self.can_const_prop[local] != ConstPropMode::FullConstProp { - trace!("can't propagate into {:?}", local); - if local != RETURN_PLACE { - self.remove_const(local); + match can_const_prop { + ConstPropMode::OnlyInsideOwnBlock => { + trace!( + "found local restricted to its block. \ + Will remove it from const-prop after block is finished. Local: {:?}", + place.local + ); + self.locals_of_current_block.insert(place.local); + } + ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => { + trace!("can't propagate into {:?}", place); + if place.local != RETURN_PLACE { + Self::remove_const(&mut self.ecx, place.local); + } } + ConstPropMode::FullConstProp => {} } + } else { + // Const prop failed, so erase the destination, ensuring that whatever happens + // from here on, does not know about the previous value. + // This is important in case we have + // ```rust + // let mut x = 42; + // x = SOME_MUTABLE_STATIC; + // // x must now be undefined + // ``` + // FIXME: we overzealously erase the entire local, because that's easier to + // implement. + trace!( + "propagation into {:?} failed. + Nuking the entire site from orbit, it's the only way to be sure", + place, + ); + Self::remove_const(&mut self.ecx, place.local); } + } else { + trace!( + "cannot propagate into {:?}, because the type of the local is generic.", + place, + ); + Self::remove_const(&mut self.ecx, place.local); } } else { match statement.kind { @@ -870,14 +938,14 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { 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(_) => {} } @@ -917,7 +985,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, @@ -931,7 +999,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); @@ -939,7 +1007,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 @@ -949,10 +1017,64 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { | TerminatorKind::DropAndReplace { .. } | TerminatorKind::Yield { .. } | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdges { .. } - | TerminatorKind::FalseUnwind { .. } => {} - //FIXME(wesleywiser) Call does have Operands that could be const-propagated - TerminatorKind::Call { .. } => {} + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => {} + // Every argument in our function calls can be const propagated. + TerminatorKind::Call { ref mut args, .. } => { + let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level; + // Constant Propagation into function call arguments is gated + // under mir-opt-level 2, because LLVM codegen gives performance + // regressions with it. + if mir_opt_level >= 2 { + for opr in args { + /* + The following code would appear to be incomplete, because + the function `Operand::place()` returns `None` if the + `Operand` is of the variant `Operand::Constant`. In this + context however, that variant will never appear. This is why: + + When constructing the MIR, all function call arguments are + copied into `Locals` of `LocalKind::Temp`. At least, all arguments + that are not unsized (Less than 0.1% are unsized. See #71170 + to learn more about those). + + This means that, conversely, all `Operands` found as function call + arguments are of the variant `Operand::Copy`. This allows us to + simplify our handling of `Operands` in this case. + */ + if let Some(l) = opr.place() { + if let Some(value) = self.get_const(l) { + if self.should_const_prop(value) { + // FIXME(felix91gr): this code only handles `Scalar` cases. + // For now, we're not handling `ScalarPair` cases because + // doing so here would require a lot of code duplication. + // We should hopefully generalize `Operand` handling into a fn, + // and use it to do const-prop here and everywhere else + // where it makes sense. + if let interpret::Operand::Immediate( + interpret::Immediate::Scalar(ScalarMaybeUninit::Scalar( + scalar, + )), + ) = *value + { + *opr = self.operand_from_scalar( + scalar, + value.layout.ty, + source_info.span, + ); + } + } + } + } + } + } + } + } + // 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 5dec2c6df99dc..5ce6f4fa7414e 100644 --- a/src/librustc_mir/transform/dump_mir.rs +++ b/src/librustc_mir/transform/dump_mir.rs @@ -7,9 +7,9 @@ use std::io; use crate::transform::{MirPass, MirSource}; use crate::util as mir_util; -use rustc::mir::{Body, BodyAndCache}; -use rustc::session::config::{OutputFilenames, OutputType}; -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..1704d8baabdc8 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(); @@ -85,15 +85,15 @@ fn find_dead_unwinds<'tcx>( .iterate_to_fixpoint() .into_results_cursor(body); for (bb, bb_data) in body.basic_blocks().iter_enumerated() { - let location = match bb_data.terminator().kind { - TerminatorKind::Drop { ref location, unwind: Some(_), .. } - | TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => location, + let place = match bb_data.terminator().kind { + TerminatorKind::Drop { ref place, unwind: Some(_), .. } + | TerminatorKind::DropAndReplace { ref place, unwind: Some(_), .. } => place, _ => continue, }; debug!("find_dead_unwinds @ {:?}: {:?}", bb, bb_data); - let path = match env.move_data.rev_lookup.find(location.as_ref()) { + let path = match env.move_data.rev_lookup.find(place.as_ref()) { LookupResult::Exact(e) => e, LookupResult::Parent(..) => { debug!("find_dead_unwinds: has parent; skipping"); @@ -101,11 +101,11 @@ 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, - location, + place, path, flow_inits.get() ); @@ -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) { @@ -213,7 +213,7 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { fn field_subpath(&self, path: Self::Path, field: Field) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { - ProjectionElem::Field(idx, _) => *idx == field, + ProjectionElem::Field(idx, _) => idx == field, _ => false, }) } @@ -221,9 +221,9 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { ProjectionElem::ConstantIndex { offset, min_length, from_end } => { - debug_assert!(size == *min_length, "min_length should be exact for arrays"); + debug_assert!(size == min_length, "min_length should be exact for arrays"); assert!(!from_end, "from_end should not be used for array element ConstantIndex"); - *offset == index + offset == index } _ => false, }) @@ -231,13 +231,13 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { fn deref_subpath(&self, path: Self::Path) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| { - *e == ProjectionElem::Deref + e == ProjectionElem::Deref }) } fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { - ProjectionElem::Downcast(_, idx) => *idx == variant, + ProjectionElem::Downcast(_, idx) => idx == variant, _ => false, }) } @@ -294,16 +294,16 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn collect_drop_flags(&mut self) { for (bb, data) in self.body.basic_blocks().iter_enumerated() { let terminator = data.terminator(); - let location = match terminator.kind { - TerminatorKind::Drop { ref location, .. } - | TerminatorKind::DropAndReplace { ref location, .. } => location, + let place = match terminator.kind { + TerminatorKind::Drop { ref place, .. } + | TerminatorKind::DropAndReplace { ref place, .. } => place, _ => continue, }; self.init_data.seek_before(self.body.terminator_loc(bb)); - let path = self.move_data().rev_lookup.find(location.as_ref()); - debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, location, path); + let path = self.move_data().rev_lookup.find(place.as_ref()); + debug!("collect_drop_flags: {:?}, place {:?} ({:?})", bb, place, path); let path = match path { LookupResult::Exact(e) => e, @@ -315,7 +315,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { terminator.source_info.span, "drop of untracked, uninitialized value {:?}, place {:?} ({:?})", bb, - location, + place, path ); } @@ -328,7 +328,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { debug!( "collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", child, - location, + place, path, (maybe_live, maybe_dead) ); @@ -346,13 +346,13 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let resume_block = self.patch.resume_block(); match terminator.kind { - TerminatorKind::Drop { ref location, target, unwind } => { + TerminatorKind::Drop { place, target, unwind } => { self.init_data.seek_before(loc); - match self.move_data().rev_lookup.find(location.as_ref()) { + match self.move_data().rev_lookup.find(place.as_ref()) { LookupResult::Exact(path) => elaborate_drop( &mut Elaborator { ctxt: self }, terminator.source_info, - location, + place, path, target, if data.is_cleanup { @@ -371,10 +371,10 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } } } - TerminatorKind::DropAndReplace { ref location, ref value, target, unwind } => { + TerminatorKind::DropAndReplace { place, ref value, target, unwind } => { assert!(!data.is_cleanup); - self.elaborate_replace(loc, location, value, target, unwind); + self.elaborate_replace(loc, place, value, target, unwind); } _ => continue, } @@ -396,7 +396,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn elaborate_replace( &mut self, loc: Location, - location: &Place<'tcx>, + place: 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 (place, Rvalue::Use(value.clone()))), source_info: terminator.source_info, }; @@ -427,14 +427,14 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { is_cleanup: false, }); - match self.move_data().rev_lookup.find(location.as_ref()) { + match self.move_data().rev_lookup.find(place.as_ref()) { LookupResult::Exact(path) => { debug!("elaborate_drop_and_replace({:?}) - tracked {:?}", terminator, path); self.init_data.seek_before(loc); elaborate_drop( &mut Elaborator { ctxt: self }, terminator.source_info, - location, + place, path, target, Unwind::To(unwind), @@ -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 { place, target, unwind: Some(unwind) }, ); } } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs deleted file mode 100644 index 996b97c03b14e..0000000000000 --- a/src/librustc_mir/transform/erase_regions.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! This pass erases all early-bound regions from the types occurring in the MIR. -//! We want to do this once just before codegen, so codegen does not have to take -//! care erasing regions all over the place. -//! N.B., we do _not_ erase regions of statements that are relevant for -//! "types-as-contracts"-validation, namely, `AcquireValid` and `ReleaseValid`. - -use crate::transform::{MirPass, MirSource}; -use rustc::mir::visit::{MutVisitor, TyContext}; -use rustc::mir::*; -use rustc::ty::subst::SubstsRef; -use rustc::ty::{self, Ty, TyCtxt}; - -struct EraseRegionsVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl EraseRegionsVisitor<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Self { - EraseRegionsVisitor { tcx } - } -} - -impl MutVisitor<'tcx> for EraseRegionsVisitor<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) { - *ty = self.tcx.erase_regions(ty); - } - - fn visit_region(&mut self, region: &mut ty::Region<'tcx>, _: Location) { - *region = self.tcx.lifetimes.re_erased; - } - - fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _: Location) { - *constant = self.tcx.erase_regions(constant); - } - - fn visit_substs(&mut self, substs: &mut SubstsRef<'tcx>, _: Location) { - *substs = self.tcx.erase_regions(substs); - } - - fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option> { - if let PlaceElem::Field(field, ty) = elem { - let new_ty = self.tcx.erase_regions(ty); - - if new_ty != *ty { - return Some(PlaceElem::Field(*field, new_ty)); - } - } - - None - } -} - -pub struct EraseRegions; - -impl<'tcx> MirPass<'tcx> for EraseRegions { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { - EraseRegionsVisitor::new(tcx).visit_body(body); - } -} diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs index 7418a7519ba2b..59be8dc224dee 100644 --- a/src/librustc_mir/transform/generator.rs +++ b/src/librustc_mir/transform/generator.rs @@ -49,26 +49,30 @@ //! 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, Visitor}; +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; +use std::{iter, ops}; pub struct StateTransform; @@ -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(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { + match terminator.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(terminator, location), } } } @@ -107,25 +114,25 @@ impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor<'tcx> { } fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) { - assert_ne!(*local, self_arg()); + assert_ne!(*local, SELF_ARG); } fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { - if place.local == self_arg() { + if place.local == SELF_ARG { replace_base( place, Place { - local: self_arg(), + local: SELF_ARG, projection: self.tcx().intern_place_elems(&[ProjectionElem::Deref]), }, 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 { - assert_ne!(*local, self_arg()); + assert_ne!(local, SELF_ARG); } } } @@ -143,15 +150,15 @@ impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> { } fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) { - assert_ne!(*local, self_arg()); + assert_ne!(*local, SELF_ARG); } fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { - if place.local == self_arg() { + if place.local == SELF_ARG { replace_base( place, Place { - local: self_arg(), + local: SELF_ARG, projection: self.tcx().intern_place_elems(&[ProjectionElem::Field( Field::new(0), self.ref_gen_ty, @@ -160,11 +167,11 @@ 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 { - assert_ne!(*local, self_arg()); + assert_ne!(local, SELF_ARG); } } } @@ -180,9 +187,7 @@ fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtx place.projection = tcx.intern_place_elems(&new_projection); } -fn self_arg() -> Local { - Local::new(1) -} +const SELF_ARG: Local = Local::from_u32(1); /// Generator has not been resumed yet. const UNRESUMED: usize = GeneratorSubsts::UNRESUMED; @@ -202,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> { @@ -218,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, } @@ -237,7 +244,7 @@ impl TransformVisitor<'tcx> { // Create a Place referencing a generator struct field fn make_field(&self, variant_index: VariantIdx, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> { - let self_place = Place::from(self_arg()); + let self_place = Place::from(SELF_ARG); let base = self.tcx.mk_place_downcast_unnamed(self_place, variant_index); let mut projection = base.projection.to_vec(); projection.push(ProjectionElem::Field(Field::new(idx), ty)); @@ -247,7 +254,7 @@ impl TransformVisitor<'tcx> { // Create a statement which changes the discriminant fn set_discr(&self, state_disc: VariantIdx, source_info: SourceInfo) -> Statement<'tcx> { - let self_place = Place::from(self_arg()); + let self_place = Place::from(SELF_ARG); Statement { source_info, kind: StatementKind::SetDiscriminant { @@ -259,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.discr_ty, 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 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) @@ -343,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) @@ -359,18 +366,11 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> { } } -fn make_generator_state_argument_indirect<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - 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 region = ty::ReFree(ty::FreeRegion { scope: def_id, bound_region: ty::BoundRegion::BrEnv }); - - let region = tcx.mk_region(region); - - let ref_gen_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }); + let ref_gen_ty = + tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty: gen_ty, mutbl: Mutability::Mut }); // Replace the by value generator argument body.local_decls.raw[1].ty = ref_gen_ty; @@ -379,10 +379,10 @@ fn make_generator_state_argument_indirect<'tcx>( 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); @@ -403,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); @@ -425,26 +415,9 @@ 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, + saved_locals: GeneratorSavedLocals, /// The set of saved locals live at each suspension point. live_locals_at_suspension_points: Vec>, @@ -456,13 +429,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(); @@ -470,16 +444,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 = @@ -497,17 +466,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 @@ -519,77 +493,102 @@ 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); + let saved_locals = GeneratorSavedLocals(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| saved_locals.renumber_bitset(&live_here)) .collect(); - let storage_conflicts = - compute_storage_conflicts(body_ref, &live_locals, &ignored, requires_storage_results); + let storage_conflicts = compute_storage_conflicts( + body_ref, + &saved_locals, + always_live_locals.clone(), + requires_storage_results, + ); LivenessInfo { - live_locals, + saved_locals, live_locals_at_suspension_points, storage_conflicts, storage_liveness: storage_liveness_map, } } -/// Renumbers the items present in `stored_locals` and applies the renumbering -/// to 'input`. +/// The set of `Local`s that must be saved across yield points. /// -/// For example, if `stored_locals = [1, 3, 5]`, this would be renumbered to -/// `[0, 1, 2]`. Thus, if `input = [3, 5]` we would return `[1, 2]`. -fn renumber_bitset( - input: &BitSet, - stored_locals: &liveness::LiveVarSet, -) -> BitSet { - assert!(stored_locals.superset(&input), "{:?} not a superset of {:?}", stored_locals, input); - let mut out = BitSet::new_empty(stored_locals.count()); - for (idx, local) in stored_locals.iter().enumerate() { - let saved_local = GeneratorSavedLocal::from(idx); - if input.contains(local) { - out.insert(saved_local); +/// `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. +struct GeneratorSavedLocals(BitSet); + +impl GeneratorSavedLocals { + /// Returns an iterator over each `GeneratorSavedLocal` along with the `Local` it corresponds + /// to. + fn iter_enumerated(&self) -> impl '_ + Iterator { + self.iter().enumerate().map(|(i, l)| (GeneratorSavedLocal::from(i), l)) + } + + /// Transforms a `BitSet` that contains only locals saved across yield points to the + /// equivalent `BitSet`. + fn renumber_bitset(&self, input: &BitSet) -> BitSet { + assert!(self.superset(&input), "{:?} not a superset of {:?}", self.0, input); + let mut out = BitSet::new_empty(self.count()); + for (saved_local, local) in self.iter_enumerated() { + if input.contains(local) { + out.insert(saved_local); + } + } + out + } + + fn get(&self, local: Local) -> Option { + if !self.contains(local) { + return None; } + + let idx = self.iter().take_while(|&l| l < local).count(); + Some(GeneratorSavedLocal::new(idx)) + } +} + +impl ops::Deref for GeneratorSavedLocals { + type Target = BitSet; + + fn deref(&self) -> &Self::Target { + &self.0 } - debug!("renumber_bitset({:?}, {:?}) => {:?}", input, stored_locals, out); - out } /// For every saved local, looks for which locals are StorageLive at the same @@ -598,24 +597,24 @@ fn renumber_bitset( /// computation; see `GeneratorLayout` for more. fn compute_storage_conflicts( body: &'mir Body<'tcx>, - stored_locals: &liveness::LiveVarSet, - ignored: &StorageIgnored, + saved_locals: &GeneratorSavedLocals, + 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()); + assert_eq!(body.local_decls.len(), saved_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(saved_locals); // Compute the storage conflicts for all eligible locals. let mut visitor = StorageConflictVisitor { body, - stored_locals: &stored_locals, + saved_locals: &saved_locals, local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()), }; @@ -632,16 +631,14 @@ fn compute_storage_conflicts( // However, in practice these bitsets are not usually large. The layout code // also needs to keep track of how many conflicts each local has, so it's // simpler to keep it this way for now. - let mut storage_conflicts = BitMatrix::new(stored_locals.count(), stored_locals.count()); - for (idx_a, local_a) in stored_locals.iter().enumerate() { - let saved_local_a = GeneratorSavedLocal::new(idx_a); + let mut storage_conflicts = BitMatrix::new(saved_locals.count(), saved_locals.count()); + for (saved_local_a, local_a) in saved_locals.iter_enumerated() { if ineligible_locals.contains(local_a) { // Conflicts with everything. storage_conflicts.insert_all_into_row(saved_local_a); } else { // Keep overlap information only for stored locals. - for (idx_b, local_b) in stored_locals.iter().enumerate() { - let saved_local_b = GeneratorSavedLocal::new(idx_b); + for (saved_local_b, local_b) in saved_locals.iter_enumerated() { if local_conflicts.contains(local_a, local_b) { storage_conflicts.insert(saved_local_a, saved_local_b); } @@ -653,7 +650,7 @@ fn compute_storage_conflicts( struct StorageConflictVisitor<'mir, 'tcx, 's> { body: &'mir Body<'tcx>, - stored_locals: &'s liveness::LiveVarSet, + saved_locals: &'s GeneratorSavedLocals, // FIXME(tmandry): Consider using sparse bitsets here once we have good // benchmarks for generators. local_conflicts: BitMatrix, @@ -662,7 +659,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>, @@ -671,7 +668,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>, @@ -684,13 +681,12 @@ 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); + eligible_storage_live.intersect(&self.saved_locals); for local in eligible_storage_live.iter() { self.local_conflicts.union_row_with(&eligible_storage_live, local); @@ -702,60 +698,73 @@ impl<'body, 'tcx, 's> StorageConflictVisitor<'body, 'tcx, 's> { } } -fn compute_layout<'tcx>( +/// Validates the typeck view of the generator against the actual set of types saved between +/// yield points. +fn sanitize_witness<'tcx>( tcx: TyCtxt<'tcx>, - source: MirSource<'tcx>, + body: &Body<'tcx>, + did: DefId, + witness: Ty<'tcx>, upvars: &Vec>, - interior: Ty<'tcx>, - movable: bool, - body: &mut BodyAndCache<'tcx>, -) -> ( - FxHashMap, VariantIdx, usize)>, - GeneratorLayout<'tcx>, - FxHashMap, + saved_locals: &GeneratorSavedLocals, ) { - // Use a liveness analysis to compute locals which are live across a suspension point - let LivenessInfo { - live_locals, - live_locals_at_suspension_points, - storage_conflicts, - storage_liveness, - } = locals_live_across_suspend_points(tcx, read_only!(body), source, movable); - - // Erase regions from the types passed in from typeck so we can compare them with - // MIR types let allowed_upvars = tcx.erase_regions(upvars); - let allowed = match interior.kind { + let allowed = match witness.kind { ty::GeneratorWitness(s) => tcx.erase_late_bound_regions(&s), - _ => bug!(), + _ => { + tcx.sess.delay_span_bug( + body.span, + &format!("unexpected generator witness type {:?}", witness.kind), + ); + return; + } }; + let param_env = tcx.param_env(did); + for (local, decl) in body.local_decls.iter_enumerated() { - // Ignore locals which are internal or not live - if !live_locals.contains(local) || decl.internal { + // Ignore locals which are internal or not saved between yields. + if !saved_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, \ but typeck only knows about {}", decl.ty, - interior + witness, ); } } +} + +fn compute_layout<'tcx>( + liveness: LivenessInfo, + body: &mut Body<'tcx>, +) -> ( + FxHashMap, VariantIdx, usize)>, + GeneratorLayout<'tcx>, + IndexVec>>, +) { + let LivenessInfo { + saved_locals, + live_locals_at_suspension_points, + storage_conflicts, + storage_liveness, + } = liveness; // Gather live local types and their indices. let mut locals = IndexVec::::new(); let mut tys = IndexVec::::new(); - for (idx, local) in live_locals.iter().enumerate() { + for (saved_local, local) in saved_locals.iter_enumerated() { locals.push(local); tys.push(body.local_decls[local].ty); - debug!("generator saved local {:?} => {:?}", GeneratorSavedLocal::from(idx), local); + debug!("generator saved local {:?} => {:?}", saved_local, local); } // Leave empty variants for the UNRESUMED, RETURNED, and POISONED states. @@ -792,7 +801,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>, @@ -806,7 +815,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 { @@ -823,11 +832,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; @@ -837,15 +842,14 @@ fn elaborate_generator_drops<'tcx>( // generator's resume function. let param_env = tcx.param_env(def_id); - let gen = self_arg(); let mut elaborator = DropShimElaborator { body, patch: MirPatch::new(body), tcx, param_env }; for (block, block_data) in body.basic_blocks().iter_enumerated() { let (target, unwind, source_info) = match block_data.terminator() { - Terminator { source_info, kind: TerminatorKind::Drop { location, target, unwind } } => { - if let Some(local) = location.as_local() { - if local == gen { + Terminator { source_info, kind: TerminatorKind::Drop { place, target, unwind } } => { + if let Some(local) = place.as_local() { + if local == SELF_ARG { (target, unwind, source_info) } else { continue; @@ -864,7 +868,7 @@ fn elaborate_generator_drops<'tcx>( elaborate_drop( &mut elaborator, *source_info, - &Place::from(gen), + Place::from(SELF_ARG), (), *target, unwind, @@ -877,16 +881,15 @@ fn elaborate_generator_drops<'tcx>( fn create_generator_drop_shim<'tcx>( tcx: TyCtxt<'tcx>, transform: &TransformVisitor<'tcx>, - def_id: DefId, 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); @@ -905,35 +908,22 @@ 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, def_id, &mut body); + 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( 0, Statement { source_info, - kind: StatementKind::Retag(RetagKind::Raw, box Place::from(self_arg())), + kind: StatementKind::Retag(RetagKind::Raw, box Place::from(SELF_ARG)), }, ) } @@ -949,23 +939,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()); @@ -981,7 +966,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 }), @@ -991,36 +976,130 @@ fn insert_panic_block<'tcx>( assert_block } +fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { + // Returning from a function with an uninhabited return type is undefined behavior. + if body.return_ty().conservative_is_privately_uninhabited(tcx) { + return false; + } + + // If there's a return terminator the function may return. + for block in body.basic_blocks() { + if let TerminatorKind::Return = block.terminator().kind { + return true; + } + } + + // Otherwise the function can't return. + false +} + +fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { + // Nothing can unwind when landing pads are off. + if tcx.sess.panic_strategy() == PanicStrategy::Abort { + return false; + } + + // Unwinds can only start at certain terminators. + for block in body.basic_blocks() { + match block.terminator().kind { + // These never unwind. + TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => {} + + // Resume will *continue* unwinding, but if there's no other unwinding terminator it + // will never be reached. + TerminatorKind::Resume => {} + + TerminatorKind::Yield { .. } => { + unreachable!("`can_unwind` called before generator transform") + } + + // These may unwind. + TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::Assert { .. } => return true, + } + } + + // If we didn't find an unwinding terminator, the function cannot unwind. + false +} + fn create_generator_resume_function<'tcx>( tcx: TyCtxt<'tcx>, transform: TransformVisitor<'tcx>, - def_id: DefId, 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 - for block in body.basic_blocks_mut() { - let source_info = block.terminator().source_info; - if let &TerminatorKind::Resume = &block.terminator().kind { - block.statements.push(transform.set_discr(VariantIdx::new(POISONED), source_info)); + if can_unwind { + let source_info = SourceInfo::outermost(body.span); + let poison_block = body.basic_blocks_mut().push(BasicBlockData { + statements: vec![transform.set_discr(VariantIdx::new(POISONED), source_info)], + terminator: Some(Terminator { source_info, kind: TerminatorKind::Resume }), + is_cleanup: true, + }); + + for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() { + let source_info = block.terminator().source_info; + + if let TerminatorKind::Resume = block.terminator().kind { + // An existing `Resume` terminator is redirected to jump to our dedicated + // "poisoning block" above. + if idx != poison_block { + *block.terminator_mut() = Terminator { + source_info, + kind: TerminatorKind::Goto { target: poison_block }, + }; + } + } else if !block.is_cleanup { + // Any terminators that *can* unwind but don't have an unwind target set are also + // pointed at our poisoning block (unless they're part of the cleanup path). + if let Some(unwind @ None) = block.terminator_mut().unwind_mut() { + *unwind = Some(poison_block); + } + } } } 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))); // Panic when resumed on the returned or poisoned state let generator_kind = body.generator_kind.unwrap(); - cases.insert(1, (RETURNED, insert_panic_block(tcx, body, ResumedAfterReturn(generator_kind)))); - cases.insert(2, (POISONED, insert_panic_block(tcx, body, ResumedAfterPanic(generator_kind)))); + + if can_unwind { + cases.insert( + 1, + (POISONED, insert_panic_block(tcx, body, ResumedAfterPanic(generator_kind))), + ); + } + + if can_return { + cases.insert( + 1, + (RETURNED, insert_panic_block(tcx, body, ResumedAfterReturn(generator_kind))), + ); + } insert_switch(body, cases, &transform, TerminatorKind::Unreachable); - make_generator_state_argument_indirect(tcx, def_id, body); + make_generator_state_argument_indirect(tcx, body); make_generator_state_argument_pinned(tcx, body); no_landing_pads(tcx, body); @@ -1032,28 +1111,19 @@ 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); + let term = + TerminatorKind::Drop { place: Place::from(SELF_ARG), target: return_block, unwind: None }; + let source_info = SourceInfo::outermost(body.span); + // 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); 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. @@ -1073,11 +1143,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 @@ -1085,7 +1155,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 @@ -1098,7 +1167,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) }); } @@ -1117,7 +1189,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, @@ -1133,7 +1205,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 { @@ -1153,17 +1225,21 @@ impl<'tcx> MirPass<'tcx> for StateTransform { ty::Generator(_, substs, movability) => { let substs = substs.as_generator(); ( - substs.upvar_tys(def_id, tcx).collect(), - substs.witness(def_id, tcx), + substs.upvar_tys().collect(), + substs.witness(), substs.discr_ty(tcx), movability == hir::Movability::Movable, ) } - _ => bug!(), + _ => { + tcx.sess + .delay_span_bug(body.span, &format!("unexpected generator type {}", gen_ty)); + return; + } }; // 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); @@ -1181,7 +1257,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, @@ -1194,11 +1270,29 @@ impl<'tcx> MirPass<'tcx> for StateTransform { }, ); + let always_live_locals = storage::AlwaysLiveLocals::new(&body); + + let liveness_info = + locals_live_across_suspend_points(tcx, body, source, &always_live_locals, movable); + + sanitize_witness(tcx, body, def_id, interior, &upvars, &liveness_info.saved_locals); + + if tcx.sess.opts.debugging_opts.validate_mir { + let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias { + assigned_local: None, + saved_locals: &liveness_info.saved_locals, + storage_conflicts: &liveness_info.storage_conflicts, + }; + + vis.visit_body(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); + let (remap, layout, storage_liveness) = compute_layout(liveness_info, body); + + let can_return = can_return(tcx, body); // Run the transformation which converts Places from Local to generator struct // accesses for locals in `remap`. @@ -1210,6 +1304,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { state_substs, remap, storage_liveness, + always_live_locals, suspension_points: Vec::new(), new_ret_local, discr_ty, @@ -1238,11 +1333,142 @@ impl<'tcx> MirPass<'tcx> for StateTransform { // Create a copy of our MIR and use it to create the drop shim for the generator let drop_shim = - create_generator_drop_shim(tcx, &transform, def_id, source, gen_ty, body, drop_clean); + create_generator_drop_shim(tcx, &transform, source, gen_ty, body, drop_clean); body.generator_drop = Some(box drop_shim); // Create the Generator::resume function - create_generator_resume_function(tcx, transform, def_id, source, body); + create_generator_resume_function(tcx, transform, source, body, can_return); + } +} + +/// Looks for any assignments between locals (e.g., `_4 = _5`) that will both be converted to fields +/// in the generator state machine but whose storage is not marked as conflicting +/// +/// Validation needs to happen immediately *before* `TransformVisitor` is invoked, not after. +/// +/// This condition would arise when the assignment is the last use of `_5` but the initial +/// definition of `_4` if we weren't extra careful to mark all locals used inside a statement as +/// conflicting. Non-conflicting generator saved locals may be stored at the same location within +/// the generator state machine, which would result in ill-formed MIR: the left-hand and right-hand +/// sides of an assignment may not alias. This caused a miscompilation in [#73137]. +/// +/// [#73137]: https://github.com/rust-lang/rust/issues/73137 +struct EnsureGeneratorFieldAssignmentsNeverAlias<'a> { + saved_locals: &'a GeneratorSavedLocals, + storage_conflicts: &'a BitMatrix, + assigned_local: Option, +} + +impl EnsureGeneratorFieldAssignmentsNeverAlias<'_> { + fn saved_local_for_direct_place(&self, place: Place<'_>) -> Option { + if place.is_indirect() { + return None; + } + + self.saved_locals.get(place.local) + } + + fn check_assigned_place(&mut self, place: Place<'tcx>, f: impl FnOnce(&mut Self)) { + if let Some(assigned_local) = self.saved_local_for_direct_place(place) { + assert!(self.assigned_local.is_none(), "`check_assigned_place` must not recurse"); + + self.assigned_local = Some(assigned_local); + f(self); + self.assigned_local = None; + } + } +} + +impl Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> { + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + let lhs = match self.assigned_local { + Some(l) => l, + None => { + // This visitor only invokes `visit_place` for the right-hand side of an assignment + // and only after setting `self.assigned_local`. However, the default impl of + // `Visitor::super_body` may call `visit_place` with a `NonUseContext` for places + // with debuginfo. Ignore them here. + assert!(!context.is_use()); + return; + } + }; + + let rhs = match self.saved_local_for_direct_place(*place) { + Some(l) => l, + None => return, + }; + + if !self.storage_conflicts.contains(lhs, rhs) { + bug!( + "Assignment between generator saved locals whose storage is not \ + marked as conflicting: {:?}: {:?} = {:?}", + location, + lhs, + rhs, + ); + } + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + match &statement.kind { + StatementKind::Assign(box (lhs, rhs)) => { + self.check_assigned_place(*lhs, |this| this.visit_rvalue(rhs, location)); + } + + // FIXME: Does `llvm_asm!` have any aliasing requirements? + StatementKind::LlvmInlineAsm(_) => {} + + StatementKind::FakeRead(..) + | StatementKind::SetDiscriminant { .. } + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Retag(..) + | StatementKind::AscribeUserType(..) + | StatementKind::Nop => {} + } + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + // Checking for aliasing in terminators is probably overkill, but until we have actual + // semantics, we should be conservative here. + match &terminator.kind { + TerminatorKind::Call { + func, + args, + destination: Some((dest, _)), + cleanup: _, + from_hir_call: _, + fn_span: _, + } => { + self.check_assigned_place(*dest, |this| { + this.visit_operand(func, location); + for arg in args { + this.visit_operand(arg, location); + } + }); + } + + TerminatorKind::Yield { value, resume: _, resume_arg, drop: _ } => { + self.check_assigned_place(*resume_arg, |this| this.visit_operand(value, location)); + } + + // FIXME: Does `asm!` have any aliasing requirements? + TerminatorKind::InlineAsm { .. } => {} + + TerminatorKind::Call { .. } + | TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Assert { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } => {} + } } } diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index b6802505df73f..068d055fa78f8 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -1,30 +1,28 @@ //! Inlining pass for MIR functions +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::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc::mir::visit::*; -use rustc::mir::*; -use rustc::session::config::Sanitizer; -use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable}; +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_target::spec::abi::Abi; use super::simplify::{remove_dead_blocks, CfgSimplifier}; use crate::transform::{MirPass, MirSource}; use std::collections::VecDeque; use std::iter; -use rustc_attr as attr; -use rustc_target::spec::abi::Abi; - const DEFAULT_THRESHOLD: usize = 50; const HINT_THRESHOLD: usize = 100; const INSTR_COST: usize = 5; const CALL_PENALTY: usize = 25; +const LANDINGPAD_PENALTY: usize = 50; +const RESUME_PENALTY: usize = 45; const UNKNOWN_SIZE_COST: usize = 10; @@ -39,7 +37,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); } @@ -52,7 +50,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 @@ -67,17 +65,10 @@ impl Inliner<'tcx> { let mut callsites = VecDeque::new(); - let mut param_env = self.tcx.param_env(self.source.def_id()); - - let substs = &InternalSubsts::identity_for_item(self.tcx, self.source.def_id()); - - // For monomorphic functions, we can use `Reveal::All` to resolve specialized instances. - if !substs.needs_subst() { - param_env = param_env.with_reveal_all(); - } + 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) = @@ -102,17 +93,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; @@ -133,6 +122,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) { @@ -186,7 +185,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; @@ -231,24 +231,8 @@ impl Inliner<'tcx> { // Avoid inlining functions marked as no_sanitize if sanitizer is enabled, // since instrumentation might be enabled and performed on the caller. - match self.tcx.sess.opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => { - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_ADDRESS) { - return false; - } - } - Some(Sanitizer::Memory) => { - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_MEMORY) { - return false; - } - } - Some(Sanitizer::Thread) => { - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_SANITIZE_THREAD) { - return false; - } - } - Some(Sanitizer::Leak) => {} - None => {} + if self.tcx.sess.opts.debugging_opts.sanitizer.intersects(codegen_fn_attrs.no_sanitize) { + return false; } let hinted = match codegen_fn_attrs.inline { @@ -318,16 +302,17 @@ impl Inliner<'tcx> { let term = blk.terminator(); let mut is_drop = false; match term.kind { - TerminatorKind::Drop { ref location, target, unwind } - | TerminatorKind::DropAndReplace { ref location, target, unwind, .. } => { + TerminatorKind::Drop { ref place, target, unwind } + | TerminatorKind::DropAndReplace { ref place, target, unwind, .. } => { is_drop = true; work_list.push(target); - // If the location doesn't actually need dropping, treat it like + // If the place doesn't actually need dropping, treat it like // a regular goto. - let ty = location.ty(callee_body, tcx).subst(tcx, callsite.substs).ty; + let ty = place.ty(callee_body, tcx).subst(tcx, callsite.substs).ty; if ty.needs_drop(tcx, param_env) { cost += CALL_PENALTY; if let Some(unwind) = unwind { + cost += LANDINGPAD_PENALTY; work_list.push(unwind); } } else { @@ -343,7 +328,7 @@ impl Inliner<'tcx> { threshold = 0; } - TerminatorKind::Call { func: Operand::Constant(ref f), .. } => { + TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => { if let ty::FnDef(def_id, _) = f.literal.ty.kind { // Don't give intrinsics the extra penalty for calls let f = tcx.fn_sig(def_id); @@ -352,9 +337,21 @@ impl Inliner<'tcx> { } else { cost += CALL_PENALTY; } + } else { + cost += CALL_PENALTY; + } + if cleanup.is_some() { + cost += LANDINGPAD_PENALTY; + } + } + TerminatorKind::Assert { cleanup, .. } => { + cost += CALL_PENALTY; + + if cleanup.is_some() { + cost += LANDINGPAD_PENALTY; } } - TerminatorKind::Assert { .. } => cost += CALL_PENALTY, + TerminatorKind::Resume => cost += RESUME_PENALTY, _ => cost += INSTR_COST, } @@ -401,8 +398,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 { @@ -445,7 +442,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, @@ -456,7 +453,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, @@ -464,9 +461,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); @@ -530,7 +527,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; @@ -564,7 +561,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"); @@ -577,7 +574,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) @@ -597,7 +594,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. @@ -615,9 +612,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 { @@ -701,18 +698,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); @@ -729,10 +714,14 @@ 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); + fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, loc: Location) { + // Don't try to modify the implicit `_0` access on return (`return` terminators are + // replaced down below anyways). + if !matches!(terminator.kind, TerminatorKind::Return) { + self.super_terminator(terminator, loc); + } - match *kind { + match terminator.kind { TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => bug!(), TerminatorKind::Goto { ref mut target } => { *target = self.update_target(*target); @@ -776,16 +765,16 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } } TerminatorKind::Return => { - *kind = TerminatorKind::Goto { target: self.return_block }; + terminator.kind = TerminatorKind::Goto { target: self.return_block }; } TerminatorKind::Resume => { if let Some(tgt) = self.cleanup_block { - *kind = TerminatorKind::Goto { target: tgt } + terminator.kind = TerminatorKind::Goto { target: tgt } } } TerminatorKind::Abort => {} TerminatorKind::Unreachable => {} - TerminatorKind::FalseEdges { ref mut real_target, ref mut imaginary_target } => { + TerminatorKind::FalseEdge { ref mut real_target, ref mut imaginary_target } => { *real_target = self.update_target(*real_target); *imaginary_target = self.update_target(*imaginary_target); } @@ -794,6 +783,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..7967137e01e54 100644 --- a/src/librustc_mir/transform/instcombine.rs +++ b/src/librustc_mir/transform/instcombine.rs @@ -1,32 +1,26 @@ //! 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_hir::Mutability; use rustc_index::vec::Idx; +use rustc_middle::mir::visit::{MutVisitor, Visitor}; +use rustc_middle::mir::{ + Body, Constant, Local, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, +}; +use rustc_middle::ty::{self, TyCtxt}; use std::mem; pub struct InstCombine; impl<'tcx> MirPass<'tcx> for InstCombine { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut 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 }; @@ -95,7 +89,9 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> { if let PlaceRef { local, projection: &[ref proj_base @ .., ProjectionElem::Deref] } = place.as_ref() { - if Place::ty_from(local, proj_base, self.body, self.tcx).ty.is_region_ptr() { + // The dereferenced place must have type `&_`. + let ty = Place::ty_from(local, proj_base, self.body, self.tcx).ty; + if let ty::Ref(_, _, Mutability::Not) = ty.kind { self.optimizations.and_stars.insert(location); } } diff --git a/src/librustc_mir/transform/instrument_coverage.rs b/src/librustc_mir/transform/instrument_coverage.rs new file mode 100644 index 0000000000000..06b648ab5a908 --- /dev/null +++ b/src/librustc_mir/transform/instrument_coverage.rs @@ -0,0 +1,193 @@ +use crate::transform::{MirPass, MirSource}; +use crate::util::patch::MirPatch; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::lang_items; +use rustc_middle::hir; +use rustc_middle::ich::StableHashingContext; +use rustc_middle::mir::interpret::{ConstValue, Scalar}; +use rustc_middle::mir::{ + self, traversal, BasicBlock, BasicBlockData, CoverageData, Operand, Place, SourceInfo, + StatementKind, Terminator, TerminatorKind, START_BLOCK, +}; +use rustc_middle::ty; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{ConstKind, FnDef}; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +/// Inserts call to count_code_region() as a placeholder to be replaced during code generation with +/// the intrinsic llvm.instrprof.increment. +pub struct InstrumentCoverage; + +/// The `query` provider for `CoverageData`, requested by `codegen_intrinsic_call()` when +/// constructing the arguments for `llvm.instrprof.increment`. +pub(crate) fn provide(providers: &mut Providers<'_>) { + providers.coverage_data = |tcx, def_id| { + let mir_body = tcx.optimized_mir(def_id); + // FIXME(richkadel): The current implementation assumes the MIR for the given DefId + // represents a single function. Validate and/or correct if inlining and/or monomorphization + // invalidates these assumptions. + let count_code_region_fn = + tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None); + let mut num_counters: u32 = 0; + // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected + // counters, with each counter having an index from `0..num_counters-1`. MIR optimization + // may split and duplicate some BasicBlock sequences. Simply counting the calls may not + // not work; but computing the num_counters by adding `1` to the highest index (for a given + // instrumented function) is valid. + for (_, data) in traversal::preorder(mir_body) { + if let Some(terminator) = &data.terminator { + if let TerminatorKind::Call { func: Operand::Constant(func), args, .. } = + &terminator.kind + { + if let FnDef(called_fn_def_id, _) = func.literal.ty.kind { + if called_fn_def_id == count_code_region_fn { + if let Operand::Constant(constant) = + args.get(0).expect("count_code_region has at least one arg") + { + if let ConstKind::Value(ConstValue::Scalar(value)) = + constant.literal.val + { + let index = value + .to_u32() + .expect("count_code_region index at arg0 is u32"); + num_counters = std::cmp::max(num_counters, index + 1); + } + } + } + } + } + } + } + let hash = if num_counters > 0 { hash_mir_source(tcx, def_id) } else { 0 }; + CoverageData { num_counters, hash } + }; +} + +struct Instrumentor<'tcx> { + tcx: TyCtxt<'tcx>, + num_counters: u32, +} + +impl<'tcx> MirPass<'tcx> for InstrumentCoverage { + fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, mir_body: &mut mir::Body<'tcx>) { + if tcx.sess.opts.debugging_opts.instrument_coverage { + // If the InstrumentCoverage pass is called on promoted MIRs, skip them. + // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 + if src.promoted.is_none() { + debug!( + "instrumenting {:?}, span: {}", + src.def_id(), + tcx.sess.source_map().span_to_string(mir_body.span) + ); + Instrumentor::new(tcx).inject_counters(mir_body); + } + } + } +} + +impl<'tcx> Instrumentor<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { tcx, num_counters: 0 } + } + + fn next_counter(&mut self) -> u32 { + let next = self.num_counters; + self.num_counters += 1; + next + } + + fn inject_counters(&mut self, mir_body: &mut mir::Body<'tcx>) { + // FIXME(richkadel): As a first step, counters are only injected at the top of each + // function. The complete solution will inject counters at each conditional code branch. + let top_of_function = START_BLOCK; + let entire_function = mir_body.span; + + self.inject_counter(mir_body, top_of_function, entire_function); + } + + fn inject_counter( + &mut self, + mir_body: &mut mir::Body<'tcx>, + next_block: BasicBlock, + code_region: Span, + ) { + let injection_point = code_region.shrink_to_lo(); + + let count_code_region_fn = function_handle( + self.tcx, + self.tcx.require_lang_item(lang_items::CountCodeRegionFnLangItem, None), + injection_point, + ); + let counter_index = Operand::const_from_scalar( + self.tcx, + self.tcx.types.u32, + Scalar::from_u32(self.next_counter()), + injection_point, + ); + + let mut patch = MirPatch::new(mir_body); + + let temp = patch.new_temp(self.tcx.mk_unit(), code_region); + let new_block = patch.new_block(placeholder_block(code_region)); + patch.patch_terminator( + new_block, + TerminatorKind::Call { + func: count_code_region_fn, + args: vec![counter_index], + // new_block will swapped with the next_block, after applying patch + destination: Some((Place::from(temp), new_block)), + cleanup: None, + from_hir_call: false, + fn_span: injection_point, + }, + ); + + patch.add_statement(new_block.start_location(), StatementKind::StorageLive(temp)); + patch.add_statement(next_block.start_location(), StatementKind::StorageDead(temp)); + + patch.apply(mir_body); + + // To insert the `new_block` in front of the first block in the counted branch (the + // `next_block`), just swap the indexes, leaving the rest of the graph unchanged. + mir_body.basic_blocks_mut().swap(next_block, new_block); + } +} + +fn function_handle<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: DefId, span: Span) -> Operand<'tcx> { + let ret_ty = tcx.fn_sig(fn_def_id).output(); + let ret_ty = ret_ty.no_bound_vars().unwrap(); + let substs = tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(ret_ty))); + Operand::function_handle(tcx, fn_def_id, substs, span) +} + +fn placeholder_block(span: Span) -> BasicBlockData<'tcx> { + BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: SourceInfo::outermost(span), + // this gets overwritten by the counter Call + kind: TerminatorKind::Unreachable, + }), + is_cleanup: false, + } +} + +fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> u64 { + let hir_node = tcx.hir().get_if_local(def_id).expect("DefId is local"); + let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); + let hir_body = tcx.hir().body(fn_body_id); + let mut hcx = tcx.create_no_span_stable_hashing_context(); + hash(&mut hcx, &hir_body.value).to_smaller_hash() +} + +fn hash( + hcx: &mut StableHashingContext<'tcx>, + node: &impl HashStable>, +) -> Fingerprint { + let mut stable_hasher = StableHasher::new(); + node.hash_stable(hcx, &mut stable_hasher); + stable_hasher.finish() +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index d4a5966af4afe..8ca240d2c7da7 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -1,21 +1,23 @@ use crate::{shim, util}; -use rustc::hir::map::Map; -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; pub mod add_moves_for_packed_drops; pub mod add_retag; pub mod check_consts; +pub mod check_packed_ref; pub mod check_unsafety; pub mod cleanup_post_borrowck; pub mod const_prop; @@ -23,20 +25,23 @@ pub mod copy_prop; pub mod deaggregator; pub mod dump_mir; pub mod elaborate_drops; -pub mod erase_regions; pub mod generator; pub mod inline; pub mod instcombine; +pub mod instrument_coverage; pub mod no_landing_pads; +pub mod nrvo; pub mod promote_consts; pub mod qualify_min_const_fn; pub mod remove_noop_landing_pads; +pub mod required_consts; pub mod rustc_peek; pub mod simplify; pub mod simplify_branches; pub mod simplify_try; pub mod uninhabited_enum_branching; pub mod unreachable_prop; +pub mod validate; pub(crate) fn provide(providers: &mut Providers<'_>) { self::check_unsafety::provide(providers); @@ -45,23 +50,25 @@ pub(crate) fn provide(providers: &mut Providers<'_>) { mir_const, mir_const_qualif, mir_validated, + mir_drops_elaborated_and_const_checked, optimized_mir, is_mir_available, promoted_mir, ..*providers }; + instrument_coverage::provide(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()); @@ -70,13 +77,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, @@ -86,7 +93,7 @@ fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> &DefIdSet { } intravisit::walk_struct_def(self, v) } - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } @@ -95,7 +102,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. @@ -133,24 +140,30 @@ 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(); + let source = MirSource { instance, promoted }; + let validate = tcx.sess.opts.debugging_opts.validate_mir; if body.phase >= mir_phase { return; } - let source = MirSource { instance, promoted }; + if validate { + validate::Validator { when: format!("input to phase {:?}", mir_phase) } + .run_pass(tcx, source, body); + } + let mut index = 0; let mut run_pass = |pass: &dyn MirPass<'tcx>| { let run_hooks = |body: &_, index, is_after| { @@ -167,18 +180,30 @@ pub fn run_passes( pass.run_pass(tcx, source, body); run_hooks(body, index, true); + if validate { + validate::Validator { when: format!("after {} in phase {:?}", pass.name(), mir_phase) } + .run_pass(tcx, source, body); + } + index += 1; }; - for pass in passes { - run_pass(*pass); + for pass_group in passes { + for pass in *pass_group { + run_pass(*pass); + } } body.phase = mir_phase; + + if mir_phase == MirPhase::Optimized { + validate::Validator { when: format!("end of phase {:?}", mir_phase) } + .run_pass(tcx, source, body); + } } fn mir_const_qualif(tcx: TyCtxt<'_>, def_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() { @@ -196,15 +221,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 @@ -212,120 +232,198 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs { validator.qualifs_in_return_place() } -fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal> { - // Unsafety check uses the raw mir, so make sure it is run +/// Make MIR ready for const evaluation. This is run on all MIR, not just on consts! +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, - &[ + &[&[ + // MIR-level lints. + &check_packed_ref::CheckPackedRef, // 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"), - ], + // If the `instrument-coverage` option is enabled, analyze the CFG, identify each + // conditional branch, construct a coverage map to be passed to LLVM, and inject counters + // where needed. + &instrument_coverage::InstrumentCoverage, + ]], ); let promoted = promote_pass.promoted_fragments.into_inner(); (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted)) } +fn mir_drops_elaborated_and_const_checked<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Steal> { + // (Mir-)Borrowck uses `mir_validated`, so we have to force it to + // execute before we can steal. + tcx.ensure().mir_borrowck(def_id); + + let (body, _) = tcx.mir_validated(def_id); + let mut body = body.steal(); + + run_post_borrowck_cleanup_passes(tcx, &mut body, def_id, None); + check_consts::post_drop_elaboration::check_live_drops(tcx, def_id, &body); + tcx.alloc_steal_mir(body) +} + +/// After this series of passes, no lifetime analysis based on borrowing can be done. +fn run_post_borrowck_cleanup_passes<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + def_id: LocalDefId, + promoted: Option, +) { + debug!("post_borrowck_cleanup({:?})", def_id); + + let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[ + // Remove all things only needed by analysis + &no_landing_pads::NoLandingPads::new(tcx), + &simplify_branches::SimplifyBranches::new("initial"), + &remove_noop_landing_pads::RemoveNoopLandingPads, + &cleanup_post_borrowck::CleanupNonCodegenStatements, + &simplify::SimplifyCfg::new("early-opt"), + // These next passes must be executed together + &add_call_guards::CriticalCallEdges, + &elaborate_drops::ElaborateDrops, + &no_landing_pads::NoLandingPads::new(tcx), + // AddMovesForPackedDrops needs to run after drop + // elaboration. + &add_moves_for_packed_drops::AddMovesForPackedDrops, + // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late, + // but before optimizations begin. + &add_retag::AddRetag, + &simplify::SimplifyCfg::new("elaborate-drops"), + ]; + + run_passes( + tcx, + body, + InstanceDef::Item(def_id.to_def_id()), + promoted, + MirPhase::DropElab, + &[post_borrowck_cleanup], + ); +} + 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 optimizations: &[&dyn MirPass<'tcx>] = &[ + &unreachable_prop::UnreachablePropagation, + &uninhabited_enum_branching::UninhabitedEnumBranching, + &simplify::SimplifyCfg::new("after-uninhabited-enum-branching"), + &inline::Inline, + // Lowering generator control-flow and variables has to happen before we do anything else + // to them. We do this inside the "optimizations" block so that it can benefit from + // optimizations that run before, that might be harder to do on the state machine than MIR + // with async primitives. + &generator::StateTransform, + &instcombine::InstCombine, + &const_prop::ConstProp, + &simplify_branches::SimplifyBranches::new("after-const-prop"), + // Run deaggregation here because: + // 1. Some codegen backends require it + // 2. It creates additional possibilities for some MIR optimizations to trigger + // FIXME(#70073): Why is this done here and not in `post_borrowck_cleanup`? + &deaggregator::Deaggregator, + &simplify_try::SimplifyArmIdentity, + &simplify_try::SimplifyBranchSame, + ©_prop::CopyPropagation, + &simplify_branches::SimplifyBranches::new("after-copy-prop"), + &remove_noop_landing_pads::RemoveNoopLandingPads, + &simplify::SimplifyCfg::new("after-remove-noop-landing-pads"), + &simplify::SimplifyCfg::new("final"), + &nrvo::RenameReturnPlace, + &simplify::SimplifyLocals, + ]; + + let no_optimizations: &[&dyn MirPass<'tcx>] = &[ + // Even if we don't do optimizations, we still have to lower generators for codegen. + &generator::StateTransform, + // FIXME(#70073): This pass is responsible for both optimization as well as some lints. + &const_prop::ConstProp, + // Even if we don't do optimizations, still run deaggregation because some backends assume + // that deaggregation always occurs. + &deaggregator::Deaggregator, + ]; + + let pre_codegen_cleanup: &[&dyn MirPass<'tcx>] = &[ + &add_call_guards::CriticalCallEdges, + // Dump the end result for testing and debugging purposes. + &dump_mir::Marker("PreCodegen"), + ]; + + let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level; + + #[rustfmt::skip] run_passes( tcx, body, - InstanceDef::Item(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. - - // From here on out, regions are gone. - &erase_regions::EraseRegions, - // 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"), + 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 @@ -334,30 +432,33 @@ fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &BodyAndCache<'_> { return shim::build_adt_ctor(tcx, def_id); } - // (Mir-)Borrowck uses `mir_validated`, so we have to force it to - // execute before we can steal. - tcx.ensure().mir_borrowck(def_id); + let def_id = def_id.expect_local(); - let (body, _) = tcx.mir_validated(def_id); - let mut body = body.steal(); + let mut body = tcx.mir_drops_elaborated_and_const_checked(def_id).steal(); run_optimization_passes(tcx, &mut body, def_id, None); - body.ensure_predecessors(); - tcx.arena.alloc(body) + + debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); + + 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_post_borrowck_cleanup_passes(tcx, &mut body, def_id, Some(p)); run_optimization_passes(tcx, &mut body, def_id, Some(p)); - body.ensure_predecessors(); } - tcx.intern_promoted(promoted) + debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR"); + + promoted } diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index e17c733e8bb5f..1d83733e4cd30 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); } } @@ -33,10 +34,10 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads<'tcx> { self.tcx } - fn visit_terminator_kind(&mut self, kind: &mut TerminatorKind<'tcx>, location: Location) { - if let Some(unwind) = kind.unwind_mut() { + fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { + if let Some(unwind) = terminator.kind.unwind_mut() { unwind.take(); } - self.super_terminator_kind(kind, location); + self.super_terminator(terminator, location); } } diff --git a/src/librustc_mir/transform/nrvo.rs b/src/librustc_mir/transform/nrvo.rs new file mode 100644 index 0000000000000..1f3d7bb7cc6f4 --- /dev/null +++ b/src/librustc_mir/transform/nrvo.rs @@ -0,0 +1,232 @@ +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; + } + }; + + 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); + + // Sometimes, the return place is assigned a local of a different but coercable type, for + // example `&mut T` instead of `&T`. Overwriting the `LocalInfo` for the return place means + // its type may no longer match the return type of its function. This doesn't cause a + // problem in codegen because these two types are layout-compatible, but may be unexpected. + debug!("_0: {:?} = {:?}: {:?}", ret_decl.ty, returned_local, renamed_decl.ty); + 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); + } + + 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, + } + } + + 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 7dd134a35d909..8bcbcd79ae60b 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,18 +144,16 @@ 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, } 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 +206,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)); @@ -213,47 +215,54 @@ 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 }); + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + self.super_terminator(terminator, location); + + match terminator.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 }) + } + _ => {} } } } + _ => {} } } - - fn visit_source_info(&mut self, source_info: &SourceInfo) { - self.span = source_info.span; - } } 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, + ccx, }; for (bb, data) in rpo { collector.visit_basic_block_data(bb, data); @@ -265,7 +274,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 +287,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 } } @@ -325,14 +334,14 @@ impl<'tcx> Validator<'_, 'tcx> { // `let _: &'static _ = &(Cell::new(1), 2).1;` let mut place_projection = &place.projection[..]; // FIXME(eddyb) use a forward loop instead of a reverse one. - while let [proj_base @ .., elem] = place_projection { + while let &[ref 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) + 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) { + if ty.is_freeze(self.tcx.at(DUMMY_SP), self.param_env) { has_mut_interior = false; break; } @@ -350,12 +359,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,20 +413,34 @@ 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!(), + } + } } } // FIXME(eddyb) maybe cache this? fn qualif_local(&self, local: Local) -> bool { - let per_local = &mut |l| self.qualif_local::(l); - if let TempState::Defined { location: loc, .. } = self.temps[local] { let num_stmts = self.body[loc.block].statements.len(); if loc.statement_index < num_stmts { let statement = &self.body[loc.block].statements[loc.statement_index]; match &statement.kind { - StatementKind::Assign(box (_, rhs)) => Q::in_rvalue(&self.item, per_local, rhs), + StatementKind::Assign(box (_, rhs)) => qualifs::in_rvalue::( + &self.ccx, + &mut |l| self.qualif_local::(l), + rhs, + ), _ => { span_bug!( statement.source_info.span, @@ -427,9 +452,9 @@ impl<'tcx> Validator<'_, 'tcx> { } else { let terminator = self.body[loc.block].terminator(); match &terminator.kind { - TerminatorKind::Call { func, args, .. } => { + TerminatorKind::Call { .. } => { let return_ty = self.body.local_decls[local].ty; - Q::in_call(&self.item, per_local, func, args, return_ty) + Q::in_any_value_of_ty(&self.ccx, return_ty) } kind => { span_bug!(terminator.source_info.span, "{:?} not promotable", kind); @@ -492,7 +517,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() { @@ -518,12 +543,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); } @@ -537,11 +562,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); } @@ -550,7 +575,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 @@ -572,6 +597,8 @@ impl<'tcx> Validator<'_, 'tcx> { } match rvalue { + Rvalue::ThreadLocalRef(_) => Err(Unpromotable), + Rvalue::NullaryOp(..) => Ok(()), Rvalue::Discriminant(place) | Rvalue::Len(place) => self.validate_place(place.as_ref()), @@ -590,7 +617,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, @@ -603,12 +630,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(_) => {} @@ -629,7 +656,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 }; } @@ -645,13 +672,13 @@ impl<'tcx> Validator<'_, 'tcx> { if has_mut_interior { let mut place_projection = place.projection; // FIXME(eddyb) use a forward loop instead of a reverse one. - while let [proj_base @ .., elem] = place_projection { + while let &[ref 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) { + if ty.is_freeze(self.tcx.at(DUMMY_SP), self.param_env) { has_mut_interior = false; break; } @@ -681,7 +708,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 { @@ -716,13 +743,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() @@ -734,11 +759,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); } _ => (), } @@ -750,8 +789,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>)>, @@ -766,7 +805,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, @@ -777,7 +816,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)), }); } @@ -806,7 +845,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, )); @@ -833,7 +872,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, @@ -860,7 +903,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { }; match terminator.kind { - TerminatorKind::Call { mut func, mut args, from_hir_call, .. } => { + TerminatorKind::Call { mut func, mut args, from_hir_call, fn_span, .. } => { self.visit_operand(&mut func, loc); for arg in &mut args { self.visit_operand(arg, loc); @@ -876,6 +919,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { cleanup: None, destination: Some((Place::from(new_temp), new_target)), from_hir_call, + fn_span, }, ..terminator }; @@ -895,14 +939,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, @@ -911,7 +955,13 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { ty, val: ty::ConstKind::Unevaluated( def_id, - InternalSubsts::identity_for_item(tcx, def_id), + InternalSubsts::for_item(tcx, def_id, |param, _| { + if let ty::GenericParamDefKind::Lifetime = param.kind { + tcx.lifetimes.re_erased.into() + } else { + tcx.mk_param_from_def(param) + } + }), Some(promoted_id), ), }), @@ -944,7 +994,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); @@ -1002,6 +1052,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!(), + } + } } }; @@ -1028,24 +1096,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); @@ -1056,24 +1115,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(), @@ -1091,7 +1148,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, @@ -1129,15 +1186,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 { place, target, .. } = &terminator.kind { + if let Some(index) = place.as_local() { + if promoted(index) { + terminator.kind = TerminatorKind::Goto { target: *target }; } } - _ => {} } } @@ -1149,22 +1203,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..caf6c7715a9e1 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, 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())); } @@ -22,27 +23,30 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) - loop { let predicates = tcx.predicates_of(current); for (predicate, _) in predicates.predicates { - match predicate { - Predicate::RegionOutlives(_) - | Predicate::TypeOutlives(_) - | Predicate::WellFormed(_) - | Predicate::Projection(_) - | Predicate::ConstEvaluatable(..) => continue, - Predicate::ObjectSafe(_) => { + match predicate.kind() { + ty::PredicateKind::RegionOutlives(_) + | ty::PredicateKind::TypeOutlives(_) + | ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::Projection(_) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => continue, + ty::PredicateKind::ObjectSafe(_) => { bug!("object safe predicate on function: {:#?}", predicate) } - Predicate::ClosureKind(..) => { + ty::PredicateKind::ClosureKind(..) => { bug!("closure kind predicate on function: {:#?}", predicate) } - Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate), - Predicate::Trait(pred, constness) => { + ty::PredicateKind::Subtype(_) => { + bug!("subtype predicate on function: {:#?}", predicate) + } + &ty::PredicateKind::Trait(pred, constness) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; } match pred.skip_binder().self_ty().kind { ty::Param(ref p) => { // Allow `T: ?const Trait` - if *constness == hir::Constness::NotConst + if constness == hir::Constness::NotConst && feature_allowed(tcx, def_id, sym::const_trait_bound_opt_out) { continue; @@ -92,7 +96,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) { @@ -144,33 +156,41 @@ fn check_rvalue( span: Span, ) -> McfResult { match rvalue { + Rvalue::ThreadLocalRef(_) => { + Err((span, "cannot access thread local storage in const fn".into())) + } Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => { check_operand(tcx, operand, span, def_id, body) } 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 +235,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 +245,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 +273,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 +283,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 +292,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 +304,7 @@ fn check_place( } } ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Deref | ProjectionElem::Index(_) => {} @@ -324,15 +342,16 @@ fn check_terminator( ) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { - TerminatorKind::FalseEdges { .. } + TerminatorKind::FalseEdge { .. } | 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::DropAndReplace { location, value, .. } => { - check_place(tcx, location, span, def_id, body)?; + TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, def_id, body), + TerminatorKind::DropAndReplace { place, value, .. } => { + check_place(tcx, *place, span, def_id, body)?; check_operand(tcx, value, span, def_id, body) } @@ -344,17 +363,19 @@ 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())) } - TerminatorKind::Call { func, args, from_hir_call: _, destination: _, cleanup: _ } => { + TerminatorKind::Call { + func, + args, + from_hir_call: _, + destination: _, + cleanup: _, + fn_span: _, + } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(def_id, _) = fn_ty.kind { if !crate::const_eval::is_min_const_fn(tcx, def_id) { @@ -383,5 +404,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..bf63bf24447a4 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; } @@ -64,7 +65,7 @@ impl RemoveNoopLandingPads { TerminatorKind::Goto { .. } | TerminatorKind::Resume | TerminatorKind::SwitchInt { .. } - | TerminatorKind::FalseEdges { .. } + | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } => { terminator.successors().all(|&succ| nop_landing_pads.contains(succ)) } @@ -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..4c30a0946bccf 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(); @@ -53,7 +53,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranches { } if (c.literal.try_eval_bool(tcx, param_env) == Some(true)) == expected => { TerminatorKind::Goto { target } } - TerminatorKind::FalseEdges { real_target, .. } => { + TerminatorKind::FalseEdge { real_target, .. } => { TerminatorKind::Goto { target: real_target } } TerminatorKind::FalseUnwind { real_target, .. } => { diff --git a/src/librustc_mir/transform/simplify_try.rs b/src/librustc_mir/transform/simplify_try.rs index 3f28f033047a2..50136ac3becca 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,324 @@ 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.is_empty() { + 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, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { + return; + } + + trace!("running SimplifyArmIdentity on {:?}", source); let (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 +365,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 +385,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 +407,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 +415,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 +431,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 +450,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/transform/validate.rs b/src/librustc_mir/transform/validate.rs new file mode 100644 index 0000000000000..c5343d9b5d014 --- /dev/null +++ b/src/librustc_mir/transform/validate.rs @@ -0,0 +1,222 @@ +//! Validates the MIR to ensure that invariants are upheld. + +use super::{MirPass, MirSource}; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::{ + mir::{ + BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, + }, + ty::{self, ParamEnv, TyCtxt}, +}; + +#[derive(Copy, Clone, Debug)] +enum EdgeKind { + Unwind, + Normal, +} + +pub struct Validator { + /// Describes at which point in the pipeline this validation is happening. + pub when: String, +} + +impl<'tcx> MirPass<'tcx> for Validator { + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + let param_env = tcx.param_env(source.def_id()); + TypeChecker { when: &self.when, source, body, tcx, param_env }.visit_body(body); + } +} + +struct TypeChecker<'a, 'tcx> { + when: &'a str, + source: MirSource<'tcx>, + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, +} + +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + fn fail(&self, location: Location, msg: impl AsRef) { + let span = self.body.source_info(location).span; + // We use `delay_span_bug` as we might see broken MIR when other errors have already + // occurred. + self.tcx.sess.diagnostic().delay_span_bug( + span, + &format!( + "broken MIR in {:?} ({}) at {:?}:\n{}", + self.source.instance, + self.when, + location, + msg.as_ref() + ), + ); + } + + fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) { + if let Some(bb) = self.body.basic_blocks().get(bb) { + let src = self.body.basic_blocks().get(location.block).unwrap(); + match (src.is_cleanup, bb.is_cleanup, edge_kind) { + // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges + (false, false, EdgeKind::Normal) + // Non-cleanup blocks can jump to cleanup blocks along unwind edges + | (false, true, EdgeKind::Unwind) + // Cleanup blocks can jump to cleanup blocks along non-unwind edges + | (true, true, EdgeKind::Normal) => {} + // All other jumps are invalid + _ => { + self.fail( + location, + format!( + "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})", + edge_kind, + bb, + src.is_cleanup, + bb.is_cleanup, + ) + ) + } + } + } else { + self.fail(location, format!("encountered jump to invalid basic block {:?}", bb)) + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { + fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { + // `Operand::Copy` is only supposed to be used with `Copy` types. + if let Operand::Copy(place) = operand { + let ty = place.ty(&self.body.local_decls, self.tcx).ty; + let span = self.body.source_info(location).span; + + if !ty.is_copy_modulo_regions(self.tcx.at(span), self.param_env) { + self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty)); + } + } + + self.super_operand(operand, location); + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + // The sides of an assignment must not alias. Currently this just checks whether the places + // are identical. + if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind { + match rvalue { + Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => { + if dest == src { + self.fail( + location, + "encountered `Assign` statement with overlapping memory", + ); + } + } + _ => {} + } + } + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + match &terminator.kind { + TerminatorKind::Goto { target } => { + self.check_edge(location, *target, EdgeKind::Normal); + } + TerminatorKind::SwitchInt { targets, values, switch_ty, discr } => { + let ty = discr.ty(&self.body.local_decls, self.tcx); + if ty != *switch_ty { + self.fail( + location, + format!( + "encountered `SwitchInt` terminator with type mismatch: {:?} != {:?}", + ty, switch_ty, + ), + ); + } + if targets.len() != values.len() + 1 { + self.fail( + location, + format!( + "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)", + values.len(), + targets.len(), + ), + ); + } + for target in targets { + self.check_edge(location, *target, EdgeKind::Normal); + } + } + TerminatorKind::Drop { target, unwind, .. } => { + self.check_edge(location, *target, EdgeKind::Normal); + if let Some(unwind) = unwind { + self.check_edge(location, *unwind, EdgeKind::Unwind); + } + } + TerminatorKind::DropAndReplace { target, unwind, .. } => { + self.check_edge(location, *target, EdgeKind::Normal); + if let Some(unwind) = unwind { + self.check_edge(location, *unwind, EdgeKind::Unwind); + } + } + TerminatorKind::Call { func, destination, cleanup, .. } => { + let func_ty = func.ty(&self.body.local_decls, self.tcx); + match func_ty.kind { + ty::FnPtr(..) | ty::FnDef(..) => {} + _ => self.fail( + location, + format!("encountered non-callable type {} in `Call` terminator", func_ty), + ), + } + if let Some((_, target)) = destination { + self.check_edge(location, *target, EdgeKind::Normal); + } + if let Some(cleanup) = cleanup { + self.check_edge(location, *cleanup, EdgeKind::Unwind); + } + } + TerminatorKind::Assert { cond, target, cleanup, .. } => { + let cond_ty = cond.ty(&self.body.local_decls, self.tcx); + if cond_ty != self.tcx.types.bool { + self.fail( + location, + format!( + "encountered non-boolean condition of type {} in `Assert` terminator", + cond_ty + ), + ); + } + self.check_edge(location, *target, EdgeKind::Normal); + if let Some(cleanup) = cleanup { + self.check_edge(location, *cleanup, EdgeKind::Unwind); + } + } + TerminatorKind::Yield { resume, drop, .. } => { + self.check_edge(location, *resume, EdgeKind::Normal); + if let Some(drop) = drop { + self.check_edge(location, *drop, EdgeKind::Normal); + } + } + TerminatorKind::FalseEdge { real_target, imaginary_target } => { + self.check_edge(location, *real_target, EdgeKind::Normal); + self.check_edge(location, *imaginary_target, EdgeKind::Normal); + } + TerminatorKind::FalseUnwind { real_target, unwind } => { + self.check_edge(location, *real_target, EdgeKind::Normal); + if let Some(unwind) = unwind { + self.check_edge(location, *unwind, EdgeKind::Unwind); + } + } + TerminatorKind::InlineAsm { destination, .. } => { + if let Some(destination) = destination { + self.check_edge(location, *destination, EdgeKind::Normal); + } + } + // Nothing to validate for these. + TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::GeneratorDrop => {} + } + } +} 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 ecf0a8ea83ca3..5f55a812a4e0d 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 } @@ -168,7 +238,7 @@ where self.elaborator.patch().patch_terminator( bb, TerminatorKind::Drop { - location: *self.place, + place: self.place, target: self.succ, unwind: self.unwind.into_option(), }, @@ -195,7 +265,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 +282,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, @@ -267,12 +337,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() } @@ -315,7 +383,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 +410,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 +423,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 +502,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 +592,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 +613,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 +625,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 { @@ -579,6 +644,7 @@ where destination: Some((unit_temp, succ)), cleanup: unwind.into_option(), from_hir_call: true, + fn_span: self.source_info.span, }, source_info: self.source_info, }), @@ -607,7 +673,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 +683,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 +691,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 +709,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 +723,7 @@ where self.elaborator.patch().patch_terminator( drop_block, TerminatorKind::Drop { - location: tcx.mk_place_deref(ptr.clone()), + place: tcx.mk_place_deref(ptr), target: loop_block, unwind: unwind.into_option(), }, @@ -685,7 +751,7 @@ where .map(|i| { ( tcx.mk_place_elem( - self.place.clone(), + self.place, ProjectionElem::ConstantIndex { offset: i, min_length: size, @@ -703,16 +769,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 +788,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 +814,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 +827,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, @@ -798,8 +864,8 @@ where fn open_drop(&mut self) -> BasicBlock { let ty = self.place_ty(self.place); match ty.kind { - ty::Closure(def_id, substs) => { - let tys: Vec<_> = substs.as_closure().upvar_tys(def_id, self.tcx()).collect(); + ty::Closure(_, substs) => { + let tys: Vec<_> = substs.as_closure().upvar_tys().collect(); self.open_drop_for_tuple(&tys) } // Note that `elaborate_drops` only drops the upvars of a generator, @@ -808,8 +874,8 @@ where // This should only happen for the self argument on the resume function. // It effetively only contains upvars until the generator transformation runs. // See librustc_body/transform/generator.rs for more details. - ty::Generator(def_id, substs, _) => { - let tys: Vec<_> = substs.as_generator().upvar_tys(def_id, self.tcx()).collect(); + ty::Generator(_, substs, _) => { + let tys: Vec<_> = substs.as_generator().upvar_tys().collect(); self.open_drop_for_tuple(&tys) } ty::Tuple(..) => { @@ -838,13 +904,6 @@ 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, @@ -863,6 +922,8 @@ where 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, @@ -879,13 +940,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 +960,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 +971,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 +979,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(); @@ -925,6 +989,7 @@ where destination: Some((unit_temp, target)), cleanup: None, from_hir_call: false, + fn_span: self.source_info.span, }; // FIXME(#43234) let free_block = self.new_block(unwind, call); @@ -935,7 +1000,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 { place: self.place, target, unwind: unwind.into_option() }; self.new_block(unwind, block) } @@ -944,6 +1009,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, @@ -992,7 +1062,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..50193c4a0db7d 100644 --- a/src/librustc_mir/util/graphviz.rs +++ b/src/librustc_mir/util/graphviz.rs @@ -1,7 +1,8 @@ -use rustc::mir::*; -use rustc::ty::TyCtxt; +use rustc_graphviz as dot; use rustc_hir::def_id::DefId; use rustc_index::vec::Idx; +use rustc_middle::mir::*; +use rustc_middle::ty::TyCtxt; use std::fmt::Debug; use std::io::{self, Write}; @@ -76,7 +77,7 @@ where /// Write a graphviz HTML-styled label for the given basic block, with /// all necessary escaping already performed. (This is suitable for /// emitting directly, as is done in this module, or for use with the -/// LabelText::HtmlStr from libgraphviz.) +/// LabelText::HtmlStr from librustc_graphviz.) /// /// `init` and `fini` are callbacks for emitting additional rows of /// data (using HTML enclosed with `` in the emitted text). 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 f8dfddef2bbd5..02614044063fc 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,16 +120,22 @@ 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)?; } writeln!(file)?; extra_data(PassWhere::BeforeCFG, &mut file)?; - write_user_type_annotations(body, &mut file)?; + write_user_type_annotations(tcx, body, &mut file)?; write_mir_fn(tcx, source, body, &mut extra_data, &mut file)?; extra_data(PassWhere::AfterCFG, &mut file)?; }; @@ -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, )?; @@ -348,7 +351,7 @@ fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: where F: FnMut(&mut ExtraComments<'tcx>), { - let mut extra_comments = ExtraComments { _tcx: tcx, comments: vec![] }; + let mut extra_comments = ExtraComments { tcx, comments: vec![] }; visit_op(&mut extra_comments); for comment in extra_comments.comments { writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?; @@ -357,7 +360,7 @@ where } struct ExtraComments<'tcx> { - _tcx: TyCtxt<'tcx>, // don't need it now, but bet we will soon + tcx: TyCtxt<'tcx>, comments: Vec, } @@ -374,7 +377,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> { self.super_constant(constant, location); let Constant { span, user_ty, literal } = constant; self.push("mir::Constant"); - self.push(&format!("+ span: {:?}", span)); + self.push(&format!("+ span: {}", self.tcx.sess.source_map().span_to_string(*span))); if let Some(user_ty) = user_ty { self.push(&format!("+ user_ty: {:?}", user_ty)); } @@ -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,23 +815,23 @@ 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), } ty::print::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere - write!(w, " {}", tcx.def_path_str(src.def_id())) + write!(w, "{}", tcx.def_path_str(src.def_id())) })?; if src.promoted.is_none() && is_function { @@ -592,12 +862,22 @@ fn write_mir_sig( Ok(()) } -fn write_user_type_annotations(body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> { +fn write_user_type_annotations( + tcx: TyCtxt<'_>, + body: &Body<'_>, + w: &mut dyn Write, +) -> io::Result<()> { if !body.user_type_annotations.is_empty() { writeln!(w, "| User Type Annotations")?; } for (index, annotation) in body.user_type_annotations.iter_enumerated() { - writeln!(w, "| {:?}: {:?} at {:?}", index.index(), annotation.user_ty, annotation.span)?; + writeln!( + w, + "| {:?}: {:?} at {}", + index.index(), + annotation.user_ty, + tcx.sess.source_map().span_to_string(annotation.span) + )?; } if !body.user_type_annotations.is_empty() { writeln!(w, "|")?; @@ -606,5 +886,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..401a5009e3cf7 100644 --- a/src/librustc_mir_build/Cargo.toml +++ b/src/librustc_mir_build/Cargo.toml @@ -10,9 +10,9 @@ path = "lib.rs" doctest = false [dependencies] -arena = { path = "../libarena" } +rustc_arena = { path = "../librustc_arena" } 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,8 +20,7 @@ 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_serialize = { path = "../librustc_serialize" } rustc_session = { path = "../librustc_session" } rustc_span = { path = "../librustc_span" } rustc_target = { path = "../librustc_target" } diff --git a/src/librustc_mir_build/build/block.rs b/src/librustc_mir_build/build/block.rs index df5526ad76281..2be4136ad42a0 100644 --- a/src/librustc_mir_build/build/block.rs +++ b/src/librustc_mir_build/build/block.rs @@ -2,14 +2,16 @@ 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_session::lint::builtin::UNSAFE_OP_IN_UNSAFE_FN; +use rustc_session::lint::Level; use rustc_span::Span; impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn ast_block( &mut self, - destination: &Place<'tcx>, + destination: Place<'tcx>, block: BasicBlock, ast_block: &'tcx hir::Block<'tcx>, source_info: SourceInfo, @@ -29,7 +31,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // 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.in_breakable_scope(None, exit_block, destination, |this| { this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) }); this.cfg.goto(unpack!(block_exit), source_info, exit_block); @@ -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 @@ -211,6 +217,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { assert_eq!(self.push_unsafe_count, 0); match self.unpushed_unsafe { Safety::Safe => {} + // no longer treat `unsafe fn`s as `unsafe` contexts (see RFC #2585) + Safety::FnUnsafe + if self.hir.tcx().lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, hir_id).0 + != Level::Allow => {} _ => return, } self.unpushed_unsafe = Safety::ExplicitUnsafe(hir_id); @@ -225,7 +235,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .push_unsafe_count .checked_sub(1) .unwrap_or_else(|| span_bug!(span, "unsafe count underflow")); - if self.push_unsafe_count == 0 { Some(self.unpushed_unsafe) } else { None } + if self.push_unsafe_count == 0 { + Some(self.unpushed_unsafe) + } else { + None + } } }; 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..5949fd1e22ce8 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.at(span), param_env)); + + // 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..e811d68d5a5f8 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,7 +256,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Literal { .. } | ExprKind::StaticRef { .. } | ExprKind::InlineAsm { .. } + | ExprKind::LlvmInlineAsm { .. } | ExprKind::Yield { .. } + | ExprKind::ThreadLocalRef(_) | ExprKind::Call { .. } => { // these are not places, so we need to make a temporary. debug_assert!(match Category::of(&expr.kind) { @@ -341,12 +343,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 +385,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..9531ff0a9071f 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> { @@ -53,6 +53,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = this.source_info(expr_span); match expr.kind { + ExprKind::ThreadLocalRef(did) => block.and(Rvalue::ThreadLocalRef(did)), ExprKind::Scope { region_scope, lint_level, value } => { let region_scope = (region_scope, source_info); this.in_scope(region_scope, lint_level, |this| this.as_rvalue(block, scope, value)) @@ -78,7 +79,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 +98,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 +110,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 +226,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 +254,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 +287,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 +320,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 +341,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 +356,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 +379,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 +431,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 34dd10cbc0fc8..901dadd661278 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(); } @@ -59,24 +63,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(tail_info) = this.block_context.currently_in_block_tail() { 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); - local_decl.internal = true; - local_decl.local_info = LocalInfo::StaticRef { def_id, is_thread_local }; + match expr.kind { + ExprKind::StaticRef { def_id, .. } => { + assert!(!this.hir.tcx().is_thread_local_static(def_id)); + local_decl.internal = true; + local_decl.local_info = Some(box LocalInfo::StaticRef { def_id, is_thread_local: false }); + } + ExprKind::ThreadLocalRef(def_id) => { + assert!(this.hir.tcx().is_thread_local_static(def_id)); + local_decl.internal = true; + local_decl.local_info = Some(box LocalInfo::StaticRef { def_id, is_thread_local: true }); + } + _ => {} } 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, // they are never assigned. ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (), ExprKind::Block { body: hir::Block { expr: None, targeted_by_break: false, .. } } - if expr_ty.is_never() => - { - () - } + if expr_ty.is_never() => {} _ => { this.cfg .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) }); diff --git a/src/librustc_mir_build/build/expr/category.rs b/src/librustc_mir_build/build/expr/category.rs index cc139dee63f92..fb4b7997b6a5a 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,8 @@ impl Category { | ExprKind::Repeat { .. } | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } - | ExprKind::InlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), + | ExprKind::ThreadLocalRef(_) + | 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..0d5bd4c7e61b9 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, }; @@ -139,34 +140,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // 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); - }, - ); + this.in_breakable_scope(Some(loop_block), exit_block, destination, move |this| { + // conduct the test, if necessary + let body_block = this.cfg.start_new_block(); + let diverge_cleanup = this.diverge_cleanup(); + this.cfg.terminate( + loop_block, + source_info, + TerminatorKind::FalseUnwind { + real_target: body_block, + unwind: Some(diverge_cleanup), + }, + ); + + // The “return” value of the loop body must always be an unit. We therefore + // introduce a unit temporary as the destination for the loop body. + let tmp = this.get_unit_temp(); + // Execute the body, branching back to the test. + let body_block_end = unpack!(this.into(tmp, body_block, body)); + this.cfg.goto(body_block_end, source_info, loop_block); + }); exit_block.unit() } - ExprKind::Call { ty, fun, args, from_hir_call } => { + ExprKind::Call { ty, fun, args, from_hir_call, fn_span } => { let intrinsic = match ty.kind { ty::FnDef(def_id, _) => { let f = ty.fn_sig(this.hir.tcx()); @@ -192,22 +188,17 @@ 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(); @@ -215,6 +206,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.record_operands_moved(&args); + debug!("into_expr: fn_span={:?}", fn_span); + this.cfg.terminate( block, source_info, @@ -228,9 +221,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { destination: if expr.ty.is_never() { None } else { - Some((*destination, success)) + Some((destination, success)) }, from_hir_call, + fn_span }, ); success.unit() @@ -278,26 +272,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 +315,84 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); block.unit() } + ExprKind::InlineAsm { template, operands, options, line_spans } => { + use crate::hair; + use rustc_middle::mir; + let operands = operands + .into_iter() + .map(|op| match op { + hair::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In { + reg, + value: unpack!(block = this.as_local_operand(block, expr)), + }, + hair::InlineAsmOperand::Out { reg, late, expr } => { + mir::InlineAsmOperand::Out { + reg, + late, + place: expr.map(|expr| unpack!(block = this.as_place(block, expr))), + } + } + hair::InlineAsmOperand::InOut { reg, late, expr } => { + let place = unpack!(block = this.as_place(block, expr)); + mir::InlineAsmOperand::InOut { + reg, + late, + // This works because asm operands must be Copy + in_value: Operand::Copy(place), + out_place: Some(place), + } + } + hair::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => { + mir::InlineAsmOperand::InOut { + reg, + late, + in_value: unpack!(block = this.as_local_operand(block, in_expr)), + out_place: out_expr.map(|out_expr| { + unpack!(block = this.as_place(block, out_expr)) + }), + } + } + hair::InlineAsmOperand::Const { expr } => mir::InlineAsmOperand::Const { + value: unpack!(block = this.as_local_operand(block, expr)), + }, + hair::InlineAsmOperand::SymFn { expr } => { + mir::InlineAsmOperand::SymFn { value: box this.as_constant(expr) } + } + hair::InlineAsmOperand::SymStatic { def_id } => { + mir::InlineAsmOperand::SymStatic { def_id } + } + }) + .collect(); + + let destination = this.cfg.start_new_block(); + + this.cfg.terminate( + block, + source_info, + TerminatorKind::InlineAsm { + template, + operands, + options, + line_spans, + destination: if options.contains(InlineAsmOptions::NORETURN) { + None + } else { + Some(destination) + }, + }, + ); + destination.unit() + } // These cases don't actually need a destination ExprKind::Assign { .. } | ExprKind::AssignOp { .. } | ExprKind::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 +415,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)); @@ -373,12 +434,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg.terminate( block, source_info, - TerminatorKind::Yield { - value, - resume, - resume_arg: *destination, - drop: cleanup, - }, + TerminatorKind::Yield { value, resume, resume_arg: destination, drop: cleanup }, ); resume.unit() } @@ -394,6 +450,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Tuple { .. } | ExprKind::Closure { .. } | ExprKind::Literal { .. } + | ExprKind::ThreadLocalRef(_) | ExprKind::StaticRef { .. } => { debug_assert!(match Category::of(&expr.kind).unwrap() { // should be handled above 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..19948196f256f 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_data_structures::{fx::{FxHashMap, FxHashSet}, stack::ensure_sufficient_stack}; use rustc_hir::HirId; use rustc_index::bit_set::BitSet; +use rustc_middle::middle::region; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty}; use rustc_span::Span; +use rustc_span::symbol::Symbol; +use rustc_target::abi::VariantIdx; use smallvec::{smallvec, SmallVec}; -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,7 +218,7 @@ 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>)>, @@ -364,7 +364,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 +399,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 +470,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 +511,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 +563,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 +581,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 +609,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 +651,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 +666,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 +764,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, @@ -882,30 +909,32 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { split_or_candidate |= self.simplify_candidate(candidate); } - if split_or_candidate { - // At least one of the candidates has been split into subcandidates. - // We need to change the candidate list to include those. - let mut new_candidates = Vec::new(); + ensure_sufficient_stack(|| { + if split_or_candidate { + // At least one of the candidates has been split into subcandidates. + // We need to change the candidate list to include those. + let mut new_candidates = Vec::new(); - for candidate in candidates { - candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); + for candidate in candidates { + candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); + } + self.match_simplified_candidates( + span, + start_block, + otherwise_block, + &mut *new_candidates, + fake_borrows, + ); + } else { + self.match_simplified_candidates( + span, + start_block, + otherwise_block, + candidates, + fake_borrows, + ); } - self.match_simplified_candidates( - span, - start_block, - otherwise_block, - &mut *new_candidates, - fake_borrows, - ); - } else { - self.match_simplified_candidates( - span, - start_block, - otherwise_block, - candidates, - fake_borrows, - ); - }; + }); } fn match_simplified_candidates( @@ -1017,7 +1046,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { matched_candidates.iter().flat_map(|candidate| &candidate.bindings) { if let Some(i) = - source.projection.iter().rposition(|elem| *elem == ProjectionElem::Deref) + source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) { let proj_base = &source.projection[..i]; @@ -1388,7 +1417,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 +1568,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) }) @@ -1691,7 +1722,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 @@ -1858,7 +1889,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 +1901,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 +1934,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 +1953,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 +1978,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 +2005,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 80fa0c44860e4..2917a771a2cf8 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, @@ -159,13 +160,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ty::Int(ity) => { let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); - let max = truncate(u128::max_value(), size); + let max = truncate(u128::MAX, size); let bias = 1u128 << (size.bits() - 1); (Some((0, max, size)), bias) } ty::Uint(uty) => { let size = Integer::from_attr(&tcx, UnsignedInt(uty)).size(); - let max = truncate(u128::max_value(), size); + let max = truncate(u128::MAX, size); (Some((0, max, size)), 0) } _ => (None, 0), @@ -209,7 +210,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { i == variant_index || { self.hir.tcx().features().exhaustive_patterns && !v - .uninhabited_from(self.hir.tcx(), substs, adt_def.adt_kind()) + .uninhabited_from( + self.hir.tcx(), + substs, + adt_def.adt_kind(), + self.hir.param_env, + ) .is_empty() } }) && (adt_def.did.is_local() diff --git a/src/librustc_mir_build/build/matches/test.rs b/src/librustc_mir_build/build/matches/test.rs index d23a2708dc478..3e7bfc7d59b9b 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); @@ -443,6 +443,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { destination: Some((eq_result, eq_block)), cleanup: Some(cleanup), from_hir_call: false, + fn_span: source_info.span }, ); @@ -582,7 +583,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 +682,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..7d89a93129b1b 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) })); } @@ -89,7 +85,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate( from_block, source_info, - TerminatorKind::FalseEdges { real_target, imaginary_target: target }, + TerminatorKind::FalseEdge { real_target, imaginary_target: target }, ); } _ => self.cfg.goto(from_block, source_info, real_target), 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 c6bc86942957c..e2cf1bce733d6 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}; 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) { @@ -39,16 +39,17 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> { .. }) | Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Method(hir::FnSig { decl, .. }, body_id), + kind: hir::ImplItemKind::Fn(hir::FnSig { decl, .. }, body_id), .. }) | Node::TraitItem(hir::TraitItem { - kind: - hir::TraitItemKind::Fn(hir::FnSig { decl, .. }, hir::TraitMethod::Provided(body_id)), + 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)), @@ -61,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 @@ -128,12 +129,8 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> { let ty = if fn_sig.c_variadic && index == fn_sig.inputs().len() { let va_list_did = tcx.require_lang_item(lang_items::VaListTypeLangItem, Some(arg.span)); - let region = tcx.mk_region(ty::ReScope(region::Scope { - id: body.value.hir_id.local_id, - data: region::ScopeData::CallSite, - })); - tcx.type_of(va_list_did).subst(tcx, &[region.into()]) + tcx.type_of(va_list_did).subst(tcx, &[tcx.lifetimes.re_erased.into()]) } else { fn_sig.inputs()[index] }; @@ -144,10 +141,9 @@ fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyAndCache<'_> { let arguments = implicit_argument.into_iter().chain(explicit_arguments); let (yield_ty, return_ty) = if body.generator_kind.is_some() { - let gen_sig = match ty.kind { - ty::Generator(gen_def_id, gen_substs, ..) => { - gen_substs.as_generator().sig(gen_def_id, tcx) - } + let gen_ty = tcx.body_tables(body_id).node_type(id); + let gen_sig = match gen_ty.kind { + ty::Generator(_, gen_substs, ..) => gen_substs.as_generator().sig(), _ => span_bug!(tcx.hir().span(id), "generator w/o generator type: {:?}", ty), }; (Some(gen_sig.yield_ty), gen_sig.return_ty) @@ -187,8 +183,19 @@ 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 + // by borrow checking. + debug_assert!( + !(body.local_decls.has_free_regions() + || body.basic_blocks().has_free_regions() + || body.var_debug_info.has_free_regions() + || body.yield_ty.has_free_regions()), + "Unexpected free regions in MIR: {:?}", + body, + ); + body }) } @@ -209,7 +216,7 @@ fn liberated_closure_env_ty( }; let closure_env_ty = tcx.closure_env_ty(closure_def_id, closure_substs).unwrap(); - tcx.liberate_late_bound_regions(closure_def_id, &closure_env_ty) + tcx.erase_late_bound_regions(&closure_env_ty) } #[derive(Debug, PartialEq, Eq)] @@ -235,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 @@ -362,13 +372,13 @@ 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 }); } } } - return None; + None } /// Looks at the topmost frame on the BlockContext and reports @@ -386,8 +396,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, } } } @@ -514,9 +526,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. @@ -524,11 +536,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 { @@ -604,7 +611,7 @@ where builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { builder.args_and_body( block, - fn_def_id, + fn_def_id.to_def_id(), &arguments, arg_scope, &body.value, @@ -628,12 +635,13 @@ where ); assert_eq!(block, builder.return_block()); - let mut spread_arg = None; - if abi == Abi::RustCall { + let spread_arg = if abi == Abi::RustCall { // RustCall pseudo-ABI untuples the last argument. - spread_arg = Some(Local::new(arguments.len())); - } - debug!("fn_id {:?} has attrs {:?}", fn_def_id, tcx.get_attrs(fn_def_id)); + Some(Local::new(arguments.len())) + } else { + None + }; + debug!("fn_id {:?} has attrs {:?}", fn_def_id, tcx.get_attrs(fn_def_id.to_def_id())); let mut body = builder.finish(); body.spread_arg = spread_arg; @@ -654,7 +662,7 @@ 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); @@ -679,7 +687,7 @@ fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'t let tcx = hir.tcx(); let owner_id = tcx.hir().body_owner(body_id); let span = tcx.hir().span(owner_id); - let ty = tcx.types.err; + let ty = tcx.ty_error(); let num_params = match hir.body_owner_kind { hir::BodyOwnerKind::Fn => tcx.hir().fn_decl_by_hir_id(owner_id).unwrap().inputs.len(), hir::BodyOwnerKind::Closure => { @@ -700,15 +708,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(); @@ -742,10 +742,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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(), @@ -796,19 +793,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 { @@ -827,11 +814,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let hir_tables = self.hir.tables(); // In analyze_closure() in upvar.rs we gathered a list of upvars used by a - // closure and we stored in a map called upvar_list in TypeckTables indexed + // indexed closure and we stored in a map called closure_captures in TypeckTables // with the closure's DefId. Here, we run through that vec of UpvarIds for // the given closure and use the necessary information to create upvar // debuginfo and to fill `self.upvar_mutbls`. - if let Some(upvars) = hir_tables.upvar_list.get(&fn_def_id) { + if let Some(upvars) = hir_tables.closure_captures.get(&fn_def_id) { let closure_env_arg = Local::new(1); let mut closure_env_projs = vec![]; let mut closure_ty = self.local_decls[closure_env_arg].ty; @@ -839,12 +826,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { closure_env_projs.push(ProjectionElem::Deref); closure_ty = ty; } - let (def_id, upvar_substs) = match closure_ty.kind { - ty::Closure(def_id, substs) => (def_id, ty::UpvarSubsts::Closure(substs)), - ty::Generator(def_id, substs, _) => (def_id, ty::UpvarSubsts::Generator(substs)), + let upvar_substs = match closure_ty.kind { + ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), + ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs), _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty), }; - let upvar_tys = upvar_substs.upvar_tys(def_id, tcx); + let upvar_tys = upvar_substs.upvar_tys(); let upvars_with_tys = upvars.iter().zip(upvar_tys); self.upvar_mutbls = upvars_with_tys .enumerate() @@ -877,10 +864,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), @@ -925,17 +909,19 @@ 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)); } @@ -960,7 +946,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( diff --git a/src/librustc_mir_build/build/scope.rs b/src/librustc_mir_build/build/scope.rs index a63ac06ec3fe9..b8df27094471f 100644 --- a/src/librustc_mir_build/build/scope.rs +++ b/src/librustc_mir_build/build/scope.rs @@ -84,8 +84,8 @@ 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_middle::middle::region; +use rustc_middle::mir::*; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::GeneratorKind; @@ -520,10 +520,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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"); @@ -989,7 +989,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let resumeblk = self.cfg.start_new_cleanup_block(); self.cfg.terminate( resumeblk, - SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span: self.fn_span }, + SourceInfo::outermost(self.fn_span), TerminatorKind::Resume, ); self.cached_resume_block = Some(resumeblk); @@ -1037,7 +1037,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, block: BasicBlock, span: Span, - location: Place<'tcx>, + place: Place<'tcx>, value: Operand<'tcx>, ) -> BlockAnd<()> { let source_info = self.source_info(span); @@ -1047,7 +1047,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, source_info, TerminatorKind::DropAndReplace { - location, + place, value, target: next_target, unwind: Some(diverge_target), @@ -1158,7 +1158,7 @@ fn build_scope_drops<'tcx>( block, source_info, TerminatorKind::Drop { - location: local.into(), + place: local.into(), target: next, unwind: Some(unwind_to), }, @@ -1272,7 +1272,7 @@ fn build_diverge_scope<'tcx>( block, source_info(drop_data.span), TerminatorKind::Drop { - location: drop_data.local.into(), + place: drop_data.local.into(), target, unwind: None, }, 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 8d7225c8c7b51..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; @@ -98,7 +98,7 @@ fn mirror_stmts<'a, 'tcx>( } } } - return result; + result } crate fn to_expr_ref<'a, 'tcx>( diff --git a/src/librustc_mir_build/hair/cx/expr.rs b/src/librustc_mir_build/hair/cx/expr.rs index 9f04bc1dc7697..a1796c9433eac 100644 --- a/src/librustc_mir_build/hair/cx/expr.rs +++ b/src/librustc_mir_build/hair/cx/expr.rs @@ -3,15 +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::{ErrorHandled, 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_hir::def_id::LocalDefId; 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> { @@ -138,11 +139,11 @@ fn make_mirror_unadjusted<'a, 'tcx>( let kind = match expr.kind { // Here comes the interesting stuff: - hir::ExprKind::MethodCall(_, method_span, ref args) => { + hir::ExprKind::MethodCall(_, method_span, ref args, fn_span) => { // Rewrite a.b(c) into UFCS form like Trait::b(a, c) let expr = method_callee(cx, expr, method_span, None); let args = args.iter().map(|e| e.to_ref()).collect(); - ExprKind::Call { ty: expr.ty, fun: expr.to_ref(), args, from_hir_call: true } + ExprKind::Call { ty: expr.ty, fun: expr.to_ref(), args, from_hir_call: true, fn_span } } hir::ExprKind::Call(ref fun, ref args) => { @@ -169,6 +170,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( fun: method.to_ref(), args: vec![fun.to_ref(), tupled_args.to_ref()], from_hir_call: true, + fn_span: expr.span, } } else { let adt_data = @@ -214,6 +216,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( fun: fun.to_ref(), args: args.to_ref(), from_hir_call: true, + fn_span: expr.span, } } } @@ -385,10 +388,10 @@ fn make_mirror_unadjusted<'a, 'tcx>( }; let upvars = cx .tcx - .upvars(def_id) + .upvars_mentioned(def_id) .iter() .flat_map(|upvars| upvars.iter()) - .zip(substs.upvar_tys(def_id, cx.tcx)) + .zip(substs.upvar_tys()) .map(|((&var_hir_id, _), ty)| capture_upvar(cx, expr, var_hir_id, ty)) .collect(); ExprKind::Closure { closure_id: def_id, substs, upvars, movability } @@ -400,6 +403,105 @@ 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, def_id) => { + InlineAsmOperand::SymStatic { def_id } + } + + _ => { + cx.tcx.sess.span_err( + expr.span, + "asm `sym` operand must point to a fn or static", + ); + + // Not a real fn, but we're not reaching codegen anyways... + ty = cx.tcx.ty_error(); + InlineAsmOperand::SymFn { + expr: Expr { + ty, + temp_lifetime, + span: expr.span, + kind: ExprKind::Literal { + literal: ty::Const::zero_sized(cx.tcx, ty), + user_ty: None, + }, + } + .to_ref(), + } + } + } + } + } + }) + .collect(), + options: asm.options, + line_spans: asm.line_spans, + }, + + hir::ExprKind::LlvmInlineAsm(ref asm) => ExprKind::LlvmInlineAsm { asm: &asm.inner, outputs: asm.outputs_exprs.to_ref(), inputs: asm.inputs_exprs.to_ref(), @@ -407,34 +509,8 @@ fn make_mirror_unadjusted<'a, 'tcx>( // Now comes the rote stuff: hir::ExprKind::Repeat(ref v, ref count) => { - let def_id = cx.tcx.hir().local_def_id(count.hir_id); - let substs = InternalSubsts::identity_for_item(cx.tcx, def_id); - let span = cx.tcx.def_span(def_id); - let count = match cx.tcx.const_eval_resolve( - ty::ParamEnv::reveal_all(), - def_id, - substs, - None, - Some(span), - ) { - Ok(cv) => { - if let Some(count) = cv.try_to_bits_for_ty( - cx.tcx, - ty::ParamEnv::reveal_all(), - cx.tcx.types.usize, - ) { - count as u64 - } else { - bug!("repeat count constant value can't be converted to usize"); - } - } - Err(ErrorHandled::Reported) => 0, - Err(ErrorHandled::TooGeneric) => { - let span = cx.tcx.def_span(def_id); - cx.tcx.sess.span_err(span, "array lengths can't depend on generic parameters"); - 0 - } - }; + 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 } } @@ -505,7 +581,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)) @@ -658,7 +734,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 { @@ -715,12 +791,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 { @@ -765,20 +841,17 @@ 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 temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - ExprKind::Deref { - arg: 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, - }, + let kind = if cx.tcx.is_thread_local_static(id) { + ExprKind::ThreadLocalRef(id) + } else { + let ptr = cx.tcx.create_static_alloc(id); + ExprKind::StaticRef { + literal: ty::Const::from_scalar(cx.tcx, Scalar::Ptr(ptr.into()), ty), + def_id: id, } - .to_ref(), - } + }; + ExprKind::Deref { arg: Expr { ty, temp_lifetime, span: expr.span, kind }.to_ref() } } Res::Local(var_hir_id) => convert_var(cx, expr, var_hir_id), @@ -794,7 +867,7 @@ fn convert_var<'tcx>( ) -> ExprKind<'tcx> { let upvar_index = cx .tables() - .upvar_list + .closure_captures .get(&cx.body_owner) .and_then(|upvars| upvars.get_full(&var_hir_id).map(|(i, _, _)| i)); @@ -812,7 +885,7 @@ fn convert_var<'tcx>( let closure_def_id = cx.body_owner; let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: LocalDefId::from_def_id(closure_def_id), + closure_expr_id: closure_def_id.expect_local(), }; let var_ty = cx.tables().node_type(var_hir_id); @@ -831,7 +904,7 @@ fn convert_var<'tcx>( let region = cx.tcx.mk_region(region); let self_expr = if let ty::Closure(_, closure_substs) = closure_ty.kind { - match cx.infcx.closure_kind(closure_def_id, closure_substs).unwrap() { + match cx.infcx.closure_kind(closure_substs).unwrap() { ty::ClosureKind::Fn => { let ref_closure_ty = cx.tcx.mk_ref( region, @@ -936,7 +1009,7 @@ fn overloaded_operator<'a, 'tcx>( args: Vec>, ) -> ExprKind<'tcx> { let fun = method_callee(cx, expr, expr.span, None); - ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false } + ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false, fn_span: expr.span } } fn overloaded_place<'a, 'tcx>( @@ -972,7 +1045,13 @@ fn overloaded_place<'a, 'tcx>( temp_lifetime, ty: ref_ty, span: expr.span, - kind: ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false }, + kind: ExprKind::Call { + ty: fun.ty, + fun: fun.to_ref(), + args, + from_hir_call: false, + fn_span: expr.span, + }, }; // construct and return a deref wrapper `*foo()` @@ -987,7 +1066,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).to_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 cb93ba7c9250f..ccff510f2d4e5 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; @@ -144,6 +146,9 @@ crate enum ExprKind<'tcx> { // Whether this is from a call in HIR, rather than from an overloaded // operator. True for overloaded function call. from_hir_call: bool, + /// This `Span` is the span of the function, without the dot and receiver + /// (e.g. `foo(a, b)` in `x.foo(a, b)` + fn_span: Span, }, Deref { arg: ExprRef<'tcx>, @@ -229,7 +234,7 @@ crate enum ExprKind<'tcx> { }, Repeat { value: ExprRef<'tcx>, - count: u64, + count: &'tcx Const<'tcx>, }, Array { fields: Vec>, @@ -278,7 +283,15 @@ crate enum ExprKind<'tcx> { def_id: DefId, }, InlineAsm { - asm: &'tcx hir::InlineAsmInner, + template: &'tcx [InlineAsmTemplatePiece], + operands: Vec>, + options: InlineAsmOptions, + line_spans: &'tcx [Span], + }, + /// An expression taking a reference to a thread local. + ThreadLocalRef(DefId), + LlvmInlineAsm { + asm: &'tcx hir::LlvmInlineAsmInner, outputs: Vec>, inputs: Vec>, }, @@ -335,6 +348,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 { + def_id: DefId, + }, +} + /////////////////////////////////////////////////////////////////////////// // The Mirror trait diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs index 37ad5f5ea4e38..6ac5d41ec6135 100644 --- a/src/librustc_mir_build/hair/pattern/_match.rs +++ b/src/librustc_mir_build/hair/pattern/_match.rs @@ -1,229 +1,274 @@ -/// Note: most tests relevant to this file can be found (at the time of writing) -/// in src/tests/ui/pattern/usefulness. -/// -/// This file includes the logic for exhaustiveness and usefulness checking for -/// pattern-matching. Specifically, given a list of patterns for a type, we can -/// tell whether: -/// (a) the patterns cover every possible constructor for the type [exhaustiveness] -/// (b) each pattern is necessary [usefulness] -/// -/// The algorithm implemented here is a modified version of the one described in: -/// http://moscova.inria.fr/~maranget/papers/warn/index.html -/// However, to save future implementors from reading the original paper, we -/// summarise the algorithm here to hopefully save time and be a little clearer -/// (without being so rigorous). -/// -/// The core of the algorithm revolves about a "usefulness" check. In particular, we -/// are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as -/// a matrix). `U(P, p)` represents whether, given an existing list of patterns -/// `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously- -/// uncovered values of the type). -/// -/// If we have this predicate, then we can easily compute both exhaustiveness of an -/// entire set of patterns and the individual usefulness of each one. -/// (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard -/// match doesn't increase the number of values we're matching) -/// (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a -/// pattern to those that have come before it doesn't increase the number of values -/// we're matching). -/// -/// 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 -/// calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the -/// new pattern `p`. -/// -/// For example, say we have the following: -/// ``` -/// // x: (Option, Result<()>) -/// match x { -/// (Some(true), _) => {} -/// (None, Err(())) => {} -/// (None, Err(_)) => {} -/// } -/// ``` -/// Here, the matrix `P` starts as: -/// [ -/// [(Some(true), _)], -/// [(None, Err(()))], -/// [(None, Err(_))], -/// ] -/// We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering -/// `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because -/// all the values it covers are already covered by row 2. -/// -/// A list of patterns can be thought of as a stack, because we are mainly interested in the top of -/// the stack at any given point, and we can pop or apply constructors to get new pattern-stacks. -/// To match the paper, the top of the stack is at the beginning / on the left. -/// -/// There are two important operations on pattern-stacks necessary to understand the algorithm: -/// 1. We can pop a given constructor off the top of a stack. This operation is called -/// `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or -/// `None`) and `p` a pattern-stack. -/// If the pattern on top of the stack can cover `c`, this removes the constructor and -/// pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns. -/// Otherwise the pattern-stack is discarded. -/// This essentially filters those pattern-stacks whose top covers the constructor `c` and -/// discards the others. -/// -/// For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we -/// pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the -/// `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get -/// nothing back. -/// -/// This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` -/// on top of the stack, and we have four cases: -/// 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We -/// push onto the stack the arguments of this constructor, and return the result: -/// r_1, .., r_a, p_2, .., p_n -/// 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and -/// return nothing. -/// 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has -/// arguments (its arity), and return the resulting stack: -/// _, .., _, p_2, .., p_n -/// 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting -/// stack: -/// S(c, (r_1, p_2, .., p_n)) -/// S(c, (r_2, p_2, .., p_n)) -/// -/// 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is -/// a pattern-stack. -/// This is used when we know there are missing constructor cases, but there might be -/// existing wildcard patterns, so to check the usefulness of the matrix, we have to check -/// all its *other* components. -/// -/// It is computed as follows. We look at the pattern `p_1` on top of the stack, -/// and we have three cases: -/// 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. -/// 1.2. `p_1 = _`. We return the rest of the stack: -/// p_2, .., p_n -/// 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting -/// stack. -/// D((r_1, p_2, .., p_n)) -/// D((r_2, p_2, .., p_n)) -/// -/// Note that the OR-patterns are not always used directly in Rust, but are used to derive the -/// exhaustive integer matching rules, so they're written here for posterity. -/// -/// Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by -/// working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with -/// the given constructor, and popping a wildcard keeps those rows that start with a wildcard. -/// -/// -/// The algorithm for computing `U` -/// ------------------------------- -/// The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). -/// That means we're going to check the components from left-to-right, so the algorithm -/// operates principally on the first component of the matrix and new pattern-stack `p`. -/// This algorithm is realised in the `is_useful` function. -/// -/// Base case. (`n = 0`, i.e., an empty tuple pattern) -/// - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), -/// then `U(P, p)` is false. -/// - Otherwise, `P` must be empty, so `U(P, p)` is true. -/// -/// Inductive step. (`n > 0`, i.e., whether there's at least one column -/// [which may then be expanded into further columns later]) -/// We're going to match on the top of the new pattern-stack, `p_1`. -/// - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern. -/// Then, the usefulness of `p_1` can be reduced to whether it is useful when -/// we ignore all the patterns in the first column of `P` that involve other constructors. -/// This is where `S(c, P)` comes in: -/// `U(P, p) := U(S(c, P), S(c, p))` -/// This special case is handled in `is_useful_specialized`. -/// -/// For example, if `P` is: -/// [ -/// [Some(true), _], -/// [None, 0], -/// ] -/// and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only -/// matches values that row 2 doesn't. For row 1 however, we need to dig into the -/// arguments of `Some` to know whether some new value is covered. So we compute -/// `U([[true, _]], [false, 0])`. -/// -/// - If `p_1 == _`, then we look at the list of constructors that appear in the first -/// component of the rows of `P`: -/// + If there are some constructors that aren't present, then we might think that the -/// wildcard `_` is useful, since it covers those constructors that weren't covered -/// before. -/// That's almost correct, but only works if there were no wildcards in those first -/// components. So we need to check that `p` is useful with respect to the rows that -/// start with a wildcard, if there are any. This is where `D` comes in: -/// `U(P, p) := U(D(P), D(p))` -/// -/// For example, if `P` is: -/// [ -/// [_, true, _], -/// [None, false, 1], -/// ] -/// and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we -/// only had row 2, we'd know that `p` is useful. However row 1 starts with a -/// wildcard, so we need to check whether `U([[true, _]], [false, 1])`. -/// -/// + Otherwise, all possible constructors (for the relevant type) are present. In this -/// case we must check whether the wildcard pattern covers any unmatched value. For -/// that, we can think of the `_` pattern as a big OR-pattern that covers all -/// possible constructors. For `Option`, that would mean `_ = None | Some(_)` for -/// example. The wildcard pattern is useful in this case if it is useful when -/// specialized to one of the possible constructors. So we compute: -/// `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` -/// -/// For example, if `P` is: -/// [ -/// [Some(true), _], -/// [None, false], -/// ] -/// and `p` is [_, false], both `None` and `Some` constructors appear in the first -/// components of `P`. We will therefore try popping both constructors in turn: we -/// compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]], -/// [false]) for the `None` constructor. The first case returns true, so we know that -/// `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched -/// before. -/// -/// - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: -/// `U(P, p) := U(P, (r_1, p_2, .., p_n)) -/// || U(P, (r_2, p_2, .., p_n))` -/// -/// Modifications to the algorithm -/// ------------------------------ -/// The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for -/// example uninhabited types and variable-length slice patterns. These are drawn attention to -/// throughout the code below. I'll make a quick note here about how exhaustive integer matching is -/// accounted for, though. -/// -/// Exhaustive integer matching -/// --------------------------- -/// An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ... -/// So to support exhaustive integer matching, we can make use of the logic in the paper for -/// OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because -/// they are likely gigantic. So we instead treat ranges as constructors of the integers. This means -/// that we have a constructor *of* constructors (the integers themselves). We then need to work -/// through all the inductive step rules above, deriving how the ranges would be treated as -/// OR-patterns, and making sure that they're treated in the same way even when they're ranges. -/// There are really only four special cases here: -/// - When we match on a constructor that's actually a range, we have to treat it as if we would -/// an OR-pattern. -/// + It turns out that we can simply extend the case for single-value patterns in -/// `specialize` to either be *equal* to a value constructor, or *contained within* a range -/// constructor. -/// + When the pattern itself is a range, you just want to tell whether any of the values in -/// the pattern range coincide with values in the constructor range, which is precisely -/// intersection. -/// Since when encountering a range pattern for a value constructor, we also use inclusion, it -/// means that whenever the constructor is a value/range and the pattern is also a value/range, -/// we can simply use intersection to test usefulness. -/// - When we're testing for usefulness of a pattern and the pattern's first component is a -/// wildcard. -/// + If all the constructors appear in the matrix, we have a slight complication. By default, -/// the behaviour (i.e., a disjunction over specialised matrices for each constructor) is -/// invalid, because we want a disjunction over every *integer* in each range, not just a -/// disjunction over every range. This is a bit more tricky to deal with: essentially we need -/// to form equivalence classes of subranges of the constructor range for which the behaviour -/// of the matrix `P` and new pattern `p` are the same. This is described in more -/// detail in `split_grouped_constructors`. -/// + If some constructors are missing from the matrix, it turns out we don't need to do -/// anything special (because we know none of the integers are actually wildcards: i.e., we -/// can't span wildcards using ranges). +//! Note: most of the tests relevant to this file can be found (at the time of writing) in +//! src/tests/ui/pattern/usefulness. +//! +//! This file includes the logic for exhaustiveness and usefulness checking for +//! pattern-matching. Specifically, given a list of patterns for a type, we can +//! tell whether: +//! (a) the patterns cover every possible constructor for the type [exhaustiveness] +//! (b) each pattern is necessary [usefulness] +//! +//! The algorithm implemented here is a modified version of the one described in: +//! http://moscova.inria.fr/~maranget/papers/warn/index.html +//! However, to save future implementors from reading the original paper, we +//! summarise the algorithm here to hopefully save time and be a little clearer +//! (without being so rigorous). +//! +//! # Premise +//! +//! The core of the algorithm revolves about a "usefulness" check. In particular, we +//! are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as +//! a matrix). `U(P, p)` represents whether, given an existing list of patterns +//! `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously- +//! uncovered values of the type). +//! +//! If we have this predicate, then we can easily compute both exhaustiveness of an +//! entire set of patterns and the individual usefulness of each one. +//! (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard +//! match doesn't increase the number of values we're matching) +//! (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a +//! pattern to those that have come before it doesn't increase the number of values +//! we're matching). +//! +//! # Core concept +//! +//! The idea that powers everything that is done in this file is the following: a value is made +//! from a constructor applied to some fields. Examples of constructors are `Some`, `None`, `(,)` +//! (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), and `2` (the +//! constructor for the number `2`). Fields are just a (possibly empty) list of values. +//! +//! Some of the constructors listed above might feel weird: `None` and `2` don't take any +//! arguments. This is part of what makes constructors so general: we will consider plain values +//! like numbers and string literals to be constructors that take no arguments, also called "0-ary +//! constructors"; they are the simplest case of constructors. This allows us to see any value as +//! made up from a tree of constructors, each having a given number of children. For example: +//! `(None, Ok(0))` is made from 4 different constructors. +//! +//! This idea can be extended to patterns: a pattern captures a set of possible values, and we can +//! describe this set using constructors. For example, `Err(_)` captures all values of the type +//! `Result` that start with the `Err` constructor (for some choice of `T` and `E`). The +//! wildcard `_` captures all values of the given type starting with any of the constructors for +//! that type. +//! +//! We use this to compute whether different patterns might capture a same value. Do the patterns +//! `Ok("foo")` and `Err(_)` capture a common value? The answer is no, because the first pattern +//! captures only values starting with the `Ok` constructor and the second only values starting +//! with the `Err` constructor. Do the patterns `Some(42)` and `Some(1..10)` intersect? They might, +//! since they both capture values starting with `Some`. To be certain, we need to dig under the +//! `Some` constructor and continue asking the question. This is the main idea behind the +//! exhaustiveness algorithm: by looking at patterns constructor-by-constructor, we can efficiently +//! figure out if some new pattern might capture a value that hadn't been captured by previous +//! patterns. +//! +//! Constructors are represented by the `Constructor` enum, and its fields by the `Fields` enum. +//! Most of the complexity of this file resides in transforming between patterns and +//! (`Constructor`, `Fields`) pairs, handling all the special cases correctly. +//! +//! Caveat: this constructors/fields distinction doesn't quite cover every Rust value. For example +//! a value of type `Rc` doesn't fit this idea very well, nor do various other things. +//! However, this idea covers most of the cases that are relevant to exhaustiveness checking. +//! +//! +//! # Algorithm +//! +//! Recall that `U(P, p)` represents whether, given an existing list of patterns (aka matrix) `P`, +//! adding a new pattern `p` will cover previously-uncovered values of the type. +//! During the course of the algorithm, the rows of the matrix won't just be individual patterns, +//! but rather partially-deconstructed patterns in the form of a list of fields. The paper +//! calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the +//! new pattern `p`. +//! +//! For example, say we have the following: +//! ``` +//! // x: (Option, Result<()>) +//! match x { +//! (Some(true), _) => {} +//! (None, Err(())) => {} +//! (None, Err(_)) => {} +//! } +//! ``` +//! Here, the matrix `P` starts as: +//! [ +//! [(Some(true), _)], +//! [(None, Err(()))], +//! [(None, Err(_))], +//! ] +//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering +//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because +//! all the values it covers are already covered by row 2. +//! +//! A list of patterns can be thought of as a stack, because we are mainly interested in the top of +//! the stack at any given point, and we can pop or apply constructors to get new pattern-stacks. +//! To match the paper, the top of the stack is at the beginning / on the left. +//! +//! There are two important operations on pattern-stacks necessary to understand the algorithm: +//! 1. We can pop a given constructor off the top of a stack. This operation is called +//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or +//! `None`) and `p` a pattern-stack. +//! If the pattern on top of the stack can cover `c`, this removes the constructor and +//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns. +//! Otherwise the pattern-stack is discarded. +//! This essentially filters those pattern-stacks whose top covers the constructor `c` and +//! discards the others. +//! +//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we +//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the +//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get +//! nothing back. +//! +//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` +//! on top of the stack, and we have four cases: +//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We +//! push onto the stack the arguments of this constructor, and return the result: +//! r_1, .., r_a, p_2, .., p_n +//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and +//! return nothing. +//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has +//! arguments (its arity), and return the resulting stack: +//! _, .., _, p_2, .., p_n +//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting +//! stack: +//! S(c, (r_1, p_2, .., p_n)) +//! S(c, (r_2, p_2, .., p_n)) +//! +//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is +//! a pattern-stack. +//! This is used when we know there are missing constructor cases, but there might be +//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check +//! all its *other* components. +//! +//! It is computed as follows. We look at the pattern `p_1` on top of the stack, +//! and we have three cases: +//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. +//! 1.2. `p_1 = _`. We return the rest of the stack: +//! p_2, .., p_n +//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting +//! stack. +//! D((r_1, p_2, .., p_n)) +//! D((r_2, p_2, .., p_n)) +//! +//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the +//! exhaustive integer matching rules, so they're written here for posterity. +//! +//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by +//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with +//! the given constructor, and popping a wildcard keeps those rows that start with a wildcard. +//! +//! +//! The algorithm for computing `U` +//! ------------------------------- +//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). +//! That means we're going to check the components from left-to-right, so the algorithm +//! operates principally on the first component of the matrix and new pattern-stack `p`. +//! This algorithm is realised in the `is_useful` function. +//! +//! Base case. (`n = 0`, i.e., an empty tuple pattern) +//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), +//! then `U(P, p)` is false. +//! - Otherwise, `P` must be empty, so `U(P, p)` is true. +//! +//! Inductive step. (`n > 0`, i.e., whether there's at least one column +//! [which may then be expanded into further columns later]) +//! We're going to match on the top of the new pattern-stack, `p_1`. +//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern. +//! Then, the usefulness of `p_1` can be reduced to whether it is useful when +//! we ignore all the patterns in the first column of `P` that involve other constructors. +//! This is where `S(c, P)` comes in: +//! `U(P, p) := U(S(c, P), S(c, p))` +//! This special case is handled in `is_useful_specialized`. +//! +//! For example, if `P` is: +//! [ +//! [Some(true), _], +//! [None, 0], +//! ] +//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only +//! matches values that row 2 doesn't. For row 1 however, we need to dig into the +//! arguments of `Some` to know whether some new value is covered. So we compute +//! `U([[true, _]], [false, 0])`. +//! +//! - If `p_1 == _`, then we look at the list of constructors that appear in the first +//! component of the rows of `P`: +//! + If there are some constructors that aren't present, then we might think that the +//! wildcard `_` is useful, since it covers those constructors that weren't covered +//! before. +//! That's almost correct, but only works if there were no wildcards in those first +//! components. So we need to check that `p` is useful with respect to the rows that +//! start with a wildcard, if there are any. This is where `D` comes in: +//! `U(P, p) := U(D(P), D(p))` +//! +//! For example, if `P` is: +//! [ +//! [_, true, _], +//! [None, false, 1], +//! ] +//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we +//! only had row 2, we'd know that `p` is useful. However row 1 starts with a +//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`. +//! +//! + Otherwise, all possible constructors (for the relevant type) are present. In this +//! case we must check whether the wildcard pattern covers any unmatched value. For +//! that, we can think of the `_` pattern as a big OR-pattern that covers all +//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for +//! example. The wildcard pattern is useful in this case if it is useful when +//! specialized to one of the possible constructors. So we compute: +//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` +//! +//! For example, if `P` is: +//! [ +//! [Some(true), _], +//! [None, false], +//! ] +//! and `p` is [_, false], both `None` and `Some` constructors appear in the first +//! components of `P`. We will therefore try popping both constructors in turn: we +//! compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]], +//! [false]) for the `None` constructor. The first case returns true, so we know that +//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched +//! before. +//! +//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: +//! `U(P, p) := U(P, (r_1, p_2, .., p_n)) +//! || U(P, (r_2, p_2, .., p_n))` +//! +//! Modifications to the algorithm +//! ------------------------------ +//! The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for +//! example uninhabited types and variable-length slice patterns. These are drawn attention to +//! throughout the code below. I'll make a quick note here about how exhaustive integer matching is +//! accounted for, though. +//! +//! Exhaustive integer matching +//! --------------------------- +//! An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ... +//! So to support exhaustive integer matching, we can make use of the logic in the paper for +//! OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because +//! they are likely gigantic. So we instead treat ranges as constructors of the integers. This means +//! that we have a constructor *of* constructors (the integers themselves). We then need to work +//! through all the inductive step rules above, deriving how the ranges would be treated as +//! OR-patterns, and making sure that they're treated in the same way even when they're ranges. +//! There are really only four special cases here: +//! - When we match on a constructor that's actually a range, we have to treat it as if we would +//! an OR-pattern. +//! + It turns out that we can simply extend the case for single-value patterns in +//! `specialize` to either be *equal* to a value constructor, or *contained within* a range +//! constructor. +//! + When the pattern itself is a range, you just want to tell whether any of the values in +//! the pattern range coincide with values in the constructor range, which is precisely +//! intersection. +//! Since when encountering a range pattern for a value constructor, we also use inclusion, it +//! means that whenever the constructor is a value/range and the pattern is also a value/range, +//! we can simply use intersection to test usefulness. +//! - When we're testing for usefulness of a pattern and the pattern's first component is a +//! wildcard. +//! + If all the constructors appear in the matrix, we have a slight complication. By default, +//! the behaviour (i.e., a disjunction over specialised matrices for each constructor) is +//! invalid, because we want a disjunction over every *integer* in each range, not just a +//! disjunction over every range. This is a bit more tricky to deal with: essentially we need +//! to form equivalence classes of subranges of the constructor range for which the behaviour +//! of the matrix `P` and new pattern `p` are the same. This is described in more +//! detail in `split_grouped_constructors`. +//! + If some constructors are missing from the matrix, it turns out we don't need to do +//! anything special (because we know none of the integers are actually wildcards: i.e., we +//! can't span wildcards using ranges). use self::Constructor::*; use self::SliceKind::*; use self::Usefulness::*; @@ -235,20 +280,18 @@ use rustc_index::vec::Idx; use super::{compare_const_vals, PatternFoldable, PatternFolder}; use super::{FieldPat, Pat, PatKind, PatRange}; -use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx}; -use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef}; +use rustc_arena::TypedArena; +use rustc_attr::{SignedInt, UnsignedInt}; +use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, RangeEnd}; - -use rustc::lint; -use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; -use rustc::mir::Field; -use rustc::util::common::ErrorReported; - -use rustc_attr::{SignedInt, UnsignedInt}; +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 arena::TypedArena; +use rustc_target::abi::{Integer, Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; use std::borrow::Cow; @@ -257,7 +300,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) @@ -288,7 +330,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 { .. } => { @@ -307,7 +349,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(), } @@ -443,13 +485,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..])) } } @@ -505,7 +545,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() @@ -515,6 +555,8 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { } /// Pretty-printer for matrices of patterns, example: +/// +/// ```text /// +++++++++++++++++++++++++++++ /// + _ + [] + /// +++++++++++++++++++++++++++++ @@ -580,31 +622,20 @@ 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.tcx.is_ty_uninhabited_from(self.module, ty, self.param_env) } else { false } } - // Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. + /// 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, ..) => { @@ -613,15 +644,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)] @@ -733,10 +755,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), @@ -771,7 +800,11 @@ impl<'tcx> Constructor<'tcx> { assert!(!adt.is_enum()); VariantIdx::new(0) } - ConstantValue(c) => cx.tcx.destructure_const(cx.param_env.and(c)).variant, + ConstantValue(c) => cx + .tcx + .destructure_const(cx.param_env.and(c)) + .variant + .expect("destructed const of adt without variant id"), _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } } @@ -861,107 +894,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` @@ -973,13 +909,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 { @@ -1044,8 +980,267 @@ 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. `kept_count` caches the number of + /// non-hidden fields. + Filtered { + fields: SmallVec<[FilteredField<'p, 'tcx>; 2]>, + kept_count: usize, + }, +} + +impl<'p, 'tcx> Fields<'p, 'tcx> { + fn empty() -> Self { + Fields::Slice(&[]) + } + + /// Construct a new `Fields` from the given pattern. Must not be used if the pattern is a field + /// of a struct/tuple/variant. + fn from_single_pattern(pat: &'p Pat<'tcx>) -> Self { + Fields::Slice(std::slice::from_ref(pat)) + } + + /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't + /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`. + fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self { + Fields::Slice(pats) + } + + /// Convenience; internal use. + fn wildcards_from_tys( + cx: &MatchCheckCtxt<'p, 'tcx>, + tys: impl IntoIterator>, + ) -> Self { + let wilds = tys.into_iter().map(Pat::wildcard_from_ty); + let pats = cx.pattern_arena.alloc_from_iter(wilds); + Fields::Slice(pats) + } + + /// Creates a new list of wildcard fields for a given constructor. + fn wildcards( + cx: &MatchCheckCtxt<'p, 'tcx>, + constructor: &Constructor<'tcx>, + ty: Ty<'tcx>, + ) -> Self { + let wildcard_from_ty = |ty| &*cx.pattern_arena.alloc(Pat::wildcard_from_ty(ty)); + + let ret = match constructor { + Single | Variant(_) => match ty.kind { + ty::Tuple(ref fs) => { + Fields::wildcards_from_tys(cx, fs.into_iter().map(|ty| ty.expect_ty())) + } + ty::Ref(_, rty, _) => Fields::from_single_pattern(wildcard_from_ty(rty)), + ty::Adt(adt, substs) => { + if adt.is_box() { + // Use T as the sub pattern type of Box. + Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0))) + } else { + let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)]; + // Whether we must not match the fields of this variant exhaustively. + let is_non_exhaustive = + variant.is_field_list_non_exhaustive() && !adt.did.is_local(); + let field_tys = variant.fields.iter().map(|field| field.ty(cx.tcx, substs)); + // In the following cases, we don't need to filter out any fields. This is + // the vast majority of real cases, since uninhabited fields are uncommon. + let has_no_hidden_fields = (adt.is_enum() && !is_non_exhaustive) + || !field_tys.clone().any(|ty| cx.is_uninhabited(ty)); + + if has_no_hidden_fields { + Fields::wildcards_from_tys(cx, field_tys) + } else { + let mut kept_count = 0; + let fields = variant + .fields + .iter() + .map(|field| { + let ty = field.ty(cx.tcx, substs); + let is_visible = adt.is_enum() + || field.vis.is_accessible_from(cx.module, cx.tcx); + let is_uninhabited = cx.is_uninhabited(ty); + + // In the cases of either a `#[non_exhaustive]` field list + // or a non-public field, we hide uninhabited fields in + // order not to reveal the uninhabitedness of the whole + // variant. + if is_uninhabited && (!is_visible || is_non_exhaustive) { + FilteredField::Hidden(ty) + } else { + kept_count += 1; + FilteredField::Kept(wildcard_from_ty(ty)) + } + }) + .collect(); + Fields::Filtered { fields, kept_count } + } + } + } + _ => Fields::empty(), + }, + Slice(slice) => match ty.kind { + ty::Slice(ty) | ty::Array(ty, _) => { + let arity = slice.arity(); + Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) + } + _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), + }, + ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(), + }; + debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); + ret + } + + /// Returns the number of patterns from the viewpoint of match-checking, i.e. excluding hidden + /// fields. This is what we want in most cases in this file, the only exception being + /// conversion to/from `Pat`. + fn len(&self) -> usize { + match self { + Fields::Slice(pats) => pats.len(), + Fields::Vec(pats) => pats.len(), + Fields::Filtered { kept_count, .. } => *kept_count, + } + } + + /// Returns the complete list of patterns, including hidden fields. + fn all_patterns(self) -> impl Iterator> { + let pats: SmallVec<[_; 2]> = match self { + Fields::Slice(pats) => pats.iter().cloned().collect(), + Fields::Vec(pats) => pats.into_iter().cloned().collect(), + Fields::Filtered { fields, .. } => { + // We don't skip any fields here. + fields.into_iter().map(|p| p.to_pattern()).collect() + } + }; + pats.into_iter() + } + + /// Overrides some of the fields with the provided patterns. Exactly like + /// `replace_fields_indexed`, except that it takes `FieldPat`s as input. + fn replace_with_fieldpats( + &self, + new_pats: impl IntoIterator>, + ) -> Self { + self.replace_fields_indexed( + new_pats.into_iter().map(|pat| (pat.field.index(), &pat.pattern)), + ) + } + + /// Overrides some of the fields with the provided patterns. This is used when a pattern + /// defines some fields but not all, for example `Foo { field1: Some(_), .. }`: here we start with a + /// `Fields` that is just one wildcard per field of the `Foo` struct, and override the entry + /// corresponding to `field1` with the pattern `Some(_)`. This is also used for slice patterns + /// for the same reason. + fn replace_fields_indexed( + &self, + new_pats: impl IntoIterator)>, + ) -> Self { + let mut fields = self.clone(); + if let Fields::Slice(pats) = fields { + fields = Fields::Vec(pats.iter().collect()); + } + + match &mut fields { + Fields::Vec(pats) => { + for (i, pat) in new_pats { + pats[i] = pat + } + } + Fields::Filtered { fields, .. } => { + for (i, pat) in new_pats { + if let FilteredField::Kept(p) = &mut fields[i] { + *p = pat + } + } + } + Fields::Slice(_) => unreachable!(), + } + fields + } + + /// Replaces contained fields with the given filtered list of patterns, e.g. taken from the + /// matrix. There must be `len()` patterns in `pats`. + fn replace_fields( + &self, + cx: &MatchCheckCtxt<'p, 'tcx>, + pats: impl IntoIterator>, + ) -> Self { + let pats: &[_] = cx.pattern_arena.alloc_from_iter(pats); + + match self { + Fields::Filtered { fields, kept_count } => { + let mut pats = pats.iter(); + let mut fields = fields.clone(); + for f in &mut fields { + if let FilteredField::Kept(p) = f { + // We take one input pattern for each `Kept` field, in order. + *p = pats.next().unwrap(); + } + } + Fields::Filtered { fields, kept_count: *kept_count } + } + _ => Fields::Slice(pats), + } + } + + fn push_on_patstack(self, stack: &[&'p Pat<'tcx>]) -> PatStack<'p, 'tcx> { + let pats: SmallVec<_> = match self { + Fields::Slice(pats) => pats.iter().chain(stack.iter().copied()).collect(), + Fields::Vec(mut pats) => { + pats.extend_from_slice(stack); + pats + } + Fields::Filtered { fields, .. } => { + // We skip hidden fields here + fields.into_iter().filter_map(|p| p.kept()).chain(stack.iter().copied()).collect() + } + }; + PatStack::from_vec(pats) } } @@ -1075,15 +1270,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, @@ -1203,17 +1399,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); @@ -1267,7 +1465,7 @@ fn all_constructors<'a, 'tcx>( def.variants .iter() .filter(|v| { - !v.uninhabited_from(cx.tcx, substs, def.adt_kind()) + !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) .contains(cx.tcx, cx.module) }) .map(|v| Variant(v.def_id)) @@ -1325,7 +1523,7 @@ fn all_constructors<'a, 'tcx>( } ty::Uint(uty) => { let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); - let max = truncate(u128::max_value(), size); + let max = truncate(u128::MAX, size); vec![make_range(0, max)] } _ => { @@ -1611,22 +1809,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; @@ -1655,7 +1854,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; @@ -1666,43 +1865,21 @@ 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, - }; + // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476). + let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty); + let pcx = PatCtxt { ty, span: v.head().span }; debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); - if let Some(constructor) = pat_constructor(cx.tcx, cx.param_env, v.head()) { + let ret = if let Some(constructor) = pat_constructor(cx.tcx, cx.param_env, v.head()) { debug!("is_useful - expanding constructor: {:#?}", constructor); split_grouped_constructors( cx.tcx, @@ -1714,7 +1891,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 { @@ -1722,11 +1910,11 @@ crate fn is_useful<'p, 'tcx>( let used_ctors: Vec> = matrix.heads().filter_map(|p| pat_constructor(cx.tcx, cx.param_env, p)).collect(); - debug!("used_ctors = {:#?}", used_ctors); + debug!("is_useful_used_ctors = {:#?}", used_ctors); // `all_ctors` are all the constructors for the given type, which // should all be represented (or caught with the wild pattern `_`). let all_ctors = all_constructors(cx, pcx); - debug!("all_ctors = {:#?}", all_ctors); + debug!("is_useful_all_ctors = {:#?}", all_ctors); // `missing_ctors` is the set of constructors from the same type as the // first column of `matrix` that are matched only by wildcard patterns @@ -1741,21 +1929,31 @@ crate fn is_useful<'p, 'tcx>( // can be big. let missing_ctors = MissingConstructors::new(all_ctors, used_ctors); - debug!("missing_ctors.empty()={:#?}", missing_ctors.is_empty(),); + debug!("is_useful_missing_ctors.empty()={:#?}", missing_ctors.is_empty(),); if missing_ctors.is_empty() { let (all_ctors, _) = missing_ctors.into_inner(); split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None) .into_iter() .map(|c| { - is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id) + is_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 @@ -1799,7 +1997,9 @@ crate fn is_useful<'p, 'tcx>( usefulness.apply_missing_ctors(cx, pcx.ty, &missing_ctors) } } - } + }; + debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret); + ret } /// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied @@ -1809,18 +2009,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) } @@ -1922,8 +2123,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), @@ -1946,15 +2147,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); } - _ => {} } } @@ -2068,7 +2266,7 @@ fn split_grouped_constructors<'p, 'tcx>( } intersection }) - .flat_map(|range| range_borders(range)); + .flat_map(range_borders); let ctor_borders = range_borders(ctor_range.clone()); let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); borders.sort_unstable(); @@ -2169,8 +2367,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); } @@ -2178,8 +2376,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); } - }, - _ => {} + } } } @@ -2288,27 +2485,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. @@ -2318,37 +2494,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 @@ -2361,11 +2540,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, .. }) => { @@ -2377,7 +2555,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) } @@ -2399,24 +2577,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 { .. } => { @@ -2424,50 +2604,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( @@ -2479,7 +2648,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, } @@ -2489,7 +2658,10 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), }; - debug!("specialize({:#?}, {:#?}) = {:#?}", pat, ctor_wild_subpatterns, result); + debug!( + "specialize({:#?}, {:#?}, {:#?}) = {:#?}", + pat, constructor, ctor_wild_subpatterns, result + ); result } diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs index 3691305181348..6fc447a87f57a 100644 --- a/src/librustc_mir_build/hair/pattern/check_match.rs +++ b/src/librustc_mir_build/hair/pattern/check_match.rs @@ -1,11 +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::hir::map::Map; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc_arena::TypedArena; use rustc_ast::ast::Mutability; use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -13,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)); } @@ -40,10 +42,11 @@ 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> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -92,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", ) @@ -144,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, |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( @@ -160,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(); } } @@ -361,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!(), @@ -411,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") @@ -481,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(); } @@ -570,7 +579,7 @@ fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`. fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> bool { - !cx.tables.node_type(hir_id).is_copy_modulo_regions(cx.tcx, cx.param_env, span) + !cx.tables.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env) } /// Check the legality of legality of by-move bindings. @@ -753,7 +762,7 @@ fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pa } impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> { - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None 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 214e75fbdde43..6dd7e0871b45e 100644 --- a/src/librustc_mir_build/hair/pattern/const_to_pat.rs +++ b/src/librustc_mir_build/hair/pattern/const_to_pat.rs @@ -1,16 +1,15 @@ -use rustc::lint; -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; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation}; -use rustc_index::vec::Idx; - -use rustc_span::Span; - use std::cell::Cell; use super::{FieldPat, Pat, PatCtxt, PatKind}; @@ -24,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) }) } } @@ -80,10 +80,14 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { - traits::type_marked_structural(self.id, self.span, &self.infcx, ty) + ty.is_structural_eq_shallow(self.infcx.tcx) } - 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. @@ -102,21 +106,49 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", cv.ty, structural ); + + // This can occur because const qualification treats all associated constants as + // opaque, whereas `search_for_structural_match_violation` tries to monomorphize them + // before it runs. + // + // FIXME(#73448): Find a way to bring const qualification into parity with + // `search_for_structural_match_violation`. + if structural.is_none() && mir_structural_match_violation { + warn!("MIR const-checker found novel structural match violation. See #73448."); + return inlined_const_as_pat; + } + 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::Opaque => { + "opaque types cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTy::Generator => { + "generators cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTy::Closure => { + "closures cannot be used in patterns".to_string() + } traits::NonStructuralMatchTy::Param => { - bug!("use of constant whose type is a parameter inside a pattern") + bug!("use of a constant whose type is a parameter inside a pattern") + } + traits::NonStructuralMatchTy::Projection => { + bug!("use of a constant whose type is a projection inside a pattern") + } + traits::NonStructuralMatchTy::Foreign => { + bug!("use of a value of a foreign type 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. @@ -131,7 +163,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, @@ -147,13 +180,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`" ); } } @@ -182,7 +220,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { let kind = match cv.ty.kind { ty::Float(_) => { tcx.struct_span_lint_hir( - ::rustc::lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, id, span, |lint| lint.build("floating-point types cannot be used in patterns").emit(), @@ -237,7 +275,9 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { PatKind::Variant { adt_def, substs, - variant_index: destructured.variant, + variant_index: destructured + .variant + .expect("destructed const of adt without variant id"), subpatterns: field_pats(destructured.fields), } } diff --git a/src/librustc_mir_build/hair/pattern/mod.rs b/src/librustc_mir_build/hair/pattern/mod.rs index 6786c35629308..5c30b2a448c6d 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 @@ -504,7 +509,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { let mut ty = self.tables.node_type(pat.hir_id); - if let ty::Error = ty.kind { + if let ty::Error(_) = ty.kind { // Avoid ICEs (e.g., #50577 and #50585). return Pat { span: pat.span, ty, kind: Box::new(PatKind::Wild) }; } @@ -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, } } @@ -702,7 +708,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { if adt_def.is_enum() { let substs = match ty.kind { ty::Adt(_, substs) | ty::FnDef(_, substs) => substs, - ty::Error => { + ty::Error(_) => { // Avoid ICE (#50585) return PatKind::Wild; } @@ -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 } => { @@ -1031,7 +1051,7 @@ crate fn compare_const_vals<'tcx>( let b_bits = b.try_eval_bits(tcx, param_env, ty); if let (Some(a), Some(b)) = (a_bits, b_bits) { - use ::rustc_apfloat::Float; + use rustc_apfloat::Float; return match ty.kind { ty::Float(ast::FloatTy::F32) => { let l = ::rustc_apfloat::ieee::Single::from_bits(a); @@ -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 3c35827d15d3e..c6d65d56c93f0 100644 --- a/src/librustc_mir_build/lib.rs +++ b/src/librustc_mir_build/lib.rs @@ -4,20 +4,24 @@ #![feature(box_patterns)] #![feature(box_syntax)] +#![feature(const_if_match)] +#![feature(const_fn)] +#![feature(const_panic)] #![feature(crate_visibility_modifier)] #![feature(bool_to_option)] +#![feature(or_patterns)] #![recursion_limit = "256"] #[macro_use] extern crate log; #[macro_use] -extern crate rustc; +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 0017f800de702..ac5d128a1baa2 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::lint::builtin::UNCONDITIONAL_RECURSION; -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::FalseEdge { .. } + | 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::FalseEdge { 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/config.rs b/src/librustc_parse/config.rs deleted file mode 100644 index d209da866e17c..0000000000000 --- a/src/librustc_parse/config.rs +++ /dev/null @@ -1,549 +0,0 @@ -//! Process the potential `cfg` attributes on a module. -//! Also determine if the module should be included in this configuration. -//! -//! This module properly belongs in rustc_expand, but for now it's tied into -//! parsing, so we leave it here to avoid complicated out-of-line dependencies. -//! -//! A principled solution to this wrong location would be to implement [#64197]. -//! -//! [#64197]: https://github.com/rust-lang/rust/issues/64197 - -use crate::{parse_in, validate_attr}; -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_errors::{error_code, struct_span_err, Applicability, Handler}; -use rustc_feature::{Feature, Features, State as FeatureState}; -use rustc_feature::{ - ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES, -}; -use rustc_session::parse::{feature_err, ParseSess}; -use rustc_span::edition::{Edition, ALL_EDITIONS}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{Span, DUMMY_SP}; - -use smallvec::SmallVec; - -/// A folder that strips out items that do not belong in the current configuration. -pub struct StripUnconfigured<'a> { - pub sess: &'a ParseSess, - pub features: Option<&'a Features>, -} - -fn get_features( - span_handler: &Handler, - krate_attrs: &[ast::Attribute], - crate_edition: Edition, - allow_features: &Option>, -) -> Features { - fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) { - let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed"); - err.span_label(span, "feature has been removed"); - if let Some(reason) = reason { - err.note(reason); - } - err.emit(); - } - - fn active_features_up_to(edition: Edition) -> impl Iterator { - ACTIVE_FEATURES.iter().filter(move |feature| { - if let Some(feature_edition) = feature.edition { - feature_edition <= edition - } else { - false - } - }) - } - - let mut features = Features::default(); - let mut edition_enabled_features = FxHashMap::default(); - - for &edition in ALL_EDITIONS { - if edition <= crate_edition { - // The `crate_edition` implies its respective umbrella feature-gate - // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX). - edition_enabled_features.insert(edition.feature_name(), edition); - } - } - - for feature in active_features_up_to(crate_edition) { - feature.set(&mut features, DUMMY_SP); - edition_enabled_features.insert(feature.name, crate_edition); - } - - // Process the edition umbrella feature-gates first, to ensure - // `edition_enabled_features` is completed before it's queried. - for attr in krate_attrs { - if !attr.check_name(sym::feature) { - continue; - } - - let list = match attr.meta_item_list() { - Some(list) => list, - None => continue, - }; - - for mi in list { - if !mi.is_word() { - continue; - } - - let name = mi.name_or_empty(); - - let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied(); - if let Some(edition) = edition { - if edition <= crate_edition { - continue; - } - - for feature in active_features_up_to(edition) { - // FIXME(Manishearth) there is currently no way to set - // lib features by edition - feature.set(&mut features, DUMMY_SP); - edition_enabled_features.insert(feature.name, edition); - } - } - } - } - - for attr in krate_attrs { - if !attr.check_name(sym::feature) { - continue; - } - - let list = match attr.meta_item_list() { - Some(list) => list, - None => continue, - }; - - let bad_input = |span| { - struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input") - }; - - for mi in list { - let name = match mi.ident() { - Some(ident) if mi.is_word() => ident.name, - Some(ident) => { - bad_input(mi.span()) - .span_suggestion( - mi.span(), - "expected just one word", - format!("{}", ident.name), - Applicability::MaybeIncorrect, - ) - .emit(); - continue; - } - None => { - bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit(); - continue; - } - }; - - if let Some(edition) = edition_enabled_features.get(&name) { - let msg = - &format!("the feature `{}` is included in the Rust {} edition", name, edition); - span_handler.struct_span_warn_with_code(mi.span(), msg, error_code!(E0705)).emit(); - continue; - } - - if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) { - // Handled in the separate loop above. - continue; - } - - let removed = REMOVED_FEATURES.iter().find(|f| name == f.name); - let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name); - if let Some(Feature { state, .. }) = removed.or(stable_removed) { - if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } = - state - { - feature_removed(span_handler, mi.span(), *reason); - continue; - } - } - - if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) { - let since = Some(Symbol::intern(since)); - features.declared_lang_features.push((name, mi.span(), since)); - continue; - } - - if let Some(allowed) = allow_features.as_ref() { - if allowed.iter().find(|&f| name.as_str() == *f).is_none() { - struct_span_err!( - span_handler, - mi.span(), - E0725, - "the feature `{}` is not in the list of allowed features", - name - ) - .emit(); - continue; - } - } - - if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) { - f.set(&mut features, mi.span()); - features.declared_lang_features.push((name, mi.span(), None)); - continue; - } - - features.declared_lib_features.push((name, mi.span())); - } - } - - features -} - -// `cfg_attr`-process the crate's attributes and compute the crate's features. -pub fn features( - mut krate: ast::Crate, - sess: &ParseSess, - edition: Edition, - allow_features: &Option>, -) -> (ast::Crate, Features) { - let mut strip_unconfigured = StripUnconfigured { sess, features: None }; - - let unconfigured_attrs = krate.attrs.clone(); - let diag = &sess.span_diagnostic; - let err_count = diag.err_count(); - let features = match strip_unconfigured.configure(krate.attrs) { - None => { - // The entire crate is unconfigured. - krate.attrs = Vec::new(); - krate.module.items = Vec::new(); - Features::default() - } - Some(attrs) => { - krate.attrs = attrs; - let features = get_features(diag, &krate.attrs, edition, allow_features); - if err_count == diag.err_count() { - // Avoid reconfiguring malformed `cfg_attr`s. - strip_unconfigured.features = Some(&features); - strip_unconfigured.configure(unconfigured_attrs); - } - features - } - }; - (krate, features) -} - -#[macro_export] -macro_rules! configure { - ($this:ident, $node:ident) => { - match $this.configure($node) { - Some(node) => node, - None => return Default::default(), - } - }; -} - -const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]"; -const CFG_ATTR_NOTE_REF: &str = "for more information, visit \ - "; - -impl<'a> StripUnconfigured<'a> { - pub fn configure(&mut self, mut node: T) -> Option { - self.process_cfg_attrs(&mut node); - self.in_cfg(node.attrs()).then_some(node) - } - - /// Parse and expand all `cfg_attr` attributes into a list of attributes - /// that are within each `cfg_attr` that has a true configuration predicate. - /// - /// Gives compiler warnings if any `cfg_attr` does not contain any - /// attributes and is in the original source code. Gives compiler errors if - /// the syntax of any `cfg_attr` is incorrect. - pub fn process_cfg_attrs(&mut self, node: &mut T) { - node.visit_attrs(|attrs| { - attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); - }); - } - - /// Parse and expand a single `cfg_attr` attribute into a list of attributes - /// when the configuration predicate is true, or otherwise expand into an - /// empty list of attributes. - /// - /// Gives a compiler warning when the `cfg_attr` contains no attributes and - /// is in the original source file. Gives a compiler error if the syntax of - /// the attribute is incorrect. - fn process_cfg_attr(&mut self, attr: Attribute) -> Vec { - if !attr.has_name(sym::cfg_attr) { - return vec![attr]; - } - - let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) { - None => return vec![], - Some(r) => r, - }; - - // Lint on zero attributes in source. - if expanded_attrs.is_empty() { - return vec![attr]; - } - - // At this point we know the attribute is considered used. - attr::mark_used(&attr); - - if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) { - return vec![]; - } - - // We call `process_cfg_attr` recursively in case there's a - // `cfg_attr` inside of another `cfg_attr`. E.g. - // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. - expanded_attrs - .into_iter() - .flat_map(|(item, span)| { - let attr = attr::mk_attr_from_item(attr.style, item, span); - self.process_cfg_attr(attr) - }) - .collect() - } - - fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> { - match attr.get_normal_item().args { - ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => { - let msg = "wrong `cfg_attr` delimiters"; - validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg); - match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { - Ok(r) => return Some(r), - Err(mut e) => { - e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) - .note(CFG_ATTR_NOTE_REF) - .emit(); - } - } - } - _ => self.error_malformed_cfg_attr_missing(attr.span), - } - None - } - - fn error_malformed_cfg_attr_missing(&self, span: Span) { - self.sess - .span_diagnostic - .struct_span_err(span, "malformed `cfg_attr` attribute input") - .span_suggestion( - span, - "missing condition and attribute", - CFG_ATTR_GRAMMAR_HELP.to_string(), - Applicability::HasPlaceholders, - ) - .note(CFG_ATTR_NOTE_REF) - .emit(); - } - - /// Determines if a node with the given attributes should be included in this configuration. - pub fn in_cfg(&self, attrs: &[Attribute]) -> bool { - attrs.iter().all(|attr| { - if !is_cfg(attr) { - return true; - } - let meta_item = match validate_attr::parse_meta(self.sess, attr) { - Ok(meta_item) => meta_item, - Err(mut err) => { - err.emit(); - return true; - } - }; - let error = |span, msg, suggestion: &str| { - let mut err = self.sess.span_diagnostic.struct_span_err(span, msg); - if !suggestion.is_empty() { - err.span_suggestion( - span, - "expected syntax is", - suggestion.into(), - Applicability::MaybeIncorrect, - ); - } - err.emit(); - true - }; - let span = meta_item.span; - match meta_item.meta_item_list() { - None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"), - Some([]) => error(span, "`cfg` predicate is not specified", ""), - Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""), - Some([single]) => match single.meta_item() { - Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features), - None => error(single.span(), "`cfg` predicate key cannot be a literal", ""), - }, - } - }) - } - - /// Visit attributes on expression and statements (but not attributes on items in blocks). - fn visit_expr_attrs(&mut self, attrs: &[Attribute]) { - // flag the offending attributes - for attr in attrs.iter() { - self.maybe_emit_expr_attr_err(attr); - } - } - - /// If attributes are not allowed on expressions, emit an error for `attr` - pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { - if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) { - let mut err = feature_err( - self.sess, - sym::stmt_expr_attributes, - attr.span, - "attributes on expressions are experimental", - ); - - if attr.is_doc_comment() { - err.help("`///` is for documentation comments. For a plain comment, use `//`."); - } - - err.emit(); - } - } - - pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - let ast::ForeignMod { abi: _, items } = foreign_mod; - items.flat_map_in_place(|item| self.configure(item)); - } - - pub fn configure_generic_params(&mut self, params: &mut Vec) { - params.flat_map_in_place(|param| self.configure(param)); - } - - fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) { - match vdata { - ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => { - fields.flat_map_in_place(|field| self.configure(field)) - } - ast::VariantData::Unit(_) => {} - } - } - - pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) { - match item { - ast::ItemKind::Struct(def, _generics) | ast::ItemKind::Union(def, _generics) => { - self.configure_variant_data(def) - } - ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => { - variants.flat_map_in_place(|variant| self.configure(variant)); - for variant in variants { - self.configure_variant_data(&mut variant.data); - } - } - _ => {} - } - } - - pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) { - match expr_kind { - ast::ExprKind::Match(_m, arms) => { - arms.flat_map_in_place(|arm| self.configure(arm)); - } - ast::ExprKind::Struct(_path, fields, _base) => { - fields.flat_map_in_place(|field| self.configure(field)); - } - _ => {} - } - } - - pub fn configure_expr(&mut self, expr: &mut P) { - self.visit_expr_attrs(expr.attrs()); - - // If an expr is valid to cfg away it will have been removed by the - // outer stmt or expression folder before descending in here. - // Anything else is always required, and thus has to error out - // in case of a cfg attr. - // - // N.B., this is intentionally not part of the visit_expr() function - // in order for filter_map_expr() to be able to avoid this check - if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) { - let msg = "removing an expression is not supported in this position"; - self.sess.span_diagnostic.span_err(attr.span, msg); - } - - self.process_cfg_attrs(expr) - } - - pub fn configure_pat(&mut self, pat: &mut P) { - if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.kind { - fields.flat_map_in_place(|field| self.configure(field)); - } - } - - pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) { - fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg)); - } -} - -impl<'a> MutVisitor for StripUnconfigured<'a> { - fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - self.configure_foreign_mod(foreign_mod); - noop_visit_foreign_mod(foreign_mod, self); - } - - fn visit_item_kind(&mut self, item: &mut ast::ItemKind) { - self.configure_item_kind(item); - noop_visit_item_kind(item, self); - } - - fn visit_expr(&mut self, expr: &mut P) { - self.configure_expr(expr); - self.configure_expr_kind(&mut expr.kind); - noop_visit_expr(expr, self); - } - - fn filter_map_expr(&mut self, expr: P) -> Option> { - let mut expr = configure!(self, expr); - self.configure_expr_kind(&mut expr.kind); - noop_visit_expr(&mut expr, self); - Some(expr) - } - - fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { - noop_flat_map_stmt(configure!(self, stmt), self) - } - - fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_item(configure!(self, item), self) - } - - fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_assoc_item(configure!(self, item), self) - } - - fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_assoc_item(configure!(self, item), self) - } - - fn visit_mac(&mut self, _mac: &mut ast::MacCall) { - // Don't configure interpolated AST (cf. issue #34171). - // Interpolated AST will get configured once the surrounding tokens are parsed. - } - - fn visit_pat(&mut self, pat: &mut P) { - self.configure_pat(pat); - noop_visit_pat(pat, self) - } - - fn visit_fn_decl(&mut self, mut fn_decl: &mut P) { - self.configure_fn_decl(&mut fn_decl); - noop_visit_fn_decl(fn_decl, self); - } -} - -fn is_cfg(attr: &Attribute) -> bool { - attr.check_name(sym::cfg) -} - -/// Process the potential `cfg` attributes on a module. -/// Also determine if the module should be included in this configuration. -pub fn process_configure_mod(sess: &ParseSess, cfg_mods: bool, attrs: &mut Vec) -> bool { - // Don't perform gated feature checking. - let mut strip_unconfigured = StripUnconfigured { sess, features: None }; - strip_unconfigured.process_cfg_attrs(attrs); - !cfg_mods || strip_unconfigured.in_cfg(&attrs) -} diff --git a/src/librustc_parse/lexer/mod.rs b/src/librustc_parse/lexer/mod.rs index f7fb704fcbc2c..8e74c3847bc90 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, RawStrError}; 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. @@ -46,12 +46,19 @@ impl<'a> StringReader<'a> { source_file: Lrc, override_span: Option, ) -> Self { - if source_file.src.is_none() { + // Make sure external source is loaded first, before accessing it. + // While this can't show up during normal parsing, `retokenize` may + // be called with a source file from an external crate. + sess.source_map().ensure_source_file_source_present(Lrc::clone(&source_file)); + + let src = if let Some(src) = &source_file.src { + Lrc::clone(&src) + } else if let Some(src) = source_file.external_src.borrow().get_source() { + Lrc::clone(&src) + } else { sess.span_diagnostic .bug(&format!("cannot lex `source_file` without source: {}", source_file.name)); - } - - let src = (*source_file.src.as_ref().unwrap()).clone(); + }; StringReader { sess, @@ -85,9 +92,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]; @@ -120,10 +124,7 @@ impl<'a> StringReader<'a> { debug!("try_next_token: {:?}({:?})", token.kind, self.str_from(start)); - // This could use `?`, but that makes code significantly (10-20%) slower. - // https://github.com/rust-lang/rust/issues/37939 let kind = self.cook_lexer_token(token.kind, start); - let span = self.mk_sp(start, self.pos); Token::new(kind, span) } @@ -148,15 +149,6 @@ impl<'a> StringReader<'a> { self.err_span(self.mk_sp(from_pos, to_pos), m) } - fn struct_span_fatal( - &self, - from_pos: BytePos, - to_pos: BytePos, - m: &str, - ) -> DiagnosticBuilder<'a> { - self.sess.span_diagnostic.struct_span_fatal(self.mk_sp(from_pos, to_pos), m) - } - fn struct_fatal_span_char( &self, from_pos: BytePos, @@ -179,14 +171,12 @@ impl<'a> StringReader<'a> { rustc_lexer::TokenKind::LineComment => { let string = self.str_from(start); // comments with only more "/"s are not doc comments - let tok = if comments::is_line_doc_comment(string) { + if comments::is_line_doc_comment(string) { self.forbid_bare_cr(start, string, "bare CR not allowed in doc-comment"); token::DocComment(Symbol::intern(string)) } else { token::Comment - }; - - tok + } } rustc_lexer::TokenKind::BlockComment { terminated } => { let string = self.str_from(start); @@ -201,17 +191,23 @@ impl<'a> StringReader<'a> { "unterminated block comment" }; let last_bpos = self.pos; - self.fatal_span_(start, last_bpos, msg).raise(); + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start, last_bpos), + msg, + error_code!(E0758), + ) + .emit(); + FatalError.raise(); } - let tok = if is_doc_comment { + if is_doc_comment { self.forbid_bare_cr(start, string, "bare CR not allowed in block doc-comment"); token::DocComment(Symbol::intern(string)) } else { token::Comment - }; - - tok + } } rustc_lexer::TokenKind::Whitespace => token::Whitespace, rustc_lexer::TokenKind::Ident | rustc_lexer::TokenKind::RawIdent => { @@ -221,8 +217,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)); } @@ -324,38 +321,49 @@ 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() + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start, suffix_start), + "unterminated character literal", + error_code!(E0762), + ) + .emit(); + FatalError.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() + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start + BytePos(1), suffix_start), + "unterminated byte constant", + error_code!(E0763), + ) + .emit(); + FatalError.raise(); } - 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() + self.sess + .span_diagnostic + .struct_span_fatal_with_code( + self.mk_sp(start, suffix_start), + "unterminated double quote string", + error_code!(E0765), + ) + .emit(); + FatalError.raise(); } - 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 { @@ -366,59 +374,30 @@ 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 { n_hashes, err } => { + self.report_raw_str_error(start, err); 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 { n_hashes, err } => { + self.report_raw_str_error(start, err); 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 { - let mut err = self.struct_span_fatal( - start, - self.pos, - "expected at least one digit in exponent", - ); - err.emit(); + self.err_span_(start, self.pos, "expected at least one digit in exponent"); } match base { @@ -437,9 +416,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); + (lit_kind, id) + } + + pub fn pos(&self) -> BytePos { + self.pos } #[inline] @@ -481,143 +469,101 @@ impl<'a> StringReader<'a> { } } - fn report_non_started_raw_string(&self, start: BytePos) -> ! { - let bad_char = self.str_from(start).chars().last().unwrap(); + fn report_raw_str_error(&self, start: BytePos, opt_err: Option) { + match opt_err { + Some(RawStrError::InvalidStarter { bad_char }) => { + self.report_non_started_raw_string(start, bad_char) + } + Some(RawStrError::NoTerminator { expected, found, possible_terminator_offset }) => self + .report_unterminated_raw_string(start, expected, possible_terminator_offset, found), + Some(RawStrError::TooManyDelimiters { found }) => { + self.report_too_many_hashes(start, found) + } + None => (), + } + } + + fn report_non_started_raw_string(&self, start: BytePos, bad_char: char) -> ! { self.struct_fatal_span_char( start, self.pos, - "found invalid character; only `#` is allowed \ - in raw string delimitation", + "found invalid character; only `#` is allowed in raw string delimitation", bad_char, ) .emit(); FatalError.raise() } - fn report_unterminated_raw_string(&self, start: BytePos, n_hashes: usize) -> ! { + 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, - ) - } - }) + /// Note: It was decided to not add a test case, because it would be to big. + /// https://github.com/rust-lang/rust/pull/50296#issuecomment-392135180 + fn report_too_many_hashes(&self, start: BytePos, found: usize) -> ! { + self.fatal_span_( + start, + self.pos, + &format!( + "too many `#` symbols: raw strings may be delimited \ + by up to 65535 `#` symbols, but found {}", + found + ), + ) + .raise(); } - fn validate_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 884499ff2dd48..be86b4b7c7720 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -3,6 +3,8 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(bindings_after_at)] +#![feature(try_blocks)] +#![feature(or_patterns)] use rustc_ast::ast; use rustc_ast::token::{self, Nonterminal}; @@ -13,10 +15,10 @@ use rustc_errors::{Diagnostic, FatalError, Level, PResult}; use rustc_session::parse::ParseSess; use rustc_span::{FileName, SourceFile, Span}; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::str; -use log::info; +use log::{debug, info}; pub const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments"); @@ -25,24 +27,6 @@ pub mod parser; use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser}; pub mod lexer; pub mod validate_attr; -#[macro_use] -pub mod config; - -#[derive(Clone)] -pub struct Directory { - pub path: PathBuf, - pub ownership: DirectoryOwnership, -} - -#[derive(Copy, Clone)] -pub enum DirectoryOwnership { - Owned { - // None if `mod.rs`, `Some("foo")` if we're in `foo.rs`. - relative: Option, - }, - UnownedViaBlock, - UnownedViaMod, -} // A bunch of utility functions of the form `parse__from_` // where includes crate, expr, item, stmt, tts, and one that @@ -67,7 +51,7 @@ macro_rules! panictry_buffer { } pub fn parse_crate_from_file<'a>(input: &Path, sess: &'a ParseSess) -> PResult<'a, ast::Crate> { - let mut parser = new_parser_from_file(sess, input); + let mut parser = new_parser_from_file(sess, input, None); parser.parse_crate_mod() } @@ -75,7 +59,7 @@ pub fn parse_crate_attrs_from_file<'a>( input: &Path, sess: &'a ParseSess, ) -> PResult<'a, Vec> { - let mut parser = new_parser_from_file(sess, input); + let mut parser = new_parser_from_file(sess, input, None); parser.parse_inner_attributes() } @@ -119,15 +103,13 @@ pub fn maybe_new_parser_from_source_str( name: FileName, source: String, ) -> Result, Vec> { - let mut parser = - maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source))?; - parser.recurse_into_file_modules = false; - Ok(parser) + maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source)) } /// Creates a new parser, handling errors as appropriate if the file doesn't exist. -pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path) -> Parser<'a> { - source_file_to_parser(sess, file_to_source_file(sess, path, None)) +/// If a span is given, that is used on an error as the as the source of the problem. +pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option) -> Parser<'a> { + source_file_to_parser(sess, file_to_source_file(sess, path, sp)) } /// Creates a new parser, returning buffered diagnostics if the file doesn't exist, @@ -140,22 +122,6 @@ pub fn maybe_new_parser_from_file<'a>( maybe_source_file_to_parser(sess, file) } -/// Given a session, a crate config, a path, and a span, add -/// the file at the given path to the `source_map`, and returns a parser. -/// On an error, uses the given span as the source of the problem. -pub fn new_sub_parser_from_file<'a>( - sess: &'a ParseSess, - path: &Path, - directory_ownership: DirectoryOwnership, - module_name: Option, - sp: Span, -) -> Parser<'a> { - let mut p = source_file_to_parser(sess, file_to_source_file(sess, path, Some(sp))); - p.directory.ownership = directory_ownership; - p.root_module_name = module_name; - p -} - /// Given a `source_file` and config, returns a parser. fn source_file_to_parser(sess: &ParseSess, source_file: Lrc) -> Parser<'_> { panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file)) @@ -257,26 +223,7 @@ pub fn stream_to_parser<'a>( stream: TokenStream, subparser_name: Option<&'static str>, ) -> Parser<'a> { - Parser::new(sess, stream, None, true, false, subparser_name) -} - -/// Given a stream, the `ParseSess` and the base directory, produces a parser. -/// -/// Use this function when you are creating a parser from the token stream -/// and also care about the current working directory of the parser (e.g., -/// you are trying to resolve modules defined inside a macro invocation). -/// -/// # Note -/// -/// The main usage of this function is outside of rustc, for those who uses -/// librustc_ast as a library. Please do not remove this function while refactoring -/// just because it is not used in rustc codebase! -pub fn stream_to_parser_with_base_dir( - sess: &ParseSess, - stream: TokenStream, - base_dir: Directory, -) -> Parser<'_> { - Parser::new(sess, stream, Some(base_dir), true, false, None) + Parser::new(sess, stream, false, subparser_name) } /// Runs the given subparser `f` on the tokens of the given `attr`'s item. @@ -286,7 +233,7 @@ pub fn parse_in<'a, T>( name: &'static str, mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, T> { - let mut parser = Parser::new(sess, tts, None, false, false, Some(name)); + let mut parser = Parser::new(sess, tts, false, Some(name)); let result = f(&mut parser)?; if parser.token != token::Eof { parser.unexpected()?; @@ -321,6 +268,12 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into()) } Nonterminal::NtTT(ref tt) => Some(tt.clone().into()), + Nonterminal::NtExpr(ref expr) => { + if expr.tokens.is_none() { + debug!("missing tokens for expr {:?}", expr); + } + prepend_attrs(sess, &expr.attrs, expr.tokens.as_ref(), span) + } _ => None, }; @@ -360,8 +313,10 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke "cached tokens found, but they're not \"probably equal\", \ going with stringified version" ); + info!("cached tokens: {:?}", tokens); + info!("reparsed tokens: {:?}", tokens_for_real); } - return tokens_for_real; + tokens_for_real } fn prepend_attrs( 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 7c1df531ad16e..fc9ffc3092447 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"; @@ -40,55 +37,12 @@ pub(super) fn dummy_arg(ident: Ident) -> Param { } pub enum Error { - FileNotFoundForModule { - mod_name: String, - default_path: String, - secondary_path: String, - dir_path: String, - }, - DuplicatePaths { - mod_name: String, - default_path: String, - secondary_path: String, - }, UselessDocComment, } impl Error { fn span_err(self, sp: impl Into, handler: &Handler) -> DiagnosticBuilder<'_> { match self { - Error::FileNotFoundForModule { - ref mod_name, - ref default_path, - ref secondary_path, - ref dir_path, - } => { - let mut err = struct_span_err!( - handler, - sp, - E0583, - "file not found for module `{}`", - mod_name, - ); - err.help(&format!( - "name the file either {} or {} inside the directory \"{}\"", - default_path, secondary_path, dir_path, - )); - err - } - Error::DuplicatePaths { ref mod_name, ref default_path, ref secondary_path } => { - let mut err = struct_span_err!( - handler, - sp, - E0584, - "file for module `{}` found at both {} and {}", - mod_name, - default_path, - secondary_path, - ); - err.help("delete or rename one of them to remove the ambiguity"); - err - } Error::UselessDocComment => { let mut err = struct_span_err!( handler, @@ -141,6 +95,7 @@ impl RecoverQPath for Expr { kind: ExprKind::Path(qself, path), attrs: AttrVec::new(), id: ast::DUMMY_NODE_ID, + tokens: None, } } } @@ -299,6 +254,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 @@ -326,6 +285,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<'_>, @@ -502,48 +484,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). @@ -557,11 +576,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, @@ -577,31 +598,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(); @@ -615,7 +631,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 { @@ -640,7 +656,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) } } @@ -660,15 +676,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)); + } } _ => {} } @@ -686,7 +720,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. @@ -894,13 +928,26 @@ 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. return self.expect(&token::Semi).map(drop); } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { // The current token is in the same line as the prior token, not recoverable. + } else if [token::Comma, token::Colon].contains(&self.token.kind) + && self.prev_token.kind == token::CloseDelim(token::Paren) + { + // Likely typo: The current token is on a new line and is expected to be + // `.`, `;`, `?`, or an operator after a close delimiter token. + // + // let a = std::process::Command::new("echo") + // .arg("1") + // ,arg("2") + // ^ + // https://github.com/rust-lang/rust/issues/72253 + self.expect(&token::Semi)?; + return Ok(()); } else if self.look_ahead(1, |t| { t == &token::CloseDelim(token::Brace) || t.can_begin_expr() && t.kind != token::Colon }) && [token::Comma, token::Colon].contains(&self.token.kind) @@ -914,7 +961,7 @@ impl<'a> Parser<'a> { self.bump(); let sp = self.prev_token.span; self.struct_span_err(sp, &msg) - .span_suggestion(sp, "change this to `;`", ";".to_string(), appl) + .span_suggestion_short(sp, "change this to `;`", ";".to_string(), appl) .emit(); return Ok(()); } else if self.look_ahead(0, |t| { @@ -1019,6 +1066,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. /// @@ -1078,7 +1158,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 c65e99842c5dd..49a5c8801766c 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -4,7 +4,8 @@ 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 log::debug; +use rustc_ast::ast::{self, AttrStyle, AttrVec, CaptureBy, Field, Lit, UnOp, DUMMY_NODE_ID}; use rustc_ast::ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind}; use rustc_ast::ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::ptr::P; @@ -13,9 +14,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 @@ -431,19 +432,23 @@ impl<'a> Parser<'a> { /// Parses a prefix-unary-operator expr. fn parse_prefix_expr(&mut self, attrs: Option) -> PResult<'a, P> { let attrs = self.parse_or_use_outer_attributes(attrs)?; - let lo = self.token.span; - // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() - let (hi, ex) = match self.token.uninterpolate().kind { - token::Not => self.parse_unary_expr(lo, UnOp::Not), // `!expr` - token::Tilde => self.recover_tilde_expr(lo), // `~expr` - token::BinOp(token::Minus) => self.parse_unary_expr(lo, UnOp::Neg), // `-expr` - token::BinOp(token::Star) => self.parse_unary_expr(lo, UnOp::Deref), // `*expr` - token::BinOp(token::And) | token::AndAnd => self.parse_borrow_expr(lo), - token::Ident(..) if self.token.is_keyword(kw::Box) => self.parse_box_expr(lo), - token::Ident(..) if self.is_mistaken_not_ident_negation() => self.recover_not_expr(lo), - _ => return self.parse_dot_or_call_expr(Some(attrs)), - }?; - Ok(self.mk_expr(lo.to(hi), ex, attrs)) + self.maybe_collect_tokens(!attrs.is_empty(), |this| { + let lo = this.token.span; + // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() + let (hi, ex) = match this.token.uninterpolate().kind { + token::Not => this.parse_unary_expr(lo, UnOp::Not), // `!expr` + token::Tilde => this.recover_tilde_expr(lo), // `~expr` + token::BinOp(token::Minus) => this.parse_unary_expr(lo, UnOp::Neg), // `-expr` + token::BinOp(token::Star) => this.parse_unary_expr(lo, UnOp::Deref), // `*expr` + token::BinOp(token::And) | token::AndAnd => this.parse_borrow_expr(lo), + token::Ident(..) if this.token.is_keyword(kw::Box) => this.parse_box_expr(lo), + token::Ident(..) if this.is_mistaken_not_ident_negation() => { + this.recover_not_expr(lo) + } + _ => return this.parse_dot_or_call_expr(Some(attrs)), + }?; + Ok(this.mk_expr(lo.to(hi), ex, attrs)) + }) } fn parse_prefix_expr_common(&mut self, lo: Span) -> PResult<'a, (Span, P)> { @@ -547,8 +552,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 +564,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 +605,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); } } @@ -635,9 +639,10 @@ impl<'a> Parser<'a> { ExprKind::Index(_, _) => "indexing", ExprKind::Try(_) => "?", ExprKind::Field(_, _) => "a field access", - ExprKind::MethodCall(_, _) => "a method call", + 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"), } ); @@ -860,6 +865,7 @@ impl<'a> Parser<'a> { return self.mk_await_expr(self_arg, lo); } + let fn_span_lo = self.token.span; let segment = self.parse_path_segment(PathStyle::Expr)?; self.check_trailing_angle_brackets(&segment, token::OpenDelim(token::Paren)); @@ -868,8 +874,9 @@ impl<'a> Parser<'a> { let mut args = self.parse_paren_expr_seq()?; args.insert(0, self_arg); + let fn_span = fn_span_lo.to(self.prev_token.span); let span = lo.to(self.prev_token.span); - Ok(self.mk_expr(span, ExprKind::MethodCall(segment, args), AttrVec::new())) + Ok(self.mk_expr(span, ExprKind::MethodCall(segment, args, fn_span), AttrVec::new())) } else { // Field access `expr.f` if let Some(args) = segment.args { @@ -925,8 +932,17 @@ impl<'a> Parser<'a> { self.parse_closure_expr(attrs) } else if self.eat_keyword(kw::If) { self.parse_if_expr(attrs) - } else if self.eat_keyword(kw::For) { - self.parse_for_expr(None, self.prev_token.span, attrs) + } else if self.check_keyword(kw::For) { + if self.choose_generics_over_qpath(1) { + // NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery, + // this is an insurance policy in case we allow qpaths in (tuple-)struct patterns. + // When `for ::Proj in $expr $block` is wanted, + // you can disambiguate in favor of a pattern with `(...)`. + self.recover_quantified_closure_expr(attrs) + } else { + assert!(self.eat_keyword(kw::For)); + self.parse_for_expr(None, self.prev_token.span, attrs) + } } else if self.eat_keyword(kw::While) { self.parse_while_expr(None, self.prev_token.span, attrs) } else if let Some(label) = self.eat_label() { @@ -989,6 +1005,21 @@ impl<'a> Parser<'a> { } } + fn maybe_collect_tokens( + &mut self, + has_outer_attrs: bool, + f: impl FnOnce(&mut Self) -> PResult<'a, P>, + ) -> PResult<'a, P> { + if has_outer_attrs { + let (mut expr, tokens) = self.collect_tokens(f)?; + debug!("maybe_collect_tokens: Collected tokens for {:?} (tokens {:?}", expr, tokens); + expr.tokens = Some(tokens); + Ok(expr) + } else { + f(self) + } + } + fn parse_lit_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.token.span; match self.parse_opt_lit() { @@ -996,7 +1027,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 => return Err(self.expected_expression_found()), + None => self.try_macro_suggestion(), } } @@ -1059,8 +1090,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) { @@ -1072,7 +1103,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)) @@ -1374,6 +1405,7 @@ impl<'a> Parser<'a> { } /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`). + /// Keep this in sync with `Token::can_begin_literal_maybe_minus`. pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P> { maybe_whole_expr!(self); @@ -1416,6 +1448,26 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs)) } + /// Recover on an explicitly quantified closure expression, e.g., `for<'a> |x: &'a u8| *x + 1`. + fn recover_quantified_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { + let lo = self.token.span; + let _ = self.parse_late_bound_lifetime_defs()?; + let span_for = lo.to(self.prev_token.span); + let closure = self.parse_closure_expr(attrs)?; + + self.struct_span_err(span_for, "cannot introduce explicit parameters for a closure") + .span_label(closure.span, "the parameters are attached to this closure") + .span_suggestion( + span_for, + "remove the parameters", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + + Ok(self.mk_expr_err(lo.to(closure.span))) + } + /// Parses a closure expression (e.g., `move |args| expr`). fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.token.span; @@ -1519,6 +1571,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 })?; @@ -1713,7 +1770,7 @@ impl<'a> Parser<'a> { } let hi = self.token.span; self.bump(); - return Ok(self.mk_expr(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs)); + Ok(self.mk_expr(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs)) } pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { @@ -1815,11 +1872,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. @@ -1862,16 +1917,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); } @@ -1890,17 +1944,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; @@ -1919,7 +1979,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 @@ -1943,15 +2007,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); @@ -1960,9 +2028,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 }`. @@ -2122,7 +2191,7 @@ impl<'a> Parser<'a> { } crate fn mk_expr(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P { - P(Expr { kind, span, attrs, id: DUMMY_NODE_ID }) + P(Expr { kind, span, attrs, id: DUMMY_NODE_ID, tokens: None }) } pub(super) fn mk_expr_err(&self, span: Span) -> P { diff --git a/src/librustc_parse/parser/generics.rs b/src/librustc_parse/parser/generics.rs index 59fd5f7c4be1f..04b64d93c70dd 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; } } @@ -157,6 +157,7 @@ impl<'a> Parser<'a> { Ok(ast::Generics { params, where_clause: WhereClause { + has_where_token: false, predicates: Vec::new(), span: self.prev_token.span.shrink_to_hi(), }, @@ -170,18 +171,22 @@ impl<'a> Parser<'a> { /// where T : Trait + 'b, 'a : 'b /// ``` pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> { - let mut where_clause = - WhereClause { predicates: Vec::new(), span: self.prev_token.span.shrink_to_hi() }; + let mut where_clause = WhereClause { + has_where_token: false, + predicates: Vec::new(), + span: self.prev_token.span.shrink_to_hi(), + }; if !self.eat_keyword(kw::Where) { return Ok(where_clause); } + where_clause.has_where_token = true; let lo = self.prev_token.span; // We are considering adding generics to the `where` keyword as an alternative higher-rank // parameter syntax (as in `where<'a>` or `where`. To avoid that being a breaking // change we parse those generics now, but report an error. - if self.choose_generics_over_qpath() { + if self.choose_generics_over_qpath(0) { let generics = self.parse_generics()?; self.struct_span_err( generics.span, @@ -257,7 +262,7 @@ impl<'a> Parser<'a> { } } - pub(super) fn choose_generics_over_qpath(&self) -> bool { + pub(super) fn choose_generics_over_qpath(&self, start: usize) -> bool { // There's an ambiguity between generic parameters and qualified paths in impls. // If we see `<` it may start both, so we have to inspect some following tokens. // The following combinations can only start generics, @@ -274,15 +279,12 @@ impl<'a> Parser<'a> { // we disambiguate it in favor of generics (`impl ::absolute::Path { ... }`) // because this is what almost always expected in practice, qualified paths in impls // (`impl ::AssocTy { ... }`) aren't even allowed by type checker at the moment. - self.token == token::Lt - && (self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) - || self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) - && self.look_ahead(2, |t| { - t == &token::Gt - || t == &token::Comma - || t == &token::Colon - || t == &token::Eq + self.look_ahead(start, |t| t == &token::Lt) + && (self.look_ahead(start + 1, |t| t == &token::Pound || t == &token::Gt) + || self.look_ahead(start + 1, |t| t.is_lifetime() || t.is_ident()) + && self.look_ahead(start + 2, |t| { + matches!(t.kind, token::Gt | token::Comma | token::Colon | token::Eq) }) - || self.is_keyword_ahead(1, &[kw::Const])) + || self.is_keyword_ahead(start + 1, &[kw::Const])) } } diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index fe5495fbf8a2b..6f13d7994d17d 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -4,25 +4,81 @@ use super::{FollowedByType, Parser, PathStyle}; use crate::maybe_whole; -use rustc_ast::ast::{self, Async, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID}; -use rustc_ast::ast::{AssocItem, AssocItemKind, ForeignItemKind, Item, ItemKind}; -use rustc_ast::ast::{BindingMode, Block, FnDecl, FnSig, MacArgs, MacCall, MacDelimiter, Param}; -use rustc_ast::ast::{Const, Defaultness, IsAuto, PathSegment, Unsafe, UseTree, UseTreeKind}; +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}; use rustc_ast::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData}; -use rustc_ast::ast::{FnHeader, ForeignItem, Mutability, SelfKind, Visibility, VisibilityKind}; +use rustc_ast::ast::{FnHeader, ForeignItem, PathSegment, Visibility, VisibilityKind}; +use rustc_ast::ast::{MacArgs, MacCall, MacDelimiter}; use rustc_ast::ptr::P; -use rustc_ast::token; +use rustc_ast::token::{self, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; 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; use std::mem; +impl<'a> Parser<'a> { + /// Parses a source module as a crate. This is the main entry point for the parser. + pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> { + let lo = self.token.span; + let (module, attrs) = self.parse_mod(&token::Eof)?; + let span = lo.to(self.token.span); + let proc_macros = Vec::new(); // Filled in by `proc_macro_harness::inject()`. + Ok(ast::Crate { attrs, module, span, proc_macros }) + } + + /// Parses a `mod { ... }` or `mod ;` item. + fn parse_item_mod(&mut self, attrs: &mut Vec) -> PResult<'a, ItemInfo> { + let id = self.parse_ident()?; + let (module, mut inner_attrs) = if self.eat(&token::Semi) { + Default::default() + } else { + self.expect(&token::OpenDelim(token::Brace))?; + self.parse_mod(&token::CloseDelim(token::Brace))? + }; + attrs.append(&mut inner_attrs); + Ok((id, ItemKind::Mod(module))) + } + + /// Parses the contents of a module (inner attributes followed by module items). + pub fn parse_mod(&mut self, term: &TokenKind) -> PResult<'a, (Mod, Vec)> { + let lo = self.token.span; + let attrs = self.parse_inner_attributes()?; + let module = self.parse_mod_items(term, lo)?; + Ok((module, attrs)) + } + + /// Given a termination token, parses all of the items in a module. + fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> { + let mut items = vec![]; + while let Some(item) = self.parse_item()? { + items.push(item); + self.maybe_consume_incorrect_semicolon(&items); + } + + if !self.eat(term) { + let token_str = super::token_descr(&self.token); + if !self.maybe_consume_incorrect_semicolon(&items) { + let msg = &format!("expected item, found {}", token_str); + let mut err = self.struct_span_err(self.token.span, msg); + err.span_label(self.token.span, "expected item"); + return Err(err); + } + } + + let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span }; + + Ok(Mod { inner: inner_lo.to(hi), items, inline: true }) + } +} + pub(super) type ItemInfo = (Ident, ItemKind); impl<'a> Parser<'a> { @@ -50,11 +106,20 @@ impl<'a> Parser<'a> { }); let mut unclosed_delims = vec![]; - let (mut item, tokens) = self.collect_tokens(|this| { + let has_attrs = !attrs.is_empty(); + let parse_item = |this: &mut Self| { let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name); unclosed_delims.append(&mut this.unclosed_delims); item - })?; + }; + + let (mut item, tokens) = if has_attrs { + let (item, tokens) = self.collect_tokens(parse_item)?; + (item, Some(tokens)) + } else { + (parse_item(self)?, None) + }; + self.unclosed_delims.append(&mut unclosed_delims); // Once we've parsed an item and recorded the tokens we got while @@ -71,9 +136,11 @@ impl<'a> Parser<'a> { // it (bad!). To work around this case for now we just avoid recording // `tokens` if we detect any inner attributes. This should help keep // expansion correct, but we should fix this bug one day! - if let Some(item) = &mut item { - if !item.attrs.iter().any(|attr| attr.style == AttrStyle::Inner) { - item.tokens = Some(tokens); + if let Some(tokens) = tokens { + if let Some(item) = &mut item { + if !item.attrs.iter().any(|attr| attr.style == AttrStyle::Inner) { + item.tokens = Some(tokens); + } } } Ok(item) @@ -258,7 +325,7 @@ impl<'a> Parser<'a> { " struct ".into(), Applicability::MaybeIncorrect, // speculative ); - return Err(err); + Err(err) } else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) { let ident = self.parse_ident().unwrap(); self.bump(); // `(` @@ -306,7 +373,7 @@ impl<'a> Parser<'a> { ); } } - return Err(err); + Err(err) } else if self.look_ahead(1, |t| *t == token::Lt) { let ident = self.parse_ident().unwrap(); self.eat_to_tokens(&[&token::Gt]); @@ -328,7 +395,7 @@ impl<'a> Parser<'a> { Applicability::MachineApplicable, ); } - return Err(err); + Err(err) } else { Ok(()) } @@ -402,7 +469,7 @@ impl<'a> Parser<'a> { self.expect_keyword(kw::Impl)?; // First, parse generic parameters if necessary. - let mut generics = if self.choose_generics_over_qpath() { + let mut generics = if self.choose_generics_over_qpath(0) { self.parse_generics()? } else { let mut generics = Generics::default(); @@ -687,7 +754,7 @@ impl<'a> Parser<'a> { /// Parses a `UseTree`. /// - /// ``` + /// ```text /// USE_TREE = [`::`] `*` | /// [`::`] `{` USE_TREE_LIST `}` | /// PATH `::` `*` | @@ -736,7 +803,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)>> { @@ -748,7 +815,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(); @@ -778,7 +845,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"; @@ -851,10 +918,12 @@ 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(); - return None; + 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 } fn error_on_foreign_const(&self, span: Span, ident: Ident) { @@ -1260,7 +1329,7 @@ impl<'a> Parser<'a> { }; self.sess.gated_spans.gate(sym::decl_macro, lo.to(self.prev_token.span)); - Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, legacy: false }))) + Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: false }))) } /// Is this unambiguously the start of a `macro_rules! foo` item defnition? @@ -1270,7 +1339,7 @@ impl<'a> Parser<'a> { && self.look_ahead(2, |t| t.is_ident()) } - /// Parses a legacy `macro_rules! foo { ... }` declarative macro. + /// Parses a `macro_rules! foo { ... }` declarative macro. fn parse_item_macro_rules(&mut self, vis: &Visibility) -> PResult<'a, ItemInfo> { self.expect_keyword(kw::MacroRules)?; // `macro_rules` self.expect(&token::Not)?; // `!` @@ -1280,7 +1349,7 @@ impl<'a> Parser<'a> { self.eat_semi_for_macro_if_needed(&body); self.complain_if_pub_macro(vis, true); - Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, legacy: true }))) + Ok((ident, ItemKind::MacroDef(ast::MacroDef { body, macro_rules: true }))) } /// Item macro invocations or `macro_rules!` definitions need inherited visibility. @@ -1438,7 +1507,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]; @@ -1453,7 +1522,7 @@ impl<'a> Parser<'a> { }) // `extern ABI fn` || self.check_keyword(kw::Extern) - && self.look_ahead(1, |t| t.can_begin_literal_or_bool()) + && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus()) && self.look_ahead(2, |t| t.is_keyword(kw::Fn)) } @@ -1465,7 +1534,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(); @@ -1492,7 +1561,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(); @@ -1592,7 +1661,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 9376c7c1c724d..7811d5fb741b2 100644 --- a/src/librustc_parse/parser/mod.rs +++ b/src/librustc_parse/parser/mod.rs @@ -1,8 +1,6 @@ pub mod attr; mod expr; mod item; -mod module; -pub use module::{ModulePath, ModulePathSuccess}; mod pat; mod path; mod ty; @@ -13,11 +11,10 @@ mod stmt; use diagnostics::Error; use crate::lexer::UnmatchedBrace; -use crate::{Directory, DirectoryOwnership}; 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, }; @@ -28,11 +25,9 @@ use rustc_ast::util::comments::{doc_comment_style, strip_doc_comment_decoration} 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; -use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::{FileName, Span, DUMMY_SP}; +use rustc_span::source_map::{respan, Span, DUMMY_SP}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use std::path::PathBuf; use std::{cmp, mem, slice}; bitflags::bitflags! { @@ -93,21 +88,9 @@ pub struct Parser<'a> { /// The previous token. pub prev_token: Token, restrictions: Restrictions, - /// Used to determine the path to externally loaded source files. - pub(super) directory: Directory, - /// `true` to parse sub-modules in other files. - // Public for rustfmt usage. - pub recurse_into_file_modules: bool, - /// Name of the root module this parser originated from. If `None`, then the - /// name is not known. This does not change while the parser is descending - /// into modules, and sub-parsers have new values for this name. - pub root_module_name: Option, expected_tokens: Vec, token_cursor: TokenCursor, desugar_doc_comments: bool, - /// `true` we should configure out of line modules as we parse. - // Public for rustfmt usage. - pub cfg_mods: bool, /// This field is used to keep track of how many left angle brackets we have seen. This is /// required in order to detect extra leading left angle brackets (`<` characters) and error /// appropriately. @@ -135,6 +118,8 @@ impl<'a> Drop for Parser<'a> { struct TokenCursor { frame: TokenCursorFrame, stack: Vec, + cur_token: Option, + collecting: Option, } #[derive(Clone)] @@ -144,30 +129,24 @@ struct TokenCursorFrame { open_delim: bool, tree_cursor: tokenstream::Cursor, close_delim: bool, - last_token: LastToken, } -/// This is used in `TokenCursorFrame` above to track tokens that are consumed -/// by the parser, and then that's transitively used to record the tokens that -/// each parse AST item is created with. -/// -/// Right now this has two states, either collecting tokens or not collecting -/// tokens. If we're collecting tokens we just save everything off into a local -/// `Vec`. This should eventually though likely save tokens from the original -/// token stream and just use slicing of token streams to avoid creation of a -/// whole new vector. -/// -/// The second state is where we're passively not recording tokens, but the last -/// token is still tracked for when we want to start recording tokens. This -/// "last token" means that when we start recording tokens we'll want to ensure -/// that this, the first token, is included in the output. -/// -/// You can find some more example usage of this in the `collect_tokens` method -/// on the parser. -#[derive(Clone)] -enum LastToken { - Collecting(Vec), - Was(Option), +/// Used to track additional state needed by `collect_tokens` +#[derive(Clone, Debug)] +struct Collecting { + /// Holds the current tokens captured during the most + /// recent call to `collect_tokens` + buf: Vec, + /// The depth of the `TokenCursor` stack at the time + /// collection was started. When we encounter a `TokenTree::Delimited`, + /// we want to record the `TokenTree::Delimited` itself, + /// but *not* any of the inner tokens while we are inside + /// the new frame (this would cause us to record duplicate tokens). + /// + /// This `depth` fields tracks stack depth we are recording tokens. + /// Only tokens encountered at this depth will be recorded. See + /// `TokenCursor::next` for more details. + depth: usize, } impl TokenCursorFrame { @@ -178,7 +157,6 @@ impl TokenCursorFrame { open_delim: delim == token::NoDelim, tree_cursor: tts.clone().into_trees(), close_delim: delim == token::NoDelim, - last_token: LastToken::Was(None), } } } @@ -188,12 +166,12 @@ impl TokenCursor { loop { let tree = if !self.frame.open_delim { self.frame.open_delim = true; - TokenTree::open_tt(self.frame.span, self.frame.delim) - } else if let Some(tree) = self.frame.tree_cursor.next() { + TokenTree::open_tt(self.frame.span, self.frame.delim).into() + } else if let Some(tree) = self.frame.tree_cursor.next_with_joint() { tree } else if !self.frame.close_delim { self.frame.close_delim = true; - TokenTree::close_tt(self.frame.span, self.frame.delim) + TokenTree::close_tt(self.frame.span, self.frame.delim).into() } else if let Some(frame) = self.stack.pop() { self.frame = frame; continue; @@ -201,12 +179,25 @@ impl TokenCursor { return Token::new(token::Eof, DUMMY_SP); }; - match self.frame.last_token { - LastToken::Collecting(ref mut v) => v.push(tree.clone().into()), - LastToken::Was(ref mut t) => *t = Some(tree.clone().into()), + // Don't set an open delimiter as our current token - we want + // to leave it as the full `TokenTree::Delimited` from the previous + // iteration of this loop + if !matches!(tree.0, TokenTree::Token(Token { kind: TokenKind::OpenDelim(_), .. })) { + self.cur_token = Some(tree.clone()); } - match tree { + if let Some(collecting) = &mut self.collecting { + if collecting.depth == self.stack.len() { + debug!( + "TokenCursor::next(): collected {:?} at depth {:?}", + tree, + self.stack.len() + ); + collecting.buf.push(tree.clone()) + } + } + + match tree.0 { TokenTree::Token(token) => return token, TokenTree::Delimited(sp, delim, tts) => { let frame = TokenCursorFrame::new(sp, delim, &tts); @@ -355,8 +346,6 @@ impl<'a> Parser<'a> { pub fn new( sess: &'a ParseSess, tokens: TokenStream, - directory: Option, - recurse_into_file_modules: bool, desugar_doc_comments: bool, subparser_name: Option<&'static str>, ) -> Self { @@ -365,19 +354,14 @@ impl<'a> Parser<'a> { token: Token::dummy(), prev_token: Token::dummy(), restrictions: Restrictions::empty(), - recurse_into_file_modules, - directory: Directory { - path: PathBuf::new(), - ownership: DirectoryOwnership::Owned { relative: None }, - }, - root_module_name: None, expected_tokens: Vec::new(), token_cursor: TokenCursor { frame: TokenCursorFrame::new(DelimSpan::dummy(), token::NoDelim, &tokens), stack: Vec::new(), + cur_token: None, + collecting: None, }, desugar_doc_comments, - cfg_mods: true, unmatched_angle_bracket_count: 0, max_angle_bracket_count: 0, unclosed_delims: Vec::new(), @@ -389,18 +373,6 @@ impl<'a> Parser<'a> { // Make parser point to the first token. parser.bump(); - if let Some(directory) = directory { - parser.directory = directory; - } else if !parser.token.span.is_dummy() { - if let Some(FileName::Real(path)) = - &sess.source_map().lookup_char_pos(parser.token.span.lo()).file.unmapped_path - { - if let Some(directory_path) = path.parent() { - parser.directory.path = directory_path.to_path_buf(); - } - } - } - parser } @@ -462,11 +434,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() { @@ -700,6 +672,26 @@ impl<'a> Parser<'a> { } } + // If this was a missing `@` in a binding pattern + // bail with a suggestion + // https://github.com/rust-lang/rust/issues/72373 + if self.prev_token.is_ident() && self.token.kind == token::DotDot { + let msg = format!( + "if you meant to bind the contents of \ + the rest of the array pattern into `{}`, use `@`", + pprust::token_to_string(&self.prev_token) + ); + expect_err + .span_suggestion_verbose( + self.prev_token.span.shrink_to_hi().until(self.token.span), + &msg, + " @ ".to_string(), + Applicability::MaybeIncorrect, + ) + .emit(); + break; + } + // Attempt to keep parsing if it was an omitted separator. match f(self) { Ok(t) => { @@ -1143,65 +1135,95 @@ impl<'a> Parser<'a> { } } - fn collect_tokens( + /// Records all tokens consumed by the provided callback, + /// including the current token. These tokens are collected + /// into a `TokenStream`, and returned along with the result + /// of the callback. + /// + /// Note: If your callback consumes an opening delimiter + /// (including the case where you call `collect_tokens` + /// when the current token is an opening delimeter), + /// you must also consume the corresponding closing delimiter. + /// + /// That is, you can consume + /// `something ([{ }])` or `([{}])`, but not `([{}]` + /// + /// This restriction shouldn't be an issue in practice, + /// since this function is used to record the tokens for + /// a parsed AST item, which always has matching delimiters. + pub fn collect_tokens( &mut self, f: impl FnOnce(&mut Self) -> PResult<'a, R>, ) -> PResult<'a, (R, TokenStream)> { // Record all tokens we parse when parsing this item. - let mut tokens = Vec::new(); - let prev_collecting = match self.token_cursor.frame.last_token { - LastToken::Collecting(ref mut list) => Some(mem::take(list)), - LastToken::Was(ref mut last) => { - tokens.extend(last.take()); - None - } - }; - self.token_cursor.frame.last_token = LastToken::Collecting(tokens); - let prev = self.token_cursor.stack.len(); + let tokens: Vec = self.token_cursor.cur_token.clone().into_iter().collect(); + debug!("collect_tokens: starting with {:?}", tokens); + + // We need special handling for the case where `collect_tokens` is called + // on an opening delimeter (e.g. '('). At this point, we have already pushed + // a new frame - however, we want to record the original `TokenTree::Delimited`, + // for consistency with the case where we start recording one token earlier. + // See `TokenCursor::next` to see how `cur_token` is set up. + let prev_depth = + if matches!(self.token_cursor.cur_token, Some((TokenTree::Delimited(..), _))) { + if self.token_cursor.stack.is_empty() { + // There is nothing below us in the stack that + // the function could consume, so the only thing it can legally + // capture is the entire contents of the current frame. + return Ok((f(self)?, TokenStream::new(tokens))); + } + // We have already recorded the full `TokenTree::Delimited` when we created + // our `tokens` vector at the start of this function. We are now inside + // a new frame corresponding to the `TokenTree::Delimited` we already recoreded. + // We don't want to record any of the tokens inside this frame, since they + // will be duplicates of the tokens nested inside the `TokenTree::Delimited`. + // Therefore, we set our recording depth to the *previous* frame. This allows + // us to record a sequence like: `(foo).bar()`: the `(foo)` will be recored + // as our initial `cur_token`, while the `.bar()` will be recored after we + // pop the `(foo)` frame. + self.token_cursor.stack.len() - 1 + } else { + self.token_cursor.stack.len() + }; + let prev_collecting = + self.token_cursor.collecting.replace(Collecting { buf: tokens, depth: prev_depth }); + let ret = f(self); - let last_token = if self.token_cursor.stack.len() == prev { - &mut self.token_cursor.frame.last_token - } else if self.token_cursor.stack.get(prev).is_none() { - // This can happen due to a bad interaction of two unrelated recovery mechanisms with - // mismatched delimiters *and* recovery lookahead on the likely typo `pub ident(` - // (#62881). - return Ok((ret?, TokenStream::default())); + + let mut collected_tokens = if let Some(collecting) = self.token_cursor.collecting.take() { + collecting.buf } else { - &mut self.token_cursor.stack[prev].last_token + let msg = "our vector went away?"; + debug!("collect_tokens: {}", msg); + self.sess.span_diagnostic.delay_span_bug(self.token.span, &msg); + // This can happen due to a bad interaction of two unrelated recovery mechanisms + // with mismatched delimiters *and* recovery lookahead on the likely typo + // `pub ident(` (#62895, different but similar to the case above). + return Ok((ret?, TokenStream::default())); }; - // Pull out the tokens that we've collected from the call to `f` above. - let mut collected_tokens = match *last_token { - LastToken::Collecting(ref mut v) => mem::take(v), - LastToken::Was(ref was) => { - let msg = format!("our vector went away? - found Was({:?})", was); - debug!("collect_tokens: {}", msg); - self.sess.span_diagnostic.delay_span_bug(self.token.span, &msg); - // This can happen due to a bad interaction of two unrelated recovery mechanisms - // with mismatched delimiters *and* recovery lookahead on the likely typo - // `pub ident(` (#62895, different but similar to the case above). - return Ok((ret?, TokenStream::default())); - } - }; + debug!("collect_tokens: got raw tokens {:?}", collected_tokens); // If we're not at EOF our current token wasn't actually consumed by // `f`, but it'll still be in our list that we pulled out. In that case // put it back. let extra_token = if self.token != token::Eof { collected_tokens.pop() } else { None }; - // If we were previously collecting tokens, then this was a recursive - // call. In that case we need to record all the tokens we collected in - // our parent list as well. To do that we push a clone of our stream - // onto the previous list. - match prev_collecting { - Some(mut list) => { - list.extend(collected_tokens.iter().cloned()); - list.extend(extra_token); - *last_token = LastToken::Collecting(list); - } - None => { - *last_token = LastToken::Was(extra_token); + if let Some(mut collecting) = prev_collecting { + // If we were previously collecting at the same depth, + // then the previous call to `collect_tokens` needs to see + // the tokens we just recorded. + // + // If we were previously recording at an lower `depth`, + // then the previous `collect_tokens` call already recorded + // this entire frame in the form of a `TokenTree::Delimited`, + // so there is nothing else for us to do. + if collecting.depth == prev_depth { + collecting.buf.extend(collected_tokens.iter().cloned()); + collecting.buf.extend(extra_token); + debug!("collect_tokens: updating previous buf to {:?}", collecting); } + self.token_cursor.collecting = Some(collecting) } Ok((ret?, TokenStream::new(collected_tokens))) @@ -1244,8 +1266,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/module.rs b/src/librustc_parse/parser/module.rs deleted file mode 100644 index b436f1969bb48..0000000000000 --- a/src/librustc_parse/parser/module.rs +++ /dev/null @@ -1,306 +0,0 @@ -use super::diagnostics::Error; -use super::item::ItemInfo; -use super::Parser; - -use crate::{new_sub_parser_from_file, DirectoryOwnership}; - -use rustc_ast::ast::{self, Attribute, Crate, Ident, ItemKind, Mod}; -use rustc_ast::attr; -use rustc_ast::token::{self, TokenKind}; -use rustc_errors::PResult; -use rustc_span::source_map::{FileName, SourceMap, Span, DUMMY_SP}; -use rustc_span::symbol::sym; - -use std::path::{self, Path, PathBuf}; - -/// Information about the path to a module. -// Public for rustfmt usage. -pub struct ModulePath { - name: String, - path_exists: bool, - pub result: Result, -} - -// Public for rustfmt usage. -pub struct ModulePathSuccess { - pub path: PathBuf, - pub directory_ownership: DirectoryOwnership, -} - -impl<'a> Parser<'a> { - /// Parses a source module as a crate. This is the main entry point for the parser. - pub fn parse_crate_mod(&mut self) -> PResult<'a, Crate> { - let lo = self.token.span; - let krate = Ok(ast::Crate { - attrs: self.parse_inner_attributes()?, - module: self.parse_mod_items(&token::Eof, lo)?, - span: lo.to(self.token.span), - // Filled in by proc_macro_harness::inject() - proc_macros: Vec::new(), - }); - krate - } - - /// Parses a `mod { ... }` or `mod ;` item. - pub(super) fn parse_item_mod(&mut self, attrs: &mut Vec) -> PResult<'a, ItemInfo> { - let in_cfg = crate::config::process_configure_mod(self.sess, self.cfg_mods, attrs); - - let id_span = self.token.span; - let id = self.parse_ident()?; - let (module, mut inner_attrs) = if self.eat(&token::Semi) { - if in_cfg && self.recurse_into_file_modules { - // This mod is in an external file. Let's go get it! - let ModulePathSuccess { path, directory_ownership } = - self.submod_path(id, &attrs, id_span)?; - self.eval_src_mod(path, directory_ownership, id.to_string(), id_span)? - } else { - (ast::Mod { inner: DUMMY_SP, items: Vec::new(), inline: false }, Vec::new()) - } - } else { - let old_directory = self.directory.clone(); - self.push_directory(id, &attrs); - - self.expect(&token::OpenDelim(token::Brace))?; - let mod_inner_lo = self.token.span; - let inner_attrs = self.parse_inner_attributes()?; - let module = self.parse_mod_items(&token::CloseDelim(token::Brace), mod_inner_lo)?; - - self.directory = old_directory; - (module, inner_attrs) - }; - attrs.append(&mut inner_attrs); - Ok((id, ItemKind::Mod(module))) - } - - /// Given a termination token, parses all of the items in a module. - fn parse_mod_items(&mut self, term: &TokenKind, inner_lo: Span) -> PResult<'a, Mod> { - let mut items = vec![]; - while let Some(item) = self.parse_item()? { - items.push(item); - self.maybe_consume_incorrect_semicolon(&items); - } - - if !self.eat(term) { - let token_str = super::token_descr(&self.token); - if !self.maybe_consume_incorrect_semicolon(&items) { - let msg = &format!("expected item, found {}", token_str); - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, "expected item"); - return Err(err); - } - } - - let hi = if self.token.span.is_dummy() { inner_lo } else { self.prev_token.span }; - - Ok(Mod { inner: inner_lo.to(hi), items, inline: true }) - } - - fn submod_path( - &mut self, - id: ast::Ident, - outer_attrs: &[Attribute], - id_sp: Span, - ) -> PResult<'a, ModulePathSuccess> { - if let Some(path) = Parser::submod_path_from_attr(outer_attrs, &self.directory.path) { - return Ok(ModulePathSuccess { - directory_ownership: match path.file_name().and_then(|s| s.to_str()) { - // All `#[path]` files are treated as though they are a `mod.rs` file. - // This means that `mod foo;` declarations inside `#[path]`-included - // files are siblings, - // - // Note that this will produce weirdness when a file named `foo.rs` is - // `#[path]` included and contains a `mod foo;` declaration. - // If you encounter this, it's your own darn fault :P - Some(_) => DirectoryOwnership::Owned { relative: None }, - _ => DirectoryOwnership::UnownedViaMod, - }, - path, - }); - } - - let relative = match self.directory.ownership { - DirectoryOwnership::Owned { relative } => relative, - DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None, - }; - let paths = - Parser::default_submod_path(id, relative, &self.directory.path, self.sess.source_map()); - - match self.directory.ownership { - DirectoryOwnership::Owned { .. } => { - paths.result.map_err(|err| self.span_fatal_err(id_sp, err)) - } - DirectoryOwnership::UnownedViaBlock => { - let msg = "Cannot declare a non-inline module inside a block \ - unless it has a path attribute"; - let mut err = self.struct_span_err(id_sp, msg); - if paths.path_exists { - let msg = format!( - "Maybe `use` the module `{}` instead of redeclaring it", - paths.name - ); - err.span_note(id_sp, &msg); - } - Err(err) - } - DirectoryOwnership::UnownedViaMod => { - let mut err = - self.struct_span_err(id_sp, "cannot declare a new module at this location"); - if !id_sp.is_dummy() { - let src_path = self.sess.source_map().span_to_filename(id_sp); - if let FileName::Real(src_path) = src_path { - if let Some(stem) = src_path.file_stem() { - let mut dest_path = src_path.clone(); - dest_path.set_file_name(stem); - dest_path.push("mod.rs"); - err.span_note( - id_sp, - &format!( - "maybe move this module `{}` to its own \ - directory via `{}`", - src_path.display(), - dest_path.display() - ), - ); - } - } - } - if paths.path_exists { - err.span_note( - id_sp, - &format!( - "... or maybe `use` the module `{}` instead \ - of possibly redeclaring it", - paths.name - ), - ); - } - Err(err) - } - } - } - - // Public for rustfmt usage. - pub fn submod_path_from_attr(attrs: &[Attribute], dir_path: &Path) -> Option { - if let Some(s) = attr::first_attr_value_str_by_name(attrs, sym::path) { - let s = s.as_str(); - - // On windows, the base path might have the form - // `\\?\foo\bar` in which case it does not tolerate - // mixed `/` and `\` separators, so canonicalize - // `/` to `\`. - #[cfg(windows)] - let s = s.replace("/", "\\"); - Some(dir_path.join(&*s)) - } else { - None - } - } - - /// Returns a path to a module. - // Public for rustfmt usage. - pub fn default_submod_path( - id: ast::Ident, - relative: Option, - dir_path: &Path, - source_map: &SourceMap, - ) -> ModulePath { - // If we're in a foo.rs file instead of a mod.rs file, - // we need to look for submodules in - // `./foo/.rs` and `./foo//mod.rs` rather than - // `./.rs` and `.//mod.rs`. - let relative_prefix_string; - let relative_prefix = if let Some(ident) = relative { - relative_prefix_string = format!("{}{}", ident.name, path::MAIN_SEPARATOR); - &relative_prefix_string - } else { - "" - }; - - let mod_name = id.name.to_string(); - let default_path_str = format!("{}{}.rs", relative_prefix, mod_name); - let secondary_path_str = - format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR); - let default_path = dir_path.join(&default_path_str); - let secondary_path = dir_path.join(&secondary_path_str); - let default_exists = source_map.file_exists(&default_path); - let secondary_exists = source_map.file_exists(&secondary_path); - - let result = match (default_exists, secondary_exists) { - (true, false) => Ok(ModulePathSuccess { - path: default_path, - directory_ownership: DirectoryOwnership::Owned { relative: Some(id) }, - }), - (false, true) => Ok(ModulePathSuccess { - path: secondary_path, - directory_ownership: DirectoryOwnership::Owned { relative: None }, - }), - (false, false) => Err(Error::FileNotFoundForModule { - mod_name: mod_name.clone(), - default_path: default_path_str, - secondary_path: secondary_path_str, - dir_path: dir_path.display().to_string(), - }), - (true, true) => Err(Error::DuplicatePaths { - mod_name: mod_name.clone(), - default_path: default_path_str, - secondary_path: secondary_path_str, - }), - }; - - ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result } - } - - /// Reads a module from a source file. - fn eval_src_mod( - &mut self, - path: PathBuf, - directory_ownership: DirectoryOwnership, - name: String, - id_sp: Span, - ) -> PResult<'a, (Mod, Vec)> { - let mut included_mod_stack = self.sess.included_mod_stack.borrow_mut(); - if let Some(i) = included_mod_stack.iter().position(|p| *p == path) { - let mut err = String::from("circular modules: "); - let len = included_mod_stack.len(); - for p in &included_mod_stack[i..len] { - err.push_str(&p.to_string_lossy()); - err.push_str(" -> "); - } - err.push_str(&path.to_string_lossy()); - return Err(self.struct_span_err(id_sp, &err[..])); - } - included_mod_stack.push(path.clone()); - drop(included_mod_stack); - - let mut p0 = - new_sub_parser_from_file(self.sess, &path, directory_ownership, Some(name), id_sp); - p0.cfg_mods = self.cfg_mods; - let mod_inner_lo = p0.token.span; - let mod_attrs = p0.parse_inner_attributes()?; - let mut m0 = p0.parse_mod_items(&token::Eof, mod_inner_lo)?; - m0.inline = false; - self.sess.included_mod_stack.borrow_mut().pop(); - Ok((m0, mod_attrs)) - } - - fn push_directory(&mut self, id: Ident, attrs: &[Attribute]) { - if let Some(path) = attr::first_attr_value_str_by_name(attrs, sym::path) { - self.directory.path.push(&*path.as_str()); - self.directory.ownership = DirectoryOwnership::Owned { relative: None }; - } else { - // We have to push on the current module name in the case of relative - // paths in order to ensure that any additional module paths from inline - // `mod x { ... }` come after the relative extension. - // - // For example, a `mod z { ... }` inside `x/y.rs` should set the current - // directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`. - if let DirectoryOwnership::Owned { relative } = &mut self.directory.ownership { - if let Some(ident) = relative.take() { - // remove the relative offset - self.directory.path.push(&*ident.as_str()); - } - } - self.directory.path.push(&*id.as_str()); - } - } -} diff --git a/src/librustc_parse/parser/pat.rs b/src/librustc_parse/parser/pat.rs index 4585941943b74..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` @@ -696,7 +717,7 @@ impl<'a> Parser<'a> { self.look_ahead(dist, |t| { t.is_path_start() // e.g. `MY_CONST`; || t.kind == token::Dot // e.g. `.5` for recovery; - || t.can_begin_literal_or_bool() // e.g. `42`. + || t.can_begin_literal_maybe_minus() // e.g. `42`. || t.is_whole_expr() }) } @@ -918,7 +939,7 @@ impl<'a> Parser<'a> { } err.emit(); } - return Ok((fields, etc)); + Ok((fields, etc)) } /// Recover on `...` as if it were `..` to avoid further errors. 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 4359823be0890..53f32b7c800bd 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -5,7 +5,6 @@ use super::pat::GateOr; use super::path::PathStyle; use super::{BlockMode, Parser, Restrictions, SemiColonMode}; use crate::maybe_whole; -use crate::DirectoryOwnership; use rustc_ast::ast; use rustc_ast::ast::{AttrStyle, AttrVec, Attribute, MacCall, MacStmtStyle}; @@ -54,7 +53,7 @@ impl<'a> Parser<'a> { // that starts like a path (1 token), but it fact not a path. // Also, we avoid stealing syntax from `parse_item_`. self.parse_stmt_path_start(lo, attrs)? - } else if let Some(item) = self.parse_stmt_item(attrs.clone())? { + } else if let Some(item) = self.parse_item_common(attrs.clone(), false, true, |_| true)? { // FIXME: Bad copy of attrs self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item))) } else if self.eat(&token::Semi) { @@ -72,13 +71,6 @@ impl<'a> Parser<'a> { Ok(Some(stmt)) } - fn parse_stmt_item(&mut self, attrs: Vec) -> PResult<'a, Option> { - let old = mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock); - let item = self.parse_item_common(attrs, false, true, |_| true)?; - self.directory.ownership = old; - Ok(item) - } - fn parse_stmt_path_start(&mut self, lo: Span, attrs: Vec) -> PResult<'a, Stmt> { let path = self.parse_path(PathStyle::Expr)?; @@ -87,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()) @@ -171,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) } } @@ -209,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) => { @@ -224,14 +216,28 @@ impl<'a> Parser<'a> { } /// Parses the RHS of a local variable declaration (e.g., '= 14;'). - fn parse_initializer(&mut self, skip_eq: bool) -> PResult<'a, Option>> { - if self.eat(&token::Eq) { - Ok(Some(self.parse_expr()?)) - } else if skip_eq { - Ok(Some(self.parse_expr()?)) - } else { - Ok(None) - } + fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option>> { + let eq_consumed = match self.token.kind { + token::BinOpEq(..) => { + // Recover `let x = 1` as `let x = 1` + self.struct_span_err( + self.token.span, + "can't reassign to an uninitialized variable", + ) + .span_suggestion_short( + self.token.span, + "initialize the variable", + "=".to_string(), + Applicability::MaybeIncorrect, + ) + .emit(); + self.bump(); + true + } + _ => self.eat(&token::Eq), + }; + + Ok(if eq_consumed || eq_optional { Some(self.parse_expr()?) } else { None }) } /// Parses a block. No inner attributes are allowed. @@ -286,7 +292,7 @@ impl<'a> Parser<'a> { _ => {} } e.span_label(sp, "expected `{`"); - return Err(e); + Err(e) } /// Parses a block. Inner attributes are allowed. 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_parse/validate_attr.rs b/src/librustc_parse/validate_attr.rs index 029aa5ed2baea..2512878ec65be 100644 --- a/src/librustc_parse/validate_attr.rs +++ b/src/librustc_parse/validate_attr.rs @@ -57,7 +57,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta }) } -crate fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) { +pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) { if let ast::MacDelimiter::Parenthesis = delim { return; } diff --git a/src/librustc_parse_format/Cargo.toml b/src/librustc_parse_format/Cargo.toml new file mode 100644 index 0000000000000..646509569f3a5 --- /dev/null +++ b/src/librustc_parse_format/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_parse_format" +version = "0.0.0" +edition = "2018" + +[lib] +name = "rustc_parse_format" +path = "lib.rs" + +[dependencies] +rustc_span = { path = "../librustc_span" } +rustc_lexer = { path = "../librustc_lexer" } diff --git a/src/librustc_parse_format/lib.rs b/src/librustc_parse_format/lib.rs new file mode 100644 index 0000000000000..a5b5a1090cbfd --- /dev/null +++ b/src/librustc_parse_format/lib.rs @@ -0,0 +1,829 @@ +//! Macro support for format strings +//! +//! These structures are used when parsing format strings for the compiler. +//! Parsing does not happen at runtime: structures of `std::fmt::rt` are +//! generated instead. + +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", + test(attr(deny(warnings))) +)] +#![feature(nll)] +#![feature(or_patterns)] +#![feature(rustc_private)] +#![feature(unicode_internals)] +#![feature(bool_to_option)] + +pub use Alignment::*; +pub use Count::*; +pub use Flag::*; +pub use Piece::*; +pub use Position::*; + +use std::iter; +use std::str; +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); + +impl InnerOffset { + fn to(self, end: InnerOffset) -> InnerSpan { + InnerSpan::new(self.0, end.0) + } +} + +/// A piece is a portion of the format string which represents the next part +/// to emit. These are emitted as a stream by the `Parser` class. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Piece<'a> { + /// A literal string which should directly be emitted + String(&'a str), + /// This describes that formatting should process the next argument (as + /// specified inside) for emission. + NextArgument(Argument<'a>), +} + +/// Representation of an argument specification. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Argument<'a> { + /// Where to find this argument + pub position: Position, + /// How to format the argument + pub format: FormatSpec<'a>, +} + +/// Specification for the formatting of an argument in the format string. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct FormatSpec<'a> { + /// Optionally specified character to fill alignment with. + pub fill: Option, + /// Optionally specified alignment. + pub align: Alignment, + /// Packed version of various flags provided. + pub flags: u32, + /// The integer precision to use. + pub precision: Count, + /// The span of the precision formatting flag (for diagnostics). + pub precision_span: Option, + /// The string width requested for the resulting format. + pub width: Count, + /// The span of the width formatting flag (for diagnostics). + pub width_span: Option, + /// The descriptor string representing the name of the format desired for + /// this argument, this can be empty or any number of characters, although + /// it is required to be one word. + pub ty: &'a str, + /// The span of the descriptor string (for diagnostics). + pub ty_span: Option, +} + +/// Enum describing where an argument for a format can be located. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Position { + /// The argument is implied to be located at an index + ArgumentImplicitlyIs(usize), + /// The argument is located at a specific index given in the format + ArgumentIs(usize), + /// The argument has a name. + ArgumentNamed(Symbol), +} + +impl Position { + pub fn index(&self) -> Option { + match self { + ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i), + _ => None, + } + } +} + +/// Enum of alignments which are supported. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Alignment { + /// The value will be aligned to the left. + AlignLeft, + /// The value will be aligned to the right. + AlignRight, + /// The value will be aligned in the center. + AlignCenter, + /// The value will take on a default alignment. + AlignUnknown, +} + +/// Various flags which can be applied to format strings. The meaning of these +/// flags is defined by the formatters themselves. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Flag { + /// A `+` will be used to denote positive numbers. + FlagSignPlus, + /// A `-` will be used to denote negative numbers. This is the default. + FlagSignMinus, + /// An alternate form will be used for the value. In the case of numbers, + /// this means that the number will be prefixed with the supplied string. + FlagAlternate, + /// For numbers, this means that the number will be padded with zeroes, + /// and the sign (`+` or `-`) will precede them. + FlagSignAwareZeroPad, + /// For Debug / `?`, format integers in lower-case hexadecimal. + FlagDebugLowerHex, + /// For Debug / `?`, format integers in upper-case hexadecimal. + FlagDebugUpperHex, +} + +/// A count is used for the precision and width parameters of an integer, and +/// can reference either an argument or a literal integer. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Count { + /// The count is specified explicitly. + CountIs(usize), + /// The count is specified by the argument with the given name. + CountIsName(Symbol), + /// The count is specified by the argument at the given index. + CountIsParam(usize), + /// The count is implied and cannot be explicitly specified. + CountImplied, +} + +pub struct ParseError { + pub description: string::String, + pub note: Option, + pub label: string::String, + pub span: InnerSpan, + pub secondary_label: Option<(string::String, InnerSpan)>, +} + +/// The parser structure for interpreting the input format string. This is +/// modeled as an iterator over `Piece` structures to form a stream of tokens +/// being output. +/// +/// 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 + pub errors: Vec, + /// Current position of implicit positional argument pointer + pub curarg: usize, + /// `Some(raw count)` when the string is "raw", used to position spans correctly + style: Option, + /// Start and end byte offset of every successfully parsed argument + pub arg_places: Vec, + /// Characters that need to be shifted + skips: Vec, + /// Span of the last opening brace seen, used for error reporting + 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, + /// Start position of the current line. + cur_line_start: usize, + /// Start and end byte offset of every line of the format string. Excludes + /// newline characters and leading whitespace. + pub line_spans: Vec, +} + +impl<'a> Iterator for Parser<'a> { + type Item = Piece<'a>; + + fn next(&mut self) -> Option> { + if let Some(&(pos, c)) = self.cur.peek() { + match c { + '{' => { + let curr_last_brace = self.last_opening_brace; + let byte_pos = self.to_span_index(pos); + self.last_opening_brace = Some(byte_pos.to(InnerOffset(byte_pos.0 + 1))); + self.cur.next(); + if self.consume('{') { + self.last_opening_brace = curr_last_brace; + + Some(String(self.string(pos + 1))) + } else { + let arg = self.argument(); + if let Some(end) = self.must_consume('}') { + let start = self.to_span_index(pos); + let end = self.to_span_index(end + 1); + if self.is_literal { + self.arg_places.push(start.to(end)); + } + } + Some(NextArgument(arg)) + } + } + '}' => { + self.cur.next(); + if self.consume('}') { + Some(String(self.string(pos + 1))) + } else { + let err_pos = self.to_span_index(pos); + self.err_with_note( + "unmatched `}` found", + "unmatched `}`", + "if you intended to print `}`, you can escape it using `}}`", + err_pos.to(err_pos), + ); + None + } + } + _ => Some(String(self.string(pos))), + } + } else { + if self.is_literal { + let start = self.to_span_index(self.cur_line_start); + let end = self.to_span_index(self.input.len()); + let span = start.to(end); + if self.line_spans.last() != Some(&span) { + self.line_spans.push(span); + } + } + None + } + } +} + +impl<'a> Parser<'a> { + /// Creates a new parser for the given format string + pub fn new( + s: &'a str, + style: Option, + 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![], + curarg: 0, + style, + arg_places: vec![], + skips, + last_opening_brace: None, + append_newline, + is_literal, + cur_line_start: 0, + line_spans: vec![], + } + } + + /// Notifies of an error. The message doesn't actually need to be of type + /// String, but I think it does when this eventually uses conditions so it + /// might as well start using it now. + fn err, S2: Into>( + &mut self, + description: S1, + label: S2, + span: InnerSpan, + ) { + self.errors.push(ParseError { + description: description.into(), + note: None, + label: label.into(), + span, + secondary_label: None, + }); + } + + /// Notifies of an error. The message doesn't actually need to be of type + /// String, but I think it does when this eventually uses conditions so it + /// might as well start using it now. + fn err_with_note< + S1: Into, + S2: Into, + S3: Into, + >( + &mut self, + description: S1, + label: S2, + note: S3, + span: InnerSpan, + ) { + self.errors.push(ParseError { + description: description.into(), + note: Some(note.into()), + label: label.into(), + span, + secondary_label: None, + }); + } + + /// Optionally consumes the specified character. If the character is not at + /// the current position, then the current iterator isn't moved and `false` is + /// returned, otherwise the character is consumed and `true` is returned. + fn consume(&mut self, c: char) -> bool { + self.consume_pos(c).is_some() + } + + /// Optionally consumes the specified character. If the character is not at + /// the current position, then the current iterator isn't moved and `None` is + /// returned, otherwise the character is consumed and the current position is + /// returned. + fn consume_pos(&mut self, c: char) -> Option { + if let Some(&(pos, maybe)) = self.cur.peek() { + if c == maybe { + self.cur.next(); + return Some(pos); + } + } + None + } + + fn to_span_index(&self, pos: usize) -> InnerOffset { + let mut pos = pos; + // This handles the raw string case, the raw argument is the number of # + // in r###"..."### (we need to add one because of the `r`). + let raw = self.style.map(|raw| raw + 1).unwrap_or(0); + for skip in &self.skips { + if pos > *skip { + pos += 1; + } else if pos == *skip && raw == 0 { + pos += 1; + } else { + break; + } + } + InnerOffset(raw + pos + 1) + } + + /// Forces consumption of the specified character. If the character is not + /// found, an error is emitted. + fn must_consume(&mut self, c: char) -> Option { + self.ws(); + + if let Some(&(pos, maybe)) = self.cur.peek() { + if c == maybe { + self.cur.next(); + Some(pos) + } else { + let pos = self.to_span_index(pos); + let description = format!("expected `'}}'`, found `{:?}`", maybe); + let label = "expected `}`".to_owned(); + let (note, secondary_label) = if c == '}' { + ( + Some( + "if you intended to print `{`, you can escape it using `{{`".to_owned(), + ), + self.last_opening_brace + .map(|sp| ("because of this opening brace".to_owned(), sp)), + ) + } else { + (None, None) + }; + self.errors.push(ParseError { + description, + note, + label, + span: pos.to(pos), + secondary_label, + }); + None + } + } else { + let description = format!("expected `{:?}` but string was terminated", c); + // point at closing `"` + let pos = self.input.len() - if self.append_newline { 1 } else { 0 }; + let pos = self.to_span_index(pos); + if c == '}' { + let label = format!("expected `{:?}`", c); + let (note, secondary_label) = if c == '}' { + ( + Some( + "if you intended to print `{`, you can escape it using `{{`".to_owned(), + ), + self.last_opening_brace + .map(|sp| ("because of this opening brace".to_owned(), sp)), + ) + } else { + (None, None) + }; + self.errors.push(ParseError { + description, + note, + label, + span: pos.to(pos), + secondary_label, + }); + } else { + self.err(description, format!("expected `{:?}`", c), pos.to(pos)); + } + None + } + } + + /// Consumes all whitespace characters until the first non-whitespace character + fn ws(&mut self) { + while let Some(&(_, c)) = self.cur.peek() { + if c.is_whitespace() { + self.cur.next(); + } else { + break; + } + } + } + + /// Parses all of a string which is to be considered a "raw literal" in a + /// format string. This is everything outside of the braces. + fn string(&mut self, start: usize) -> &'a str { + // we may not consume the character, peek the iterator + while let Some(&(pos, c)) = self.cur.peek() { + match c { + '{' | '}' => { + return &self.input[start..pos]; + } + '\n' if self.is_literal => { + let start = self.to_span_index(self.cur_line_start); + let end = self.to_span_index(pos); + self.line_spans.push(start.to(end)); + self.cur_line_start = pos + 1; + self.cur.next(); + } + _ => { + if self.is_literal && pos == self.cur_line_start && c.is_whitespace() { + self.cur_line_start = pos + c.len_utf8(); + } + self.cur.next(); + } + } + } + &self.input[start..self.input.len()] + } + + /// 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 = match self.mode { + ParseMode::Format => self.format(), + ParseMode::InlineAsm => self.inline_asm(), + }; + + // Resolve position after parsing format spec. + let pos = match pos { + Some(position) => position, + None => { + let i = self.curarg; + self.curarg += 1; + ArgumentImplicitlyIs(i) + } + }; + + Argument { position: pos, format } + } + + /// Parses a positional argument for a format. This could either be an + /// integer index of an argument, a named argument, or a blank string. + /// Returns `Some(parsed_position)` if the position is not implicitly + /// consuming a macro argument, `None` if it's the case. + fn position(&mut self) -> Option { + if let Some(i) = self.integer() { + Some(ArgumentIs(i)) + } else { + match self.cur.peek() { + Some(&(_, c)) if rustc_lexer::is_id_start(c) => { + Some(ArgumentNamed(Symbol::intern(self.word()))) + } + + // This is an `ArgumentNext`. + // Record the fact and do the resolution after parsing the + // format spec, to make things like `{:.*}` work. + _ => None, + } + } + } + + /// Parses a format specifier at the current position, returning all of the + /// relevant information in the `FormatSpec` struct. + fn format(&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; + } + + // fill character + if let Some(&(_, c)) = self.cur.peek() { + match self.cur.clone().nth(1) { + Some((_, '>' | '<' | '^')) => { + spec.fill = Some(c); + self.cur.next(); + } + _ => {} + } + } + // Alignment + if self.consume('<') { + spec.align = AlignLeft; + } else if self.consume('>') { + spec.align = AlignRight; + } else if self.consume('^') { + spec.align = AlignCenter; + } + // Sign flags + if self.consume('+') { + spec.flags |= 1 << (FlagSignPlus as u32); + } else if self.consume('-') { + spec.flags |= 1 << (FlagSignMinus as u32); + } + // Alternate marker + if self.consume('#') { + spec.flags |= 1 << (FlagAlternate as u32); + } + // Width and precision + let mut havewidth = false; + + if self.consume('0') { + // small ambiguity with '0$' as a format string. In theory this is a + // '0' flag and then an ill-formatted format string with just a '$' + // and no count, but this is better if we instead interpret this as + // no '0' flag and '0$' as the width instead. + if self.consume('$') { + spec.width = CountIsParam(0); + havewidth = true; + } else { + spec.flags |= 1 << (FlagSignAwareZeroPad as u32); + } + } + if !havewidth { + let width_span_start = if let Some((pos, _)) = self.cur.peek() { *pos } else { 0 }; + let (w, sp) = self.count(width_span_start); + spec.width = w; + spec.width_span = sp; + } + if let Some(start) = self.consume_pos('.') { + if let Some(end) = self.consume_pos('*') { + // Resolve `CountIsNextParam`. + // We can do this immediately as `position` is resolved later. + let i = self.curarg; + self.curarg += 1; + spec.precision = CountIsParam(i); + spec.precision_span = + Some(self.to_span_index(start).to(self.to_span_index(end + 1))); + } else { + let (p, sp) = self.count(start); + spec.precision = p; + spec.precision_span = sp; + } + } + let ty_span_start = self.cur.peek().map(|(pos, _)| *pos); + // Optional radix followed by the actual format specifier + if self.consume('x') { + if self.consume('?') { + spec.flags |= 1 << (FlagDebugLowerHex as u32); + spec.ty = "?"; + } else { + spec.ty = "x"; + } + } else if self.consume('X') { + if self.consume('?') { + spec.flags |= 1 << (FlagDebugUpperHex as u32); + spec.ty = "?"; + } else { + spec.ty = "X"; + } + } else if self.consume('?') { + spec.ty = "?"; + } else { + 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 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. + fn count(&mut self, start: usize) -> (Count, Option) { + if let Some(i) = self.integer() { + if let Some(end) = self.consume_pos('$') { + let span = self.to_span_index(start).to(self.to_span_index(end + 1)); + (CountIsParam(i), Some(span)) + } else { + (CountIs(i), None) + } + } else { + let tmp = self.cur.clone(); + let word = self.word(); + if word.is_empty() { + self.cur = tmp; + (CountImplied, None) + } else if self.consume('$') { + (CountIsName(Symbol::intern(word)), None) + } else { + self.cur = tmp; + (CountImplied, None) + } + } + } + + /// Parses a word starting at the current position. A word is the same as + /// Rust identifier, except that it can't start with `_` character. + fn word(&mut self) -> &'a str { + let start = match self.cur.peek() { + Some(&(pos, c)) if rustc_lexer::is_id_start(c) => { + self.cur.next(); + pos + } + _ => { + return ""; + } + }; + let mut end = None; + while let Some(&(pos, c)) = self.cur.peek() { + if rustc_lexer::is_id_continue(c) { + self.cur.next(); + } else { + end = Some(pos); + break; + } + } + let end = end.unwrap_or(self.input.len()); + let word = &self.input[start..end]; + if word == "_" { + self.err_with_note( + "invalid argument name `_`", + "invalid argument name", + "argument name cannot be a single underscore", + self.to_span_index(start).to(self.to_span_index(end)), + ); + } + word + } + + /// Optionally parses an integer at the current position. This doesn't deal + /// with overflow at all, it's just accumulating digits. + fn integer(&mut self) -> Option { + let mut cur = 0; + let mut found = false; + while let Some(&(_, c)) = self.cur.peek() { + if let Some(i) = c.to_digit(10) { + cur = cur * 10 + i as usize; + found = true; + self.cur.next(); + } else { + break; + } + } + found.then_some(cur) + } +} + +/// 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/librustc_parse_format/tests.rs b/src/librustc_parse_format/tests.rs new file mode 100644 index 0000000000000..9932c1df7a935 --- /dev/null +++ b/src/librustc_parse_format/tests.rs @@ -0,0 +1,296 @@ +use super::*; + +fn same(fmt: &'static str, p: &[Piece<'static>]) { + let parser = Parser::new(fmt, None, None, false, ParseMode::Format); + assert_eq!(parser.collect::>>(), p); +} + +fn fmtdflt() -> FormatSpec<'static> { + return FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountImplied, + width: CountImplied, + precision_span: None, + width_span: None, + ty: "", + ty_span: None, + }; +} + +fn musterr(s: &str) { + let mut p = Parser::new(s, None, None, false, ParseMode::Format); + p.next(); + assert!(!p.errors.is_empty()); +} + +#[test] +fn simple() { + same("asdf", &[String("asdf")]); + same("a{{b", &[String("a"), String("{b")]); + same("a}}b", &[String("a"), String("}b")]); + same("a}}", &[String("a"), String("}")]); + same("}}", &[String("}")]); + same("\\}}", &[String("\\"), String("}")]); +} + +#[test] +fn invalid01() { + musterr("{") +} +#[test] +fn invalid02() { + musterr("}") +} +#[test] +fn invalid04() { + musterr("{3a}") +} +#[test] +fn invalid05() { + musterr("{:|}") +} +#[test] +fn invalid06() { + musterr("{:>>>}") +} + +#[test] +fn format_nothing() { + same("{}", &[NextArgument(Argument { position: ArgumentImplicitlyIs(0), format: fmtdflt() })]); +} +#[test] +fn format_position() { + same("{3}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]); +} +#[test] +fn format_position_nothing_else() { + same("{3:}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]); +} +#[test] +fn format_type() { + same( + "{3:x}", + &[NextArgument(Argument { + position: ArgumentIs(3), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountImplied, + width: CountImplied, + precision_span: None, + width_span: None, + ty: "x", + ty_span: None, + }, + })], + ); +} +#[test] +fn format_align_fill() { + same( + "{3:>}", + &[NextArgument(Argument { + position: ArgumentIs(3), + format: FormatSpec { + fill: None, + align: AlignRight, + flags: 0, + precision: CountImplied, + width: CountImplied, + precision_span: None, + width_span: None, + ty: "", + ty_span: None, + }, + })], + ); + same( + "{3:0<}", + &[NextArgument(Argument { + position: ArgumentIs(3), + format: FormatSpec { + fill: Some('0'), + align: AlignLeft, + flags: 0, + precision: CountImplied, + width: CountImplied, + precision_span: None, + width_span: None, + ty: "", + ty_span: None, + }, + })], + ); + same( + "{3:*(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { +pub(crate) fn target_from_impl_item<'tcx>( + tcx: TyCtxt<'tcx>, + impl_item: &hir::ImplItem<'_>, +) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, - hir::ImplItemKind::Method(..) => { + hir::ImplItemKind::Fn(..) => { let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id); let containing_item = tcx.hir().expect_item(parent_hir_id); let containing_impl_is_for_trait = match &containing_item.kind { @@ -38,7 +40,7 @@ fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) Target::Method(MethodKind::Inherent) } } - hir::ImplItemKind::TyAlias(..) | hir::ImplItemKind::OpaqueTy(..) => Target::AssocTy, + hir::ImplItemKind::TyAlias(..) => Target::AssocTy, } } @@ -77,8 +79,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 +92,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 +142,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 +152,7 @@ impl CheckAttrVisitor<'tcx> { .emit(); false } - Target::Fn | Target::Method(MethodKind::Inherent) => true, - Target::Method(_) => { - struct_span_err!( - self.tcx.sess, - *attr_span, - E0738, - "`#[track_caller]` may not be used on trait methods", - ) - .emit(); - false - } + Target::Fn | Target::Method(..) | Target::ForeignFn => true, _ => { struct_span_err!( self.tcx.sess, @@ -213,8 +204,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 +360,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 +381,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 +392,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 af06fc0c0026d..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::session::config::nightly_options; -use rustc::session::parse::feature_err; -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 7460b8e2fe930..503fbb64db83d 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 => {} @@ -210,7 +212,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { NestedVisitorMap::None @@ -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::OpaqueDef(item_id, _) = ty.kind { + let item = self.tcx.hir().expect_item(item_id.id); + intravisit::walk_item(self, item); } intravisit::walk_ty(self, ty); } @@ -391,7 +392,7 @@ impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> { let trait_item = self.krate.trait_item(trait_item_ref.id); match trait_item.kind { hir::TraitItemKind::Const(_, Some(_)) - | hir::TraitItemKind::Fn(_, hir::TraitMethod::Provided(_)) => { + | hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => { if has_allow_dead_code_or_lang_attr( self.tcx, trait_item.hir_id, @@ -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,26 +646,29 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { impl_item.hir_id, impl_item.span, impl_item.ident.name, - "associated const", "used", ); } self.visit_nested_body(body_id) } - hir::ImplItemKind::Method(_, body_id) => { + 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) } - hir::ImplItemKind::OpaqueTy(..) | hir::ImplItemKind::TyAlias(..) => {} + hir::ImplItemKind::TyAlias(..) => {} } } @@ -682,11 +676,11 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> { fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { match trait_item.kind { hir::TraitItemKind::Const(_, Some(body_id)) - | hir::TraitItemKind::Fn(_, hir::TraitMethod::Provided(body_id)) => { + | hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)) => { self.visit_nested_body(body_id) } hir::TraitItemKind::Const(_, None) - | hir::TraitItemKind::Fn(_, hir::TraitMethod::Required(_)) + | hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_)) | hir::TraitItemKind::Type(..) => {} } } 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 2cabe786d0cda..e0ad0ac77476f 100644 --- a/src/librustc_passes/entry.rs +++ b/src/librustc_passes/entry.rs @@ -1,14 +1,14 @@ -use rustc::hir::map::Map; -use rustc::session::config::EntryFnType; -use rustc::session::{config, Session}; -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_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}; @@ -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().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 77% rename from src/librustc/hir/map/hir_id_validator.rs rename to src/librustc_passes/hir_id_validator.rs index 796f489547269..80dfcd9c2417a 100644 --- a/src/librustc/hir/map/hir_id_validator.rs +++ b/src/librustc_passes/hir_id_validator.rs @@ -1,12 +1,12 @@ -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; -use rustc_hir::def_id::{DefId, DefIndex, CRATE_DEF_INDEX}; +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 }, ); }); @@ -32,7 +32,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { struct HirIdValidator<'a, 'hir> { hir_map: Map<'hir>, - owner_def_index: Option, + owner: Option, hir_ids_seen: FxHashSet, errors: &'a Lock>, } @@ -46,7 +46,7 @@ impl<'a, 'hir> OuterVisitor<'a, 'hir> { fn new_inner_visitor(&self, hir_map: Map<'hir>) -> HirIdValidator<'a, 'hir> { HirIdValidator { hir_map, - owner_def_index: None, + owner: None, hir_ids_seen: Default::default(), errors: self.errors, } @@ -78,12 +78,12 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { } fn check)>(&mut self, hir_id: HirId, walk: F) { - assert!(self.owner_def_index.is_none()); - let owner_def_index = self.hir_map.local_def_id(hir_id).index; - self.owner_def_index = Some(owner_def_index); + assert!(self.owner.is_none()); + let owner = self.hir_map.local_def_id(hir_id); + self.owner = Some(owner); walk(self); - if owner_def_index == CRATE_DEF_INDEX { + if owner.local_def_index == CRATE_DEF_INDEX { return; } @@ -105,27 +105,26 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { let mut missing_items = Vec::with_capacity(missing.len()); for local_id in missing { - let hir_id = - HirId { owner: owner_def_index, local_id: ItemLocalId::from_u32(local_id) }; + let hir_id = HirId { owner, local_id: ItemLocalId::from_u32(local_id) }; trace!("missing hir id {:#?}", hir_id); missing_items.push(format!( "[local_id: {}, owner: {}]", local_id, - self.hir_map.def_path(DefId::local(owner_def_index)).to_string_no_crate() + self.hir_map.def_path(owner).to_string_no_crate() )); } self.error(|| { format!( "ItemLocalIds not assigned densely in {}. \ Max ItemLocalId = {}, missing IDs = {:?}; seens IDs = {:?}", - self.hir_map.def_path(DefId::local(owner_def_index)).to_string_no_crate(), + self.hir_map.def_path(owner).to_string_no_crate(), max, missing_items, self.hir_ids_seen .iter() - .map(|&local_id| HirId { owner: owner_def_index, local_id }) + .map(|&local_id| HirId { owner, local_id }) .map(|h| format!("({:?} {})", h, self.hir_map.node_to_string(h))) .collect::>() ) @@ -142,25 +141,15 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { } fn visit_id(&mut self, hir_id: HirId) { - let owner = self.owner_def_index.expect("no owner_def_index"); - - if hir_id == hir::DUMMY_HIR_ID { - self.error(|| { - format!( - "HirIdValidator: HirId {:?} is invalid", - self.hir_map.node_to_string(hir_id) - ) - }); - return; - } + let owner = self.owner.expect("no owner"); if owner != hir_id.owner { self.error(|| { format!( "HirIdValidator: The recorded owner of {} is {} instead of {}", self.hir_map.node_to_string(hir_id), - self.hir_map.def_path(DefId::local(hir_id.owner)).to_string_no_crate(), - self.hir_map.def_path(DefId::local(owner)).to_string_no_crate() + self.hir_map.def_path(hir_id.owner).to_string_no_crate(), + self.hir_map.def_path(owner).to_string_no_crate() ) }); } 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 5cf9666797ebb..c8666ba1fd078 100644 --- a/src/librustc_passes/intrinsicck.rs +++ b/src/librustc_passes/intrinsicck.rs @@ -1,14 +1,17 @@ -use rustc::hir::map::Map; -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) { @@ -119,10 +122,275 @@ 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.at(DUMMY_SP), self.param_env) { + 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 = "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> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -131,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); @@ -139,26 +407,30 @@ impl Visitor<'tcx> for ItemVisitor<'tcx> { } impl Visitor<'tcx> for ExprVisitor<'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } 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..f4167c8644e6e 100644 --- a/src/librustc_passes/lang_items.rs +++ b/src/librustc_passes/lang_items.rs @@ -7,19 +7,21 @@ //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. //! * Functions called by the compiler itself. +use crate::check_attr::target_from_impl_item; 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_ast::ast::Attribute; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::lang_items::{extract, ITEM_REFS}; -use rustc_hir::{LangItem, LanguageItems, Target}; +use rustc_hir::{HirId, LangItem, LanguageItems, Target}; -use rustc::ty::query::Providers; +use rustc_middle::ty::query::Providers; struct LanguageItemCollector<'tcx> { items: LanguageItems, @@ -28,13 +30,38 @@ struct LanguageItemCollector<'tcx> { impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> { fn visit_item(&mut self, item: &hir::Item<'_>) { - if let Some((value, span)) = extract(&item.attrs) { - let actual_target = Target::from_item(item); + self.check_for_lang(Target::from_item(item), item.hir_id, item.attrs) + } + + fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { + self.check_for_lang( + Target::from_trait_item(trait_item), + trait_item.hir_id, + trait_item.attrs, + ) + } + + fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { + self.check_for_lang( + target_from_impl_item(self.tcx, impl_item), + impl_item.hir_id, + impl_item.attrs, + ) + } +} + +impl LanguageItemCollector<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> LanguageItemCollector<'tcx> { + LanguageItemCollector { tcx, items: LanguageItems::new() } + } + + fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId, attrs: &[Attribute]) { + if let Some((value, span)) = extract(&attrs) { match ITEM_REFS.get(&*value.as_str()).cloned() { // 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); + let def_id = self.tcx.hir().local_def_id(hir_id); + self.collect_item(item_index, def_id.to_def_id()); } // Known lang item with attribute on incorrect target. Some((_, expected_target)) => { @@ -71,20 +98,6 @@ impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> { } } - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) { - // At present, lang items are always items, not trait items. - } - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) { - // At present, lang items are always items, not impl items. - } -} - -impl LanguageItemCollector<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> LanguageItemCollector<'tcx> { - LanguageItemCollector { tcx, items: LanguageItems::new() } - } - fn collect_item(&mut self, item_index: usize, item_def_id: DefId) { // Check for duplicates. if let Some(original_def_id) = self.items.items[item_index] { @@ -169,6 +182,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 9f6598a0ec1fe..2419e6965968e 100644 --- a/src/librustc_passes/layout_test.rs +++ b/src/librustc_passes/layout_test.rs @@ -1,40 +1,40 @@ -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 { // if the `rustc_attrs` feature is not enabled, don't bother testing layout - tcx.hir().krate().visit_all_item_likes(&mut VarianceTest { tcx }); + tcx.hir().krate().visit_all_item_likes(&mut LayoutTest { tcx }); } } -struct VarianceTest<'tcx> { +struct LayoutTest<'tcx> { tcx: TyCtxt<'tcx>, } -impl ItemLikeVisitor<'tcx> for VarianceTest<'tcx> { +impl ItemLikeVisitor<'tcx> for LayoutTest<'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { let item_def_id = self.tcx.hir().local_def_id(item.hir_id); - if let ItemKind::TyAlias(..) = item.kind { - for attr in self.tcx.get_attrs(item_def_id).iter() { - if attr.check_name(sym::rustc_layout) { - self.dump_layout_of(item_def_id, item, attr); + match item.kind { + ItemKind::TyAlias(..) + | ItemKind::Enum(..) + | ItemKind::Struct(..) + | ItemKind::Union(..) => { + 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); + } } } + _ => {} } } @@ -42,8 +42,8 @@ impl ItemLikeVisitor<'tcx> for VarianceTest<'tcx> { fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} } -impl VarianceTest<'tcx> { - fn dump_layout_of(&self, item_def_id: DefId, item: &hir::Item<'tcx>, attr: &Attribute) { +impl LayoutTest<'tcx> { + 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); @@ -81,6 +81,15 @@ impl VarianceTest<'tcx> { ); } + sym::debug => { + let normalized_ty = + self.tcx.normalize_erasing_regions(param_env.with_reveal_all(), ty); + self.tcx.sess.span_err( + item.span, + &format!("layout_of({:?}) = {:#?}", normalized_ty, *ty_layout), + ); + } + name => { self.tcx.sess.span_err( meta_item.span(), @@ -105,9 +114,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 030d0893b0274..ff5dabd5418c9 100644 --- a/src/librustc_passes/liveness.rs +++ b/src/librustc_passes/liveness.rs @@ -76,46 +76,34 @@ //! is not just used to generate a new value. For example, `x += 1` is //! a read but not a use. This is used to generate better warnings. //! -//! ## Special Variables +//! ## Special nodes and variables //! -//! We generate various special variables for various, well, special purposes. -//! These are described in the `specials` struct: -//! -//! - `exit_ln`: a live node that is generated to represent every 'exit' from -//! the function, whether it be by explicit return, panic, or other means. -//! -//! - `fallthrough_ln`: a live node that represents a fallthrough -//! -//! - `clean_exit_var`: a synthetic variable that is only 'read' from the -//! fallthrough node. It is only live if the function could converge -//! via means other than an explicit `return` expression. That is, it is -//! only dead if the end of the function's block can never be reached. -//! It is the responsibility of typeck to ensure that there are no -//! `return` expressions in a function declared as diverging. +//! We generate various special nodes for various, well, special purposes. +//! These are described in the `Specials` struct. use self::LiveNodeKind::*; use self::VarKind::*; -use rustc::hir::map::Map; -use rustc::lint; -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_span::symbol::sym; +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, 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); @@ -140,6 +128,7 @@ enum LiveNodeKind { UpvarNode(Span), ExprNode(Span), VarDefNode(Span), + ClosureNode, ExitNode, } @@ -149,6 +138,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_string(s)), ExprNode(s) => format!("Expr node [{}]", sm.span_to_string(s)), VarDefNode(s) => format!("Var def node [{}]", sm.span_to_string(s)), + ClosureNode => "Closure node".to_owned(), ExitNode => "Exit node".to_owned(), } } @@ -245,15 +235,15 @@ 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, + Upvar(HirId, Symbol), } struct IrMaps<'tcx> { @@ -306,10 +296,9 @@ impl IrMaps<'tcx> { self.num_vars += 1; match vk { - Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) => { + Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) | Upvar(node_id, _) => { self.variable_map.insert(node_id, v); } - CleanExit => {} } debug!("{:?} is {:?}", v, vk); @@ -328,15 +317,14 @@ impl IrMaps<'tcx> { fn variable_name(&self, var: Variable) -> String { match self.var_kinds[var.get()] { - Local(LocalInfo { name, .. }) | Param(_, name) => name.to_string(), - CleanExit => "".to_owned(), + Local(LocalInfo { name, .. }) | Param(_, name) | Upvar(_, name) => name.to_string(), } } fn variable_is_shorthand(&self, var: Variable) -> bool { match self.var_kinds[var.get()] { Local(LocalInfo { is_shorthand, .. }) => is_shorthand, - Param(..) | CleanExit => false, + Param(..) | Upvar(..) => false, } } @@ -357,11 +345,11 @@ fn visit_fn<'tcx>( sp: Span, id: hir::HirId, ) { - debug!("visit_fn"); + debug!("visit_fn {:?}", id); // 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 { @@ -377,6 +365,14 @@ fn visit_fn<'tcx>( let body = ir.tcx.hir().body(body_id); + if let Some(upvars) = ir.tcx.upvars_mentioned(def_id) { + for (&var_hir_id, _upvar) in upvars { + debug!("adding upvar {:?}", var_hir_id); + let var_name = ir.tcx.hir().name(var_hir_id); + fn_maps.add_variable(Upvar(var_hir_id, var_name)); + } + } + for param in body.params { let is_shorthand = match param.pat.kind { rustc_hir::PatKind::Struct(..) => true, @@ -398,11 +394,13 @@ fn visit_fn<'tcx>( intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id); // compute liveness - let mut lsets = Liveness::new(&mut fn_maps, body_id); - let entry_ln = lsets.compute(&body.value); + let mut lsets = Liveness::new(&mut fn_maps, def_id); + let entry_ln = lsets.compute(fk, &body, sp, id); + lsets.log_liveness(entry_ln, id); // check for various error conditions lsets.visit_body(body); + lsets.warn_about_unused_upvars(entry_ln); lsets.warn_about_unused_args(body, entry_ln); } @@ -462,11 +460,8 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) { // live nodes required for uses or definitions of variables: hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => { debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res); - if let Res::Local(var_hir_id) = path.res { - let upvars = ir.tcx.upvars(ir.body_owner); - if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) { - ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); - } + if let Res::Local(_var_hir_id) = path.res { + ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span)); } intravisit::walk_expr(ir, expr); } @@ -481,22 +476,15 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) { // construction site. let mut call_caps = Vec::new(); let closure_def_id = ir.tcx.hir().local_def_id(expr.hir_id); - if let Some(upvars) = ir.tcx.upvars(closure_def_id) { - let parent_upvars = ir.tcx.upvars(ir.body_owner); - call_caps.extend(upvars.iter().filter_map(|(&var_id, upvar)| { - let has_parent = - parent_upvars.map_or(false, |upvars| upvars.contains_key(&var_id)); - if !has_parent { - let upvar_ln = ir.add_live_node(UpvarNode(upvar.span)); - Some(CaptureInfo { ln: upvar_ln, var_hid: var_id }) - } else { - None - } + if let Some(upvars) = ir.tcx.upvars_mentioned(closure_def_id) { + call_caps.extend(upvars.iter().map(|(&var_id, upvar)| { + let upvar_ln = ir.add_live_node(UpvarNode(upvar.span)); + CaptureInfo { ln: upvar_ln, var_hid: var_id } })); } 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 +521,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(..) @@ -646,9 +635,13 @@ impl RWUTable { #[derive(Copy, Clone)] struct Specials { + /// A live node representing a point of execution before closure entry & + /// after closure exit. Used to calculate liveness of captured variables + /// through calls to the same closure. Used for Fn & FnMut closures only. + closure_ln: LiveNode, + /// A live node representing every 'exit' from the function, whether it be + /// by explicit return, panic, or other means. exit_ln: LiveNode, - fallthrough_ln: LiveNode, - clean_exit_var: Variable, } const ACC_READ: u32 = 1; @@ -658,6 +651,7 @@ const ACC_USE: u32 = 4; struct Liveness<'a, 'tcx> { ir: &'a mut IrMaps<'tcx>, tables: &'a ty::TypeckTables<'tcx>, + param_env: ty::ParamEnv<'tcx>, s: Specials, successors: Vec, rwu_table: RWUTable, @@ -670,18 +664,14 @@ struct Liveness<'a, 'tcx> { } impl<'a, 'tcx> Liveness<'a, 'tcx> { - fn new(ir: &'a mut IrMaps<'tcx>, body: hir::BodyId) -> 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 - // an implicit return + fn new(ir: &'a mut IrMaps<'tcx>, def_id: LocalDefId) -> Liveness<'a, 'tcx> { let specials = Specials { + closure_ln: ir.add_live_node(ClosureNode), exit_ln: ir.add_live_node(ExitNode), - fallthrough_ln: ir.add_live_node(ExitNode), - clean_exit_var: ir.add_variable(CleanExit), }; - let tables = ir.tcx.body_tables(body); + let tables = ir.tcx.typeck_tables_of(def_id); + let param_env = ir.tcx.param_env(def_id); let num_live_nodes = ir.num_live_nodes; let num_vars = ir.num_vars; @@ -689,6 +679,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { Liveness { ir, tables, + param_env, s: specials, successors: vec![invalid_node(); num_live_nodes], rwu_table: RWUTable::new(num_live_nodes * num_vars), @@ -773,12 +764,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { fn write_vars(&self, wr: &mut dyn Write, ln: LiveNode, mut test: F) -> io::Result<()> where - F: FnMut(usize) -> LiveNode, + F: FnMut(usize) -> bool, { let node_base_idx = self.idx(ln, Variable(0)); for var_idx in 0..self.ir.num_vars { let idx = node_base_idx + var_idx; - if test(idx).is_valid() { + if test(idx) { write!(wr, " {:?}", Variable(var_idx as u32))?; } } @@ -791,14 +782,31 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { { let wr = &mut wr as &mut dyn Write; write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln)); - self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx)); + self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_valid()); write!(wr, " writes"); - self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx)); + self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_valid()); + write!(wr, " uses"); + self.write_vars(wr, ln, |idx| self.rwu_table.get_used(idx)); + write!(wr, " precedes {:?}]", self.successors[ln.get()]); } String::from_utf8(wr).unwrap() } + fn log_liveness(&self, entry_ln: LiveNode, hir_id: hir::HirId) { + // hack to skip the loop unless debug! is enabled: + debug!( + "^^ liveness computation results for body {} (entry={:?})", + { + for ln_idx in 0..self.ir.num_live_nodes { + debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32))); + } + hir_id + }, + entry_ln + ); + } + fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) { self.successors[ln.get()] = succ_ln; @@ -861,7 +869,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { first_merge, any_changed ); - return any_changed; + any_changed } // Indicates that a local variable was *defined*; we know that no @@ -899,40 +907,94 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.rwu_table.assign_unpacked(idx, rwu); } - 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) - ); + fn compute( + &mut self, + fk: FnKind<'_>, + body: &hir::Body<'_>, + span: Span, + id: hir::HirId, + ) -> LiveNode { + debug!("compute: using id for body, {:?}", body.value); - // the fallthrough exit is only for those cases where we do not - // explicitly return: - let s = self.s; - self.init_from_succ(s.fallthrough_ln, s.exit_ln); - self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ); + // # Liveness of captured variables + // + // When computing the liveness for captured variables we take into + // account how variable is captured (ByRef vs ByValue) and what is the + // closure kind (Generator / FnOnce vs Fn / FnMut). + // + // Variables captured by reference are assumed to be used on the exit + // from the closure. + // + // In FnOnce closures, variables captured by value are known to be dead + // on exit since it is impossible to call the closure again. + // + // In Fn / FnMut closures, variables captured by value are live on exit + // if they are live on the entry to the closure, since only the closure + // itself can access them on subsequent calls. + + if let Some(upvars) = self.ir.tcx.upvars_mentioned(self.ir.body_owner) { + // Mark upvars captured by reference as used after closure exits. + for (&var_hir_id, upvar) in upvars.iter().rev() { + let upvar_id = ty::UpvarId { + var_path: ty::UpvarPath { hir_id: var_hir_id }, + closure_expr_id: self.ir.body_owner.expect_local(), + }; + match self.tables.upvar_capture(upvar_id) { + ty::UpvarCapture::ByRef(_) => { + let var = self.variable(var_hir_id, upvar.span); + self.acc(self.s.exit_ln, var, ACC_READ | ACC_USE); + } + ty::UpvarCapture::ByValue => {} + } + } + } - let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln); + let succ = self.propagate_through_expr(&body.value, self.s.exit_ln); - // hack to skip the loop unless debug! is enabled: - debug!( - "^^ liveness computation results for body {} (entry={:?})", - { - for ln_idx in 0..self.ir.num_live_nodes { - debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32))); - } - body.hir_id + match fk { + FnKind::Method(..) | FnKind::ItemFn(..) => return succ, + FnKind::Closure(..) => {} + } + + let ty = self.tables.node_type(id); + match ty.kind { + ty::Closure(_def_id, substs) => match substs.as_closure().kind() { + ty::ClosureKind::Fn => {} + ty::ClosureKind::FnMut => {} + ty::ClosureKind::FnOnce => return succ, }, - entry_ln - ); + ty::Generator(..) => return succ, + _ => { + span_bug!(span, "type of closure expr {:?} is not a closure {:?}", id, ty,); + } + }; + + // Propagate through calls to the closure. + let mut first_merge = true; + loop { + self.init_from_succ(self.s.closure_ln, succ); + for param in body.params { + param.pat.each_binding(|_bm, hir_id, _x, ident| { + let var = self.variable(hir_id, ident.span); + self.define(self.s.closure_ln, var); + }) + } - entry_ln + if !self.merge_from_succ(self.s.exit_ln, self.s.closure_ln, first_merge) { + break; + } + first_merge = false; + assert_eq!(succ, self.propagate_through_expr(&body.value, self.s.exit_ln)); + } + + succ } fn propagate_through_block(&mut self, blk: &hir::Block<'_>, succ: LiveNode) -> LiveNode { 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)) } @@ -953,7 +1015,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, @@ -976,7 +1038,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 @@ -987,10 +1049,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. @@ -1125,8 +1184,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Call(ref f, ref args) => { - let m = self.ir.tcx.parent_module(expr.hir_id); - let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) { + let m = self.ir.tcx.parent_module(expr.hir_id).to_def_id(); + let succ = if self.ir.tcx.is_ty_uninhabited_from( + m, + self.tables.expr_ty(expr), + self.param_env, + ) { self.s.exit_ln } else { succ @@ -1135,9 +1198,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&f, succ) } - hir::ExprKind::MethodCall(.., ref args) => { - let m = self.ir.tcx.parent_module(expr.hir_id); - let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) { + hir::ExprKind::MethodCall(.., ref args, _) => { + let m = self.ir.tcx.parent_module(expr.hir_id).to_def_id(); + let succ = if self.ir.tcx.is_ty_uninhabited_from( + m, + self.tables.expr_ty(expr), + self.param_env, + ) { self.s.exit_ln } else { succ @@ -1173,6 +1240,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; @@ -1299,14 +1424,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { acc: u32, ) -> LiveNode { match path.res { - Res::Local(hid) => { - let upvars = self.ir.tcx.upvars(self.ir.body_owner); - if !upvars.map_or(false, |upvars| upvars.contains_key(&hid)) { - self.access_var(hir_id, hid, succ, acc, path.span) - } else { - succ - } - } + Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span), _ => succ, } } @@ -1333,11 +1451,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); @@ -1359,7 +1473,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Checking for error conditions impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -1398,6 +1512,27 @@ 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::Out { expr, .. } => { + if let Some(expr) = expr { + this.check_place(expr); + } + } + hir::InlineAsmOperand::InOut { expr, .. } => { + this.check_place(expr); + } + hir::InlineAsmOperand::SplitInOut { out_expr, .. } => { + if let Some(out_expr) = out_expr { + this.check_place(out_expr); + } + } + _ => {} + } + } + } + + hir::ExprKind::LlvmInlineAsm(ref asm) => { for input in asm.inputs_exprs { this.visit_expr(input); } @@ -1448,16 +1583,13 @@ impl<'tcx> Liveness<'_, 'tcx> { match expr.kind { hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => { if let Res::Local(var_hid) = path.res { - let upvars = self.ir.tcx.upvars(self.ir.body_owner); - if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hid)) { - // Assignment to an immutable variable or argument: only legal - // if there is no later assignment. If this local is actually - // mutable, then check for a reassignment to flag the mutability - // as being used. - let ln = self.live_node(expr.hir_id, expr.span); - let var = self.variable(var_hid, expr.span); - self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var); - } + // Assignment to an immutable variable or argument: only legal + // if there is no later assignment. If this local is actually + // mutable, then check for a reassignment to flag the mutability + // as being used. + let ln = self.live_node(expr.hir_id, expr.span); + let var = self.variable(var_hid, expr.span); + self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var); } } _ => { @@ -1473,11 +1605,60 @@ impl<'tcx> Liveness<'_, 'tcx> { if name.is_empty() || name.as_bytes()[0] == b'_' { None } else { Some(name) } } + fn warn_about_unused_upvars(&self, entry_ln: LiveNode) { + let upvars = match self.ir.tcx.upvars_mentioned(self.ir.body_owner) { + None => return, + Some(upvars) => upvars, + }; + for (&var_hir_id, upvar) in upvars.iter() { + let var = self.variable(var_hir_id, upvar.span); + let upvar_id = ty::UpvarId { + var_path: ty::UpvarPath { hir_id: var_hir_id }, + closure_expr_id: self.ir.body_owner.expect_local(), + }; + match self.tables.upvar_capture(upvar_id) { + ty::UpvarCapture::ByValue => {} + ty::UpvarCapture::ByRef(..) => continue, + }; + if self.used_on_entry(entry_ln, var) { + if self.live_on_entry(entry_ln, var).is_none() { + if let Some(name) = self.should_warn(var) { + self.ir.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_ASSIGNMENTS, + var_hir_id, + vec![upvar.span], + |lint| { + lint.build(&format!("value captured by `{}` is never read", name)) + .help("did you mean to capture by reference instead?") + .emit(); + }, + ); + } + } + } else { + if let Some(name) = self.should_warn(var) { + self.ir.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_VARIABLES, + var_hir_id, + vec![upvar.span], + |lint| { + lint.build(&format!("unused variable: `{}`", name)) + .help("did you mean to capture by reference instead?") + .emit(); + }, + ); + } + } + } + } + fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) { for p in body.params { self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| { if self.live_on_entry(ln, var).is_none() { - self.report_dead_assign(hir_id, spans, var, true); + self.report_unsed_assign(hir_id, spans, var, |name| { + format!("value passed to `{}` is never read", name) + }); } }); } @@ -1491,28 +1672,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 @@ -1523,8 +1709,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)) @@ -1534,31 +1720,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( - "consider prefixing with an underscore", - spans.iter().map(|span| (*span, format!("_{}", name))).collect(), + "if this is intentional, prefix it with an underscore", + non_shorthands + .into_iter() + .map(|(_, span)| (span, format!("_{}", name))) + .collect::>(), Applicability::MachineApplicable, ); } + err.emit() }, ); @@ -1568,35 +1772,30 @@ impl<'tcx> Liveness<'_, 'tcx> { fn warn_about_dead_assign(&self, spans: Vec, hir_id: HirId, ln: LiveNode, var: Variable) { if self.live_on_exit(ln, var).is_none() { - self.report_dead_assign(hir_id, spans, var, false); + self.report_unsed_assign(hir_id, spans, var, |name| { + format!("value assigned to `{}` is never read", name) + }); } } - fn report_dead_assign(&self, hir_id: HirId, spans: Vec, var: Variable, is_param: bool) { + fn report_unsed_assign( + &self, + hir_id: HirId, + spans: Vec, + var: Variable, + message: impl Fn(&str) -> String, + ) { if let Some(name) = self.should_warn(var) { - if is_param { - self.ir.tcx.struct_span_lint_hir( - lint::builtin::UNUSED_ASSIGNMENTS, - hir_id, - spans, - |lint| { - lint.build(&format!("value passed to `{}` is never read", name)) - .help("maybe it is overwritten before being read?") - .emit(); - }, - ) - } else { - self.ir.tcx.struct_span_lint_hir( - lint::builtin::UNUSED_ASSIGNMENTS, - hir_id, - spans, - |lint| { - lint.build(&format!("value assigned to `{}` is never read", name)) - .help("maybe it is overwritten before being read?") - .emit(); - }, - ) - } + self.ir.tcx.struct_span_lint_hir( + lint::builtin::UNUSED_ASSIGNMENTS, + hir_id, + spans, + |lint| { + lint.build(&message(&name)) + .help("maybe it is overwritten before being read?") + .emit(); + }, + ) } } } diff --git a/src/librustc_passes/loops.rs b/src/librustc_passes/loops.rs index 1daef45a1f591..767a6909d31d4 100644 --- a/src/librustc_passes/loops.rs +++ b/src/librustc_passes/loops.rs @@ -1,14 +1,15 @@ 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::hygiene::DesugaringKind; use rustc_span::Span; #[derive(Clone, Copy, Debug, PartialEq)] @@ -68,7 +69,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 +80,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) => (), @@ -201,7 +204,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { label: &Destination, cf_type: &str, ) -> bool { - if self.cx == LabeledBlock { + if !span.is_desugaring(DesugaringKind::QuestionMark) && self.cx == LabeledBlock { if label.label.is_none() { struct_span_err!( self.sess, @@ -222,7 +225,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { return true; } } - return false; + false } fn emit_unlabled_cf_in_while_condition(&mut self, span: Span, cf_type: &str) { struct_span_err!( diff --git a/src/librustc_passes/reachable.rs b/src/librustc_passes/reachable.rs index ad415ed8f62cf..c9a4428c007aa 100644 --- a/src/librustc_passes/reachable.rs +++ b/src/librustc_passes/reachable.rs @@ -5,36 +5,31 @@ // makes all other generics or inline functions that it references // reachable as well. -use rustc::hir::map::Map; -use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; -use rustc::middle::privacy; -use rustc::session::config; -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::intravisit; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +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_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; } match item.kind { - hir::ItemKind::Fn(ref sig, ..) if sig.header.is_const() => { - return true; - } + hir::ItemKind::Fn(ref sig, ..) if sig.header.is_const() => true, hir::ItemKind::Impl { .. } | hir::ItemKind::Fn(..) => { let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id)); generics.requires_monomorphization(tcx) @@ -46,25 +41,21 @@ 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_def_id()); + 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)); if codegen_fn_attrs.requests_inline() || generics.requires_monomorphization(tcx) { return true; } - if let hir::ImplItemKind::Method(method_sig, _) = &impl_item.kind { + if let hir::ImplItemKind::Fn(method_sig, _) = &impl_item.kind { if method_sig.header.is_const() { 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"), } } @@ -83,7 +74,7 @@ struct ReachableContext<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for ReachableContext<'a, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -113,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); } @@ -146,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; } @@ -162,14 +153,14 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { }, Some(Node::TraitItem(trait_method)) => match trait_method.kind { hir::TraitItemKind::Const(_, ref default) => default.is_some(), - hir::TraitItemKind::Fn(_, hir::TraitMethod::Provided(_)) => true, - hir::TraitItemKind::Fn(_, hir::TraitMethod::Required(_)) + hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)) => true, + hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_)) | hir::TraitItemKind::Type(..) => false, }, Some(Node::ImplItem(impl_item)) => { match impl_item.kind { hir::ImplItemKind::Const(..) => true, - hir::ImplItemKind::Method(..) => { + hir::ImplItemKind::Fn(..) => { let attrs = self.tcx.codegen_fn_attrs(def_id); let generics = self.tcx.generics_of(def_id); if generics.requires_monomorphization(self.tcx) || attrs.requests_inline() { @@ -179,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); @@ -189,7 +180,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { } } } - hir::ImplItemKind::OpaqueTy(..) | hir::ImplItemKind::TyAlias(_) => false, + hir::ImplItemKind::TyAlias(_) => false, } } Some(_) => false, @@ -278,11 +269,11 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { Node::TraitItem(trait_method) => { match trait_method.kind { hir::TraitItemKind::Const(_, None) - | hir::TraitItemKind::Fn(_, hir::TraitMethod::Required(_)) => { + | hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_)) => { // Keep going, nothing to get exported } hir::TraitItemKind::Const(_, Some(body_id)) - | hir::TraitItemKind::Fn(_, hir::TraitMethod::Provided(body_id)) => { + | hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)) => { self.visit_nested_body(body_id); } hir::TraitItemKind::Type(..) => {} @@ -292,13 +283,13 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { hir::ImplItemKind::Const(_, body) => { self.visit_nested_body(body); } - hir::ImplItemKind::Method(_, body) => { + hir::ImplItemKind::Fn(_, body) => { let did = self.tcx.hir().get_parent_did(search_item); if method_might_be_inlined(self.tcx, impl_item, did) { self.visit_nested_body(body) } } - hir::ImplItemKind::OpaqueTy(..) | hir::ImplItemKind::TyAlias(_) => {} + hir::ImplItemKind::TyAlias(_) => {} }, Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(.., body, _, _), .. }) => { self.visit_nested_body(body); @@ -366,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())), ); } } @@ -379,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().iter().any(|ty| { + *ty == CrateType::Rlib || *ty == CrateType::Dylib || *ty == CrateType::ProcMacro + }); let mut reachable_context = ReachableContext { tcx, tables: &ty::TypeckTables::empty(None), @@ -405,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); } } @@ -425,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 640a3a35aa032..a6fa677cbc0af 100644 --- a/src/librustc_passes/region.rs +++ b/src/librustc_passes/region.rs @@ -6,10 +6,6 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html -use rustc::hir::map::Map; -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; @@ -17,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; @@ -28,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 @@ -566,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(_) @@ -696,7 +697,7 @@ impl<'tcx> RegionResolutionVisitor<'tcx> { } impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -718,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 { @@ -772,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>) { @@ -787,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()); } } @@ -797,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 6cf4132141b13..054748c09fc44 100644 --- a/src/librustc_passes/stability.rs +++ b/src/librustc_passes/stability.rs @@ -1,14 +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::lint; -use rustc::middle::privacy::AccessLevels; -use rustc::middle::stability::{DeprecationEntry, Index}; -use rustc::session::parse::feature_err; -use rustc::session::Session; -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}; @@ -18,6 +10,14 @@ 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; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_trait_selection::traits::misc::can_type_implement_copy; @@ -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 { @@ -465,7 +467,7 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> { |v| intravisit::walk_crate(v, krate), ); } - return index; + index } /// Cross-references the feature names of unstable APIs with enabled @@ -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 d20237a22d99d..99b4ef9d12fcd 100644 --- a/src/librustc_passes/upvars.rs +++ b/src/librustc_passes/upvars.rs @@ -1,22 +1,21 @@ //! Upvar (closure capture) collection from cross-body HIR uses of `Res::Local`s. -use rustc::hir::map::Map; -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<'_>) { - providers.upvars = |tcx, def_id| { + providers.upvars_mentioned = |tcx, def_id| { if !tcx.is_closure(def_id) { 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(); @@ -44,7 +43,7 @@ struct LocalCollector { } impl Visitor<'tcx> for LocalCollector { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -73,7 +72,7 @@ impl CaptureCollector<'_, '_> { } impl Visitor<'tcx> for CaptureCollector<'a, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -90,7 +89,7 @@ impl Visitor<'tcx> for CaptureCollector<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Closure(..) = expr.kind { let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id); - if let Some(upvars) = self.tcx.upvars(closure_def_id) { + if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { // Every capture of a closure expression is a local in scope, // that is moved/copied/borrowed into the closure value, and // for this analysis they are like any other access to a local. diff --git a/src/librustc_passes/weak_lang_items.rs b/src/librustc_passes/weak_lang_items.rs index d85c7d8c6de04..f2f07b5d4fb26 100644 --- a/src/librustc_passes/weak_lang_items.rs +++ b/src/librustc_passes/weak_lang_items.rs @@ -1,16 +1,16 @@ //! Validity checking for weak lang items -use rustc::middle::lang_items; -use rustc::middle::lang_items::whitelisted; -use rustc::session::config; - -use rustc::hir::map::Map; -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::lang_items::ITEM_REFS; use rustc_hir::weak_lang_items::WEAK_ITEMS_REFS; +use rustc_middle::middle::lang_items::whitelisted; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::CrateType; +use rustc_span::symbol::sym; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -39,13 +39,13 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem 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, + let needs_check = tcx.sess.crate_types().iter().any(|kind| match *kind { + CrateType::Dylib + | CrateType::ProcMacro + | CrateType::Cdylib + | CrateType::Executable + | CrateType::Staticlib => true, + CrateType::Rlib => false, }); if !needs_check { return; @@ -72,11 +72,21 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { } impl<'a, 'tcx> Context<'a, 'tcx> { - fn register(&mut self, name: Symbol, span: Span) { + fn register(&mut self, name: Symbol, span: Span, hir_id: hir::HirId) { if let Some(&item) = WEAK_ITEMS_REFS.get(&name) { if self.items.require(item).is_err() { self.items.missing.push(item); } + } else if name == sym::count_code_region { + // `core::intrinsics::code_count_region()` is (currently) the only `extern` lang item + // that is never actually linked. It is not a `weak_lang_item` that can be registered + // when used, and should be registered here instead. + if let Some((item_index, _)) = ITEM_REFS.get(&*name.as_str()).cloned() { + if self.items.items[item_index].is_none() { + let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id(); + self.items.items[item_index] = Some(item_def_id); + } + } } else { struct_span_err!(self.tcx.sess, span, E0264, "unknown external lang item: `{}`", name) .emit(); @@ -85,15 +95,15 @@ impl<'a, 'tcx> Context<'a, 'tcx> { } impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; - fn nested_visit_map(&mut self) -> NestedVisitorMap> { + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) { if let Some((lang_item, _)) = hir::lang_items::extract(&i.attrs) { - self.register(lang_item, i.span); + self.register(lang_item, i.span, i.hir_id); } intravisit::walk_foreign_item(self, i) } diff --git a/src/librustc_plugin_impl/Cargo.toml b/src/librustc_plugin_impl/Cargo.toml index 8931307259021..38cfbd48de246 100644 --- a/src/librustc_plugin_impl/Cargo.toml +++ b/src/librustc_plugin_impl/Cargo.toml @@ -11,10 +11,11 @@ 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" } rustc_metadata = { path = "../librustc_metadata" } rustc_ast = { path = "../librustc_ast" } +rustc_session = { path = "../librustc_session" } rustc_span = { path = "../librustc_span" } 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 9bd9bcb25ba88..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::session::Session; -use rustc_ast::ast::{Crate, Ident}; +use rustc_ast::ast::Crate; use rustc_errors::struct_span_err; use rustc_metadata::locator; -use rustc_span::symbol::sym; +use rustc_middle::middle::cstore::MetadataLoader; +use rustc_session::Session; +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 6d1272c117b4d..6110d2ef7fc9a 100644 --- a/src/librustc_privacy/Cargo.toml +++ b/src/librustc_privacy/Cargo.toml @@ -9,12 +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_ast = { path = "../librustc_ast" } +rustc_session = { path = "../librustc_session" } 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 28a2987301f50..9e6e7ea962bc3 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::lint; -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; @@ -90,14 +90,14 @@ where fn visit_predicates(&mut self, predicates: ty::GenericPredicates<'tcx>) -> bool { let ty::GenericPredicates { parent: _, predicates } = predicates; for (predicate, _span) in predicates { - match predicate { - ty::Predicate::Trait(poly_predicate, _) => { + match predicate.kind() { + ty::PredicateKind::Trait(poly_predicate, _) => { let ty::TraitPredicate { trait_ref } = *poly_predicate.skip_binder(); if self.visit_trait(trait_ref) { return true; } } - ty::Predicate::Projection(poly_predicate) => { + ty::PredicateKind::Projection(poly_predicate) => { let ty::ProjectionPredicate { projection_ty, ty } = *poly_predicate.skip_binder(); if ty.visit_with(self) { @@ -107,13 +107,13 @@ where return true; } } - ty::Predicate::TypeOutlives(poly_predicate) => { + ty::PredicateKind::TypeOutlives(poly_predicate) => { let ty::OutlivesPredicate(ty, _region) = *poly_predicate.skip_binder(); if ty.visit_with(self) { return true; } } - ty::Predicate::RegionOutlives(..) => {} + ty::PredicateKind::RegionOutlives(..) => {} _ => bug!("unexpected predicate: {:?}", predicate), } } @@ -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` @@ -176,7 +176,7 @@ where // All traits in the list are considered the "primary" part of the type // and are visited by shallow visitors. for predicate in *predicates.skip_binder() { - let trait_ref = match *predicate { + let trait_ref = match predicate { ty::ExistentialPredicate::Trait(trait_ref) => trait_ref, ty::ExistentialPredicate::Projection(proj) => proj.trait_ref(tcx), ty::ExistentialPredicate::AutoTrait(def_id) => { @@ -220,7 +220,7 @@ where | ty::Ref(..) | ty::FnPtr(..) | ty::Param(..) - | ty::Error + | ty::Error(_) | ty::GeneratorWitness(..) => {} ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => { bug!("unexpected type: {:?}", ty) @@ -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)); @@ -327,7 +328,7 @@ fn def_id_visibility<'tcx>( } Node::Expr(expr) => { return ( - ty::Visibility::Restricted(tcx.parent_module(expr.hir_id)), + ty::Visibility::Restricted(tcx.parent_module(expr.hir_id).to_def_id()), expr.span, "private", ); @@ -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,10 +612,9 @@ 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 | DefKind::ConstParam | DefKind::Ctor(_, _) | DefKind::Enum @@ -623,7 +624,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 +664,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 +920,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)); } } @@ -920,14 +934,20 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { } fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) { - if attr::find_transparency(&md.attrs, md.ast.legacy).0 != Transparency::Opaque { + if attr::find_transparency(&md.attrs, md.ast.macro_rules).0 != Transparency::Opaque { self.update(md.hir_id, Some(AccessLevel::Public)); return; } 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 +1009,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 +1032,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 +1044,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 +1066,7 @@ impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> { def.variant_descr(), self.tcx.def_path_str(def.did) ) - .span_label(span, format!("field `{}` is private", field.ident)) + .span_label(span, label) .emit(); } } @@ -1066,7 +1094,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 +1117,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 +1172,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 +1180,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. @@ -1180,7 +1204,11 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> { fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { let is_error = !self.item_is_accessible(def_id); if is_error { - self.tcx.sess.span_err(self.span, &format!("{} `{}` is private", kind, descr)); + self.tcx + .sess + .struct_span_err(self.span, &format!("{} `{}` is private", kind, descr)) + .span_label(self.span, &format!("private {}", kind)) + .emit(); } is_error } @@ -1233,7 +1261,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()) { @@ -1267,7 +1301,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { return; } } - hir::ExprKind::MethodCall(_, span, _) => { + hir::ExprKind::MethodCall(_, span, _, _) => { // Method calls have to be checked specially. self.span = span; if let Some(def_id) = self.tables.type_dependent_def_id(expr.hir_id) { @@ -1298,23 +1332,27 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> { _ => None, }; let def = def.filter(|(kind, _)| match kind { - DefKind::AssocFn - | DefKind::AssocConst - | DefKind::AssocTy - | DefKind::AssocOpaqueTy - | DefKind::Static => true, + DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static => true, _ => false, }); if let Some((kind, def_id)) = def { 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 msg = format!("{} `{}` is private", kind.descr(def_id), name); - self.tcx.sess.span_err(span, &msg); + let kind = kind.descr(def_id); + 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; } } @@ -1415,15 +1453,15 @@ 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, } } else { - return false; + false } } @@ -1447,7 +1485,7 @@ impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { } impl<'a, 'b, 'tcx, 'v> Visitor<'v> for ObsoleteCheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> { - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -1536,8 +1574,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 } @@ -1556,12 +1594,10 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { || items.iter().any(|impl_item_ref| { let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); match impl_item.kind { - hir::ImplItemKind::Const(..) | hir::ImplItemKind::Method(..) => { + hir::ImplItemKind::Const(..) | hir::ImplItemKind::Fn(..) => { self.access_levels.is_reachable(impl_item_ref.id.hir_id) } - hir::ImplItemKind::OpaqueTy(..) | hir::ImplItemKind::TyAlias(_) => { - false - } + hir::ImplItemKind::TyAlias(_) => false, } }); @@ -1578,8 +1614,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> { // types in private items. let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); match impl_item.kind { - hir::ImplItemKind::Const(..) - | hir::ImplItemKind::Method(..) + hir::ImplItemKind::Const(..) | hir::ImplItemKind::Fn(..) if self .item_is_public(&impl_item.hir_id, &impl_item.vis) => { @@ -1629,7 +1664,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); } @@ -1798,8 +1833,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, }; @@ -1838,7 +1873,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { && self.tcx.is_private_dep(item_id.krate); log::debug!("leaks_private_dep(item_id={:?})={}", item_id, ret); - return ret; + ret } } @@ -1889,7 +1924,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, @@ -1908,11 +1943,8 @@ 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. - AssocItemKind::OpaqueTy => (false, true), }; check.in_assoc_ty = is_assoc_ty; check.generics().predicates(); @@ -2033,14 +2065,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 new file mode 100644 index 0000000000000..73d50f84fe836 --- /dev/null +++ b/src/librustc_query_system/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_query_system" +version = "0.0.0" +edition = "2018" + +[lib] +name = "rustc_query_system" +path = "lib.rs" +doctest = false + +[dependencies] +rustc_arena = { path = "../librustc_arena" } +log = { version = "0.4", features = ["release_max_level_info", "std"] } +rustc-rayon-core = "0.3.0" +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_errors = { path = "../librustc_errors" } +rustc_index = { path = "../librustc_index" } +rustc_serialize = { path = "../librustc_serialize" } +rustc_span = { path = "../librustc_span" } +parking_lot = "0.10" +smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/src/librustc/dep_graph/README.md b/src/librustc_query_system/dep_graph/README.md similarity index 100% rename from src/librustc/dep_graph/README.md rename to src/librustc_query_system/dep_graph/README.md diff --git a/src/librustc_query_system/dep_graph/debug.rs b/src/librustc_query_system/dep_graph/debug.rs new file mode 100644 index 0000000000000..718a2f1039a4d --- /dev/null +++ b/src/librustc_query_system/dep_graph/debug.rs @@ -0,0 +1,58 @@ +//! Code for debugging the dep-graph. + +use super::{DepKind, DepNode}; +use std::error::Error; + +/// A dep-node filter goes from a user-defined string to a query over +/// nodes. Right now the format is like this: +/// +/// x & y & z +/// +/// where the format-string of the dep-node must contain `x`, `y`, and +/// `z`. +#[derive(Debug)] +pub struct DepNodeFilter { + text: String, +} + +impl DepNodeFilter { + pub fn new(text: &str) -> Self { + DepNodeFilter { text: text.trim().to_string() } + } + + /// Returns `true` if all nodes always pass the filter. + pub fn accepts_all(&self) -> bool { + self.text.is_empty() + } + + /// Tests whether `node` meets the filter, returning true if so. + pub fn test(&self, node: &DepNode) -> bool { + let debug_str = format!("{:?}", node); + self.text.split('&').map(|s| s.trim()).all(|f| debug_str.contains(f)) + } +} + +/// A filter like `F -> G` where `F` and `G` are valid dep-node +/// filters. This can be used to test the source/target independently. +pub struct EdgeFilter { + pub source: DepNodeFilter, + pub target: DepNodeFilter, +} + +impl EdgeFilter { + pub fn new(test: &str) -> Result> { + let parts: Vec<_> = test.split("->").collect(); + if parts.len() != 2 { + Err(format!("expected a filter like `a&b -> c&d`, not `{}`", test).into()) + } else { + Ok(EdgeFilter { + source: DepNodeFilter::new(parts[0]), + target: DepNodeFilter::new(parts[1]), + }) + } + } + + pub fn test(&self, source: &DepNode, target: &DepNode) -> bool { + self.source.test(source) && self.target.test(target) + } +} diff --git a/src/librustc_query_system/dep_graph/dep_node.rs b/src/librustc_query_system/dep_graph/dep_node.rs new file mode 100644 index 0000000000000..002b0f9c165dd --- /dev/null +++ b/src/librustc_query_system/dep_graph/dep_node.rs @@ -0,0 +1,178 @@ +//! This module defines the `DepNode` type which the compiler uses to represent +//! nodes in the dependency graph. A `DepNode` consists of a `DepKind` (which +//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc) +//! and a `Fingerprint`, a 128 bit hash value the exact meaning of which +//! depends on the node's `DepKind`. Together, the kind and the fingerprint +//! fully identify a dependency node, even across multiple compilation sessions. +//! In other words, the value of the fingerprint does not depend on anything +//! that is specific to a given compilation session, like an unpredictable +//! interning key (e.g., NodeId, DefId, Symbol) or the numeric value of a +//! pointer. The concept behind this could be compared to how git commit hashes +//! uniquely identify a given commit and has a few advantages: +//! +//! * A `DepNode` can simply be serialized to disk and loaded in another session +//! without the need to do any "rebasing (like we have to do for Spans and +//! NodeIds) or "retracing" like we had to do for `DefId` in earlier +//! implementations of the dependency graph. +//! * A `Fingerprint` is just a bunch of bits, which allows `DepNode` to +//! implement `Copy`, `Sync`, `Send`, `Freeze`, etc. +//! * Since we just have a bit pattern, `DepNode` can be mapped from disk into +//! memory without any post-processing (e.g., "abomination-style" pointer +//! reconstruction). +//! * Because a `DepNode` is self-contained, we can instantiate `DepNodes` that +//! refer to things that do not exist anymore. In previous implementations +//! `DepNode` contained a `DefId`. A `DepNode` referring to something that +//! had been removed between the previous and the current compilation session +//! could not be instantiated because the current compilation session +//! contained no `DefId` for thing that had been removed. +//! +//! `DepNode` definition happens in `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. +//! +//! Because the macro sees what parameters a given `DepKind` requires, it can +//! "infer" some properties for each kind of `DepNode`: +//! +//! * Whether a `DepNode` of a given kind has any parameters at all. Some +//! `DepNode`s could represent global concepts with only one value. +//! * Whether it is possible, in principle, to reconstruct a query key from a +//! given `DepNode`. Many `DepKind`s only require a single `DefId` parameter, +//! in which case it is possible to map the node's fingerprint back to the +//! `DefId` it was computed from. In other cases, too much information gets +//! lost during fingerprint computation. + +use super::{DepContext, DepKind}; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; + +use std::fmt; +use std::hash::Hash; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] +pub struct DepNode { + pub kind: K, + pub hash: Fingerprint, +} + +impl DepNode { + /// Creates a new, parameterless DepNode. This method will assert + /// that the DepNode corresponding to the given DepKind actually + /// does not require any parameters. + pub fn new_no_params(kind: K) -> 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + K::debug_node(self, f) + } +} + +pub trait DepNodeParams: fmt::Debug + Sized { + fn can_reconstruct_query_key() -> bool; + + /// This method turns the parameters of a DepNodeConstructor into an opaque + /// Fingerprint to be used in DepNode. + /// Not all DepNodeParams support being turned into a Fingerprint (they + /// don't need to if the corresponding DepNode is anonymous). + fn to_fingerprint(&self, _: Ctxt) -> Fingerprint { + panic!("Not implemented. Accidentally called on anonymous node?") + } + + fn to_debug_str(&self, _: Ctxt) -> String { + format!("{:?}", self) + } + + /// This method tries to recover the query key from the given `DepNode`, + /// something which is needed when forcing `DepNode`s during red-green + /// evaluation. The query system will only call this method if + /// `can_reconstruct_query_key()` is `true`. + /// It is always valid to return `None` here, in which case incremental + /// compilation will treat the query as having changed instead of forcing it. + fn recover(tcx: Ctxt, dep_node: &DepNode) -> Option; +} + +impl DepNodeParams for T +where + T: HashStable + fmt::Debug, +{ + #[inline] + default fn can_reconstruct_query_key() -> bool { + false + } + + default fn to_fingerprint(&self, tcx: Ctxt) -> Fingerprint { + let mut hcx = tcx.create_stable_hashing_context(); + let mut hasher = StableHasher::new(); + + self.hash_stable(&mut hcx, &mut hasher); + + hasher.finish() + } + + default fn to_debug_str(&self, _: Ctxt) -> String { + format!("{:?}", *self) + } + + default fn recover(_: Ctxt, _: &DepNode) -> Option { + None + } +} + +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)] +pub struct WorkProductId { + hash: Fingerprint, +} + +impl WorkProductId { + pub fn from_cgu_name(cgu_name: &str) -> WorkProductId { + let mut hasher = StableHasher::new(); + cgu_name.len().hash(&mut hasher); + cgu_name.hash(&mut hasher); + WorkProductId { hash: hasher.finish() } + } + + pub fn from_fingerprint(fingerprint: Fingerprint) -> 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/dep_graph/graph.rs b/src/librustc_query_system/dep_graph/graph.rs similarity index 80% rename from src/librustc/dep_graph/graph.rs rename to src/librustc_query_system/dep_graph/graph.rs index 97114b9e313f1..04a45090b7226 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc_query_system/dep_graph/graph.rs @@ -1,32 +1,31 @@ -use crate::ty::{self, TyCtxt}; -use parking_lot::{Condvar, Mutex}; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::QueryInvocationId; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering}; +use rustc_data_structures::unlikely; use rustc_errors::Diagnostic; -use rustc_hir::def_id::DefId; use rustc_index::vec::{Idx, IndexVec}; -use smallvec::SmallVec; + +use parking_lot::{Condvar, Mutex}; +use smallvec::{smallvec, SmallVec}; use std::collections::hash_map::Entry; use std::env; use std::hash::Hash; +use std::marker::PhantomData; use std::mem; use std::sync::atomic::Ordering::Relaxed; -use crate::ich::{Fingerprint, StableHashingContext, StableHashingContextProvider}; - use super::debug::EdgeFilter; -use super::dep_node::{DepKind, DepNode, WorkProductId}; use super::prev::PreviousDepGraph; use super::query::DepGraphQuery; -use super::safe::DepGraphSafe; use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; +use super::{DepContext, DepKind, DepNode, WorkProductId}; #[derive(Clone)] -pub struct DepGraph { - data: Option>, +pub struct DepGraph { + data: Option>>, /// This field is used for assigning DepNodeIndices when running in /// non-incremental mode. Even in non-incremental mode we make sure that @@ -65,16 +64,16 @@ impl DepNodeColor { } } -struct DepGraphData { +struct DepGraphData { /// The new encoding of the dependency graph, optimized for red/green /// tracking. The `current` field is the dependency graph of only the /// current compilation session: We don't merge the previous dep-graph into /// current one anymore. - current: CurrentDepGraph, + current: CurrentDepGraph, /// The dep-graph from the previous compilation session. It contains all /// nodes and edges as well as all fingerprints of nodes that have them. - previous: PreviousDepGraph, + previous: PreviousDepGraph, colors: DepNodeColorMap, @@ -90,12 +89,12 @@ struct DepGraphData { /// this map. We can later look for and extract that data. previous_work_products: FxHashMap, - dep_node_debug: Lock>, + dep_node_debug: Lock, String>>, } -pub fn hash_result(hcx: &mut StableHashingContext<'_>, result: &R) -> Option +pub fn hash_result(hcx: &mut HashCtxt, result: &R) -> Option where - R: for<'a> HashStable>, + R: HashStable, { let mut stable_hasher = StableHasher::new(); result.hash_stable(hcx, &mut stable_hasher); @@ -103,11 +102,11 @@ where Some(stable_hasher.finish()) } -impl DepGraph { +impl DepGraph { pub fn new( - prev_graph: PreviousDepGraph, + prev_graph: PreviousDepGraph, prev_work_products: FxHashMap, - ) -> DepGraph { + ) -> DepGraph { let prev_graph_node_count = prev_graph.node_count(); DepGraph { @@ -124,7 +123,7 @@ impl DepGraph { } } - pub fn new_disabled() -> DepGraph { + pub fn new_disabled() -> DepGraph { DepGraph { data: None, virtual_dep_node_index: Lrc::new(AtomicU32::new(0)) } } @@ -134,7 +133,7 @@ impl DepGraph { self.data.is_some() } - pub fn query(&self) -> DepGraphQuery { + pub fn query(&self) -> DepGraphQuery { let data = self.data.as_ref().unwrap().current.data.lock(); let nodes: Vec<_> = data.iter().map(|n| n.node).collect(); let mut edges = Vec::new(); @@ -150,9 +149,8 @@ impl DepGraph { pub fn assert_ignored(&self) { if let Some(..) = self.data { - ty::tls::with_context_opt(|icx| { - let icx = if let Some(icx) = icx { icx } else { return }; - assert!(icx.task_deps.is_none(), "expected no task dependency tracking"); + K::read_deps(|task_deps| { + assert!(task_deps.is_none(), "expected no task dependency tracking"); }) } } @@ -161,11 +159,7 @@ impl DepGraph { where OP: FnOnce() -> R, { - ty::tls::with_context(|icx| { - let icx = ty::tls::ImplicitCtxt { task_deps: None, ..icx.clone() }; - - ty::tls::enter_context(&icx, |_| op()) - }) + K::with_deps(None, op) } /// Starts a new dep-graph task. Dep-graph tasks are specified @@ -195,17 +189,14 @@ impl DepGraph { /// `arg` parameter. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/incremental-compilation.html - pub fn with_task<'a, C, A, R>( + pub fn with_task, A, R>( &self, - key: DepNode, - cx: C, + key: DepNode, + cx: Ctxt, arg: A, - task: fn(C, A) -> R, - hash_result: impl FnOnce(&mut StableHashingContext<'_>, &R) -> Option, - ) -> (R, DepNodeIndex) - where - C: DepGraphSafe + StableHashingContextProvider<'a>, - { + task: fn(Ctxt, A) -> R, + hash_result: impl FnOnce(&mut Ctxt::StableHashingContext, &R) -> Option, + ) -> (R, DepNodeIndex) { self.with_task_impl( key, cx, @@ -218,6 +209,7 @@ impl DepGraph { node: Some(_key), reads: SmallVec::new(), read_set: Default::default(), + phantom_data: PhantomData, }) }, |data, key, fingerprint, task| data.complete_task(key, task.unwrap(), fingerprint), @@ -225,44 +217,36 @@ impl DepGraph { ) } - fn with_task_impl<'a, C, A, R>( + fn with_task_impl, A, R>( &self, - key: DepNode, - cx: C, + key: DepNode, + cx: Ctxt, arg: A, no_tcx: bool, - task: fn(C, A) -> R, - create_task: fn(DepNode) -> Option, + task: fn(Ctxt, A) -> R, + create_task: fn(DepNode) -> Option>, finish_task_and_alloc_depnode: fn( - &CurrentDepGraph, - DepNode, + &CurrentDepGraph, + DepNode, Fingerprint, - Option, + Option>, ) -> DepNodeIndex, - hash_result: impl FnOnce(&mut StableHashingContext<'_>, &R) -> Option, - ) -> (R, DepNodeIndex) - where - C: DepGraphSafe + StableHashingContextProvider<'a>, - { + 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(|deps| Lock::new(deps)); + let task_deps = create_task(key).map(Lock::new); // In incremental mode, hash the result of the task. We don't // do anything with the hash yet, but we are computing it // 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) } else { - ty::tls::with_context(|icx| { - let icx = - ty::tls::ImplicitCtxt { task_deps: task_deps.as_ref(), ..icx.clone() }; - - ty::tls::enter_context(&icx, |_| task(cx, arg)) - }) + K::with_deps(task_deps.as_ref(), || task(cx, arg)) }; let current_fingerprint = hash_result(&mut hcx, &result); @@ -274,7 +258,7 @@ impl DepGraph { task_deps.map(|lock| lock.into_inner()), ); - let print_status = cfg!(debug_assertions) && hcx.sess().opts.debugging_opts.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) { @@ -322,27 +306,16 @@ impl DepGraph { /// Executes something within an "anonymous" task, that is, a task the /// `DepNode` of which is determined by the list of inputs it read from. - pub fn with_anon_task(&self, dep_kind: DepKind, op: OP) -> (R, DepNodeIndex) + pub fn with_anon_task(&self, dep_kind: K, op: OP) -> (R, DepNodeIndex) where OP: FnOnce() -> R, { if let Some(ref data) = self.data { - let (result, task_deps) = ty::tls::with_context(|icx| { - let task_deps = Lock::new(TaskDeps { - #[cfg(debug_assertions)] - node: None, - reads: SmallVec::new(), - read_set: Default::default(), - }); + let task_deps = Lock::new(TaskDeps::default()); - let r = { - let icx = ty::tls::ImplicitCtxt { task_deps: Some(&task_deps), ..icx.clone() }; + let result = K::with_deps(Some(&task_deps), op); + let task_deps = task_deps.into_inner(); - ty::tls::enter_context(&icx, |_| op()) - }; - - (r, task_deps.into_inner()) - }); let dep_node_index = data.current.complete_anon_task(dep_kind, task_deps); (result, dep_node_index) } else { @@ -352,17 +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<'a, C, A, R>( + pub fn with_eval_always_task, A, R>( &self, - key: DepNode, - cx: C, + key: DepNode, + cx: Ctxt, arg: A, - task: fn(C, A) -> R, - hash_result: impl FnOnce(&mut StableHashingContext<'_>, &R) -> Option, - ) -> (R, DepNodeIndex) - where - C: DepGraphSafe + StableHashingContextProvider<'a>, - { + task: fn(Ctxt, A) -> R, + hash_result: impl FnOnce(&mut Ctxt::StableHashingContext, &R) -> Option, + ) -> (R, DepNodeIndex) { self.with_task_impl( key, cx, @@ -376,14 +346,14 @@ impl DepGraph { } #[inline] - pub fn read(&self, v: DepNode) { + pub fn read(&self, v: DepNode) { if let Some(ref data) = self.data { let map = data.current.node_to_node_index.get_shard_by_value(&v).lock(); if let Some(dep_node_index) = map.get(&v).copied() { std::mem::drop(map); data.read_index(dep_node_index); } else { - bug!("DepKind {:?} should be pre-allocated but isn't.", v.kind) + panic!("DepKind {:?} should be pre-allocated but isn't.", v.kind) } } } @@ -396,7 +366,7 @@ impl DepGraph { } #[inline] - pub fn dep_node_index_of(&self, dep_node: &DepNode) -> DepNodeIndex { + pub fn dep_node_index_of(&self, dep_node: &DepNode) -> DepNodeIndex { self.data .as_ref() .unwrap() @@ -410,7 +380,7 @@ impl DepGraph { } #[inline] - pub fn dep_node_exists(&self, dep_node: &DepNode) -> bool { + pub fn dep_node_exists(&self, dep_node: &DepNode) -> bool { if let Some(ref data) = self.data { data.current .node_to_node_index @@ -428,12 +398,12 @@ impl DepGraph { data[dep_node_index].fingerprint } - pub fn prev_fingerprint_of(&self, dep_node: &DepNode) -> Option { + pub fn prev_fingerprint_of(&self, dep_node: &DepNode) -> Option { self.data.as_ref().unwrap().previous.fingerprint_of(dep_node) } #[inline] - pub fn prev_dep_node_index_of(&self, dep_node: &DepNode) -> SerializedDepNodeIndex { + pub fn prev_dep_node_index_of(&self, dep_node: &DepNode) -> SerializedDepNodeIndex { self.data.as_ref().unwrap().previous.node_to_index(dep_node) } @@ -450,7 +420,7 @@ impl DepGraph { } #[inline(always)] - pub fn register_dep_node_debug_str(&self, dep_node: DepNode, debug_str_gen: F) + pub fn register_dep_node_debug_str(&self, dep_node: DepNode, debug_str_gen: F) where F: FnOnce() -> String, { @@ -463,7 +433,7 @@ impl DepGraph { dep_node_debug.borrow_mut().insert(dep_node, debug_str); } - pub(super) fn dep_node_debug_str(&self, dep_node: DepNode) -> Option { + pub fn dep_node_debug_str(&self, dep_node: DepNode) -> Option { self.data.as_ref()?.dep_node_debug.borrow().get(&dep_node).cloned() } @@ -480,7 +450,7 @@ impl DepGraph { } } - pub fn serialize(&self) -> SerializedDepGraph { + pub fn serialize(&self) -> SerializedDepGraph { let data = self.data.as_ref().unwrap().current.data.lock(); let fingerprints: IndexVec = @@ -508,7 +478,7 @@ impl DepGraph { SerializedDepGraph { nodes, fingerprints, edge_list_indices, edge_list_data } } - pub fn node_color(&self, dep_node: &DepNode) -> Option { + pub fn node_color(&self, dep_node: &DepNode) -> Option { if let Some(ref data) = self.data { if let Some(prev_index) = data.previous.node_to_index_opt(dep_node) { return data.colors.get(prev_index); @@ -526,10 +496,10 @@ impl DepGraph { /// A node will have an index, when it's already been marked green, or when we can mark it /// green. This function will mark the current task as a reader of the specified node, when /// a node index can be found for that node. - pub fn try_mark_green_and_read( + pub fn try_mark_green_and_read>( &self, - tcx: TyCtxt<'_>, - dep_node: &DepNode, + tcx: Ctxt, + dep_node: &DepNode, ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> { self.try_mark_green(tcx, dep_node).map(|(prev_index, dep_node_index)| { debug_assert!(self.is_green(&dep_node)); @@ -538,10 +508,10 @@ impl DepGraph { }) } - pub fn try_mark_green( + pub fn try_mark_green>( &self, - tcx: TyCtxt<'_>, - dep_node: &DepNode, + tcx: Ctxt, + dep_node: &DepNode, ) -> Option<(SerializedDepNodeIndex, DepNodeIndex)> { debug_assert!(!dep_node.kind.is_eval_always()); @@ -566,12 +536,12 @@ impl DepGraph { } /// Try to mark a dep-node which existed in the previous compilation session as green. - fn try_mark_previous_green<'tcx>( + fn try_mark_previous_green>( &self, - tcx: TyCtxt<'tcx>, - data: &DepGraphData, + tcx: Ctxt, + data: &DepGraphData, prev_dep_node_index: SerializedDepNodeIndex, - dep_node: &DepNode, + dep_node: &DepNode, ) -> Option { debug!("try_mark_previous_green({:?}) - BEGIN", dep_node); @@ -653,50 +623,6 @@ impl DepGraph { current_deps.push(node_index); continue; } - } else { - // 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_dep_node.kind { - DepKind::hir_owner - | DepKind::hir_owner_items - | DepKind::CrateMetadata => { - if let Some(def_id) = dep_dep_node.extract_def_id(tcx) { - if def_id_corresponds_to_hir_dep_node(tcx, def_id) { - if dep_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_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 None; - } - } else { - // If the node does not exist anymore, we - // just fail to mark green. - return None; - } - } - _ => { - // For other kinds of nodes it's OK to be - // forced. - } - } } // We failed to mark it green, so we try to force the query. @@ -705,7 +631,7 @@ impl DepGraph { dependency {:?}", dep_node, dep_dep_node ); - if crate::ty::query::force_from_dep_node(tcx, dep_dep_node) { + if tcx.try_force_from_dep_node(dep_dep_node) { let dep_dep_node_color = data.colors.get(dep_dep_node_index); match dep_dep_node_color { @@ -726,8 +652,8 @@ impl DepGraph { return None; } None => { - if !tcx.sess.has_errors_or_delayed_span_bugs() { - bug!( + if !tcx.has_errors_or_delayed_span_bugs() { + panic!( "try_mark_previous_green() - Forcing the DepNode \ should have set its color" ) @@ -784,7 +710,7 @@ impl DepGraph { // FIXME: Store the fact that a node has diagnostics in a bit in the dep graph somewhere // Maybe store a list on disk and encode this fact in the DepNodeState - let diagnostics = tcx.queries.on_disk_cache.load_diagnostics(tcx, prev_dep_node_index); + let diagnostics = tcx.load_diagnostics(prev_dep_node_index); #[cfg(not(parallel_compiler))] debug_assert!( @@ -810,10 +736,10 @@ impl DepGraph { /// This may be called concurrently on multiple threads for the same dep node. #[cold] #[inline(never)] - fn emit_diagnostics<'tcx>( + fn emit_diagnostics>( &self, - tcx: TyCtxt<'tcx>, - data: &DepGraphData, + tcx: Ctxt, + data: &DepGraphData, dep_node_index: DepNodeIndex, prev_dep_node_index: SerializedDepNodeIndex, diagnostics: Vec, @@ -832,9 +758,9 @@ impl DepGraph { mem::drop(emitting); // Promote the previous diagnostics to the current session. - tcx.queries.on_disk_cache.store_diagnostics(dep_node_index, diagnostics.clone().into()); + tcx.store_diagnostics(dep_node_index, diagnostics.clone().into()); - let handle = tcx.sess.diagnostic(); + let handle = tcx.diagnostic(); for diagnostic in diagnostics { handle.emit_diagnostic(&diagnostic); @@ -863,7 +789,7 @@ impl DepGraph { // Returns true if the given node has been marked as green during the // current compilation session. Used in various assertions - pub fn is_green(&self, dep_node: &DepNode) -> bool { + pub fn is_green(&self, dep_node: &DepNode) -> bool { self.node_color(dep_node).map(|c| c.is_green()).unwrap_or(false) } @@ -875,15 +801,15 @@ impl DepGraph { // // This method will only load queries that will end up in the disk cache. // Other queries will not be executed. - pub fn exec_cache_promotions(&self, tcx: TyCtxt<'_>) { - let _prof_timer = tcx.prof.generic_activity("incr_comp_query_cache_promotion"); + pub fn exec_cache_promotions>(&self, tcx: Ctxt) { + let _prof_timer = tcx.profiler().generic_activity("incr_comp_query_cache_promotion"); let data = self.data.as_ref().unwrap(); for prev_index in data.colors.values.indices() { match data.colors.get(prev_index) { Some(DepNodeColor::Green(_)) => { let dep_node = data.previous.index_to_node(prev_index); - dep_node.try_load_from_on_disk_cache(tcx); + tcx.try_load_from_on_disk_cache(&dep_node); } None | Some(DepNodeColor::Red) => { // We can skip red nodes because a node can only be marked @@ -900,11 +826,6 @@ impl DepGraph { } } -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 -} - /// A "work product" is an intermediate result that we save into the /// incremental directory for later re-use. The primary example are /// the object files that we save for each partition at code @@ -939,21 +860,14 @@ fn def_id_corresponds_to_hir_dep_node(tcx: TyCtxt<'_>, def_id: DefId) -> bool { #[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)] -struct DepNodeData { - node: DepNode, - edges: SmallVec<[DepNodeIndex; 8]>, +struct DepNodeData { + node: DepNode, + edges: EdgesVec, fingerprint: Fingerprint, } @@ -972,9 +886,9 @@ struct DepNodeData { /// The only operation that must manipulate both locks is adding new nodes, in which case /// we first acquire the `node_to_node_index` lock and then, once a new node is to be inserted, /// acquire the lock on `data.` -pub(super) struct CurrentDepGraph { - data: Lock>, - node_to_node_index: Sharded>, +pub(super) struct CurrentDepGraph { + data: Lock>>, + node_to_node_index: Sharded, DepNodeIndex>>, /// Used to trap when a specific edge is added to the graph. /// This is used for debug purposes and is only active with `debug_assertions`. @@ -1000,8 +914,8 @@ pub(super) struct CurrentDepGraph { total_duplicate_read_count: AtomicU64, } -impl CurrentDepGraph { - fn new(prev_graph_node_count: usize) -> CurrentDepGraph { +impl CurrentDepGraph { + fn new(prev_graph_node_count: usize) -> CurrentDepGraph { use std::time::{SystemTime, UNIX_EPOCH}; let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); @@ -1013,7 +927,7 @@ impl CurrentDepGraph { match env::var("RUST_FORBID_DEP_GRAPH_EDGE") { Ok(s) => match EdgeFilter::new(&s) { Ok(f) => Some(f), - Err(err) => bug!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err), + Err(err) => panic!("RUST_FORBID_DEP_GRAPH_EDGE invalid: {}", err), }, Err(_) => None, } @@ -1044,14 +958,14 @@ impl CurrentDepGraph { fn complete_task( &self, - node: DepNode, - task_deps: TaskDeps, + node: DepNode, + task_deps: TaskDeps, fingerprint: Fingerprint, ) -> DepNodeIndex { self.alloc_node(node, task_deps.reads, fingerprint) } - fn complete_anon_task(&self, kind: DepKind, task_deps: TaskDeps) -> DepNodeIndex { + fn complete_anon_task(&self, kind: K, task_deps: TaskDeps) -> DepNodeIndex { debug_assert!(!kind.is_eval_always()); let mut hasher = StableHasher::new(); @@ -1077,8 +991,8 @@ impl CurrentDepGraph { fn alloc_node( &self, - dep_node: DepNode, - edges: SmallVec<[DepNodeIndex; 8]>, + dep_node: DepNode, + edges: EdgesVec, fingerprint: Fingerprint, ) -> DepNodeIndex { debug_assert!( @@ -1089,8 +1003,8 @@ impl CurrentDepGraph { fn intern_node( &self, - dep_node: DepNode, - edges: SmallVec<[DepNodeIndex; 8]>, + dep_node: DepNode, + edges: EdgesVec, fingerprint: Fingerprint, ) -> DepNodeIndex { match self.node_to_node_index.get_shard_by_value(&dep_node).lock().entry(dep_node) { @@ -1106,18 +1020,31 @@ impl CurrentDepGraph { } } -impl DepGraphData { +impl DepGraphData { #[inline(never)] fn read_index(&self, source: DepNodeIndex) { - ty::tls::with_context_opt(|icx| { - let icx = if let Some(icx) = icx { icx } else { return }; - if let Some(task_deps) = icx.task_deps { + K::read_deps(|task_deps| { + if let Some(task_deps) = task_deps { let mut task_deps = task_deps.lock(); + let task_deps = &mut *task_deps; if cfg!(debug_assertions) { self.current.total_read_count.fetch_add(1, Relaxed); } - if task_deps.read_set.insert(source) { + + // As long as we only have a low number of reads we can avoid doing a hash + // insert and potentially allocating/reallocating the hashmap + let new_read = if task_deps.reads.len() < TASK_DEPS_READS_CAP { + task_deps.reads.iter().all(|other| *other != source) + } else { + task_deps.read_set.insert(source) + }; + if new_read { task_deps.reads.push(source); + if task_deps.reads.len() == TASK_DEPS_READS_CAP { + // Fill `read_set` with what we have so far so we can use the hashset next + // time + task_deps.read_set.extend(task_deps.reads.iter().copied()); + } #[cfg(debug_assertions)] { @@ -1126,7 +1053,7 @@ impl DepGraphData { if let Some(ref forbidden_edge) = self.current.forbidden_edge { let source = data[source].node; if forbidden_edge.test(&source, &target) { - bug!("forbidden edge {:?} -> {:?} created", source, target) + panic!("forbidden edge {:?} -> {:?} created", source, target) } } } @@ -1139,11 +1066,28 @@ impl DepGraphData { } } -pub struct TaskDeps { +/// The capacity of the `reads` field `SmallVec` +const TASK_DEPS_READS_CAP: usize = 8; +type EdgesVec = SmallVec<[DepNodeIndex; TASK_DEPS_READS_CAP]>; + +pub struct TaskDeps { #[cfg(debug_assertions)] - node: Option, - reads: SmallVec<[DepNodeIndex; 8]>, + node: Option>, + reads: EdgesVec, read_set: FxHashSet, + phantom_data: PhantomData>, +} + +impl Default for TaskDeps { + fn default() -> Self { + Self { + #[cfg(debug_assertions)] + node: None, + reads: EdgesVec::new(), + read_set: FxHashSet::default(), + phantom_data: PhantomData, + } + } } // A data structure that stores Option values as a contiguous @@ -1161,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 new file mode 100644 index 0000000000000..e8d02692f37ba --- /dev/null +++ b/src/librustc_query_system/dep_graph/mod.rs @@ -0,0 +1,85 @@ +pub mod debug; +mod dep_node; +mod graph; +mod prev; +mod query; +mod serialized; + +pub use dep_node::{DepNode, DepNodeParams, WorkProductId}; +pub use graph::{hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, WorkProduct}; +pub use prev::PreviousDepGraph; +pub use query::DepGraphQuery; +pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex}; + +use rustc_data_structures::profiling::SelfProfilerRef; +use rustc_data_structures::sync::Lock; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_errors::Diagnostic; + +use std::fmt; +use std::hash::Hash; + +pub trait DepContext: Copy { + type DepKind: self::DepKind; + 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; + + /// Return whether the current session is tainted by errors. + fn has_errors_or_delayed_span_bugs(&self) -> bool; + + /// Return the diagnostic handler. + fn diagnostic(&self) -> &rustc_errors::Handler; + + /// Load data from the on-disk cache. + fn try_load_from_on_disk_cache(&self, dep_node: &DepNode); + + /// Load diagnostics associated to the node in the previous session. + fn load_diagnostics(&self, prev_dep_node_index: SerializedDepNodeIndex) -> Vec; + + /// 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; + + /// Return whether this kind requires additional parameters to be executed. + fn has_params(&self) -> bool; + + /// Implementation of `std::fmt::Debug` for `DepNode`. + fn debug_node(node: &DepNode, f: &mut fmt::Formatter<'_>) -> fmt::Result; + + /// Execute the operation with provided dependencies. + fn with_deps(deps: Option<&Lock>>, op: OP) -> R + where + OP: FnOnce() -> R; + + /// Access dependencies from current implicit context. + fn read_deps(op: OP) + where + OP: for<'a> FnOnce(Option<&'a Lock>>); + + fn can_reconstruct_query_key(&self) -> bool; +} diff --git a/src/librustc_query_system/dep_graph/prev.rs b/src/librustc_query_system/dep_graph/prev.rs new file mode 100644 index 0000000000000..5cba64cac4b34 --- /dev/null +++ b/src/librustc_query_system/dep_graph/prev.rs @@ -0,0 +1,61 @@ +use super::serialized::{SerializedDepGraph, SerializedDepNodeIndex}; +use super::{DepKind, DepNode}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; + +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct PreviousDepGraph { + data: SerializedDepGraph, + index: FxHashMap, SerializedDepNodeIndex>, +} + +impl Default for PreviousDepGraph { + fn default() -> Self { + PreviousDepGraph { data: Default::default(), index: Default::default() } + } +} + +impl PreviousDepGraph { + pub fn new(data: SerializedDepGraph) -> PreviousDepGraph { + let index: FxHashMap<_, _> = + data.nodes.iter_enumerated().map(|(idx, &dep_node)| (dep_node, idx)).collect(); + PreviousDepGraph { data, index } + } + + #[inline] + pub fn edge_targets_from( + &self, + dep_node_index: SerializedDepNodeIndex, + ) -> &[SerializedDepNodeIndex] { + self.data.edge_targets_from(dep_node_index) + } + + #[inline] + pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> DepNode { + self.data.nodes[dep_node_index] + } + + #[inline] + pub fn node_to_index(&self, dep_node: &DepNode) -> SerializedDepNodeIndex { + self.index[dep_node] + } + + #[inline] + pub fn node_to_index_opt(&self, dep_node: &DepNode) -> Option { + self.index.get(dep_node).cloned() + } + + #[inline] + pub fn fingerprint_of(&self, dep_node: &DepNode) -> Option { + self.index.get(dep_node).map(|&node_index| self.data.fingerprints[node_index]) + } + + #[inline] + pub fn fingerprint_by_index(&self, dep_node_index: SerializedDepNodeIndex) -> Fingerprint { + self.data.fingerprints[dep_node_index] + } + + pub fn node_count(&self) -> usize { + self.index.len() + } +} diff --git a/src/librustc_query_system/dep_graph/query.rs b/src/librustc_query_system/dep_graph/query.rs new file mode 100644 index 0000000000000..fb313d2658ff4 --- /dev/null +++ b/src/librustc_query_system/dep_graph/query.rs @@ -0,0 +1,74 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::graph::implementation::{ + Direction, Graph, NodeIndex, INCOMING, OUTGOING, +}; + +use super::{DepKind, DepNode}; + +pub struct DepGraphQuery { + pub graph: Graph, ()>, + pub indices: FxHashMap, NodeIndex>, +} + +impl DepGraphQuery { + pub fn new(nodes: &[DepNode], edges: &[(DepNode, DepNode)]) -> DepGraphQuery { + let mut graph = Graph::with_capacity(nodes.len(), edges.len()); + let mut indices = FxHashMap::default(); + for node in nodes { + indices.insert(*node, graph.add_node(*node)); + } + + for &(ref source, ref target) in edges { + let source = indices[source]; + let target = indices[target]; + graph.add_edge(source, target, ()); + } + + DepGraphQuery { graph, indices } + } + + pub fn contains_node(&self, node: &DepNode) -> bool { + self.indices.contains_key(&node) + } + + pub fn nodes(&self) -> Vec<&DepNode> { + self.graph.all_nodes().iter().map(|n| &n.data).collect() + } + + pub fn edges(&self) -> Vec<(&DepNode, &DepNode)> { + self.graph + .all_edges() + .iter() + .map(|edge| (edge.source(), edge.target())) + .map(|(s, t)| (self.graph.node_data(s), self.graph.node_data(t))) + .collect() + } + + fn reachable_nodes(&self, node: &DepNode, direction: Direction) -> Vec<&DepNode> { + if let Some(&index) = self.indices.get(node) { + self.graph.depth_traverse(index, direction).map(|s| self.graph.node_data(s)).collect() + } else { + vec![] + } + } + + /// All nodes reachable from `node`. In other words, things that + /// will have to be recomputed if `node` changes. + pub fn transitive_successors(&self, node: &DepNode) -> Vec<&DepNode> { + self.reachable_nodes(node, OUTGOING) + } + + /// All nodes that can reach `node`. + pub fn transitive_predecessors(&self, node: &DepNode) -> Vec<&DepNode> { + self.reachable_nodes(node, INCOMING) + } + + /// Just the outgoing edges from `node`. + pub fn immediate_successors(&self, node: &DepNode) -> Vec<&DepNode> { + if let Some(&index) = self.indices.get(&node) { + self.graph.successor_nodes(index).map(|s| self.graph.node_data(s)).collect() + } else { + vec![] + } + } +} diff --git a/src/librustc_query_system/dep_graph/serialized.rs b/src/librustc_query_system/dep_graph/serialized.rs new file mode 100644 index 0000000000000..4a89da23ea6a5 --- /dev/null +++ b/src/librustc_query_system/dep_graph/serialized.rs @@ -0,0 +1,45 @@ +//! The data that we will serialize and deserialize. + +use super::{DepKind, DepNode}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_index::vec::IndexVec; + +rustc_index::newtype_index! { + pub struct SerializedDepNodeIndex { .. } +} + +/// Data for use when recompiling the **current crate**. +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct SerializedDepGraph { + /// The set of all DepNodes in the graph + pub nodes: IndexVec>, + /// The set of all Fingerprints in the graph. Each Fingerprint corresponds to + /// the DepNode at the same index in the nodes vector. + pub fingerprints: IndexVec, + /// For each DepNode, stores the list of edges originating from that + /// DepNode. Encoded as a [start, end) pair indexing into edge_list_data, + /// which holds the actual DepNodeIndices of the target nodes. + pub edge_list_indices: IndexVec, + /// A flattened list of all edge targets in the graph. Edge sources are + /// implicit in edge_list_indices. + pub edge_list_data: Vec, +} + +impl Default for SerializedDepGraph { + fn default() -> Self { + SerializedDepGraph { + nodes: Default::default(), + fingerprints: Default::default(), + edge_list_indices: Default::default(), + edge_list_data: Default::default(), + } + } +} + +impl SerializedDepGraph { + #[inline] + pub fn edge_targets_from(&self, source: SerializedDepNodeIndex) -> &[SerializedDepNodeIndex] { + let targets = self.edge_list_indices[source]; + &self.edge_list_data[targets.0 as usize..targets.1 as usize] + } +} diff --git a/src/librustc_query_system/lib.rs b/src/librustc_query_system/lib.rs new file mode 100644 index 0000000000000..3afc4565933e5 --- /dev/null +++ b/src/librustc_query_system/lib.rs @@ -0,0 +1,16 @@ +#![feature(bool_to_option)] +#![feature(const_fn)] +#![feature(const_if_match)] +#![feature(const_panic)] +#![feature(core_intrinsics)] +#![feature(hash_raw_entry)] +#![feature(min_specialization)] +#![feature(stmt_expr_attributes)] + +#[macro_use] +extern crate log; +#[macro_use] +extern crate rustc_data_structures; + +pub mod dep_graph; +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..f0beec0a17726 --- /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 rustc_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..549056570f9bc --- /dev/null +++ b/src/librustc_query_system/query/config.rs @@ -0,0 +1,133 @@ +//! 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_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, + }; +} diff --git a/src/librustc_query_system/query/job.rs b/src/librustc_query_system/query/job.rs new file mode 100644 index 0000000000000..190312bb33001 --- /dev/null +++ b/src/librustc_query_system/query/job.rs @@ -0,0 +1,570 @@ +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 { + if let Some(pos) = jobs.iter().position(|j| j == &r.1) { + jobs.remove(pos); + } + } + + // 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..6f6104c3d6932 100644 --- a/src/librustc_resolve/Cargo.toml +++ b/src/librustc_resolve/Cargo.toml @@ -14,8 +14,8 @@ doctest = false bitflags = "1.2.1" log = "0.4" rustc_ast = { path = "../librustc_ast" } -arena = { path = "../libarena" } -rustc = { path = "../librustc" } +rustc_arena = { path = "../librustc_arena" } +rustc_middle = { path = "../librustc_middle" } rustc_ast_lowering = { path = "../librustc_ast_lowering" } rustc_ast_pretty = { path = "../librustc_ast_pretty" } rustc_attr = { path = "../librustc_attr" } @@ -24,6 +24,7 @@ rustc_errors = { path = "../librustc_errors" } rustc_expand = { path = "../librustc_expand" } rustc_feature = { path = "../librustc_feature" } rustc_hir = { path = "../librustc_hir" } +rustc_index = { path = "../librustc_index" } rustc_metadata = { path = "../librustc_metadata" } rustc_session = { path = "../librustc_session" } rustc_span = { path = "../librustc_span" } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 0c6edd3e47d89..ef43f597eab47 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -7,7 +7,7 @@ use crate::def_collector::collect_definitions; use crate::imports::{Import, ImportKind}; -use crate::macros::{LegacyBinding, LegacyScope}; +use crate::macros::{MacroRulesBinding, MacroRulesScope}; use crate::Namespace::{self, MacroNS, TypeNS, ValueNS}; use crate::{CrateLint, Determinacy, PathResult, ResolutionError, VisResolutionError}; use crate::{ @@ -15,26 +15,26 @@ 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_ast_lowering::Resolver as ResolverAstLowering; use rustc_attr as attr; use rustc_data_structures::sync::Lrc; 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, LocalDefId, 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,24 +96,30 @@ impl<'a> Resolver<'a> { } crate fn get_module(&mut self, def_id: DefId) -> Module<'a> { - if def_id.krate == LOCAL_CRATE { + // If this is a local module, it will be in `module_map`, no need to recalculate it. + if let Some(def_id) = def_id.as_local() { return self.module_map[&def_id]; } + // Cache module resolution if let Some(&module) = self.extern_module_map.get(&def_id) { return module; } let (name, parent) = if def_id.index == CRATE_DEF_INDEX { + // This is the crate root (self.cstore().crate_name_untracked(def_id.krate), None) } else { let def_key = self.cstore().def_key(def_id); ( + // This unwrap is safe: crates must always have a name def_key.disambiguated_data.data.get_opt_name().unwrap(), + // This unwrap is safe since we know this isn't the root Some(self.get_module(DefId { index: def_key.parent.unwrap(), ..def_id })), ) }; + // Allocate and return a new module with the information we found let kind = ModuleKind::Def(DefKind::Mod, def_id, name); let module = self.arenas.alloc_module(ModuleData::new( parent, @@ -127,11 +133,11 @@ impl<'a> Resolver<'a> { } crate fn macro_def_scope(&mut self, expn_id: ExpnId) -> Module<'a> { - let def_id = match self.macro_defs.get(&expn_id) { - Some(def_id) => *def_id, + let def_id = match expn_id.expn_data().macro_def_id { + Some(def_id) => def_id, None => return self.ast_transform_scopes.get(&expn_id).unwrap_or(&self.graph_root), }; - if let Some(id) = self.definitions.as_local_node_id(def_id) { + if let Some(id) = def_id.as_local() { self.local_macro_def_scopes[&id] } else { let module_def_id = ty::DefIdTree::parent(&*self, def_id).unwrap(); @@ -165,11 +171,11 @@ impl<'a> Resolver<'a> { &mut self, fragment: &AstFragment, parent_scope: ParentScope<'a>, - ) -> LegacyScope<'a> { - collect_definitions(&mut self.definitions, fragment, parent_scope.expansion); + ) -> MacroRulesScope<'a> { + collect_definitions(self, fragment, parent_scope.expansion); let mut visitor = BuildReducedGraphVisitor { r: self, parent_scope }; fragment.visit_with(&mut visitor); - visitor.parent_scope.legacy + visitor.parent_scope.macro_rules } crate fn build_reduced_graph_external(&mut self, module: Module<'a>) { @@ -293,7 +299,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 +433,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 +442,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;` @@ -457,6 +486,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { module_path.push(Segment { ident: Ident { name: kw::PathRoot, span: source.ident.span }, id: Some(self.r.next_node_id()), + has_generic_args: false, }); source.ident.name = crate_name; } @@ -618,13 +648,14 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } else if orig_name == Some(kw::SelfLower) { self.r.graph_root } else { + let def_id = self.r.local_def_id(item.id); let crate_id = - self.r.crate_loader.process_extern_crate(item, &self.r.definitions); - self.r.extern_crate_map.insert(item.id, crate_id); + self.r.crate_loader.process_extern_crate(item, &self.r.definitions, def_id); + self.r.extern_crate_map.insert(def_id, crate_id); self.r.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }) }; - let used = self.process_legacy_macro_imports(item, module); + let used = self.process_macro_use_imports(item, module); let binding = (module, ty::Visibility::Public, sp, expansion).to_name_binding(self.r.arenas); let import = self.r.arenas.alloc_import(Import { @@ -645,7 +676,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.r.potentially_unused_imports.push(import); let imported_binding = self.r.import(binding, import); if ptr::eq(parent, self.r.graph_root) { - if let Some(entry) = self.r.extern_prelude.get(&ident.modern()) { + if let Some(entry) = self.r.extern_prelude.get(&ident.normalize_to_macros_2_0()) + { if expansion != ExpnId::root() && orig_name.is_some() && entry.extern_crate_item.is_none() @@ -656,10 +688,12 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } } let entry = - self.r.extern_prelude.entry(ident.modern()).or_insert(ExternPreludeEntry { - extern_crate_item: None, - introduced_by_item: true, - }); + self.r.extern_prelude.entry(ident.normalize_to_macros_2_0()).or_insert( + ExternPreludeEntry { + extern_crate_item: None, + introduced_by_item: true, + }, + ); entry.extern_crate_item = Some(imported_binding); if orig_name.is_some() { entry.introduced_by_item = true; @@ -671,13 +705,19 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { ItemKind::Mod(..) if ident.name == kw::Invalid => {} // Crate root 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 def_id = self.r.local_def_id(item.id); + 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); @@ -688,15 +728,15 @@ 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.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.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.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 @@ -705,17 +745,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } // These items live in the type namespace. - ItemKind::TyAlias(_, _, _, ref ty) => { - let def_kind = match ty.as_deref().and_then(|ty| ty.kind.opaque_top_hack()) { - None => DefKind::TyAlias, - Some(_) => DefKind::OpaqueTy, - }; - let res = Res::Def(def_kind, self.r.definitions.local_def_id(item.id)); + ItemKind::TyAlias(..) => { + let res = Res::Def(DefKind::TyAlias, self.r.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.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( @@ -730,14 +766,14 @@ 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.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.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)); @@ -747,14 +783,16 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // If this is a tuple or unit struct, define a name // in the value namespace as well. if let Some(ctor_node_id) = vdata.ctor_id() { - let mut ctor_vis = vis; // If the structure is marked as non_exhaustive then lower the visibility // to within the crate. - if vis == ty::Visibility::Public + let mut ctor_vis = if vis == ty::Visibility::Public && attr::contains_name(&item.attrs, sym::non_exhaustive) { - ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); - } + ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)) + } else { + vis + }; + for field in vdata.fields() { // NOTE: The field may be an expansion placeholder, but expansion sets // correct visibilities for unnamed field placeholders specifically, so the @@ -768,7 +806,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.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)); @@ -776,7 +814,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.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)); @@ -785,7 +823,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.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); @@ -811,13 +849,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { 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) + (Res::Def(DefKind::Fn, self.r.local_def_id(item.id).to_def_id()), ValueNS) } ForeignItemKind::Static(..) => { - (Res::Def(DefKind::Static, self.r.definitions.local_def_id(item.id)), ValueNS) + (Res::Def(DefKind::Static, self.r.local_def_id(item.id).to_def_id()), ValueNS) } ForeignItemKind::TyAlias(..) => { - (Res::Def(DefKind::ForeignTy, self.r.definitions.local_def_id(item.id)), TypeNS) + (Res::Def(DefKind::ForeignTy, self.r.local_def_id(item.id).to_def_id()), TypeNS) } ForeignItemKind::MacCall(_) => unreachable!(), }; @@ -850,9 +888,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), @@ -862,30 +898,46 @@ 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, + _, + ) | 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(..) @@ -894,12 +946,15 @@ 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).method_has_self_argument { + if cstore + .associated_item_cloned_untracked(def_id, self.r.session) + .fn_has_self_parameter + { self.r.has_self.insert(def_id); } } @@ -913,9 +968,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } } - fn legacy_import_macro( + fn add_macro_use_binding( &mut self, - name: ast::Name, + name: Symbol, binding: &'a NameBinding<'a>, span: Span, allow_shadowing: bool, @@ -929,7 +984,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } /// Returns `true` if we should consider the underlying `extern crate` to be used. - fn process_legacy_macro_imports(&mut self, item: &Item, module: Module<'a>) -> bool { + fn process_macro_use_imports(&mut self, item: &Item, module: Module<'a>) -> bool { let mut import_all = None; let mut single_imports = Vec::new(); for attr in &item.attrs { @@ -1004,7 +1059,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { module.for_each_child(self, |this, ident, ns, binding| { if ns == MacroNS { let imported_binding = this.r.import(binding, import); - this.legacy_import_macro(ident.name, imported_binding, span, allow_shadowing); + this.add_macro_use_binding(ident.name, imported_binding, span, allow_shadowing); } }); } else { @@ -1021,7 +1076,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let import = macro_use_import(self, ident.span); self.r.potentially_unused_imports.push(import); let imported_binding = self.r.import(binding, import); - self.legacy_import_macro( + self.add_macro_use_binding( ident.name, imported_binding, ident.span, @@ -1060,7 +1115,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { false } - fn visit_invoc(&mut self, id: NodeId) -> LegacyScope<'a> { + fn visit_invoc(&mut self, id: NodeId) -> MacroRulesScope<'a> { let invoc_id = id.placeholder_to_expn_id(); self.parent_scope.module.unexpanded_invocations.borrow_mut().insert(invoc_id); @@ -1068,7 +1123,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let old_parent_scope = self.r.invocation_parent_scopes.insert(invoc_id, self.parent_scope); assert!(old_parent_scope.is_none(), "invocation data is reset for an invocation"); - LegacyScope::Invocation(invoc_id) + MacroRulesScope::Invocation(invoc_id) } fn proc_macro_stub(item: &ast::Item) -> Option<(MacroKind, Ident, Span)> { @@ -1089,37 +1144,43 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // Mark the given macro as unused unless its name starts with `_`. // Macro uses will remove items from this set, and the remaining // items will be reported as `unused_macros`. - fn insert_unused_macro(&mut self, ident: Ident, node_id: NodeId, span: Span) { + fn insert_unused_macro( + &mut self, + ident: Ident, + def_id: LocalDefId, + node_id: NodeId, + span: Span, + ) { if !ident.as_str().starts_with('_') { - self.r.unused_macros.insert(node_id, span); + self.r.unused_macros.insert(def_id, (node_id, span)); } } - fn define_macro(&mut self, item: &ast::Item) -> LegacyScope<'a> { + fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScope<'a> { let parent_scope = self.parent_scope; let expansion = parent_scope.expansion; - let (ext, ident, span, is_legacy) = match &item.kind { + let def_id = self.r.local_def_id(item.id); + let (ext, ident, span, macro_rules) = match &item.kind { ItemKind::MacroDef(def) => { let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition())); - (ext, item.ident, item.span, def.legacy) + (ext, item.ident, item.span, def.macro_rules) } ItemKind::Fn(..) => match Self::proc_macro_stub(item) { Some((macro_kind, ident, span)) => { - self.r.proc_macro_stubs.insert(item.id); + self.r.proc_macro_stubs.insert(def_id); (self.r.dummy_ext(macro_kind), ident, span, false) } - None => return parent_scope.legacy, + None => return parent_scope.macro_rules, }, _ => unreachable!(), }; - let def_id = self.r.definitions.local_def_id(item.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); + let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id.to_def_id()); + self.r.macro_map.insert(def_id.to_def_id(), ext); + self.r.local_macro_def_scopes.insert(def_id, parent_scope.module); - if is_legacy { - let ident = ident.modern(); + if macro_rules { + let ident = ident.normalize_to_macros_2_0(); self.r.macro_names.insert(ident); let is_macro_export = attr::contains_name(&item.attrs, sym::macro_export); let vis = if is_macro_export { @@ -1135,21 +1196,28 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport)); } else { self.r.check_reserved_macro_name(ident, res); - self.insert_unused_macro(ident, item.id, span); + self.insert_unused_macro(ident, def_id, item.id, span); } - LegacyScope::Binding(self.r.arenas.alloc_legacy_binding(LegacyBinding { - parent_legacy_scope: parent_scope.legacy, + MacroRulesScope::Binding(self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding { + parent_macro_rules_scope: parent_scope.macro_rules, binding, ident, })) } else { let module = parent_scope.module; - let vis = self.resolve_visibility(&item.vis); + let vis = match item.kind { + // Visibilities must not be resolved non-speculatively twice + // and we already resolved this one as a `fn` item visibility. + ItemKind::Fn(..) => self + .resolve_visibility_speculative(&item.vis, true) + .unwrap_or(ty::Visibility::Public), + _ => self.resolve_visibility(&item.vis), + }; if vis != ty::Visibility::Public { - self.insert_unused_macro(ident, item.id, span); + self.insert_unused_macro(ident, def_id, item.id, span); } self.r.define(module, ident, MacroNS, (res, vis, span, expansion)); - self.parent_scope.legacy + self.parent_scope.macro_rules } } } @@ -1163,7 +1231,7 @@ macro_rules! method { visit::$walk(self, node); } } - } + }; } impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { @@ -1174,29 +1242,29 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { fn visit_item(&mut self, item: &'b Item) { let macro_use = match item.kind { ItemKind::MacroDef(..) => { - self.parent_scope.legacy = self.define_macro(item); + self.parent_scope.macro_rules = self.define_macro(item); return; } ItemKind::MacCall(..) => { - self.parent_scope.legacy = self.visit_invoc(item.id); + self.parent_scope.macro_rules = self.visit_invoc(item.id); return; } ItemKind::Mod(..) => self.contains_macro_use(&item.attrs), _ => false, }; let orig_current_module = self.parent_scope.module; - let orig_current_legacy_scope = self.parent_scope.legacy; + let orig_current_macro_rules_scope = self.parent_scope.macro_rules; self.build_reduced_graph_for_item(item); visit::walk_item(self, item); self.parent_scope.module = orig_current_module; if !macro_use { - self.parent_scope.legacy = orig_current_legacy_scope; + self.parent_scope.macro_rules = orig_current_macro_rules_scope; } } fn visit_stmt(&mut self, stmt: &'b ast::Stmt) { if let ast::StmtKind::MacCall(..) = stmt.kind { - self.parent_scope.legacy = self.visit_invoc(stmt.id); + self.parent_scope.macro_rules = self.visit_invoc(stmt.id); } else { visit::walk_stmt(self, stmt); } @@ -1214,11 +1282,11 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { fn visit_block(&mut self, block: &'b Block) { let orig_current_module = self.parent_scope.module; - let orig_current_legacy_scope = self.parent_scope.legacy; + let orig_current_macro_rules_scope = self.parent_scope.macro_rules; self.build_reduced_graph_for_block(block); visit::walk_block(self, block); self.parent_scope.module = orig_current_module; - self.parent_scope.legacy = orig_current_legacy_scope; + self.parent_scope.macro_rules = orig_current_macro_rules_scope; } fn visit_assoc_item(&mut self, item: &'b AssocItem, ctxt: AssocCtxt) { @@ -1236,7 +1304,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.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, _, _) => { @@ -1338,7 +1406,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.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)); @@ -1356,7 +1424,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.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 722f843ab6e7e..0ca01a384e73e 100644 --- a/src/librustc_resolve/check_unused.rs +++ b/src/librustc_resolve/check_unused.rs @@ -26,12 +26,14 @@ use crate::imports::ImportKind; use crate::Resolver; -use rustc::{lint, ty}; use rustc_ast::ast; use rustc_ast::node_id::NodeMap; use rustc_ast::visit::{self, Visitor}; +use rustc_ast_lowering::Resolver as ResolverAstLowering; 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}; @@ -63,8 +65,9 @@ impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> { fn check_import(&mut self, id: ast::NodeId) { let mut used = false; self.r.per_ns(|this, ns| used |= this.used_imports.contains(&(id, ns))); + let def_id = self.r.local_def_id(id); if !used { - if self.r.maybe_unused_trait_imports.contains(&id) { + if self.r.maybe_unused_trait_imports.contains(&def_id) { // Check later. return; } @@ -72,7 +75,7 @@ impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> { } else { // This trait import is definitely used, in a way other than // method resolution. - self.r.maybe_unused_trait_imports.remove(&id); + self.r.maybe_unused_trait_imports.remove(&def_id); if let Some(i) = self.unused_imports.get_mut(&self.base_id) { i.unused.remove(&id); } @@ -232,7 +235,7 @@ impl Resolver<'_> { if let ImportKind::MacroUse = import.kind { if !import.span.is_dummy() { self.lint_buffer.buffer_lint( - lint::builtin::MACRO_USE_EXTERN_CRATE, + MACRO_USE_EXTERN_CRATE, import.id, import.span, "deprecated `#[macro_use]` attribute used to \ @@ -244,12 +247,12 @@ impl Resolver<'_> { } } ImportKind::ExternCrate { .. } => { - self.maybe_unused_extern_crates.push((import.id, import.span)); + let def_id = self.local_def_id(import.id); + self.maybe_unused_extern_crates.push((def_id, import.span)); } ImportKind::MacroUse => { - let lint = lint::builtin::UNUSED_IMPORTS; let msg = "unused `#[macro_use]` import"; - self.lint_buffer.buffer_lint(lint, import.id, import.span, msg); + self.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.id, import.span, msg); } _ => {} } @@ -314,7 +317,7 @@ impl Resolver<'_> { }; visitor.r.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNUSED_IMPORTS, + UNUSED_IMPORTS, unused.use_tree_id, ms, &msg, diff --git a/src/librustc_resolve/def_collector.rs b/src/librustc_resolve/def_collector.rs index 16359cc743767..f1063f42c91ec 100644 --- a/src/librustc_resolve/def_collector.rs +++ b/src/librustc_resolve/def_collector.rs @@ -1,38 +1,41 @@ +use crate::Resolver; use log::debug; -use rustc::hir::map::definitions::*; use rustc_ast::ast::*; use rustc_ast::token::{self, Token}; use rustc_ast::visit::{self, FnKind}; +use rustc_ast::walk_list; +use rustc_ast_lowering::Resolver as ResolverAstLowering; use rustc_expand::expand::AstFragment; -use rustc_hir::def_id::DefIndex; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::definitions::*; use rustc_span::hygiene::ExpnId; use rustc_span::symbol::{kw, sym}; use rustc_span::Span; crate fn collect_definitions( - definitions: &mut Definitions, + resolver: &mut Resolver<'_>, fragment: &AstFragment, expansion: ExpnId, ) { - let parent_def = definitions.invocation_parent(expansion); - fragment.visit_with(&mut DefCollector { definitions, parent_def, expansion }); + let parent_def = resolver.invocation_parents[&expansion]; + fragment.visit_with(&mut DefCollector { resolver, parent_def, expansion }); } /// Creates `DefId`s for nodes in the AST. -struct DefCollector<'a> { - definitions: &'a mut Definitions, - parent_def: DefIndex, +struct DefCollector<'a, 'b> { + resolver: &'a mut Resolver<'b>, + parent_def: LocalDefId, expansion: ExpnId, } -impl<'a> DefCollector<'a> { - fn create_def(&mut self, node_id: NodeId, data: DefPathData, span: Span) -> DefIndex { +impl<'a, 'b> DefCollector<'a, 'b> { + fn create_def(&mut self, node_id: NodeId, data: DefPathData, span: Span) -> LocalDefId { let parent_def = self.parent_def; debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def); - self.definitions.create_def_with_parent(parent_def, node_id, data, self.expansion, span) + self.resolver.create_def(parent_def, node_id, data, self.expansion, span) } - fn with_parent(&mut self, parent_def: DefIndex, f: F) { + fn with_parent(&mut self, parent_def: LocalDefId, f: F) { let orig_parent_def = std::mem::replace(&mut self.parent_def, parent_def); f(self); self.parent_def = orig_parent_def; @@ -42,12 +45,13 @@ impl<'a> DefCollector<'a> { let index = |this: &Self| { index.unwrap_or_else(|| { let node_id = NodeId::placeholder_from_expn_id(this.expansion); - this.definitions.placeholder_field_index(node_id) + this.resolver.placeholder_field_indices[&node_id] }) }; if field.is_placeholder { - self.definitions.set_placeholder_field_index(field.id, index(self)); + let old_index = self.resolver.placeholder_field_indices.insert(field.id, index(self)); + assert!(old_index.is_none(), "placeholder field index is reset for a node ID"); self.visit_macro_invoc(field.id); } else { let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name); @@ -57,11 +61,13 @@ impl<'a> DefCollector<'a> { } fn visit_macro_invoc(&mut self, id: NodeId) { - self.definitions.set_invocation_parent(id.placeholder_to_expn_id(), self.parent_def); + let old_parent = + self.resolver.invocation_parents.insert(id.placeholder_to_expn_id(), self.parent_def); + assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation"); } } -impl<'a> visit::Visitor<'a> for DefCollector<'a> { +impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_item(&mut self, i: &'a Item) { debug!("visit_item: {:?}", i); @@ -117,10 +123,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; } } @@ -200,7 +204,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { fn visit_pat(&mut self, pat: &'a Pat) { match pat.kind { - PatKind::MacCall(..) => return self.visit_macro_invoc(pat.id), + PatKind::MacCall(..) => self.visit_macro_invoc(pat.id), _ => visit::walk_pat(self, pat), } } diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 47a05ec90d42f..2854683b61bab 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -1,10 +1,8 @@ use std::cmp::Reverse; +use std::ptr; use log::debug; -use rustc::bug; -use rustc::session::Session; -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,15 +11,20 @@ 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}; use crate::path_names_to_string; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; -use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot}; +use crate::{ + BindingError, CrateLint, HasGenericParams, MacroRulesScope, Module, ModuleOrUniformRoot, +}; use crate::{NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet, Segment}; @@ -44,7 +47,9 @@ 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, + pub accessible: bool, } /// Adjust the impl span so that just the `impl` keyword is taken by removing @@ -56,8 +61,7 @@ crate struct ImportSuggestion { /// `source_map` functions and this function to something more robust. fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { let impl_span = sm.span_until_char(impl_span, '<'); - let impl_span = sm.span_until_whitespace(impl_span); - impl_span + sm.span_until_whitespace(impl_span) } impl<'a> Resolver<'a> { @@ -104,7 +108,7 @@ impl<'a> Resolver<'a> { match outer_res { Res::SelfTy(maybe_trait_defid, maybe_impl_defid) => { if let Some(impl_span) = - maybe_impl_defid.and_then(|def_id| self.definitions.opt_span(def_id)) + maybe_impl_defid.and_then(|def_id| self.opt_span(def_id)) { err.span_label( reduce_impl_span_to_impl_keyword(sm, impl_span), @@ -123,12 +127,12 @@ impl<'a> Resolver<'a> { return err; } Res::Def(DefKind::TyParam, def_id) => { - if let Some(span) = self.definitions.opt_span(def_id) { + if let Some(span) = self.opt_span(def_id) { err.span_label(span, "type parameter from outer function"); } } Res::Def(DefKind::ConstParam, def_id) => { - if let Some(span) = self.definitions.opt_span(def_id) { + if let Some(span) = self.opt_span(def_id) { err.span_label(span, "const parameter from outer function"); } } @@ -298,13 +302,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, @@ -498,12 +529,12 @@ impl<'a> Resolver<'a> { } } } - Scope::MacroRules(legacy_scope) => { - if let LegacyScope::Binding(legacy_binding) = legacy_scope { - let res = legacy_binding.binding.res(); + Scope::MacroRules(macro_rules_scope) => { + if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope { + let res = macro_rules_binding.binding.res(); if filter_fn(res) { suggestions - .push(TypoSuggestion::from_res(legacy_binding.ident.name, res)) + .push(TypoSuggestion::from_res(macro_rules_binding.ident.name, res)) } } } @@ -599,6 +630,7 @@ impl<'a> Resolver<'a> { &mut self, lookup_ident: Ident, namespace: Namespace, + parent_scope: &ParentScope<'a>, start_module: Module<'a>, crate_name: Ident, filter_fn: FilterFn, @@ -609,23 +641,49 @@ impl<'a> Resolver<'a> { let mut candidates = Vec::new(); let mut seen_modules = FxHashSet::default(); let not_local_module = crate_name.name != kw::Crate; - let mut worklist = vec![(start_module, Vec::::new(), not_local_module)]; - - while let Some((in_module, path_segments, in_module_is_extern)) = worklist.pop() { + let mut worklist = + vec![(start_module, Vec::::new(), true, not_local_module)]; + let mut worklist_via_import = vec![]; + + while let Some((in_module, path_segments, accessible, in_module_is_extern)) = + match worklist.pop() { + None => worklist_via_import.pop(), + Some(x) => Some(x), + } + { // We have to visit module children in deterministic order to avoid // instabilities in reported imports (#43552). in_module.for_each_child(self, |this, ident, ns, name_binding| { - // avoid imports entirely - if name_binding.is_import() && !name_binding.is_extern_crate() { + // avoid non-importable candidates + if !name_binding.is_importable() { return; } - // avoid non-importable candidates as well - if !name_binding.is_importable() { + + let child_accessible = + accessible && this.is_accessible_from(name_binding.vis, parent_scope.module); + + // do not venture inside inaccessible items of other crates + if in_module_is_extern && !child_accessible { + return; + } + + let via_import = name_binding.is_import() && !name_binding.is_extern_crate(); + + // There is an assumption elsewhere that paths of variants are in the enum's + // declaration and not imported. With this assumption, the variant component is + // chopped and the rest of the path is assumed to be the enum's own path. For + // errors where a variant is used as the type instead of the enum, this causes + // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`. + if via_import && name_binding.is_possibly_imported_variant() { return; } // collect results based on the filter function - if ident.name == lookup_ident.name && ns == namespace { + // avoid suggesting anything from the same module in which we are resolving + if ident.name == lookup_ident.name + && ns == namespace + && !ptr::eq(in_module, parent_scope.module) + { let res = name_binding.res(); if filter_fn(res) { // create the path @@ -638,19 +696,28 @@ impl<'a> Resolver<'a> { segms.push(ast::PathSegment::from_ident(ident)); let path = Path { span: name_binding.span, segments: segms }; - // the entity is accessible in the following cases: - // 1. if it's defined in the same crate, it's always - // accessible (since private entities can be made public) - // 2. if it's defined in another crate, it's accessible - // only if both the module is public and the entity is - // declared as public (due to pruning, we don't explore - // outside crate private modules => no need to check this) - if !in_module_is_extern || name_binding.vis == ty::Visibility::Public { - let did = match res { - Res::Def(DefKind::Ctor(..), did) => this.parent(did), - _ => res.opt_def_id(), - }; - candidates.push(ImportSuggestion { did, path }); + let did = match res { + Res::Def(DefKind::Ctor(..), did) => this.parent(did), + _ => res.opt_def_id(), + }; + + if child_accessible { + // Remove invisible match if exists + if let Some(idx) = candidates + .iter() + .position(|v: &ImportSuggestion| v.did == did && !v.accessible) + { + candidates.remove(idx); + } + } + + if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { + candidates.push(ImportSuggestion { + did, + descr: res.descr(), + path, + accessible: child_accessible, + }); } } } @@ -664,20 +731,23 @@ impl<'a> Resolver<'a> { let is_extern_crate_that_also_appears_in_prelude = name_binding.is_extern_crate() && lookup_ident.span.rust_2018(); - let is_visible_to_user = - !in_module_is_extern || name_binding.vis == ty::Visibility::Public; - - if !is_extern_crate_that_also_appears_in_prelude && is_visible_to_user { - // add the module to the lookup + if !is_extern_crate_that_also_appears_in_prelude { let is_extern = in_module_is_extern || name_binding.is_extern_crate(); + // add the module to the lookup if seen_modules.insert(module.def_id().unwrap()) { - worklist.push((module, path_segments, is_extern)); + if via_import { &mut worklist_via_import } else { &mut worklist } + .push((module, path_segments, child_accessible, is_extern)); } } } }) } + // If only some candidates are accessible, take just them + if !candidates.iter().all(|v: &ImportSuggestion| !v.accessible) { + candidates = candidates.into_iter().filter(|x| x.accessible).collect(); + } + candidates } @@ -692,6 +762,7 @@ impl<'a> Resolver<'a> { &mut self, lookup_ident: Ident, namespace: Namespace, + parent_scope: &ParentScope<'a>, filter_fn: FilterFn, ) -> Vec where @@ -700,6 +771,7 @@ impl<'a> Resolver<'a> { let mut suggestions = self.lookup_import_candidates_from_module( lookup_ident, namespace, + parent_scope, self.graph_root, Ident::with_dummy_span(kw::Crate), &filter_fn, @@ -724,6 +796,7 @@ impl<'a> Resolver<'a> { suggestions.extend(self.lookup_import_candidates_from_module( lookup_ident, namespace, + parent_scope, crate_root, ident, &filter_fn, @@ -756,7 +829,7 @@ impl<'a> Resolver<'a> { let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident); err.span_note(ident.span, &msg); } - if self.macro_names.contains(&ident.modern()) { + if self.macro_names.contains(&ident.normalize_to_macros_2_0()) { err.help("have you added the `#[macro_use]` on the module/import?"); } } @@ -785,16 +858,16 @@ impl<'a> Resolver<'a> { Applicability::MaybeIncorrect, ); let def_span = suggestion.res.opt_def_id().and_then(|def_id| match def_id.krate { - LOCAL_CRATE => self.definitions.opt_span(def_id), + LOCAL_CRATE => self.opt_span(def_id), _ => 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( - span, + self.session.source_map().guess_head_span(span), &format!( "similarly named {} `{}` defined here", suggestion.res.descr(), @@ -916,50 +989,81 @@ impl<'a> Resolver<'a> { err.emit(); } - crate fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) { - let PrivacyError { ident, binding, .. } = *privacy_error; - let session = &self.session; - let mk_struct_span_error = |is_constructor| { - let mut descr = binding.res().descr().to_string(); - if is_constructor { - descr += " constructor"; - } - if binding.is_import() { - descr += " import"; - } - - let mut err = - struct_span_err!(session, ident.span, E0603, "{} `{}` is private", descr, ident); - - err.span_label(ident.span, &format!("this {} is private", descr)); - err.span_note( - session.source_map().def_span(binding.span), - &format!("the {} `{}` is defined here", descr, ident), - ); - - err - }; - - let mut err = if let NameBindingKind::Res( + /// If the binding refers to a tuple struct constructor with fields, + /// returns the span of its fields. + fn ctor_fields_span(&self, binding: &NameBinding<'_>) -> Option { + if let NameBindingKind::Res( Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id), _, ) = binding.kind { let def_id = (&*self).parent(ctor_def_id).expect("no parent for a constructor"); if let Some(fields) = self.field_names.get(&def_id) { - let mut err = mk_struct_span_error(true); let first_field = fields.first().expect("empty field list in the map"); - err.span_label( - fields.iter().fold(first_field.span, |acc, field| acc.to(field.span)), - "a constructor is private if any of the fields is private", - ); - err - } else { - mk_struct_span_error(false) + return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span))); } - } else { - mk_struct_span_error(false) - }; + } + None + } + + crate fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) { + let PrivacyError { ident, binding, .. } = *privacy_error; + + let res = binding.res(); + let ctor_fields_span = self.ctor_fields_span(binding); + let plain_descr = res.descr().to_string(); + let nonimport_descr = + if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; + let import_descr = nonimport_descr.clone() + " import"; + let get_descr = + |b: &NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; + + // Print the primary message. + let descr = get_descr(binding); + let mut err = + struct_span_err!(self.session, ident.span, E0603, "{} `{}` is private", descr, ident); + err.span_label(ident.span, &format!("private {}", descr)); + if let Some(span) = ctor_fields_span { + err.span_label(span, "a constructor is private if any of the fields is private"); + } + + // Print the whole import chain to make it easier to see what happens. + let first_binding = binding; + let mut next_binding = Some(binding); + let mut next_ident = ident; + while let Some(binding) = next_binding { + let name = next_ident; + next_binding = match binding.kind { + _ if res == Res::Err => None, + NameBindingKind::Import { binding, import, .. } => match import.kind { + _ if binding.span.is_dummy() => None, + ImportKind::Single { source, .. } => { + next_ident = source; + Some(binding) + } + ImportKind::Glob { .. } | ImportKind::MacroUse => Some(binding), + ImportKind::ExternCrate { .. } => None, + }, + _ => None, + }; + + let first = ptr::eq(binding, first_binding); + let descr = get_descr(binding); + let msg = format!( + "{and_refers_to}the {item} `{name}`{which} is defined here{dots}", + and_refers_to = if first { "" } else { "...and refers to " }, + item = descr, + name = name, + which = if first { "" } else { " which" }, + dots = if next_binding.is_some() { "..." } else { "" }, + ); + 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()); + } + err.span_note(note_span, &msg); + } err.emit(); } @@ -998,7 +1102,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`? @@ -1018,7 +1122,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`? @@ -1050,7 +1154,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`? @@ -1070,7 +1174,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`? @@ -1114,7 +1218,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; @@ -1412,29 +1516,33 @@ 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, + instead: bool, found_use: bool, ) { 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<_> = candidates.iter().map(|c| path_names_to_string(&c.path)).collect(); + 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); - if let Some(span) = span { + let instead = if instead { " instead" } else { "" }; + let mut msg = format!("consider importing {} {}{}", determiner, kind, instead); + + 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. @@ -1444,12 +1552,13 @@ crate fn show_candidates( err.span_suggestions(span, &msg, path_strings.into_iter(), Applicability::Unspecified); } else { - let mut msg = msg; msg.push(':'); + for candidate in path_strings { msg.push('\n'); msg.push_str(&candidate); } + err.note(&msg); } } diff --git a/src/librustc_resolve/imports.rs b/src/librustc_resolve/imports.rs index 485b86636a0ba..8a6541b399e38 100644 --- a/src/librustc_resolve/imports.rs +++ b/src/librustc_resolve/imports.rs @@ -9,22 +9,23 @@ 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::lint::builtin::{PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS}; -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_ast_lowering::Resolver as ResolverAstLowering; use rustc_data_structures::fx::FxHashSet; 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 +58,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, @@ -416,7 +417,8 @@ impl<'a> Resolver<'a> { None => return Err((Undetermined, Weak::Yes)), }; let tmp_parent_scope; - let (mut adjusted_parent_scope, mut ident) = (parent_scope, ident.modern()); + let (mut adjusted_parent_scope, mut ident) = + (parent_scope, ident.normalize_to_macros_2_0()); match ident.span.glob_adjust(module.expansion, glob_import.span) { Some(Some(def)) => { tmp_parent_scope = @@ -873,6 +875,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// consolidate multiple unresolved import errors into a single diagnostic. fn finalize_import(&mut self, import: &'b Import<'b>) -> Option { let orig_vis = import.vis.replace(ty::Visibility::Invisible); + let orig_blacklisted_binding = match &import.kind { + ImportKind::Single { target_bindings, .. } => { + Some(mem::replace(&mut self.r.blacklisted_binding, target_bindings[TypeNS].get())) + } + _ => None, + }; let prev_ambiguity_errors_len = self.r.ambiguity_errors.len(); let path_res = self.r.resolve_path( &import.module_path, @@ -883,6 +891,9 @@ impl<'a, 'b> ImportResolver<'a, 'b> { import.crate_lint(), ); let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len; + if let Some(orig_blacklisted_binding) = orig_blacklisted_binding { + self.r.blacklisted_binding = orig_blacklisted_binding; + } import.vis.set(orig_vis); if let PathResult::Failed { .. } | PathResult::NonModule(..) = path_res { // Consider erroneous imports used to avoid duplicate diagnostics. @@ -1107,7 +1118,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { match binding.kind { // Never suggest the name that has binding error // i.e., the name that cannot be previously resolved - NameBindingKind::Res(Res::Err, _) => return None, + NameBindingKind::Res(Res::Err, _) => None, _ => Some(&i.name), } } @@ -1383,8 +1394,8 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let is_good_import = binding.is_import() && !binding.is_ambiguity() && !ident.span.from_expansion(); if is_good_import || binding.is_macro_def() { - let res = binding.res(); - if res != Res::Err { + let res = binding.res().map_id(|id| this.local_def_id(id)); + if res != def::Res::Err { reexports.push(Export { ident, res, span: binding.span, vis: binding.vis }); } } @@ -1431,7 +1442,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() @@ -1457,7 +1468,9 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if !reexports.is_empty() { if let Some(def_id) = module.def_id() { - self.r.export_map.insert(def_id, reexports); + // Call to `expect_local` should be fine because current + // code is only called for local modules. + self.r.export_map.insert(def_id.expect_local(), reexports); } } } diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 7efd9e90cebb9..6f769c3c59cae 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -11,25 +11,29 @@ 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, lint, span_bug}; use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{unwrap_or, walk_list}; +use rustc_ast_lowering::Resolver as ResolverAstLowering; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::DiagnosticId; 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_span::symbol::{kw, sym}; +use rustc_middle::{bug, span_bug}; +use rustc_session::lint; +use rustc_span::def_id::LocalDefId; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use smallvec::{smallvec, SmallVec}; use log::debug; +use rustc_span::source_map::{respan, Spanned}; use std::collections::BTreeSet; -use std::mem::replace; +use std::mem::{replace, take}; mod diagnostics; crate mod lifetimes; @@ -233,21 +237,31 @@ impl<'a> PathSource<'a> { } } + fn is_call(self) -> bool { + match self { + PathSource::Expr(Some(&Expr { kind: ExprKind::Call(..), .. })) => true, + _ => false, + } + } + 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 { @@ -255,27 +269,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, }, @@ -284,20 +300,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, }, @@ -315,8 +330,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), } @@ -342,7 +357,7 @@ struct DiagnosticMetadata<'ast> { currently_processing_generics: bool, /// The current enclosing function (used for better errors). - current_function: Option, + current_function: Option<(FnKind<'ast>, Span)>, /// A list of labels as of yet unused. Labels will be removed from this map when /// they are used (in a `break` or `continue` statement) @@ -458,10 +473,11 @@ 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)); + let previous_value = + replace(&mut self.diagnostic_metadata.current_function, Some((fn_kind, sp))); debug!("(resolving function) entering function"); let declaration = fn_kind.decl(); @@ -692,7 +708,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } fn with_scope(&mut self, id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T { - let id = self.r.definitions.local_def_id(id); + let id = self.r.local_def_id(id); let module = self.r.module_map.get(&id).cloned(); // clones a reference if let Some(module) = module { // Move down in the graph. @@ -744,7 +760,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.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); }); @@ -824,7 +840,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.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); @@ -865,7 +881,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.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); @@ -935,7 +951,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { _ => unreachable!(), }; - let ident = param.ident.modern(); + let ident = param.ident.normalize_to_macros_2_0(); debug!("with_generic_param_rib: {}", param.id); if seen_bindings.contains_key(&ident) { @@ -946,7 +962,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.local_def_id(param.id).to_def_id()); match param.kind { GenericParamKind::Type { .. } => { @@ -1096,7 +1112,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.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. @@ -1189,7 +1205,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. @@ -1317,7 +1333,8 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // 3) Report all missing variables we found. let mut missing_vars = missing_vars.iter_mut().collect::>(); - missing_vars.sort(); + missing_vars.sort_by_key(|(sym, _err)| sym.as_str()); + for (name, mut v) in missing_vars { if inconsistent_vars.contains_key(name) { v.could_be_path = false; @@ -1464,7 +1481,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Add the binding to the local ribs, if it doesn't already exist in the bindings map. // (We must not add it if it's in the bindings map because that breaks the assumptions // later passes make about or-patterns.) - let ident = ident.modern_and_legacy(); + let ident = ident.normalize_to_macro_rules(); let mut bound_iter = bindings.iter().filter(|(_, set)| set.contains(&ident)); // Already bound in a product pattern? e.g. `(a, a)` which is not allowed. @@ -1517,11 +1534,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; } @@ -1529,26 +1555,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), @@ -1571,7 +1590,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { _ => span_bug!( ident.span, "unexpected resolution for an identifier in pattern: {:?}", - res + res, ), } } @@ -1612,15 +1631,83 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let report_errors = |this: &mut Self, res: Option| { let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res); + let def_id = this.parent_scope.module.normal_ancestor_id; - let node_id = this.r.definitions.as_local_node_id(def_id).unwrap(); - let better = res.is_some(); + let instead = res.is_some(); let suggestion = if res.is_none() { this.report_missing_type_error(path) } else { None }; - this.r.use_injections.push(UseError { err, candidates, node_id, better, suggestion }); + + this.r.use_injections.push(UseError { err, candidates, def_id, instead, suggestion }); + PartialRes::new(Res::Err) }; + // For paths originating from calls (like in `HashMap::new()`), tries + // to enrich the plain `failed to resolve: ...` message with hints + // about possible missing imports. + // + // Similar thing, for types, happens in `report_errors` above. + let report_errors_for_call = |this: &mut Self, parent_err: Spanned>| { + if !source.is_call() { + return Some(parent_err); + } + + // Before we start looking for candidates, we have to get our hands + // on the type user is trying to perform invocation on; basically: + // we're transforming `HashMap::new` into just `HashMap` + let path = if let Some((_, path)) = path.split_last() { + path + } else { + return Some(parent_err); + }; + + let (mut err, candidates) = + this.smart_resolve_report_errors(path, span, PathSource::Type, None); + + if candidates.is_empty() { + err.cancel(); + return Some(parent_err); + } + + // There are two different error messages user might receive at + // this point: + // - E0412 cannot find type `{}` in this scope + // - E0433 failed to resolve: use of undeclared type or module `{}` + // + // The first one is emitted for paths in type-position, and the + // latter one - for paths in expression-position. + // + // Thus (since we're in expression-position at this point), not to + // confuse the user, we want to keep the *message* from E0432 (so + // `parent_err`), but we want *hints* from E0412 (so `err`). + // + // And that's what happens below - we're just mixing both messages + // into a single one. + let mut parent_err = this.r.into_struct_error(parent_err.span, parent_err.node); + + parent_err.cancel(); + + err.message = take(&mut parent_err.message); + err.code = take(&mut parent_err.code); + err.children = take(&mut parent_err.children); + + drop(parent_err); + + let def_id = this.parent_scope.module.normal_ancestor_id; + + this.r.use_injections.push(UseError { + err, + candidates, + def_id, + instead: false, + suggestion: None, + }); + + // We don't return `Some(parent_err)` here, because the error will + // be already printed as part of the `use` injections + None + }; + let partial_res = match self.resolve_qpath_anywhere( id, qself, @@ -1630,14 +1717,15 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { source.defer_to_typeck(), crate_lint, ) { - Some(partial_res) if partial_res.unresolved_segments() == 0 => { + Ok(Some(partial_res)) if partial_res.unresolved_segments() == 0 => { if is_expected(partial_res.base_res()) || partial_res.base_res() == Res::Err { partial_res } else { report_errors(self, Some(partial_res.base_res())) } } - Some(partial_res) if source.defer_to_typeck() => { + + Ok(Some(partial_res)) if source.defer_to_typeck() => { // Not fully resolved associated item `T::A::B` or `::A::B` // or `::A::B`. If `B` should be resolved in value namespace then // it needs to be added to the trait map. @@ -1648,25 +1736,34 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } let mut std_path = vec![Segment::from_ident(Ident::with_dummy_span(sym::std))]; + std_path.extend(path); + if self.r.primitive_type_table.primitive_types.contains_key(&path[0].ident.name) { - let cl = CrateLint::No; - let ns = Some(ns); if let PathResult::Module(_) | PathResult::NonModule(_) = - self.resolve_path(&std_path, ns, false, span, cl) + self.resolve_path(&std_path, Some(ns), false, span, CrateLint::No) { - // check if we wrote `str::from_utf8` instead of `std::str::from_utf8` + // Check if we wrote `str::from_utf8` instead of `std::str::from_utf8` let item_span = path.iter().last().map(|segment| segment.ident.span).unwrap_or(span); - debug!("accessed item from `std` submodule as a bare type {:?}", std_path); + let mut hm = self.r.session.confused_type_with_std_module.borrow_mut(); hm.insert(item_span, span); - // In some places (E0223) we only have access to the full path hm.insert(span, span); } } + partial_res } + + Err(err) => { + if let Some(err) = report_errors_for_call(self, err) { + self.r.report_error(err.span, err.node); + } + + PartialRes::new(Res::Err) + } + _ => report_errors(self, None), }; @@ -1675,6 +1772,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Avoid recording definition of `A::B` in `::B::C`. self.r.record_partial_res(id, partial_res); } + partial_res } @@ -1704,17 +1802,16 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { span: Span, defer_to_typeck: bool, crate_lint: CrateLint, - ) -> Option { + ) -> Result, Spanned>> { let mut fin_res = None; + for (i, ns) in [primary_ns, TypeNS, ValueNS].iter().cloned().enumerate() { if i == 0 || ns != primary_ns { - match self.resolve_qpath(id, qself, path, ns, span, crate_lint) { - // If defer_to_typeck, then resolution > no resolution, - // otherwise full resolution > partial resolution > no resolution. + match self.resolve_qpath(id, qself, path, ns, span, crate_lint)? { Some(partial_res) if partial_res.unresolved_segments() == 0 || defer_to_typeck => { - return Some(partial_res); + return Ok(Some(partial_res)); } partial_res => { if fin_res.is_none() { @@ -1725,19 +1822,19 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } } - // `MacroNS` assert!(primary_ns != MacroNS); + if qself.is_none() { let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident); let path = Path { segments: path.iter().map(path_seg).collect(), span }; if let Ok((_, res)) = self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false) { - return Some(PartialRes::new(res)); + return Ok(Some(PartialRes::new(res))); } } - fin_res + Ok(fin_res) } /// Handles paths that may refer to associated items. @@ -1749,7 +1846,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ns: Namespace, span: Span, crate_lint: CrateLint, - ) -> Option { + ) -> Result, Spanned>> { debug!( "resolve_qpath(id={:?}, qself={:?}, path={:?}, ns={:?}, span={:?})", id, qself, path, ns, span, @@ -1760,10 +1857,10 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // This is a case like `::B`, where there is no // trait to resolve. In that case, we leave the `B` // segment to be resolved by type-check. - return Some(PartialRes::with_unresolved_segments( + return Ok(Some(PartialRes::with_unresolved_segments( Res::Def(DefKind::Mod, DefId::local(CRATE_DEF_INDEX)), path.len(), - )); + ))); } // Make sure `A::B` in `::C` is a trait item. @@ -1793,10 +1890,10 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // The remaining segments (the `C` in our example) will // have to be resolved by type-check, since that requires doing // trait resolution. - return Some(PartialRes::with_unresolved_segments( + return Ok(Some(PartialRes::with_unresolved_segments( partial_res.base_res(), partial_res.unresolved_segments() + path.len() - qself.position - 1, - )); + ))); } let result = match self.resolve_path(&path, Some(ns), true, span, crate_lint) { @@ -1831,11 +1928,10 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { PartialRes::new(module.res().unwrap()) } PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => { - self.r.report_error(span, ResolutionError::FailedToResolve { label, suggestion }); - PartialRes::new(Res::Err) + return Err(respan(span, ResolutionError::FailedToResolve { label, suggestion })); } - PathResult::Module(..) | PathResult::Failed { .. } => return None, - PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"), + PathResult::Module(..) | PathResult::Failed { .. } => return Ok(None), + PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"), }; if path.len() > 1 @@ -1855,7 +1951,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { module.res().unwrap() } - _ => return Some(result), + _ => return Ok(Some(result)), } }; if result.base_res() == unqualified_result { @@ -1864,7 +1960,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } } - Some(result) + Ok(Some(result)) } fn with_resolved_label(&mut self, label: Option { + fn to_json(&self) -> Json { + Json::Array(self.iter().map(|elt| elt.to_json()).collect()) + } +} + +impl ToJson for BTreeMap { + fn to_json(&self) -> Json { + let mut d = BTreeMap::new(); + for (key, value) in self { + d.insert(key.to_string(), value.to_json()); + } + Json::Object(d) + } +} + +impl ToJson for HashMap { + fn to_json(&self) -> Json { + let mut d = BTreeMap::new(); + for (key, value) in self { + d.insert((*key).clone(), value.to_json()); + } + Json::Object(d) + } +} + +impl ToJson for Option { + fn to_json(&self) -> Json { + match *self { + None => Json::Null, + Some(ref value) => value.to_json(), + } + } +} + +struct FormatShim<'a, 'b> { + inner: &'a mut fmt::Formatter<'b>, +} + +impl<'a, 'b> fmt::Write for FormatShim<'a, 'b> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_str(s) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl fmt::Display for Json { + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = Encoder::new(&mut shim); + match self.encode(&mut encoder) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl<'a> fmt::Display for PrettyJson<'a> { + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = PrettyEncoder::new(&mut shim); + match self.inner.encode(&mut encoder) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl<'a, T: Encodable> fmt::Display for AsJson<'a, T> { + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = Encoder::new(&mut shim); + match self.inner.encode(&mut encoder) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl<'a, T> AsPrettyJson<'a, T> { + /// Sets the indentation level for the emitted JSON + pub fn indent(mut self, indent: usize) -> AsPrettyJson<'a, T> { + self.indent = Some(indent); + self + } +} + +impl<'a, T: Encodable> fmt::Display for AsPrettyJson<'a, T> { + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = PrettyEncoder::new(&mut shim); + if let Some(n) = self.indent { + encoder.set_indent(n); + } + match self.inner.encode(&mut encoder) { + Ok(_) => Ok(()), + Err(_) => Err(fmt::Error), + } + } +} + +impl FromStr for Json { + type Err = BuilderError; + fn from_str(s: &str) -> Result { + from_str(s) + } +} + +#[cfg(test)] +mod tests; diff --git a/src/libserialize/json/tests.rs b/src/librustc_serialize/json/tests.rs similarity index 100% rename from src/libserialize/json/tests.rs rename to src/librustc_serialize/json/tests.rs diff --git a/src/libserialize/leb128.rs b/src/librustc_serialize/leb128.rs similarity index 100% rename from src/libserialize/leb128.rs rename to src/librustc_serialize/leb128.rs diff --git a/src/librustc_serialize/lib.rs b/src/librustc_serialize/lib.rs new file mode 100644 index 0000000000000..3dc3e78382096 --- /dev/null +++ b/src/librustc_serialize/lib.rs @@ -0,0 +1,31 @@ +//! Support code for encoding and decoding types. + +/* +Core encoding and decoding interfaces. +*/ + +#![doc( + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/", + test(attr(allow(unused_variables), deny(warnings))) +)] +#![feature(box_syntax)] +#![feature(min_specialization)] +#![feature(never_type)] +#![feature(nll)] +#![feature(associated_type_bounds)] +#![cfg_attr(test, feature(test))] +#![allow(rustc::internal)] + +pub use self::serialize::{Decodable, Decoder, Encodable, Encoder}; + +pub use self::serialize::{SpecializationError, SpecializedDecoder, SpecializedEncoder}; +pub use self::serialize::{UseSpecializedDecodable, UseSpecializedEncodable}; + +mod collection_impls; +mod serialize; + +pub mod json; + +pub mod leb128; +pub mod opaque; diff --git a/src/libserialize/opaque.rs b/src/librustc_serialize/opaque.rs similarity index 100% rename from src/libserialize/opaque.rs rename to src/librustc_serialize/opaque.rs diff --git a/src/libserialize/serialize.rs b/src/librustc_serialize/serialize.rs similarity index 96% rename from src/libserialize/serialize.rs rename to src/librustc_serialize/serialize.rs index 8c6548cd3c5b2..29c5737ad895a 100644 --- a/src/libserialize/serialize.rs +++ b/src/librustc_serialize/serialize.rs @@ -635,24 +635,6 @@ impl Decodable for PhantomData { } } -impl<'a, T: ?Sized + Encodable> Encodable for &'a T { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - (**self).encode(s) - } -} - -impl Encodable for Box { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - (**self).encode(s) - } -} - -impl Decodable for Box { - fn decode(d: &mut D) -> Result, D::Error> { - Ok(box Decodable::decode(d)?) - } -} - impl Decodable for Box<[T]> { fn decode(d: &mut D) -> Result, D::Error> { let v: Vec = Decodable::decode(d)?; @@ -706,6 +688,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>, @@ -984,8 +990,20 @@ impl Decodable for T { // for this exact reason. // May be fixable in a simpler fashion via the // more complex lattice model for specialization. -impl<'a, T: ?Sized + Encodable> UseSpecializedEncodable for &'a T {} -impl UseSpecializedEncodable for Box {} -impl UseSpecializedDecodable for Box {} +impl<'a, T: ?Sized + Encodable> UseSpecializedEncodable for &'a T { + fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} +impl UseSpecializedEncodable for Box { + fn default_encode(&self, s: &mut S) -> Result<(), S::Error> { + (**self).encode(s) + } +} +impl UseSpecializedDecodable for Box { + fn default_decode(d: &mut D) -> Result, D::Error> { + Ok(box Decodable::decode(d)?) + } +} impl<'a, T: Decodable> UseSpecializedDecodable for &'a T {} impl<'a, T: Decodable> UseSpecializedDecodable for &'a [T] {} diff --git a/src/librustc_serialize/tests/json.rs b/src/librustc_serialize/tests/json.rs new file mode 100644 index 0000000000000..59c481edbca3a --- /dev/null +++ b/src/librustc_serialize/tests/json.rs @@ -0,0 +1,1268 @@ +#![allow(rustc::internal)] + +use json::DecoderError::*; +use json::ErrorCode::*; +use json::Json::*; +use json::JsonEvent::*; +use json::ParserError::*; +use json::{ + from_str, DecodeResult, Decoder, DecoderError, Encoder, EncoderError, Json, JsonEvent, Parser, + StackElement, +}; +use rustc_serialize::json; +use rustc_serialize::{Decodable, Encodable}; + +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::string; +use Animal::*; + +#[derive(RustcDecodable, Eq, PartialEq, Debug)] +struct OptionData { + opt: Option, +} + +#[test] +fn test_decode_option_none() { + let s = "{}"; + let obj: OptionData = json::decode(s).unwrap(); + assert_eq!(obj, OptionData { opt: None }); +} + +#[test] +fn test_decode_option_some() { + let s = "{ \"opt\": 10 }"; + let obj: OptionData = json::decode(s).unwrap(); + assert_eq!(obj, OptionData { opt: Some(10) }); +} + +#[test] +fn test_decode_option_malformed() { + check_err::( + "{ \"opt\": [] }", + ExpectedError("Number".to_string(), "[]".to_string()), + ); + check_err::( + "{ \"opt\": false }", + ExpectedError("Number".to_string(), "false".to_string()), + ); +} + +#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] +enum Animal { + Dog, + Frog(string::String, isize), +} + +#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] +struct Inner { + a: (), + b: usize, + c: Vec, +} + +#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] +struct Outer { + inner: Vec, +} + +fn mk_object(items: &[(string::String, Json)]) -> Json { + let mut d = BTreeMap::new(); + + for item in items { + match *item { + (ref key, ref value) => { + d.insert((*key).clone(), (*value).clone()); + } + } + } + + Object(d) +} + +#[test] +fn test_from_str_trait() { + let s = "null"; + assert!(s.parse::().unwrap() == s.parse().unwrap()); +} + +#[test] +fn test_write_null() { + assert_eq!(Null.to_string(), "null"); + assert_eq!(Null.pretty().to_string(), "null"); +} + +#[test] +fn test_write_i64() { + assert_eq!(U64(0).to_string(), "0"); + assert_eq!(U64(0).pretty().to_string(), "0"); + + assert_eq!(U64(1234).to_string(), "1234"); + assert_eq!(U64(1234).pretty().to_string(), "1234"); + + assert_eq!(I64(-5678).to_string(), "-5678"); + assert_eq!(I64(-5678).pretty().to_string(), "-5678"); + + assert_eq!(U64(7650007200025252000).to_string(), "7650007200025252000"); + assert_eq!(U64(7650007200025252000).pretty().to_string(), "7650007200025252000"); +} + +#[test] +fn test_write_f64() { + assert_eq!(F64(3.0).to_string(), "3.0"); + assert_eq!(F64(3.0).pretty().to_string(), "3.0"); + + assert_eq!(F64(3.1).to_string(), "3.1"); + assert_eq!(F64(3.1).pretty().to_string(), "3.1"); + + assert_eq!(F64(-1.5).to_string(), "-1.5"); + assert_eq!(F64(-1.5).pretty().to_string(), "-1.5"); + + assert_eq!(F64(0.5).to_string(), "0.5"); + assert_eq!(F64(0.5).pretty().to_string(), "0.5"); + + assert_eq!(F64(f64::NAN).to_string(), "null"); + assert_eq!(F64(f64::NAN).pretty().to_string(), "null"); + + assert_eq!(F64(f64::INFINITY).to_string(), "null"); + assert_eq!(F64(f64::INFINITY).pretty().to_string(), "null"); + + assert_eq!(F64(f64::NEG_INFINITY).to_string(), "null"); + assert_eq!(F64(f64::NEG_INFINITY).pretty().to_string(), "null"); +} + +#[test] +fn test_write_str() { + assert_eq!(String("".to_string()).to_string(), "\"\""); + assert_eq!(String("".to_string()).pretty().to_string(), "\"\""); + + assert_eq!(String("homura".to_string()).to_string(), "\"homura\""); + assert_eq!(String("madoka".to_string()).pretty().to_string(), "\"madoka\""); +} + +#[test] +fn test_write_bool() { + assert_eq!(Boolean(true).to_string(), "true"); + assert_eq!(Boolean(true).pretty().to_string(), "true"); + + assert_eq!(Boolean(false).to_string(), "false"); + assert_eq!(Boolean(false).pretty().to_string(), "false"); +} + +#[test] +fn test_write_array() { + assert_eq!(Array(vec![]).to_string(), "[]"); + assert_eq!(Array(vec![]).pretty().to_string(), "[]"); + + assert_eq!(Array(vec![Boolean(true)]).to_string(), "[true]"); + assert_eq!( + Array(vec![Boolean(true)]).pretty().to_string(), + "\ + [\n \ + true\n\ + ]" + ); + + let long_test_array = + Array(vec![Boolean(false), Null, Array(vec![String("foo\nbar".to_string()), F64(3.5)])]); + + assert_eq!(long_test_array.to_string(), "[false,null,[\"foo\\nbar\",3.5]]"); + assert_eq!( + long_test_array.pretty().to_string(), + "\ + [\n \ + false,\n \ + null,\n \ + [\n \ + \"foo\\nbar\",\n \ + 3.5\n \ + ]\n\ + ]" + ); +} + +#[test] +fn test_write_object() { + assert_eq!(mk_object(&[]).to_string(), "{}"); + assert_eq!(mk_object(&[]).pretty().to_string(), "{}"); + + assert_eq!(mk_object(&[("a".to_string(), Boolean(true))]).to_string(), "{\"a\":true}"); + assert_eq!( + mk_object(&[("a".to_string(), Boolean(true))]).pretty().to_string(), + "\ + {\n \ + \"a\": true\n\ + }" + ); + + let complex_obj = mk_object(&[( + "b".to_string(), + Array(vec![ + mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]), + mk_object(&[("d".to_string(), String("".to_string()))]), + ]), + )]); + + assert_eq!( + complex_obj.to_string(), + "{\ + \"b\":[\ + {\"c\":\"\\f\\r\"},\ + {\"d\":\"\"}\ + ]\ + }" + ); + assert_eq!( + complex_obj.pretty().to_string(), + "\ + {\n \ + \"b\": [\n \ + {\n \ + \"c\": \"\\f\\r\"\n \ + },\n \ + {\n \ + \"d\": \"\"\n \ + }\n \ + ]\n\ + }" + ); + + let a = mk_object(&[ + ("a".to_string(), Boolean(true)), + ( + "b".to_string(), + Array(vec![ + mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]), + mk_object(&[("d".to_string(), String("".to_string()))]), + ]), + ), + ]); + + // We can't compare the strings directly because the object fields be + // printed in a different order. + assert_eq!(a.clone(), a.to_string().parse().unwrap()); + assert_eq!(a.clone(), a.pretty().to_string().parse().unwrap()); +} + +#[test] +fn test_write_enum() { + let animal = Dog; + assert_eq!(json::as_json(&animal).to_string(), "\"Dog\""); + assert_eq!(json::as_pretty_json(&animal).to_string(), "\"Dog\""); + + let animal = Frog("Henry".to_string(), 349); + assert_eq!( + json::as_json(&animal).to_string(), + "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}" + ); + assert_eq!( + json::as_pretty_json(&animal).to_string(), + "{\n \ + \"variant\": \"Frog\",\n \ + \"fields\": [\n \ + \"Henry\",\n \ + 349\n \ + ]\n\ + }" + ); +} + +macro_rules! check_encoder_for_simple { + ($value:expr, $expected:expr) => {{ + let s = json::as_json(&$value).to_string(); + assert_eq!(s, $expected); + + let s = json::as_pretty_json(&$value).to_string(); + assert_eq!(s, $expected); + }}; +} + +#[test] +fn test_write_some() { + check_encoder_for_simple!(Some("jodhpurs".to_string()), "\"jodhpurs\""); +} + +#[test] +fn test_write_none() { + check_encoder_for_simple!(None::, "null"); +} + +#[test] +fn test_write_char() { + check_encoder_for_simple!('a', "\"a\""); + check_encoder_for_simple!('\t', "\"\\t\""); + check_encoder_for_simple!('\u{0000}', "\"\\u0000\""); + check_encoder_for_simple!('\u{001b}', "\"\\u001b\""); + check_encoder_for_simple!('\u{007f}', "\"\\u007f\""); + check_encoder_for_simple!('\u{00a0}', "\"\u{00a0}\""); + check_encoder_for_simple!('\u{abcd}', "\"\u{abcd}\""); + check_encoder_for_simple!('\u{10ffff}', "\"\u{10ffff}\""); +} + +#[test] +fn test_trailing_characters() { + assert_eq!(from_str("nulla"), Err(SyntaxError(TrailingCharacters, 1, 5))); + assert_eq!(from_str("truea"), Err(SyntaxError(TrailingCharacters, 1, 5))); + assert_eq!(from_str("falsea"), Err(SyntaxError(TrailingCharacters, 1, 6))); + assert_eq!(from_str("1a"), Err(SyntaxError(TrailingCharacters, 1, 2))); + assert_eq!(from_str("[]a"), Err(SyntaxError(TrailingCharacters, 1, 3))); + assert_eq!(from_str("{}a"), Err(SyntaxError(TrailingCharacters, 1, 3))); +} + +#[test] +fn test_read_identifiers() { + assert_eq!(from_str("n"), Err(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(from_str("nul"), Err(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(from_str("t"), Err(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(from_str("truz"), Err(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(from_str("f"), Err(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(from_str("faz"), Err(SyntaxError(InvalidSyntax, 1, 3))); + + assert_eq!(from_str("null"), Ok(Null)); + assert_eq!(from_str("true"), Ok(Boolean(true))); + assert_eq!(from_str("false"), Ok(Boolean(false))); + assert_eq!(from_str(" null "), Ok(Null)); + assert_eq!(from_str(" true "), Ok(Boolean(true))); + assert_eq!(from_str(" false "), Ok(Boolean(false))); +} + +#[test] +fn test_decode_identifiers() { + let v: () = json::decode("null").unwrap(); + assert_eq!(v, ()); + + let v: bool = json::decode("true").unwrap(); + assert_eq!(v, true); + + let v: bool = json::decode("false").unwrap(); + assert_eq!(v, false); +} + +#[test] +fn test_read_number() { + assert_eq!(from_str("+"), Err(SyntaxError(InvalidSyntax, 1, 1))); + assert_eq!(from_str("."), Err(SyntaxError(InvalidSyntax, 1, 1))); + assert_eq!(from_str("NaN"), Err(SyntaxError(InvalidSyntax, 1, 1))); + assert_eq!(from_str("-"), Err(SyntaxError(InvalidNumber, 1, 2))); + assert_eq!(from_str("00"), Err(SyntaxError(InvalidNumber, 1, 2))); + assert_eq!(from_str("1."), Err(SyntaxError(InvalidNumber, 1, 3))); + assert_eq!(from_str("1e"), Err(SyntaxError(InvalidNumber, 1, 3))); + assert_eq!(from_str("1e+"), Err(SyntaxError(InvalidNumber, 1, 4))); + + assert_eq!(from_str("18446744073709551616"), Err(SyntaxError(InvalidNumber, 1, 20))); + assert_eq!(from_str("-9223372036854775809"), Err(SyntaxError(InvalidNumber, 1, 21))); + + assert_eq!(from_str("3"), Ok(U64(3))); + assert_eq!(from_str("3.1"), Ok(F64(3.1))); + assert_eq!(from_str("-1.2"), Ok(F64(-1.2))); + assert_eq!(from_str("0.4"), Ok(F64(0.4))); + assert_eq!(from_str("0.4e5"), Ok(F64(0.4e5))); + assert_eq!(from_str("0.4e+15"), Ok(F64(0.4e15))); + assert_eq!(from_str("0.4e-01"), Ok(F64(0.4e-01))); + assert_eq!(from_str(" 3 "), Ok(U64(3))); + + assert_eq!(from_str("-9223372036854775808"), Ok(I64(i64::MIN))); + assert_eq!(from_str("9223372036854775807"), Ok(U64(i64::MAX as u64))); + assert_eq!(from_str("18446744073709551615"), Ok(U64(u64::MAX))); +} + +#[test] +fn test_decode_numbers() { + let v: f64 = json::decode("3").unwrap(); + assert_eq!(v, 3.0); + + let v: f64 = json::decode("3.1").unwrap(); + assert_eq!(v, 3.1); + + let v: f64 = json::decode("-1.2").unwrap(); + assert_eq!(v, -1.2); + + let v: f64 = json::decode("0.4").unwrap(); + assert_eq!(v, 0.4); + + let v: f64 = json::decode("0.4e5").unwrap(); + assert_eq!(v, 0.4e5); + + let v: f64 = json::decode("0.4e15").unwrap(); + assert_eq!(v, 0.4e15); + + let v: f64 = json::decode("0.4e-01").unwrap(); + assert_eq!(v, 0.4e-01); + + let v: u64 = json::decode("0").unwrap(); + assert_eq!(v, 0); + + let v: u64 = json::decode("18446744073709551615").unwrap(); + assert_eq!(v, u64::MAX); + + let v: i64 = json::decode("-9223372036854775808").unwrap(); + assert_eq!(v, i64::MIN); + + let v: i64 = json::decode("9223372036854775807").unwrap(); + assert_eq!(v, i64::MAX); + + let res: DecodeResult = json::decode("765.25"); + assert_eq!(res, Err(ExpectedError("Integer".to_string(), "765.25".to_string()))); +} + +#[test] +fn test_read_str() { + assert_eq!(from_str("\""), Err(SyntaxError(EOFWhileParsingString, 1, 2))); + assert_eq!(from_str("\"lol"), Err(SyntaxError(EOFWhileParsingString, 1, 5))); + + assert_eq!(from_str("\"\""), Ok(String("".to_string()))); + assert_eq!(from_str("\"foo\""), Ok(String("foo".to_string()))); + assert_eq!(from_str("\"\\\"\""), Ok(String("\"".to_string()))); + assert_eq!(from_str("\"\\b\""), Ok(String("\x08".to_string()))); + assert_eq!(from_str("\"\\n\""), Ok(String("\n".to_string()))); + assert_eq!(from_str("\"\\r\""), Ok(String("\r".to_string()))); + assert_eq!(from_str("\"\\t\""), Ok(String("\t".to_string()))); + assert_eq!(from_str(" \"foo\" "), Ok(String("foo".to_string()))); + assert_eq!(from_str("\"\\u12ab\""), Ok(String("\u{12ab}".to_string()))); + assert_eq!(from_str("\"\\uAB12\""), Ok(String("\u{AB12}".to_string()))); +} + +#[test] +fn test_decode_str() { + let s = [ + ("\"\"", ""), + ("\"foo\"", "foo"), + ("\"\\\"\"", "\""), + ("\"\\b\"", "\x08"), + ("\"\\n\"", "\n"), + ("\"\\r\"", "\r"), + ("\"\\t\"", "\t"), + ("\"\\u12ab\"", "\u{12ab}"), + ("\"\\uAB12\"", "\u{AB12}"), + ]; + + for &(i, o) in &s { + let v: string::String = json::decode(i).unwrap(); + assert_eq!(v, o); + } +} + +#[test] +fn test_read_array() { + assert_eq!(from_str("["), Err(SyntaxError(EOFWhileParsingValue, 1, 2))); + assert_eq!(from_str("[1"), Err(SyntaxError(EOFWhileParsingArray, 1, 3))); + assert_eq!(from_str("[1,"), Err(SyntaxError(EOFWhileParsingValue, 1, 4))); + assert_eq!(from_str("[1,]"), Err(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(from_str("[6 7]"), Err(SyntaxError(InvalidSyntax, 1, 4))); + + assert_eq!(from_str("[]"), Ok(Array(vec![]))); + assert_eq!(from_str("[ ]"), Ok(Array(vec![]))); + assert_eq!(from_str("[true]"), Ok(Array(vec![Boolean(true)]))); + assert_eq!(from_str("[ false ]"), Ok(Array(vec![Boolean(false)]))); + assert_eq!(from_str("[null]"), Ok(Array(vec![Null]))); + assert_eq!(from_str("[3, 1]"), Ok(Array(vec![U64(3), U64(1)]))); + assert_eq!(from_str("\n[3, 2]\n"), Ok(Array(vec![U64(3), U64(2)]))); + assert_eq!(from_str("[2, [4, 1]]"), Ok(Array(vec![U64(2), Array(vec![U64(4), U64(1)])]))); +} + +#[test] +fn test_decode_array() { + let v: Vec<()> = json::decode("[]").unwrap(); + assert_eq!(v, []); + + let v: Vec<()> = json::decode("[null]").unwrap(); + assert_eq!(v, [()]); + + let v: Vec = json::decode("[true]").unwrap(); + assert_eq!(v, [true]); + + let v: Vec = json::decode("[3, 1]").unwrap(); + assert_eq!(v, [3, 1]); + + let v: Vec> = json::decode("[[3], [1, 2]]").unwrap(); + assert_eq!(v, [vec![3], vec![1, 2]]); +} + +#[test] +fn test_decode_tuple() { + let t: (usize, usize, usize) = json::decode("[1, 2, 3]").unwrap(); + assert_eq!(t, (1, 2, 3)); + + let t: (usize, string::String) = json::decode("[1, \"two\"]").unwrap(); + assert_eq!(t, (1, "two".to_string())); +} + +#[test] +fn test_decode_tuple_malformed_types() { + assert!(json::decode::<(usize, string::String)>("[1, 2]").is_err()); +} + +#[test] +fn test_decode_tuple_malformed_length() { + assert!(json::decode::<(usize, usize)>("[1, 2, 3]").is_err()); +} + +#[test] +fn test_read_object() { + assert_eq!(from_str("{"), Err(SyntaxError(EOFWhileParsingObject, 1, 2))); + assert_eq!(from_str("{ "), Err(SyntaxError(EOFWhileParsingObject, 1, 3))); + assert_eq!(from_str("{1"), Err(SyntaxError(KeyMustBeAString, 1, 2))); + assert_eq!(from_str("{ \"a\""), Err(SyntaxError(EOFWhileParsingObject, 1, 6))); + assert_eq!(from_str("{\"a\""), Err(SyntaxError(EOFWhileParsingObject, 1, 5))); + assert_eq!(from_str("{\"a\" "), Err(SyntaxError(EOFWhileParsingObject, 1, 6))); + + assert_eq!(from_str("{\"a\" 1"), Err(SyntaxError(ExpectedColon, 1, 6))); + assert_eq!(from_str("{\"a\":"), Err(SyntaxError(EOFWhileParsingValue, 1, 6))); + assert_eq!(from_str("{\"a\":1"), Err(SyntaxError(EOFWhileParsingObject, 1, 7))); + assert_eq!(from_str("{\"a\":1 1"), Err(SyntaxError(InvalidSyntax, 1, 8))); + assert_eq!(from_str("{\"a\":1,"), Err(SyntaxError(EOFWhileParsingObject, 1, 8))); + + assert_eq!(from_str("{}").unwrap(), mk_object(&[])); + assert_eq!(from_str("{\"a\": 3}").unwrap(), mk_object(&[("a".to_string(), U64(3))])); + + assert_eq!( + from_str("{ \"a\": null, \"b\" : true }").unwrap(), + mk_object(&[("a".to_string(), Null), ("b".to_string(), Boolean(true))]) + ); + assert_eq!( + from_str("\n{ \"a\": null, \"b\" : true }\n").unwrap(), + mk_object(&[("a".to_string(), Null), ("b".to_string(), Boolean(true))]) + ); + assert_eq!( + from_str("{\"a\" : 1.0 ,\"b\": [ true ]}").unwrap(), + mk_object(&[("a".to_string(), F64(1.0)), ("b".to_string(), Array(vec![Boolean(true)]))]) + ); + assert_eq!( + from_str( + "{\ + \"a\": 1.0, \ + \"b\": [\ + true,\ + \"foo\\nbar\", \ + { \"c\": {\"d\": null} } \ + ]\ + }" + ) + .unwrap(), + mk_object(&[ + ("a".to_string(), F64(1.0)), + ( + "b".to_string(), + Array(vec![ + Boolean(true), + String("foo\nbar".to_string()), + mk_object(&[("c".to_string(), mk_object(&[("d".to_string(), Null)]))]) + ]) + ) + ]) + ); +} + +#[test] +fn test_decode_struct() { + let s = "{ + \"inner\": [ + { \"a\": null, \"b\": 2, \"c\": [\"abc\", \"xyz\"] } + ] + }"; + + let v: Outer = json::decode(s).unwrap(); + assert_eq!( + v, + Outer { inner: vec![Inner { a: (), b: 2, c: vec!["abc".to_string(), "xyz".to_string()] }] } + ); +} + +#[derive(RustcDecodable)] +struct FloatStruct { + f: f64, + a: Vec, +} +#[test] +fn test_decode_struct_with_nan() { + let s = "{\"f\":null,\"a\":[null,123]}"; + let obj: FloatStruct = json::decode(s).unwrap(); + assert!(obj.f.is_nan()); + assert!(obj.a[0].is_nan()); + assert_eq!(obj.a[1], 123f64); +} + +#[test] +fn test_decode_option() { + let value: Option = json::decode("null").unwrap(); + assert_eq!(value, None); + + let value: Option = json::decode("\"jodhpurs\"").unwrap(); + assert_eq!(value, Some("jodhpurs".to_string())); +} + +#[test] +fn test_decode_enum() { + let value: Animal = json::decode("\"Dog\"").unwrap(); + assert_eq!(value, Dog); + + let s = "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}"; + let value: Animal = json::decode(s).unwrap(); + assert_eq!(value, Frog("Henry".to_string(), 349)); +} + +#[test] +fn test_decode_map() { + let s = "{\"a\": \"Dog\", \"b\": {\"variant\":\"Frog\",\ + \"fields\":[\"Henry\", 349]}}"; + let mut map: BTreeMap = json::decode(s).unwrap(); + + assert_eq!(map.remove(&"a".to_string()), Some(Dog)); + assert_eq!(map.remove(&"b".to_string()), Some(Frog("Henry".to_string(), 349))); +} + +#[test] +fn test_multiline_errors() { + assert_eq!(from_str("{\n \"foo\":\n \"bar\""), Err(SyntaxError(EOFWhileParsingObject, 3, 8))); +} + +#[derive(RustcDecodable)] +#[allow(dead_code)] +struct DecodeStruct { + x: f64, + y: bool, + z: string::String, + w: Vec, +} +#[derive(RustcDecodable)] +enum DecodeEnum { + A(f64), + B(string::String), +} +fn check_err(to_parse: &'static str, expected: DecoderError) { + let res: DecodeResult = match from_str(to_parse) { + Err(e) => Err(ParseError(e)), + Ok(json) => Decodable::decode(&mut Decoder::new(json)), + }; + match res { + Ok(_) => panic!("`{:?}` parsed & decoded ok, expecting error `{:?}`", to_parse, expected), + Err(ParseError(e)) => panic!("`{:?}` is not valid json: {:?}", to_parse, e), + Err(e) => { + assert_eq!(e, expected); + } + } +} +#[test] +fn test_decode_errors_struct() { + check_err::("[]", ExpectedError("Object".to_string(), "[]".to_string())); + check_err::( + "{\"x\": true, \"y\": true, \"z\": \"\", \"w\": []}", + ExpectedError("Number".to_string(), "true".to_string()), + ); + check_err::( + "{\"x\": 1, \"y\": [], \"z\": \"\", \"w\": []}", + ExpectedError("Boolean".to_string(), "[]".to_string()), + ); + check_err::( + "{\"x\": 1, \"y\": true, \"z\": {}, \"w\": []}", + ExpectedError("String".to_string(), "{}".to_string()), + ); + check_err::( + "{\"x\": 1, \"y\": true, \"z\": \"\", \"w\": null}", + ExpectedError("Array".to_string(), "null".to_string()), + ); + check_err::( + "{\"x\": 1, \"y\": true, \"z\": \"\"}", + MissingFieldError("w".to_string()), + ); +} +#[test] +fn test_decode_errors_enum() { + check_err::("{}", MissingFieldError("variant".to_string())); + check_err::( + "{\"variant\": 1}", + ExpectedError("String".to_string(), "1".to_string()), + ); + check_err::("{\"variant\": \"A\"}", MissingFieldError("fields".to_string())); + check_err::( + "{\"variant\": \"A\", \"fields\": null}", + ExpectedError("Array".to_string(), "null".to_string()), + ); + check_err::( + "{\"variant\": \"C\", \"fields\": []}", + UnknownVariantError("C".to_string()), + ); +} + +#[test] +fn test_find() { + let json_value = from_str("{\"dog\" : \"cat\"}").unwrap(); + let found_str = json_value.find("dog"); + assert!(found_str.unwrap().as_string().unwrap() == "cat"); +} + +#[test] +fn test_find_path() { + let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); + let found_str = json_value.find_path(&["dog", "cat", "mouse"]); + assert!(found_str.unwrap().as_string().unwrap() == "cheese"); +} + +#[test] +fn test_search() { + let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); + let found_str = json_value.search("mouse").and_then(|j| j.as_string()); + assert!(found_str.unwrap() == "cheese"); +} + +#[test] +fn test_index() { + let json_value = from_str("{\"animals\":[\"dog\",\"cat\",\"mouse\"]}").unwrap(); + let ref array = json_value["animals"]; + assert_eq!(array[0].as_string().unwrap(), "dog"); + assert_eq!(array[1].as_string().unwrap(), "cat"); + assert_eq!(array[2].as_string().unwrap(), "mouse"); +} + +#[test] +fn test_is_object() { + let json_value = from_str("{}").unwrap(); + assert!(json_value.is_object()); +} + +#[test] +fn test_as_object() { + let json_value = from_str("{}").unwrap(); + let json_object = json_value.as_object(); + assert!(json_object.is_some()); +} + +#[test] +fn test_is_array() { + let json_value = from_str("[1, 2, 3]").unwrap(); + assert!(json_value.is_array()); +} + +#[test] +fn test_as_array() { + let json_value = from_str("[1, 2, 3]").unwrap(); + let json_array = json_value.as_array(); + let expected_length = 3; + assert!(json_array.is_some() && json_array.unwrap().len() == expected_length); +} + +#[test] +fn test_is_string() { + let json_value = from_str("\"dog\"").unwrap(); + assert!(json_value.is_string()); +} + +#[test] +fn test_as_string() { + let json_value = from_str("\"dog\"").unwrap(); + let json_str = json_value.as_string(); + let expected_str = "dog"; + assert_eq!(json_str, Some(expected_str)); +} + +#[test] +fn test_is_number() { + let json_value = from_str("12").unwrap(); + assert!(json_value.is_number()); +} + +#[test] +fn test_is_i64() { + let json_value = from_str("-12").unwrap(); + assert!(json_value.is_i64()); + + let json_value = from_str("12").unwrap(); + assert!(!json_value.is_i64()); + + let json_value = from_str("12.0").unwrap(); + assert!(!json_value.is_i64()); +} + +#[test] +fn test_is_u64() { + let json_value = from_str("12").unwrap(); + assert!(json_value.is_u64()); + + let json_value = from_str("-12").unwrap(); + assert!(!json_value.is_u64()); + + let json_value = from_str("12.0").unwrap(); + assert!(!json_value.is_u64()); +} + +#[test] +fn test_is_f64() { + let json_value = from_str("12").unwrap(); + assert!(!json_value.is_f64()); + + let json_value = from_str("-12").unwrap(); + assert!(!json_value.is_f64()); + + let json_value = from_str("12.0").unwrap(); + assert!(json_value.is_f64()); + + let json_value = from_str("-12.0").unwrap(); + assert!(json_value.is_f64()); +} + +#[test] +fn test_as_i64() { + let json_value = from_str("-12").unwrap(); + let json_num = json_value.as_i64(); + assert_eq!(json_num, Some(-12)); +} + +#[test] +fn test_as_u64() { + let json_value = from_str("12").unwrap(); + let json_num = json_value.as_u64(); + assert_eq!(json_num, Some(12)); +} + +#[test] +fn test_as_f64() { + let json_value = from_str("12.0").unwrap(); + let json_num = json_value.as_f64(); + assert_eq!(json_num, Some(12f64)); +} + +#[test] +fn test_is_boolean() { + let json_value = from_str("false").unwrap(); + assert!(json_value.is_boolean()); +} + +#[test] +fn test_as_boolean() { + let json_value = from_str("false").unwrap(); + let json_bool = json_value.as_boolean(); + let expected_bool = false; + assert!(json_bool.is_some() && json_bool.unwrap() == expected_bool); +} + +#[test] +fn test_is_null() { + let json_value = from_str("null").unwrap(); + assert!(json_value.is_null()); +} + +#[test] +fn test_as_null() { + let json_value = from_str("null").unwrap(); + let json_null = json_value.as_null(); + let expected_null = (); + assert!(json_null.is_some() && json_null.unwrap() == expected_null); +} + +#[test] +fn test_encode_hashmap_with_numeric_key() { + use std::collections::HashMap; + use std::str::from_utf8; + let mut hm: HashMap = HashMap::new(); + hm.insert(1, true); + let mut mem_buf = Vec::new(); + write!(&mut mem_buf, "{}", json::as_pretty_json(&hm)).unwrap(); + let json_str = from_utf8(&mem_buf[..]).unwrap(); + match from_str(json_str) { + Err(_) => panic!("Unable to parse json_str: {:?}", json_str), + _ => {} // it parsed and we are good to go + } +} + +#[test] +fn test_prettyencode_hashmap_with_numeric_key() { + use std::collections::HashMap; + use std::str::from_utf8; + let mut hm: HashMap = HashMap::new(); + hm.insert(1, true); + let mut mem_buf = Vec::new(); + write!(&mut mem_buf, "{}", json::as_pretty_json(&hm)).unwrap(); + let json_str = from_utf8(&mem_buf[..]).unwrap(); + match from_str(json_str) { + Err(_) => panic!("Unable to parse json_str: {:?}", json_str), + _ => {} // it parsed and we are good to go + } +} + +#[test] +fn test_prettyencoder_indent_level_param() { + use std::collections::BTreeMap; + use std::str::from_utf8; + + let mut tree = BTreeMap::new(); + + tree.insert("hello".to_string(), String("guten tag".to_string())); + tree.insert("goodbye".to_string(), String("sayonara".to_string())); + + let json = Array( + // The following layout below should look a lot like + // the pretty-printed JSON (indent * x) + vec![ + // 0x + String("greetings".to_string()), // 1x + Object(tree), // 1x + 2x + 2x + 1x + ], // 0x + // End JSON array (7 lines) + ); + + // Helper function for counting indents + fn indents(source: &str) -> usize { + let trimmed = source.trim_start_matches(' '); + source.len() - trimmed.len() + } + + // Test up to 4 spaces of indents (more?) + for i in 0..4 { + let mut writer = Vec::new(); + write!(&mut writer, "{}", json::as_pretty_json(&json).indent(i)).unwrap(); + + let printed = from_utf8(&writer[..]).unwrap(); + + // Check for indents at each line + let lines: Vec<&str> = printed.lines().collect(); + assert_eq!(lines.len(), 7); // JSON should be 7 lines + + assert_eq!(indents(lines[0]), 0 * i); // [ + assert_eq!(indents(lines[1]), 1 * i); // "greetings", + assert_eq!(indents(lines[2]), 1 * i); // { + assert_eq!(indents(lines[3]), 2 * i); // "hello": "guten tag", + assert_eq!(indents(lines[4]), 2 * i); // "goodbye": "sayonara" + assert_eq!(indents(lines[5]), 1 * i); // }, + assert_eq!(indents(lines[6]), 0 * i); // ] + + // Finally, test that the pretty-printed JSON is valid + from_str(printed).ok().expect("Pretty-printed JSON is invalid!"); + } +} + +#[test] +fn test_hashmap_with_enum_key() { + use std::collections::HashMap; + #[derive(RustcEncodable, Eq, Hash, PartialEq, RustcDecodable, Debug)] + enum Enum { + Foo, + #[allow(dead_code)] + Bar, + } + let mut map = HashMap::new(); + map.insert(Enum::Foo, 0); + let result = json::encode(&map).unwrap(); + assert_eq!(&result[..], r#"{"Foo":0}"#); + let decoded: HashMap = json::decode(&result).unwrap(); + assert_eq!(map, decoded); +} + +#[test] +fn test_hashmap_with_numeric_key_can_handle_double_quote_delimited_key() { + use std::collections::HashMap; + let json_str = "{\"1\":true}"; + let json_obj = match from_str(json_str) { + Err(_) => panic!("Unable to parse json_str: {:?}", json_str), + Ok(o) => o, + }; + let mut decoder = Decoder::new(json_obj); + let _hm: HashMap = Decodable::decode(&mut decoder).unwrap(); +} + +#[test] +fn test_hashmap_with_numeric_key_will_error_with_string_keys() { + use std::collections::HashMap; + let json_str = "{\"a\":true}"; + let json_obj = match from_str(json_str) { + Err(_) => panic!("Unable to parse json_str: {:?}", json_str), + Ok(o) => o, + }; + let mut decoder = Decoder::new(json_obj); + let result: Result, DecoderError> = Decodable::decode(&mut decoder); + assert_eq!(result, Err(ExpectedError("Number".to_string(), "a".to_string()))); +} + +fn assert_stream_equal(src: &str, expected: Vec<(JsonEvent, Vec>)>) { + let mut parser = Parser::new(src.chars()); + let mut i = 0; + loop { + let evt = match parser.next() { + Some(e) => e, + None => { + break; + } + }; + let (ref expected_evt, ref expected_stack) = expected[i]; + if !parser.stack().is_equal_to(expected_stack) { + panic!("Parser stack is not equal to {:?}", expected_stack); + } + assert_eq!(&evt, expected_evt); + i += 1; + } +} +#[test] +fn test_streaming_parser() { + assert_stream_equal( + r#"{ "foo":"bar", "array" : [0, 1, 2, 3, 4, 5], "idents":[null,true,false]}"#, + vec![ + (ObjectStart, vec![]), + (StringValue("bar".to_string()), vec![StackElement::Key("foo")]), + (ArrayStart, vec![StackElement::Key("array")]), + (U64Value(0), vec![StackElement::Key("array"), StackElement::Index(0)]), + (U64Value(1), vec![StackElement::Key("array"), StackElement::Index(1)]), + (U64Value(2), vec![StackElement::Key("array"), StackElement::Index(2)]), + (U64Value(3), vec![StackElement::Key("array"), StackElement::Index(3)]), + (U64Value(4), vec![StackElement::Key("array"), StackElement::Index(4)]), + (U64Value(5), vec![StackElement::Key("array"), StackElement::Index(5)]), + (ArrayEnd, vec![StackElement::Key("array")]), + (ArrayStart, vec![StackElement::Key("idents")]), + (NullValue, vec![StackElement::Key("idents"), StackElement::Index(0)]), + (BooleanValue(true), vec![StackElement::Key("idents"), StackElement::Index(1)]), + (BooleanValue(false), vec![StackElement::Key("idents"), StackElement::Index(2)]), + (ArrayEnd, vec![StackElement::Key("idents")]), + (ObjectEnd, vec![]), + ], + ); +} +fn last_event(src: &str) -> JsonEvent { + let mut parser = Parser::new(src.chars()); + let mut evt = NullValue; + loop { + evt = match parser.next() { + Some(e) => e, + None => return evt, + } + } +} + +#[test] +fn test_read_object_streaming() { + assert_eq!(last_event("{ "), Error(SyntaxError(EOFWhileParsingObject, 1, 3))); + assert_eq!(last_event("{1"), Error(SyntaxError(KeyMustBeAString, 1, 2))); + assert_eq!(last_event("{ \"a\""), Error(SyntaxError(EOFWhileParsingObject, 1, 6))); + assert_eq!(last_event("{\"a\""), Error(SyntaxError(EOFWhileParsingObject, 1, 5))); + assert_eq!(last_event("{\"a\" "), Error(SyntaxError(EOFWhileParsingObject, 1, 6))); + + assert_eq!(last_event("{\"a\" 1"), Error(SyntaxError(ExpectedColon, 1, 6))); + assert_eq!(last_event("{\"a\":"), Error(SyntaxError(EOFWhileParsingValue, 1, 6))); + assert_eq!(last_event("{\"a\":1"), Error(SyntaxError(EOFWhileParsingObject, 1, 7))); + assert_eq!(last_event("{\"a\":1 1"), Error(SyntaxError(InvalidSyntax, 1, 8))); + assert_eq!(last_event("{\"a\":1,"), Error(SyntaxError(EOFWhileParsingObject, 1, 8))); + assert_eq!(last_event("{\"a\":1,}"), Error(SyntaxError(TrailingComma, 1, 8))); + + assert_stream_equal("{}", vec![(ObjectStart, vec![]), (ObjectEnd, vec![])]); + assert_stream_equal( + "{\"a\": 3}", + vec![ + (ObjectStart, vec![]), + (U64Value(3), vec![StackElement::Key("a")]), + (ObjectEnd, vec![]), + ], + ); + assert_stream_equal( + "{ \"a\": null, \"b\" : true }", + vec![ + (ObjectStart, vec![]), + (NullValue, vec![StackElement::Key("a")]), + (BooleanValue(true), vec![StackElement::Key("b")]), + (ObjectEnd, vec![]), + ], + ); + assert_stream_equal( + "{\"a\" : 1.0 ,\"b\": [ true ]}", + vec![ + (ObjectStart, vec![]), + (F64Value(1.0), vec![StackElement::Key("a")]), + (ArrayStart, vec![StackElement::Key("b")]), + (BooleanValue(true), vec![StackElement::Key("b"), StackElement::Index(0)]), + (ArrayEnd, vec![StackElement::Key("b")]), + (ObjectEnd, vec![]), + ], + ); + assert_stream_equal( + r#"{ + "a": 1.0, + "b": [ + true, + "foo\nbar", + { "c": {"d": null} } + ] + }"#, + vec![ + (ObjectStart, vec![]), + (F64Value(1.0), vec![StackElement::Key("a")]), + (ArrayStart, vec![StackElement::Key("b")]), + (BooleanValue(true), vec![StackElement::Key("b"), StackElement::Index(0)]), + ( + StringValue("foo\nbar".to_string()), + vec![StackElement::Key("b"), StackElement::Index(1)], + ), + (ObjectStart, vec![StackElement::Key("b"), StackElement::Index(2)]), + ( + ObjectStart, + vec![StackElement::Key("b"), StackElement::Index(2), StackElement::Key("c")], + ), + ( + NullValue, + vec![ + StackElement::Key("b"), + StackElement::Index(2), + StackElement::Key("c"), + StackElement::Key("d"), + ], + ), + ( + ObjectEnd, + vec![StackElement::Key("b"), StackElement::Index(2), StackElement::Key("c")], + ), + (ObjectEnd, vec![StackElement::Key("b"), StackElement::Index(2)]), + (ArrayEnd, vec![StackElement::Key("b")]), + (ObjectEnd, vec![]), + ], + ); +} +#[test] +fn test_read_array_streaming() { + assert_stream_equal("[]", vec![(ArrayStart, vec![]), (ArrayEnd, vec![])]); + assert_stream_equal("[ ]", vec![(ArrayStart, vec![]), (ArrayEnd, vec![])]); + assert_stream_equal( + "[true]", + vec![ + (ArrayStart, vec![]), + (BooleanValue(true), vec![StackElement::Index(0)]), + (ArrayEnd, vec![]), + ], + ); + assert_stream_equal( + "[ false ]", + vec![ + (ArrayStart, vec![]), + (BooleanValue(false), vec![StackElement::Index(0)]), + (ArrayEnd, vec![]), + ], + ); + assert_stream_equal( + "[null]", + vec![(ArrayStart, vec![]), (NullValue, vec![StackElement::Index(0)]), (ArrayEnd, vec![])], + ); + assert_stream_equal( + "[3, 1]", + vec![ + (ArrayStart, vec![]), + (U64Value(3), vec![StackElement::Index(0)]), + (U64Value(1), vec![StackElement::Index(1)]), + (ArrayEnd, vec![]), + ], + ); + assert_stream_equal( + "\n[3, 2]\n", + vec![ + (ArrayStart, vec![]), + (U64Value(3), vec![StackElement::Index(0)]), + (U64Value(2), vec![StackElement::Index(1)]), + (ArrayEnd, vec![]), + ], + ); + assert_stream_equal( + "[2, [4, 1]]", + vec![ + (ArrayStart, vec![]), + (U64Value(2), vec![StackElement::Index(0)]), + (ArrayStart, vec![StackElement::Index(1)]), + (U64Value(4), vec![StackElement::Index(1), StackElement::Index(0)]), + (U64Value(1), vec![StackElement::Index(1), StackElement::Index(1)]), + (ArrayEnd, vec![StackElement::Index(1)]), + (ArrayEnd, vec![]), + ], + ); + + assert_eq!(last_event("["), Error(SyntaxError(EOFWhileParsingValue, 1, 2))); + + assert_eq!(from_str("["), Err(SyntaxError(EOFWhileParsingValue, 1, 2))); + assert_eq!(from_str("[1"), Err(SyntaxError(EOFWhileParsingArray, 1, 3))); + assert_eq!(from_str("[1,"), Err(SyntaxError(EOFWhileParsingValue, 1, 4))); + assert_eq!(from_str("[1,]"), Err(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(from_str("[6 7]"), Err(SyntaxError(InvalidSyntax, 1, 4))); +} +#[test] +fn test_trailing_characters_streaming() { + assert_eq!(last_event("nulla"), Error(SyntaxError(TrailingCharacters, 1, 5))); + assert_eq!(last_event("truea"), Error(SyntaxError(TrailingCharacters, 1, 5))); + assert_eq!(last_event("falsea"), Error(SyntaxError(TrailingCharacters, 1, 6))); + assert_eq!(last_event("1a"), Error(SyntaxError(TrailingCharacters, 1, 2))); + assert_eq!(last_event("[]a"), Error(SyntaxError(TrailingCharacters, 1, 3))); + assert_eq!(last_event("{}a"), Error(SyntaxError(TrailingCharacters, 1, 3))); +} +#[test] +fn test_read_identifiers_streaming() { + assert_eq!(Parser::new("null".chars()).next(), Some(NullValue)); + assert_eq!(Parser::new("true".chars()).next(), Some(BooleanValue(true))); + assert_eq!(Parser::new("false".chars()).next(), Some(BooleanValue(false))); + + assert_eq!(last_event("n"), Error(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(last_event("nul"), Error(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(last_event("t"), Error(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(last_event("truz"), Error(SyntaxError(InvalidSyntax, 1, 4))); + assert_eq!(last_event("f"), Error(SyntaxError(InvalidSyntax, 1, 2))); + assert_eq!(last_event("faz"), Error(SyntaxError(InvalidSyntax, 1, 3))); +} + +#[test] +fn test_to_json() { + use json::ToJson; + use std::collections::{BTreeMap, HashMap}; + + let array2 = Array(vec![U64(1), U64(2)]); + let array3 = Array(vec![U64(1), U64(2), U64(3)]); + let object = { + let mut tree_map = BTreeMap::new(); + tree_map.insert("a".to_string(), U64(1)); + tree_map.insert("b".to_string(), U64(2)); + Object(tree_map) + }; + + assert_eq!(array2.to_json(), array2); + assert_eq!(object.to_json(), object); + assert_eq!(3_isize.to_json(), I64(3)); + assert_eq!(4_i8.to_json(), I64(4)); + assert_eq!(5_i16.to_json(), I64(5)); + assert_eq!(6_i32.to_json(), I64(6)); + assert_eq!(7_i64.to_json(), I64(7)); + assert_eq!(8_usize.to_json(), U64(8)); + assert_eq!(9_u8.to_json(), U64(9)); + assert_eq!(10_u16.to_json(), U64(10)); + assert_eq!(11_u32.to_json(), U64(11)); + assert_eq!(12_u64.to_json(), U64(12)); + assert_eq!(13.0_f32.to_json(), F64(13.0_f64)); + assert_eq!(14.0_f64.to_json(), F64(14.0_f64)); + assert_eq!(().to_json(), Null); + assert_eq!(f32::INFINITY.to_json(), Null); + assert_eq!(f64::NAN.to_json(), Null); + assert_eq!(true.to_json(), Boolean(true)); + assert_eq!(false.to_json(), Boolean(false)); + assert_eq!("abc".to_json(), String("abc".to_string())); + assert_eq!("abc".to_string().to_json(), String("abc".to_string())); + assert_eq!((1_usize, 2_usize).to_json(), array2); + assert_eq!((1_usize, 2_usize, 3_usize).to_json(), array3); + assert_eq!([1_usize, 2_usize].to_json(), array2); + assert_eq!((&[1_usize, 2_usize, 3_usize]).to_json(), array3); + assert_eq!((vec![1_usize, 2_usize]).to_json(), array2); + assert_eq!(vec![1_usize, 2_usize, 3_usize].to_json(), array3); + let mut tree_map = BTreeMap::new(); + tree_map.insert("a".to_string(), 1 as usize); + tree_map.insert("b".to_string(), 2); + assert_eq!(tree_map.to_json(), object); + let mut hash_map = HashMap::new(); + hash_map.insert("a".to_string(), 1 as usize); + hash_map.insert("b".to_string(), 2); + assert_eq!(hash_map.to_json(), object); + assert_eq!(Some(15).to_json(), I64(15)); + assert_eq!(Some(15 as usize).to_json(), U64(15)); + assert_eq!(None::.to_json(), Null); +} + +#[test] +fn test_encode_hashmap_with_arbitrary_key() { + use std::collections::HashMap; + #[derive(PartialEq, Eq, Hash, RustcEncodable)] + struct ArbitraryType(usize); + let mut hm: HashMap = HashMap::new(); + hm.insert(ArbitraryType(1), true); + let mut mem_buf = string::String::new(); + let mut encoder = Encoder::new(&mut mem_buf); + let result = hm.encode(&mut encoder); + match result.unwrap_err() { + EncoderError::BadHashmapKey => (), + _ => panic!("expected bad hash map key"), + } +} diff --git a/src/libserialize/tests/leb128.rs b/src/librustc_serialize/tests/leb128.rs similarity index 97% rename from src/libserialize/tests/leb128.rs rename to src/librustc_serialize/tests/leb128.rs index bf5afb409f861..b0f7e785b7877 100644 --- a/src/libserialize/tests/leb128.rs +++ b/src/librustc_serialize/tests/leb128.rs @@ -1,4 +1,3 @@ -extern crate serialize as rustc_serialize; use rustc_serialize::leb128::*; macro_rules! impl_test_unsigned_leb128 { diff --git a/src/libserialize/tests/opaque.rs b/src/librustc_serialize/tests/opaque.rs similarity index 87% rename from src/libserialize/tests/opaque.rs rename to src/librustc_serialize/tests/opaque.rs index aa099bb8a3689..c827391700709 100644 --- a/src/libserialize/tests/opaque.rs +++ b/src/librustc_serialize/tests/opaque.rs @@ -1,7 +1,5 @@ #![allow(rustc::internal)] -extern crate serialize as rustc_serialize; - use rustc_serialize::opaque::{Decoder, Encoder}; use rustc_serialize::{Decodable, Encodable}; use std::fmt::Debug; @@ -53,7 +51,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 +59,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 +90,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/librustc_session/Cargo.toml b/src/librustc_session/Cargo.toml index 3895d0f8061c0..abce7359c0ed7 100644 --- a/src/librustc_session/Cargo.toml +++ b/src/librustc_session/Cargo.toml @@ -9,14 +9,15 @@ name = "rustc_session" path = "lib.rs" [dependencies] +bitflags = "1.2.1" +getopts = "0.2" log = "0.4" rustc_errors = { path = "../librustc_errors" } rustc_feature = { path = "../librustc_feature" } rustc_target = { path = "../librustc_target" } -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_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 c273e7fdbf916..c5a866817cb4a 100644 --- a/src/librustc_session/config.rs +++ b/src/librustc_session/config.rs @@ -5,11 +5,12 @@ pub use crate::options::*; use crate::lint; use crate::search_paths::SearchPath; -use crate::utils::NativeLibraryKind; +use crate::utils::NativeLibKind; use crate::{early_error, early_warn, Session}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::impl_stable_hash_via_hash; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_target::spec::{Target, TargetTriple}; @@ -18,9 +19,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, @@ -36,39 +38,72 @@ pub struct Config { pub ptr_width: u32, } -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum Sanitizer { - Address, - Leak, - Memory, - Thread, +bitflags! { + #[derive(Default, RustcEncodable, RustcDecodable)] + pub struct SanitizerSet: u8 { + const ADDRESS = 1 << 0; + const LEAK = 1 << 1; + const MEMORY = 1 << 2; + const THREAD = 1 << 3; + } } -impl fmt::Display for Sanitizer { +/// Formats a sanitizer set as a comma separated list of sanitizers' names. +impl fmt::Display for SanitizerSet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Sanitizer::Address => "address".fmt(f), - Sanitizer::Leak => "leak".fmt(f), - Sanitizer::Memory => "memory".fmt(f), - Sanitizer::Thread => "thread".fmt(f), + let mut first = true; + for s in *self { + let name = match s { + SanitizerSet::ADDRESS => "address", + SanitizerSet::LEAK => "leak", + SanitizerSet::MEMORY => "memory", + SanitizerSet::THREAD => "thread", + _ => panic!("unrecognized sanitizer {:?}", s), + }; + if !first { + f.write_str(",")?; + } + f.write_str(name)?; + first = false; } + Ok(()) } } -impl FromStr for Sanitizer { - type Err = (); - fn from_str(s: &str) -> Result { - match s { - "address" => Ok(Sanitizer::Address), - "leak" => Ok(Sanitizer::Leak), - "memory" => Ok(Sanitizer::Memory), - "thread" => Ok(Sanitizer::Thread), - _ => Err(()), - } +impl IntoIterator for SanitizerSet { + type Item = SanitizerSet; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + [SanitizerSet::ADDRESS, SanitizerSet::LEAK, SanitizerSet::MEMORY, SanitizerSet::THREAD] + .iter() + .copied() + .filter(|&s| self.contains(s)) + .collect::>() + .into_iter() } } -/// The different settings that the `-Z control_flow_guard` flag can have. +impl HashStable for SanitizerSet { + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + self.bits().hash_stable(ctx, hasher); + } +} + +/// The different settings that the `-Z strip` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum Strip { + /// Do not strip at all. + None, + + /// Strip debuginfo. + Debuginfo, + + /// Strip all symbols. + Symbols, +} + +/// The different settings that the `-Z control-flow-guard` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum CFGuard { /// Do not emit Control Flow Guard metadata or checks. @@ -616,10 +651,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 +658,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, } } } @@ -716,10 +747,12 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { } } } - if let Some(s) = &sess.opts.debugging_opts.sanitizer { + + for s in sess.opts.debugging_opts.sanitizer { let symbol = Symbol::intern(&s.to_string()); ret.insert((sym::sanitize, Some(symbol))); } + if sess.opts.debug_assertions { ret.insert((Symbol::intern("debug_assertions"), None)); } @@ -748,25 +781,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 +1049,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 + } else { + passed_arg_pos + }; if lint_name == "help" { describe_lints = true; } else { @@ -1140,7 +1186,7 @@ pub fn parse_error_format( _ => {} } - return error_format; + error_format } fn parse_crate_edition(matches: &getopts::Matches) -> Edition { @@ -1286,33 +1332,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(|m| PathBuf::from(m)) -} - fn collect_print_requests( cg: &mut CodegenOptions, dopts: &mut DebuggingOptions, @@ -1328,18 +1347,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 +1415,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 +1455,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!( @@ -1469,7 +1475,7 @@ fn select_debuginfo( fn parse_libs( matches: &getopts::Matches, error_format: ErrorOutputType, -) -> Vec<(String, Option, Option)> { +) -> Vec<(String, Option, NativeLibKind)> { matches .opt_strs("l") .into_iter() @@ -1479,13 +1485,11 @@ fn parse_libs( let mut parts = s.splitn(2, '='); let kind = parts.next().unwrap(); let (name, kind) = match (parts.next(), kind) { - (None, name) => (name, None), - (Some(name), "dylib") => (name, Some(NativeLibraryKind::NativeUnknown)), - (Some(name), "framework") => (name, Some(NativeLibraryKind::NativeFramework)), - (Some(name), "static") => (name, Some(NativeLibraryKind::NativeStatic)), - (Some(name), "static-nobundle") => { - (name, Some(NativeLibraryKind::NativeStaticNobundle)) - } + (None, name) => (name, NativeLibKind::Unspecified), + (Some(name), "dylib") => (name, NativeLibKind::Dylib), + (Some(name), "framework") => (name, NativeLibKind::Framework), + (Some(name), "static") => (name, NativeLibKind::StaticBundle), + (Some(name), "static-nobundle") => (name, NativeLibKind::StaticNoBundle), (_, s) => { early_error( error_format, @@ -1497,9 +1501,7 @@ fn parse_libs( ); } }; - if kind == Some(NativeLibraryKind::NativeStaticNobundle) - && !nightly_options::is_nightly_build() - { + if kind == NativeLibKind::StaticNoBundle && !nightly_options::is_nightly_build() { early_error( error_format, "the library kind 'static-nobundle' is only \ @@ -1515,10 +1517,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 +1670,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 +1679,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 +1687,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 +1705,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; @@ -1827,6 +1849,7 @@ fn parse_pretty( } } }; + log::debug!("got unpretty option: {:?}", first); first } } @@ -1955,11 +1978,11 @@ impl PpMode { use PpMode::*; use PpSourceMode::*; match *self { - PpmSource(PpmNormal) | PpmSource(PpmEveryBodyLoops) | PpmSource(PpmIdentified) => false, + PpmSource(PpmNormal | PpmIdentified) => false, - PpmSource(PpmExpanded) - | PpmSource(PpmExpandedIdentified) - | PpmSource(PpmExpandedHygiene) + PpmSource( + PpmExpanded | PpmEveryBodyLoops | PpmExpandedIdentified | PpmExpandedHygiene, + ) | PpmHir(_) | PpmHirTree(_) | PpmMir @@ -1998,13 +2021,15 @@ impl PpMode { crate mod dep_tracking { use super::{ CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, - OutputTypes, Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion, + OutputTypes, Passes, SanitizerSet, SourceFileHashAlgorithm, SwitchWithOptPath, + SymbolManglingVersion, }; use crate::lint; - use crate::utils::NativeLibraryKind; + use crate::utils::NativeLibKind; 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,11 +2077,13 @@ 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); impl_dep_tracking_hash_via_hash!(Option); - impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(CrateType); impl_dep_tracking_hash_via_hash!(MergeFunctions); impl_dep_tracking_hash_via_hash!(PanicStrategy); @@ -2067,27 +2094,22 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(DebugInfo); impl_dep_tracking_hash_via_hash!(UnstableFeatures); impl_dep_tracking_hash_via_hash!(OutputTypes); - impl_dep_tracking_hash_via_hash!(NativeLibraryKind); - impl_dep_tracking_hash_via_hash!(Sanitizer); - impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(NativeLibKind); + impl_dep_tracking_hash_via_hash!(SanitizerSet); impl_dep_tracking_hash_via_hash!(CFGuard); impl_dep_tracking_hash_via_hash!(TargetTriple); impl_dep_tracking_hash_via_hash!(Edition); impl_dep_tracking_hash_via_hash!(LinkerPluginLto); impl_dep_tracking_hash_via_hash!(SwitchWithOptPath); impl_dep_tracking_hash_via_hash!(SymbolManglingVersion); + impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_for_sortable_vec_of!(String); impl_dep_tracking_hash_for_sortable_vec_of!(PathBuf); impl_dep_tracking_hash_for_sortable_vec_of!(CrateType); impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); - impl_dep_tracking_hash_for_sortable_vec_of!(( - String, - Option, - Option - )); + impl_dep_tracking_hash_for_sortable_vec_of!((String, Option, NativeLibKind)); impl_dep_tracking_hash_for_sortable_vec_of!((String, u64)); - impl_dep_tracking_hash_for_sortable_vec_of!(Sanitizer); impl DepTrackingHash for (T1, T2) where diff --git a/src/librustc_session/filesearch.rs b/src/librustc_session/filesearch.rs index 05e6da43ea7a9..5586b82b0edc0 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; @@ -41,30 +41,34 @@ impl<'a> FileSearch<'a> { make_target_lib_path(self.sysroot, self.triple) } + pub fn get_selfcontained_lib_path(&self) -> PathBuf { + self.get_lib_path().join("self-contained") + } + 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()); } } } @@ -94,7 +98,7 @@ impl<'a> FileSearch<'a> { p.push(RUST_LIB_DIR); p.push(&self.triple); p.push("bin"); - vec![p] + vec![p.clone(), p.join("self-contained")] } } @@ -143,8 +147,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 4101c32d547aa..be9d2e7be2777 100644 --- a/src/librustc_session/lib.rs +++ b/src/librustc_session/lib.rs @@ -1,10 +1,8 @@ #![feature(crate_visibility_modifier)] -#![feature(test)] +#![feature(or_patterns)] -// 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 _; +#[macro_use] +extern crate bitflags; pub mod cgu_reuse_tracker; pub mod utils; @@ -21,3 +19,7 @@ pub mod search_paths; 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 3b79972ccbf5b..0dcbee08abea1 100644 --- a/src/librustc_session/lint.rs +++ b/src/librustc_session/lint.rs @@ -1,6 +1,7 @@ pub use self::Level::*; use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; use rustc_span::edition::Edition; use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol}; @@ -84,6 +85,11 @@ pub struct Lint { pub future_incompatible: Option, pub is_plugin: bool, + + /// `Some` if this lint is feature gated, otherwise `None`. + pub feature_gate: Option, + + pub crate_level_only: bool, } /// Extra information for a future incompatibility lint. @@ -106,6 +112,8 @@ impl Lint { is_plugin: false, report_in_external_macro: false, future_incompatible: None, + feature_gate: None, + crate_level_only: false, } } @@ -194,7 +202,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. @@ -206,7 +214,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. @@ -274,7 +283,9 @@ macro_rules! declare_lint { ); ); ($vis: vis $NAME: ident, $Level: ident, $desc: expr, - $(@future_incompatible = $fi:expr;)? $($v:ident),*) => ( + $(@future_incompatible = $fi:expr;)? + $(@feature_gate = $gate:expr;)? + $($v:ident),*) => ( $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { name: stringify!($NAME), default_level: $crate::lint::$Level, @@ -283,6 +294,7 @@ macro_rules! declare_lint { is_plugin: false, $($v: true,)* $(future_incompatible: Some($fi),)* + $(feature_gate: Some($gate),)* ..$crate::lint::Lint::default_fields_for_macro() }; ); @@ -326,6 +338,8 @@ macro_rules! declare_tool_lint { report_in_external_macro: $external, future_incompatible: None, is_plugin: true, + feature_gate: None, + crate_level_only: false, }; ); } @@ -345,14 +359,14 @@ pub trait LintPass { fn name(&self) -> &'static str; } -/// Implements `LintPass for $name` with the given list of `Lint` statics. +/// Implements `LintPass for $ty` with the given list of `Lint` statics. #[macro_export] macro_rules! impl_lint_pass { - ($name:ident => [$($lint:expr),* $(,)?]) => { - impl $crate::lint::LintPass for $name { - fn name(&self) -> &'static str { stringify!($name) } + ($ty:ty => [$($lint:expr),* $(,)?]) => { + impl $crate::lint::LintPass for $ty { + fn name(&self) -> &'static str { stringify!($ty) } } - impl $name { + impl $ty { pub fn get_lints() -> $crate::lint::LintArray { $crate::lint_array!($($lint),*) } } }; @@ -367,3 +381,45 @@ macro_rules! declare_lint_pass { $crate::impl_lint_pass!($name => [$($lint),*]); }; } + +pub fn add_elided_lifetime_in_path_suggestion( + sess: &crate::Session, + db: &mut DiagnosticBuilder<'_>, + n: usize, + path_span: Span, + incl_angl_brckt: bool, + insertion_span: Span, + anon_lts: String, +) { + let (replace_span, suggestion) = if incl_angl_brckt { + (insertion_span, anon_lts) + } else { + // When possible, prefer a suggestion that replaces the whole + // `Path` expression with `Path<'_, T>`, rather than inserting `'_, ` + // at a point (which makes for an ugly/confusing label) + if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) { + // But our spans can get out of whack due to macros; if the place we think + // we want to insert `'_` isn't even within the path expression's span, we + // should bail out of making any suggestion rather than panicking on a + // subtract-with-overflow or string-slice-out-out-bounds (!) + // FIXME: can we do better? + if insertion_span.lo().0 < path_span.lo().0 { + return; + } + let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize; + if insertion_index > snippet.len() { + return; + } + let (before, after) = snippet.split_at(insertion_index); + (path_span, format!("{}{}{}", before, anon_lts, after)) + } else { + (insertion_span, anon_lts) + } + }; + db.span_suggestion( + replace_span, + &format!("indicate the anonymous lifetime{}", pluralize!(n)), + suggestion, + Applicability::MachineApplicable, + ); +} diff --git a/src/librustc_session/lint/builtin.rs b/src/librustc_session/lint/builtin.rs index f8a4e024605aa..5deee6eb48e6a 100644 --- a/src/librustc_session/lint/builtin.rs +++ b/src/librustc_session/lint/builtin.rs @@ -7,6 +7,7 @@ use crate::lint::FutureIncompatibleInfo; use crate::{declare_lint, declare_lint_pass}; use rustc_span::edition::Edition; +use rustc_span::symbol::sym; declare_lint! { pub ILL_FORMED_ATTRIBUTE_INPUT, @@ -16,6 +17,7 @@ declare_lint! { reference: "issue #57571 ", edition: None, }; + crate_level_only } declare_lint! { @@ -71,6 +73,13 @@ declare_lint! { "extern crates that are never used" } +declare_lint! { + pub UNUSED_CRATE_DEPENDENCIES, + Allow, + "crate dependencies that are never used", + crate_level_only +} + declare_lint! { pub UNUSED_QUALIFICATIONS, Allow, @@ -159,7 +168,8 @@ declare_lint! { declare_lint! { pub UNKNOWN_CRATE_TYPES, Deny, - "unknown crate type found in `#[crate_type]` directive" + "unknown crate type found in `#[crate_type]` directive", + crate_level_only } declare_lint! { @@ -216,10 +226,16 @@ declare_lint! { "lints that have been renamed or removed" } +declare_lint! { + pub UNALIGNED_REFERENCES, + Allow, + "detects unaligned references to fields of packed structs", +} + declare_lint! { pub SAFE_PACKED_BORROWS, Warn, - "safe borrows of fields of packed structs were was erroneously allowed", + "safe borrows of fields of packed structs were erroneously allowed", @future_incompatible = FutureIncompatibleInfo { reference: "issue #46043 ", edition: None, @@ -326,7 +342,8 @@ declare_lint! { declare_lint! { pub ELIDED_LIFETIMES_IN_PATHS, Allow, - "hidden lifetime parameters in types are deprecated" + "hidden lifetime parameters in types are deprecated", + crate_level_only } declare_lint! { @@ -386,6 +403,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, @@ -434,6 +463,7 @@ declare_lint! { reference: "issue #52234 ", edition: None, }; + crate_level_only } declare_lint! { @@ -446,7 +476,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 +526,29 @@ 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! { + pub UNSAFE_OP_IN_UNSAFE_FN, + Allow, + "unsafe operations in unsafe functions without an explicit unsafe block are deprecated", + @feature_gate = sym::unsafe_block_in_unsafe_fn; +} + +declare_lint! { + pub CENUM_IMPL_DROP_CAST, + Warn, + "a C-like enum implementing Drop is cast", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #73333 ", + edition: None, + }; +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -505,6 +558,7 @@ declare_lint_pass! { UNCONDITIONAL_PANIC, UNUSED_IMPORTS, UNUSED_EXTERN_CRATES, + UNUSED_CRATE_DEPENDENCIES, UNUSED_QUALIFICATIONS, UNKNOWN_LINTS, UNUSED_VARIABLES, @@ -527,6 +581,7 @@ declare_lint_pass! { INVALID_TYPE_PARAM_DEFAULT, CONST_ERR, RENAMED_AND_REMOVED_LINTS, + UNALIGNED_REFERENCES, SAFE_PACKED_BORROWS, PATTERNS_IN_FNS_WITHOUT_BODY, MISSING_FRAGMENT_SPECIFIER, @@ -547,6 +602,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 +619,10 @@ declare_lint_pass! { INDIRECT_STRUCTURAL_MATCH, SOFT_UNSTABLE, INLINE_NO_SANITIZE, + ASM_SUB_REGISTER, + UNSAFE_OP_IN_UNSAFE_FN, + INCOMPLETE_INCLUDE, + CENUM_IMPL_DROP_CAST, ] } diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index a1ecf4e8528be..973891eb84eda 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -3,13 +3,14 @@ use crate::config::*; use crate::early_error; use crate::lint; use crate::search_paths::SearchPath; -use crate::utils::NativeLibraryKind; +use crate::utils::NativeLibKind; -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; @@ -92,7 +93,7 @@ top_level_options!( describe_lints: bool [UNTRACKED], output_types: OutputTypes [TRACKED], search_paths: Vec [UNTRACKED], - libs: Vec<(String, Option, Option)> [TRACKED], + libs: Vec<(String, Option, NativeLibKind)> [TRACKED], maybe_sysroot: Option [UNTRACKED], target_triple: TargetTriple [TRACKED], @@ -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_sanitizers: &str = "comma separated list of sanitizers: `address`, `leak`, `memory` or `thread`"; + pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; + pub const parse_cfguard: &str = + "either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; + 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 } } @@ -457,24 +459,15 @@ macro_rules! options { true } - fn parse_sanitizer(slot: &mut Option, v: Option<&str>) -> bool { - if let Some(Ok(s)) = v.map(str::parse) { - *slot = Some(s); - true - } else { - false - } - } - - fn parse_sanitizer_list(slot: &mut Vec, v: Option<&str>) -> bool { + fn parse_sanitizers(slot: &mut SanitizerSet, v: Option<&str>) -> bool { if let Some(v) = v { - for s in v.split(',').map(str::parse) { - if let Ok(s) = s { - if !slot.contains(&s) { - slot.push(s); - } - } else { - return false; + for s in v.split(',') { + *slot |= match s { + "address" => SanitizerSet::ADDRESS, + "leak" => SanitizerSet::LEAK, + "memory" => SanitizerSet::MEMORY, + "thread" => SanitizerSet::THREAD, + _ => return false, } } true @@ -484,31 +477,46 @@ 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_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool { + fn parse_strip(slot: &mut Strip, v: Option<&str>) -> bool { match v { - Some("disabled") => *slot = CFGuard::Disabled, - Some("nochecks") => *slot = CFGuard::NoChecks, - Some("checks") => *slot = CFGuard::Checks, + 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 { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { + CFGuard::Checks + } else { + CFGuard::Disabled + }; + return true + } + } + + *slot = match v { + None => CFGuard::Checks, + Some("checks") => CFGuard::Checks, + Some("nochecks") => CFGuard::NoChecks, + Some(_) => return false, + }; + true + } + fn parse_linker_flavor(slote: &mut Option, v: Option<&str>) -> bool { match v.and_then(LinkerFlavor::from_str) { Some(lf) => *slote = Some(lf), @@ -608,6 +616,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 +652,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, [TRACKED], + "use Windows Control Flow Guard (default: no)"), + 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 +838,211 @@ 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_coverage: bool = (false, parse_bool, [TRACKED], + "instrument the generated code with LLVM code region counters to (in the \ + future) generate coverage reports (default: no; note, the compiler build \ + config must include `profiler = true`)"), + 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)"), + profile_emit: Option = (None, parse_opt_pathbuf, [TRACKED], + "file path to emit profiling data at runtime when using 'profile' \ + (default based on relative source path)"), + 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: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], + "use a sanitizer"), + sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED], + "enable origins tracking in MemorySanitizer"), + sanitizer_recover: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED], + "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)"), + span_debug: bool = (false, parse_bool, [UNTRACKED], + "forward proc_macro::Span's `Debug` impl to `Span`"), + // 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 +1053,19 @@ 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"), + validate_mir: bool = (false, parse_bool, [UNTRACKED], + "validate MIR after each transformation"), + 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 new file mode 100644 index 0000000000000..52216188397d7 --- /dev/null +++ b/src/librustc_session/output.rs @@ -0,0 +1,222 @@ +//! Related to out filenames of compilation (e.g. save analysis, binaries). +use crate::config::{CrateType, Input, OutputFilenames, OutputType}; +use crate::Session; +use rustc_ast::{ast, attr}; +use rustc_span::symbol::sym; +use rustc_span::Span; +use std::path::{Path, PathBuf}; + +pub fn out_filename( + sess: &Session, + crate_type: CrateType, + outputs: &OutputFilenames, + crate_name: &str, +) -> PathBuf { + let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); + let out_filename = outputs + .outputs + .get(&OutputType::Exe) + .and_then(|s| s.to_owned()) + .or_else(|| outputs.single_output_file.clone()) + .unwrap_or(default_filename); + + check_file_is_writeable(&out_filename, sess); + + out_filename +} + +/// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers +/// check this already -- however, the Linux linker will happily overwrite a +/// read-only file. We should be consistent. +pub fn check_file_is_writeable(file: &Path, sess: &Session) { + if !is_writeable(file) { + sess.fatal(&format!( + "output file {} is not writeable -- check its \ + permissions", + file.display() + )); + } +} + +fn is_writeable(p: &Path) -> bool { + match p.metadata() { + Err(..) => true, + Ok(m) => !m.permissions().readonly(), + } +} + +pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input: &Input) -> String { + let validate = |s: String, span: Option| { + validate_crate_name(sess, &s, span); + s + }; + + // Look in attributes 100% of the time to make sure the attribute is marked + // as used. After doing this, however, we still prioritize a crate name from + // the command line over one found in the #[crate_name] attribute. If we + // find both we ensure that they're the same later on as well. + let attr_crate_name = + attr::find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s))); + + if let Some(sess) = sess { + if let Some(ref s) = sess.opts.crate_name { + if let Some((attr, name)) = attr_crate_name { + if name.as_str() != *s { + let msg = format!( + "`--crate-name` and `#[crate_name]` are \ + required to match, but `{}` != `{}`", + s, name + ); + sess.span_err(attr.span, &msg); + } + } + return validate(s.clone(), None); + } + } + + if let Some((attr, s)) = attr_crate_name { + return validate(s.to_string(), Some(attr.span)); + } + if let Input::File(ref path) = *input { + if let Some(s) = path.file_stem().and_then(|s| s.to_str()) { + if s.starts_with('-') { + let msg = format!( + "crate names cannot start with a `-`, but \ + `{}` has a leading hyphen", + s + ); + if let Some(sess) = sess { + sess.err(&msg); + } + } else { + return validate(s.replace("-", "_"), None); + } + } + } + + "rust_out".to_string() +} + +pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option) { + let mut err_count = 0; + { + let mut say = |s: &str| { + match (sp, sess) { + (_, None) => panic!("{}", s), + (Some(sp), Some(sess)) => sess.span_err(sp, s), + (None, Some(sess)) => sess.err(s), + } + err_count += 1; + }; + if s.is_empty() { + say("crate name must not be empty"); + } + for c in s.chars() { + if c.is_alphanumeric() { + continue; + } + if c == '_' { + continue; + } + say(&format!("invalid character `{}` in crate name: `{}`", c, s)); + } + } + + if err_count > 0 { + sess.unwrap().abort_if_errors(); + } +} + +pub fn filename_for_metadata( + sess: &Session, + crate_name: &str, + outputs: &OutputFilenames, +) -> PathBuf { + let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); + + let out_filename = outputs + .single_output_file + .clone() + .unwrap_or_else(|| outputs.out_directory.join(&format!("lib{}.rmeta", libname))); + + check_file_is_writeable(&out_filename, sess); + + out_filename +} + +pub fn filename_for_input( + sess: &Session, + crate_type: CrateType, + crate_name: &str, + outputs: &OutputFilenames, +) -> PathBuf { + let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); + + match crate_type { + 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)) + } + 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)) + } + 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..]) } + } + } +} + +/// Returns default crate type for target +/// +/// Default crate type is used when crate type isn't provided neither +/// through cmd line arguments nor through crate attributes +/// +/// It is CrateType::Executable for all platforms but iOS as there is no +/// 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) -> CrateType { + if !sess.target.target.options.executables { + CrateType::Staticlib + } else { + CrateType::Executable + } +} + +/// Checks if target supports crate_type as output +pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool { + match crate_type { + CrateType::Cdylib | CrateType::Dylib | CrateType::ProcMacro => { + if !sess.target.target.options.dynamic_linking { + return true; + } + if sess.crt_static(Some(crate_type)) + && !sess.target.target.options.crt_static_allows_dylibs + { + return true; + } + } + _ => {} + } + if sess.target.target.options.only_cdylib { + match crate_type { + CrateType::ProcMacro | CrateType::Dylib => return true, + _ => {} + } + } + if !sess.target.target.options.executables { + if crate_type == CrateType::Executable { + return true; + } + } + + false +} diff --git a/src/librustc_session/parse.rs b/src/librustc_session/parse.rs index 387d35422c43e..93b27cabfb67b 100644 --- a/src/librustc_session/parse.rs +++ b/src/librustc_session/parse.rs @@ -4,7 +4,7 @@ use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId}; use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync::{Lock, Lrc, Once}; +use rustc_data_structures::sync::{Lock, Lrc, OnceCell}; use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; use rustc_errors::{error_code, Applicability, DiagnosticBuilder}; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; @@ -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>( @@ -105,7 +119,7 @@ pub struct ParseSess { pub unstable_features: UnstableFeatures, pub config: CrateConfig, pub edition: Edition, - pub missing_fragment_specifiers: Lock>, + pub missing_fragment_specifiers: Lock>, /// Places where raw identifiers were used. This is used for feature-gating raw identifiers. pub raw_identifier_spans: Lock>, /// Used to determine and report recursive module inclusions. @@ -116,10 +130,13 @@ pub struct ParseSess { /// operation token that followed it, but that the parser cannot identify without further /// analysis. pub ambiguous_block_expr_parse: Lock>, - pub injected_crate_name: Once, + pub injected_crate_name: OnceCell, 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, + /// Environment variables accessed during the build and their values when they exist. + pub env_depinfo: Lock)>>, } impl ParseSess { @@ -135,15 +152,17 @@ impl ParseSess { unstable_features: UnstableFeatures::from_environment(), config: FxHashSet::default(), edition: ExpnId::root().expn_data().edition, - missing_fragment_specifiers: Lock::new(FxHashSet::default()), + missing_fragment_specifiers: Default::default(), raw_identifier_spans: Lock::new(Vec::new()), included_mod_stack: Lock::new(vec![]), source_map, buffered_lints: Lock::new(vec![]), ambiguous_block_expr_parse: Lock::new(FxHashMap::default()), - injected_crate_name: Once::new(), + injected_crate_name: OnceCell::new(), gated_spans: GatedSpans::default(), + symbol_gallery: SymbolGallery::default(), reached_eof: Lock::new(false), + env_depinfo: Default::default(), } } @@ -158,6 +177,10 @@ impl ParseSess { &self.source_map } + pub fn clone_source_map(&self) -> Lrc { + self.source_map.clone() + } + pub fn buffer_lint( &self, lint: &'static Lint, 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 d5046bdbe29e4..2ea312c42dc64 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -1,44 +1,40 @@ +use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; - -use crate::cgu_reuse_tracker::CguReuseTracker; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - -use crate::config::{self, OutputType, PrintRequest, Sanitizer, SwitchWithOptPath}; +use crate::config::{self, CrateType, OutputType, PrintRequest, SanitizerSet, SwitchWithOptPath}; use crate::filesearch; use crate::lint; +use crate::parse::ParseSess; use crate::search_paths::{PathKind, SearchPath}; -use rustc_data_structures::profiling::duration_to_secs_str; -use rustc_errors::ErrorReported; -use rustc_data_structures::base_n; -use rustc_data_structures::impl_stable_hash_via_hash; +pub use rustc_ast::crate_disambiguator::CrateDisambiguator; +use rustc_data_structures::flock; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::jobserver::{self, Client}; +use rustc_data_structures::profiling::{duration_to_secs_str, SelfProfiler, SelfProfilerRef}; use rustc_data_structures::sync::{ - self, AtomicU64, AtomicUsize, Lock, Lrc, Once, OneThread, Ordering, Ordering::SeqCst, + self, AtomicU64, AtomicUsize, Lock, Lrc, OnceCell, OneThread, Ordering, Ordering::SeqCst, }; - -use crate::parse::ParseSess; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter; -use rustc_errors::emitter::HumanReadableErrorType; -use rustc_errors::emitter::{Emitter, EmitterWriter}; +use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; -use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_errors::registry::Registry; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported}; use rustc_span::edition::Edition; -use rustc_span::source_map; -use rustc_span::{MultiSpan, Span}; - -use rustc_data_structures::flock; -use rustc_data_structures::jobserver::{self, Client}; -use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; -use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; +use rustc_span::source_map::{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::fmt; use std::io::Write; use std::num::NonZeroU32; +use std::ops::{Div, Mul}; use std::path::PathBuf; +use std::str::FromStr; use std::sync::Arc; use std::time::Duration; @@ -61,6 +57,46 @@ pub enum CtfeBacktrace { Immediate, } +/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against +/// limits are consistent throughout the compiler. +#[derive(Clone, Copy, Debug)] +pub struct Limit(pub usize); + +impl Limit { + /// Create a new limit from a `usize`. + pub fn new(value: usize) -> Self { + Limit(value) + } + + /// Check that `value` is within the limit. Ensures that the same comparisons are used + /// throughout the compiler, as mismatches can cause ICEs, see #72540. + pub fn value_within_limit(&self, value: usize) -> bool { + value <= self.0 + } +} + +impl fmt::Display for Limit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Div for Limit { + type Output = Limit; + + fn div(self, rhs: usize) -> Self::Output { + Limit::new(self.0 / rhs) + } +} + +impl Mul for Limit { + type Output = Limit; + + fn mul(self, rhs: usize) -> Self::Output { + Limit::new(self.0 * rhs) + } +} + /// Represents the data associated with a compilation /// session for a single crate. pub struct Session { @@ -83,25 +119,25 @@ 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>, + crate_types: OnceCell>, /// 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 /// multiple crates with the same name to coexist. See the /// `rustc_codegen_llvm::back::symbol_names` module for more information. - pub crate_disambiguator: Once, + pub crate_disambiguator: OnceCell, - features: Once, + features: OnceCell, /// The maximum recursion limit for potentially infinitely recursive /// operations such as auto-dereference and monomorphization. - pub recursion_limit: Once, + pub recursion_limit: OnceCell, /// The maximum length of types during monomorphization. - pub type_length_limit: Once, + pub type_length_limit: OnceCell, /// The maximum blocks a const expression can evaluate. - pub const_eval_limit: Once, + pub const_eval_limit: OnceCell, incr_comp_session: OneThread>, /// Used for incremental compilation tests. Will only be populated if @@ -151,6 +187,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 { @@ -161,7 +218,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, } @@ -190,8 +247,66 @@ 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() + self.crate_disambiguator.get().copied().unwrap() + } + + pub fn crate_types(&self) -> &[CrateType] { + self.crate_types.get().unwrap().as_slice() + } + + pub fn init_crate_types(&self, crate_types: Vec) { + self.crate_types.set(crate_types).expect("`crate_types` was initialized twice") + } + + pub fn recursion_limit(&self) -> Limit { + self.recursion_limit.get().copied().unwrap() + } + + pub fn type_length_limit(&self) -> Limit { + self.type_length_limit.get().copied().unwrap() + } + + pub fn const_eval_limit(&self) -> Limit { + self.const_eval_limit.get().copied().unwrap() } pub fn struct_span_warn>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> { @@ -326,6 +441,9 @@ impl Session { pub fn span_note_without_error>(&self, sp: S, msg: &str) { self.diagnostic().span_note_without_error(sp, msg) } + pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_note_without_error(msg) + } pub fn diagnostic(&self) -> &rustc_errors::Handler { &self.parse_sess.span_diagnostic @@ -408,7 +526,7 @@ impl Session { } #[inline] - pub fn source_map(&self) -> &source_map::SourceMap { + pub fn source_map(&self) -> &SourceMap { self.parse_sess.source_map() } pub fn verbose(&self) -> bool { @@ -447,11 +565,14 @@ impl Session { /// dependency tracking. Use tcx.features() instead. #[inline] pub fn features_untracked(&self) -> &rustc_feature::Features { - self.features.get() + self.features.get().unwrap() } pub fn init_features(&self, features: rustc_feature::Features) { - self.features.set(features); + match self.features.set(features) { + Ok(()) => {} + Err(_) => panic!("`features` was initialized twice"), + } } /// Calculates the flavor of LTO to use for this compilation. @@ -529,21 +650,13 @@ impl Session { } pub fn fewer_names(&self) -> bool { let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly) - || self.opts.output_types.contains_key(&OutputType::Bitcode); - - // Address sanitizer and memory sanitizer use alloca name when reporting an issue. - let more_names = match self.opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => true, - Some(Sanitizer::Memory) => true, - _ => more_names, - }; + || self.opts.output_types.contains_key(&OutputType::Bitcode) + // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue. + || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY); 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 } @@ -555,25 +668,41 @@ impl Session { .unwrap_or(self.opts.debug_assertions) } - pub fn crt_static(&self) -> 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() - } else { - self.target.target.options.crt_static_default + /// Check whether this compile session and crate type use static crt. + 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; } - } - pub fn crt_static_feature(&self) -> 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 the target we're compiling for requests a static crt by default, - // then see if the `-crt-static` feature was passed to disable that. - // Otherwise if we don't have a static crt by default then see if the - // `+crt-static` feature was passed. - if self.target.target.options.crt_static_default { !found_negative } else { found_positive } + if found_positive || found_negative { + found_positive + } 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. + // We can't check `#![crate_type = "proc-macro"]` here. + false + } else { + self.target.target.options.crt_static_default + } + } + + 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 { @@ -588,6 +717,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 { @@ -709,8 +865,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: {}", @@ -728,7 +884,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; @@ -760,6 +916,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 @@ -849,29 +1012,20 @@ impl Session { // then try to skip it where possible. dbg_opts.plt.unwrap_or(needs_plt || !full_relro) } -} -pub fn build_session( - sopts: config::Options, - 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(), - ) + /// Checks if LLVM lifetime markers should be emitted. + pub fn emit_lifetime_markers(&self) -> bool { + self.opts.optimize != config::OptLevel::No + // AddressSanitizer uses lifetimes to detect use after scope bugs. + // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables. + || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY) + } } fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, - source_map: &Lrc, + source_map: Lrc, emitter_dest: Option>, ) -> Box { let macro_backtrace = sopts.debugging_opts.macro_backtrace; @@ -880,17 +1034,14 @@ fn default_emitter( let (short, color_config) = kind.unzip(); if let HumanReadableErrorType::AnnotateSnippet(_) = kind { - let emitter = AnnotateSnippetEmitterWriter::new( - Some(source_map.clone()), - short, - macro_backtrace, - ); - Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing())) + let emitter = + AnnotateSnippetEmitterWriter::new(Some(source_map), short, macro_backtrace); + Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) } else { let emitter = match dst { None => EmitterWriter::stderr( color_config, - Some(source_map.clone()), + Some(source_map), short, sopts.debugging_opts.teach, sopts.debugging_opts.terminal_width, @@ -898,7 +1049,7 @@ fn default_emitter( ), Some(dst) => EmitterWriter::new( dst, - Some(source_map.clone()), + Some(source_map), short, false, // no teach messages when writing to a buffer false, // no colors when writing to a buffer @@ -906,29 +1057,23 @@ 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( - JsonEmitter::stderr( - Some(registry), - source_map.clone(), - pretty, - json_rendered, - macro_backtrace, - ) - .ui_testing(sopts.debugging_opts.ui_testing()), + JsonEmitter::stderr(Some(registry), source_map, pretty, json_rendered, macro_backtrace) + .ui_testing(sopts.debugging_opts.ui_testing), ), (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new( JsonEmitter::new( dst, Some(registry), - source_map.clone(), + source_map, pretty, json_rendered, macro_backtrace, ) - .ui_testing(sopts.debugging_opts.ui_testing()), + .ui_testing(sopts.debugging_opts.ui_testing), ), } } @@ -938,13 +1083,13 @@ pub enum DiagnosticOutput { Raw(Box), } -pub fn build_session_with_source_map( +pub fn build_session( sopts: config::Options, local_crate_source_file: Option, registry: rustc_errors::registry::Registry, - source_map: Lrc, diagnostics_output: DiagnosticOutput, - lint_caps: FxHashMap, + driver_lint_caps: FxHashMap, + file_loader: Option>, ) -> Session { // 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 @@ -963,23 +1108,33 @@ pub fn build_session_with_source_map( DiagnosticOutput::Default => None, DiagnosticOutput::Raw(write) => Some(write), }; - let emitter = default_emitter(&sopts, registry, &source_map, write_dest); - let diagnostic_handler = rustc_errors::Handler::with_emitter_and_flags( + 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.clone(), write_dest); + + 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 = @@ -1001,12 +1156,6 @@ 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 sysroot = match &sopts.maybe_sysroot { Some(sysroot) => sysroot.clone(), @@ -1058,6 +1207,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, @@ -1069,12 +1244,12 @@ fn build_session_( local_crate_source_file, working_dir, one_time_diagnostics: Default::default(), - crate_types: Once::new(), - crate_disambiguator: Once::new(), - features: Once::new(), - recursion_limit: Once::new(), - type_length_limit: Once::new(), - const_eval_limit: Once::new(), + crate_types: OnceCell::new(), + crate_disambiguator: OnceCell::new(), + features: OnceCell::new(), + recursion_limit: OnceCell::new(), + type_length_limit: OnceCell::new(), + const_eval_limit: OnceCell::new(), incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), cgu_reuse_tracker, prof, @@ -1082,7 +1257,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(), @@ -1096,6 +1271,10 @@ 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); @@ -1134,6 +1313,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 @@ -1153,65 +1349,48 @@ fn validate_commandline_args_with_session_available(sess: &Session) { ); } + const ASAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-fuchsia", + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-fuchsia", + "x86_64-unknown-linux-gnu", + ]; + const LSAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + const MSAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"]; + const TSAN_SUPPORTED_TARGETS: &[&str] = + &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + // Sanitizers can only be used on some tested platforms. - if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer { - const ASAN_SUPPORTED_TARGETS: &[&str] = &[ - "x86_64-unknown-linux-gnu", - "x86_64-apple-darwin", - "x86_64-fuchsia", - "aarch64-fuchsia", - ]; - const TSAN_SUPPORTED_TARGETS: &[&str] = - &["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"]; - const LSAN_SUPPORTED_TARGETS: &[&str] = - &["x86_64-unknown-linux-gnu", "x86_64-apple-darwin"]; - const MSAN_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"]; - - let supported_targets = match *sanitizer { - Sanitizer::Address => ASAN_SUPPORTED_TARGETS, - Sanitizer::Thread => TSAN_SUPPORTED_TARGETS, - Sanitizer::Leak => LSAN_SUPPORTED_TARGETS, - Sanitizer::Memory => MSAN_SUPPORTED_TARGETS, + for s in sess.opts.debugging_opts.sanitizer { + let supported_targets = match s { + SanitizerSet::ADDRESS => ASAN_SUPPORTED_TARGETS, + SanitizerSet::LEAK => LSAN_SUPPORTED_TARGETS, + SanitizerSet::MEMORY => MSAN_SUPPORTED_TARGETS, + SanitizerSet::THREAD => TSAN_SUPPORTED_TARGETS, + _ => panic!("unrecognized sanitizer {}", s), }; - if !supported_targets.contains(&&*sess.opts.target_triple.triple()) { sess.err(&format!( - "{:?}Sanitizer only works with the `{}` target", - sanitizer, - supported_targets.join("` or `") + "`-Zsanitizer={}` only works with targets: {}", + s, + supported_targets.join(", ") )); } + let conflicting = sess.opts.debugging_opts.sanitizer - s; + if !conflicting.is_empty() { + sess.err(&format!( + "`-Zsanitizer={}` is incompatible with `-Zsanitizer={}`", + s, conflicting, + )); + // Don't report additional errors. + break; + } } } -/// Hash value constructed out of all the `-C metadata` arguments passed to the -/// compiler. Together with the crate-name forms a unique global identifier for -/// the crate. -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, RustcEncodable, RustcDecodable)] -pub struct CrateDisambiguator(Fingerprint); - -impl CrateDisambiguator { - pub fn to_fingerprint(self) -> Fingerprint { - self.0 - } -} - -impl fmt::Display for CrateDisambiguator { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - let (a, b) = self.0.as_value(); - let as_u128 = a as u128 | ((b as u128) << 64); - f.write_str(&base_n::encode(as_u128, base_n::CASE_INSENSITIVE)) - } -} - -impl From for CrateDisambiguator { - fn from(fingerprint: Fingerprint) -> CrateDisambiguator { - CrateDisambiguator(fingerprint) - } -} - -impl_stable_hash_via_hash!(CrateDisambiguator); - /// Holds data on the current incremental compilation session, if there is one. #[derive(Debug)] pub enum IncrCompSession { diff --git a/src/librustc_session/utils.rs b/src/librustc_session/utils.rs index fda11b647490f..b97308c22cb7d 100644 --- a/src/librustc_session/utils.rs +++ b/src/librustc_session/utils.rs @@ -11,17 +11,22 @@ impl Session { } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)] -pub enum NativeLibraryKind { - /// native static library (.a archive) - NativeStatic, - /// native static library, which doesn't get bundled into .rlibs - NativeStaticNobundle, - /// macOS-specific - NativeFramework, - /// Windows dynamic library without import library. - NativeRawDylib, - /// default way to specify a dynamic library - NativeUnknown, +pub enum NativeLibKind { + /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) included + /// when linking a final binary, but not when archiving an rlib. + StaticNoBundle, + /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) included + /// when linking a final binary, but also included when archiving an rlib. + StaticBundle, + /// Dynamic library (e.g. `libfoo.so` on Linux) + /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC). + Dylib, + /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library. + RawDylib, + /// A macOS-specific kind of dynamic libraries. + Framework, + /// The library kind wasn't specified, `Dylib` is currently used as a default. + Unspecified, } -rustc_data_structures::impl_stable_hash_via_hash!(NativeLibraryKind); +rustc_data_structures::impl_stable_hash_via_hash!(NativeLibKind); diff --git a/src/librustc_span/Cargo.toml b/src/librustc_span/Cargo.toml index c3fa2331d2b16..2a7a774872525 100644 --- a/src/librustc_span/Cargo.toml +++ b/src/librustc_span/Cargo.toml @@ -10,12 +10,14 @@ path = "lib.rs" doctest = false [dependencies] -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } rustc_macros = { path = "../librustc_macros" } rustc_data_structures = { path = "../librustc_data_structures" } rustc_index = { path = "../librustc_index" } -arena = { path = "../libarena" } +rustc_arena = { path = "../librustc_arena" } scoped-tls = "1.0" unicode-width = "0.1.4" cfg-if = "0.1.2" log = "0.4" +sha-1 = "0.8" +md-5 = "0.8" diff --git a/src/librustc_span/caching_source_map_view.rs b/src/librustc_span/caching_source_map_view.rs index d6725160a5d02..68b0bd1a57418 100644 --- a/src/librustc_span/caching_source_map_view.rs +++ b/src/librustc_span/caching_source_map_view.rs @@ -99,10 +99,6 @@ impl<'sm> CachingSourceMapView<'sm> { cache_entry.line_end = line_bounds.1; cache_entry.time_stamp = self.time_stamp; - return Some(( - cache_entry.file.clone(), - cache_entry.line_number, - pos - cache_entry.line_start, - )); + Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line_start)) } } diff --git a/src/librustc_span/def_id.rs b/src/librustc_span/def_id.rs index a2944782e91d4..0a70be1f152e9 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 { @@ -25,7 +24,7 @@ pub enum CrateNum { /// Item definitions in the currently-compiled crate would have the `CrateNum` /// `LOCAL_CRATE` in their `DefId`. -pub const LOCAL_CRATE: CrateNum = CrateNum::Index(CrateId::from_u32_const(0)); +pub const LOCAL_CRATE: CrateNum = CrateNum::Index(CrateId::from_u32(0)); impl Idx for CrateNum { #[inline] @@ -105,19 +104,8 @@ impl ::std::fmt::Debug for CrateNum { } } -#[derive( - Copy, - Clone, - Hash, - PartialEq, - Eq, - PartialOrd, - Ord, - Debug, - RustcEncodable, - RustcDecodable, - HashStable_Generic -)] +#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable_Generic)] pub struct DefPathHash(pub Fingerprint); impl Borrow for DefPathHash { @@ -145,6 +133,8 @@ impl rustc_serialize::UseSpecializedDecodable for DefIndex {} /// A `DefId` identifies a particular *definition*, by combining a crate /// index and a def index. +/// +/// You can create a `DefId` from a `LocalDefId` using `local_def_id.to_def_id()`. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] pub struct DefId { pub krate: CrateNum, @@ -164,8 +154,13 @@ impl DefId { } #[inline] - pub fn to_local(self) -> LocalDefId { - LocalDefId::from_def_id(self) + pub fn as_local(self) -> Option { + if self.is_local() { Some(LocalDefId { local_def_index: self.index }) } else { None } + } + + #[inline] + pub fn expect_local(self) -> LocalDefId { + self.as_local().unwrap_or_else(|| panic!("DefId::expect_local: `{:?}` isn't local", self)) } pub fn is_top_level_module(self) -> bool { @@ -210,19 +205,31 @@ rustc_data_structures::define_id_collections!(DefIdMap, DefIdSet, DefId); /// few cases where we know that only DefIds from the local crate are expected /// and a DefId from a different crate would signify a bug somewhere. This /// is when LocalDefId comes in handy. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct LocalDefId(DefIndex); +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct LocalDefId { + pub local_def_index: DefIndex, +} -impl LocalDefId { +impl Idx for LocalDefId { + #[inline] + fn new(idx: usize) -> Self { + LocalDefId { local_def_index: Idx::new(idx) } + } #[inline] - pub fn from_def_id(def_id: DefId) -> LocalDefId { - assert!(def_id.is_local()); - LocalDefId(def_id.index) + fn index(self) -> usize { + self.local_def_index.index() } +} +impl LocalDefId { #[inline] pub fn to_def_id(self) -> DefId { - DefId { krate: LOCAL_CRATE, index: self.0 } + 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 } } diff --git a/src/librustc_span/edition.rs b/src/librustc_span/edition.rs index 3017191563b0a..b1ac7f04321eb 100644 --- a/src/librustc_span/edition.rs +++ b/src/librustc_span/edition.rs @@ -5,18 +5,8 @@ use std::str::FromStr; use rustc_macros::HashStable_Generic; /// The edition of the compiler (RFC 2052) -#[derive( - Clone, - Copy, - Hash, - PartialEq, - PartialOrd, - Debug, - RustcEncodable, - RustcDecodable, - Eq, - HashStable_Generic -)] +#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Debug, RustcEncodable, RustcDecodable, Eq)] +#[derive(HashStable_Generic)] pub enum Edition { // editions must be kept in order, oldest to newest /// The 2015 edition diff --git a/src/librustc_span/hygiene.rs b/src/librustc_span/hygiene.rs index a368a881674d8..c0fb84e741f4a 100644 --- a/src/librustc_span/hygiene.rs +++ b/src/librustc_span/hygiene.rs @@ -25,6 +25,7 @@ // because getting it wrong can lead to nested `HygieneData::with` calls that // trigger runtime aborts. (Fortunately these are obvious and easy to fix.) +use crate::def_id::{DefId, CRATE_DEF_INDEX}; use crate::edition::Edition; use crate::symbol::{kw, sym, Symbol}; use crate::GLOBALS; @@ -59,18 +60,8 @@ pub struct ExpnId(u32); /// A property of a macro expansion that determines how identifiers /// produced by that expansion are resolved. -#[derive( - Copy, - Clone, - PartialEq, - Eq, - PartialOrd, - Hash, - Debug, - RustcEncodable, - RustcDecodable, - HashStable_Generic -)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(HashStable_Generic)] pub enum Transparency { /// Identifier produced by a transparent expansion is always resolved at call-site. /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this. @@ -165,7 +156,12 @@ crate struct HygieneData { impl HygieneData { crate fn new(edition: Edition) -> Self { HygieneData { - expn_data: vec![Some(ExpnData::default(ExpnKind::Root, DUMMY_SP, edition))], + expn_data: vec![Some(ExpnData::default( + ExpnKind::Root, + DUMMY_SP, + edition, + Some(DefId::local(CRATE_DEF_INDEX)), + ))], syntax_context_data: vec![SyntaxContextData { outer_expn: ExpnId::root(), outer_transparency: Transparency::Opaque, @@ -201,11 +197,11 @@ impl HygieneData { true } - fn modern(&self, ctxt: SyntaxContext) -> SyntaxContext { + fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext { self.syntax_context_data[ctxt.0 as usize].opaque } - fn modern_and_legacy(&self, ctxt: SyntaxContext) -> SyntaxContext { + fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent } @@ -266,9 +262,9 @@ impl HygieneData { let call_site_ctxt = self.expn_data(expn_id).call_site.ctxt(); let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { - self.modern(call_site_ctxt) + self.normalize_to_macros_2_0(call_site_ctxt) } else { - self.modern_and_legacy(call_site_ctxt) + self.normalize_to_macro_rules(call_site_ctxt) }; if call_site_ctxt == SyntaxContext::root() { @@ -491,10 +487,10 @@ impl SyntaxContext { HygieneData::with(|data| data.adjust(self, expn_id)) } - /// Like `SyntaxContext::adjust`, but also modernizes `self`. - pub fn modernize_and_adjust(&mut self, expn_id: ExpnId) -> Option { + /// Like `SyntaxContext::adjust`, but also normalizes `self` to macros 2.0. + pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { HygieneData::with(|data| { - *self = data.modern(*self); + *self = data.normalize_to_macros_2_0(*self); data.adjust(self, expn_id) }) } @@ -527,7 +523,7 @@ impl SyntaxContext { pub fn glob_adjust(&mut self, expn_id: ExpnId, glob_span: Span) -> Option> { HygieneData::with(|data| { let mut scope = None; - let mut glob_ctxt = data.modern(glob_span.ctxt()); + let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { scope = Some(data.remove_mark(&mut glob_ctxt).0); if data.remove_mark(self).0 != scope.unwrap() { @@ -558,7 +554,7 @@ impl SyntaxContext { return None; } - let mut glob_ctxt = data.modern(glob_span.ctxt()); + let mut glob_ctxt = data.normalize_to_macros_2_0(glob_span.ctxt()); let mut marks = Vec::new(); while !data.is_descendant_of(expn_id, data.outer_expn(glob_ctxt)) { marks.push(data.remove_mark(&mut glob_ctxt)); @@ -574,20 +570,20 @@ impl SyntaxContext { pub fn hygienic_eq(self, other: SyntaxContext, expn_id: ExpnId) -> bool { HygieneData::with(|data| { - let mut self_modern = data.modern(self); - data.adjust(&mut self_modern, expn_id); - self_modern == data.modern(other) + let mut self_normalized = data.normalize_to_macros_2_0(self); + data.adjust(&mut self_normalized, expn_id); + self_normalized == data.normalize_to_macros_2_0(other) }) } #[inline] - pub fn modern(self) -> SyntaxContext { - HygieneData::with(|data| data.modern(self)) + pub fn normalize_to_macros_2_0(self) -> SyntaxContext { + HygieneData::with(|data| data.normalize_to_macros_2_0(self)) } #[inline] - pub fn modern_and_legacy(self) -> SyntaxContext { - HygieneData::with(|data| data.modern_and_legacy(self)) + pub fn normalize_to_macro_rules(self) -> SyntaxContext { + HygieneData::with(|data| data.normalize_to_macro_rules(self)) } #[inline] @@ -671,7 +667,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>, @@ -683,11 +679,19 @@ pub struct ExpnData { pub local_inner_macros: bool, /// Edition of the crate in which the macro is defined. pub edition: Edition, + /// The `DefId` of the macro being invoked, + /// if this `ExpnData` corresponds to a macro invocation + pub macro_def_id: Option, } impl ExpnData { /// Constructs expansion data with default properties. - pub fn default(kind: ExpnKind, call_site: Span, edition: Edition) -> ExpnData { + pub fn default( + kind: ExpnKind, + call_site: Span, + edition: Edition, + macro_def_id: Option, + ) -> ExpnData { ExpnData { kind, parent: ExpnId::root(), @@ -697,6 +701,7 @@ impl ExpnData { allow_internal_unsafe: false, local_inner_macros: false, edition, + macro_def_id, } } @@ -705,10 +710,11 @@ impl ExpnData { call_site: Span, edition: Edition, allow_internal_unstable: Lrc<[Symbol]>, + macro_def_id: Option, ) -> ExpnData { ExpnData { allow_internal_unstable: Some(allow_internal_unstable), - ..ExpnData::default(kind, call_site, edition) + ..ExpnData::default(kind, call_site, edition, macro_def_id) } } @@ -747,17 +753,8 @@ impl ExpnKind { } /// The kind of macro invocation or definition. -#[derive( - Clone, - Copy, - PartialEq, - Eq, - RustcEncodable, - RustcDecodable, - Hash, - Debug, - HashStable_Generic -)] +#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +#[derive(HashStable_Generic)] pub enum MacroKind { /// A bang macro `foo!()`. Bang, diff --git a/src/librustc_span/lib.rs b/src/librustc_span/lib.rs index b5224e57cd2b6..9624006683467 100644 --- a/src/librustc_span/lib.rs +++ b/src/librustc_span/lib.rs @@ -6,9 +6,17 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(crate_visibility_modifier)] +#![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; @@ -17,6 +25,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; mod caching_source_map_view; pub mod source_map; pub use self::caching_source_map_view::CachingSourceMapView; +use source_map::SourceMap; pub mod edition; use edition::Edition; @@ -24,7 +33,7 @@ pub mod hygiene; use hygiene::Transparency; pub use hygiene::{DesugaringKind, ExpnData, ExpnId, ExpnKind, MacroKind, SyntaxContext}; pub mod def_id; -use def_id::DefId; +use def_id::{CrateNum, DefId, LOCAL_CRATE}; mod span_encoding; pub use span_encoding::{Span, DUMMY_SP}; @@ -43,9 +52,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::path::{Path, PathBuf}; +use std::str::FromStr; + +use md5::Md5; +use sha1::Digest; +use sha1::Sha1; #[cfg(test)] mod tests; @@ -54,6 +68,7 @@ pub struct Globals { symbol_interner: Lock, span_interner: Lock, hygiene_data: Lock, + source_map: Lock>>, } impl Globals { @@ -62,27 +77,68 @@ impl Globals { symbol_interner: Lock::new(symbol::Interner::fresh()), span_interner: Lock::new(span_encoding::SpanInterner::default()), hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), + source_map: Lock::new(None), } } } scoped_tls::scoped_thread_local!(pub static GLOBALS: Globals); +// FIXME: Perhaps this should not implement Rustc{Decodable, Encodable} +// +// FIXME: We should use this enum or something like it to get rid of the +// use of magic `/rust/1.x/...` paths across the board. +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, RustcDecodable, RustcEncodable)] +#[derive(HashStable_Generic)] +pub enum RealFileName { + Named(PathBuf), + /// For de-virtualized paths (namely paths into libstd that have been mapped + /// to the appropriate spot on the local host's file system), + Devirtualized { + /// `local_path` is the (host-dependent) local path to the file. + local_path: PathBuf, + /// `virtual_name` is the stable path rustc will store internally within + /// build artifacts. + virtual_name: PathBuf, + }, +} + +impl RealFileName { + /// Returns the path suitable for reading from the file system on the local host. + /// Avoid embedding this in build artifacts; see `stable_name` for that. + pub fn local_path(&self) -> &Path { + match self { + RealFileName::Named(p) + | RealFileName::Devirtualized { local_path: p, virtual_name: _ } => &p, + } + } + + /// Returns the path suitable for reading from the file system on the local host. + /// Avoid embedding this in build artifacts; see `stable_name` for that. + pub fn into_local_path(self) -> PathBuf { + match self { + RealFileName::Named(p) + | RealFileName::Devirtualized { local_path: p, virtual_name: _ } => p, + } + } + + /// Returns the path suitable for embedding into build artifacts. Note that + /// a virtualized path will not correspond to a valid file system path; see + /// `local_path` for something that is more likely to return paths into the + /// local host file system. + pub fn stable_name(&self) -> &Path { + match self { + RealFileName::Named(p) + | RealFileName::Devirtualized { local_path: _, virtual_name: p } => &p, + } + } +} + /// Differentiates between real files and common virtual files. -#[derive( - Debug, - Eq, - PartialEq, - Clone, - Ord, - PartialOrd, - Hash, - RustcDecodable, - RustcEncodable, - HashStable_Generic -)] +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, RustcDecodable, RustcEncodable)] +#[derive(HashStable_Generic)] pub enum FileName { - Real(PathBuf), + Real(RealFileName), /// Call to `quote!`. QuoteExpansion(u64), /// Command line. @@ -98,13 +154,21 @@ pub enum FileName { /// Custom sources for explicit parser calls from plugins and drivers. Custom(String), DocTest(PathBuf, isize), + /// Post-substitution inline assembly from LLVM + InlineAsm(u64), } impl std::fmt::Display for FileName { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use FileName::*; match *self { - Real(ref path) => write!(fmt, "{}", path.display()), + Real(RealFileName::Named(ref path)) => write!(fmt, "{}", path.display()), + // FIXME: might be nice to display both compoments of Devirtualized. + // But for now (to backport fix for issue #70924), best to not + // perturb diagnostics so its obvious test suite still works. + Real(RealFileName::Devirtualized { ref local_path, virtual_name: _ }) => { + write!(fmt, "{}", local_path.display()) + } QuoteExpansion(_) => write!(fmt, ""), MacroExpansion(_) => write!(fmt, ""), Anon(_) => write!(fmt, ""), @@ -113,6 +177,7 @@ impl std::fmt::Display for FileName { CliCrateAttr(_) => write!(fmt, ""), Custom(ref s) => write!(fmt, "<{}>", s), DocTest(ref path, _) => write!(fmt, "{}", path.display()), + InlineAsm(_) => write!(fmt, ""), } } } @@ -120,7 +185,7 @@ impl std::fmt::Display for FileName { impl From for FileName { fn from(p: PathBuf) -> Self { assert!(!p.to_string_lossy().ends_with('>')); - FileName::Real(p) + FileName::Real(RealFileName::Named(p)) } } @@ -136,7 +201,8 @@ impl FileName { | CliCrateAttr(_) | Custom(_) | QuoteExpansion(_) - | DocTest(_, _) => false, + | DocTest(_, _) + | InlineAsm(_) => false, } } @@ -179,6 +245,12 @@ impl FileName { pub fn doc_test_source_code(path: PathBuf, line: isize) -> FileName { FileName::DocTest(path, line) } + + pub fn inline_asm_source_code(src: &str) -> FileName { + let mut hasher = StableHasher::new(); + src.hash(&mut hasher); + FileName::InlineAsm(hasher.finish()) + } } /// Spans represent a region of code, used for error reporting. Positions in spans @@ -548,9 +620,9 @@ impl Span { } #[inline] - pub fn modernize_and_adjust(&mut self, expn_id: ExpnId) -> Option { + pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { let mut span = self.data(); - let mark = span.ctxt.modernize_and_adjust(expn_id); + let mark = span.ctxt.normalize_to_macros_2_0_and_adjust(expn_id); *self = Span::new(span.lo, span.hi, span.ctxt); mark } @@ -576,15 +648,15 @@ impl Span { } #[inline] - pub fn modern(self) -> Span { + pub fn normalize_to_macros_2_0(self) -> Span { let span = self.data(); - span.with_ctxt(span.ctxt.modern()) + span.with_ctxt(span.ctxt.normalize_to_macros_2_0()) } #[inline] - pub fn modern_and_legacy(self) -> Span { + pub fn normalize_to_macro_rules(self) -> Span { let span = self.data(); - span.with_ctxt(span.ctxt.modern_and_legacy()) + span.with_ctxt(span.ctxt.normalize_to_macro_rules()) } } @@ -628,12 +700,52 @@ impl rustc_serialize::UseSpecializedDecodable for Span { } } +/// Calls the provided closure, using the provided `SourceMap` to format +/// any spans that are debug-printed during the closure'e exectuino. +/// +/// Normally, the global `TyCtxt` is used to retrieve the `SourceMap` +/// (see `rustc_interface::callbacks::span_debug1). However, some parts +/// of the compiler (e.g. `rustc_parse`) may debug-print `Span`s before +/// a `TyCtxt` is available. In this case, we fall back to +/// the `SourceMap` provided to this function. If that is not available, +/// we fall back to printing the raw `Span` field values +pub fn with_source_map T>(source_map: Lrc, f: F) -> T { + GLOBALS.with(|globals| { + *globals.source_map.borrow_mut() = Some(source_map); + }); + struct ClearSourceMap; + impl Drop for ClearSourceMap { + fn drop(&mut self) { + GLOBALS.with(|globals| { + globals.source_map.borrow_mut().take(); + }); + } + } + + let _guard = ClearSourceMap; + f() +} + +pub fn debug_with_source_map( + span: Span, + f: &mut fmt::Formatter<'_>, + source_map: &SourceMap, +) -> fmt::Result { + write!(f, "{} ({:?})", source_map.span_to_string(span), span.ctxt()) +} + pub fn default_span_debug(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Span") - .field("lo", &span.lo()) - .field("hi", &span.hi()) - .field("ctxt", &span.ctxt()) - .finish() + GLOBALS.with(|globals| { + if let Some(source_map) = &*globals.source_map.borrow() { + debug_with_source_map(span, f, source_map) + } else { + f.debug_struct("Span") + .field("lo", &span.lo()) + .field("hi", &span.hi()) + .field("ctxt", &span.ctxt()) + .finish() + } + }) } impl fmt::Debug for Span { @@ -658,7 +770,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![] } } @@ -836,30 +949,42 @@ pub struct NormalizedPos { pub diff: u32, } -/// The state of the lazy external source loading mechanism of a `SourceFile`. -#[derive(PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone, Debug)] pub enum ExternalSource { + /// No external source has to be loaded, since the `SourceFile` represents a local crate. + Unneeded, + Foreign { + kind: ExternalSourceKind, + /// This SourceFile's byte-offset within the source_map of its original crate + original_start_pos: BytePos, + /// The end of this SourceFile within the source_map of its original crate + original_end_pos: BytePos, + }, +} + +/// The state of the lazy external source loading mechanism of a `SourceFile`. +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum ExternalSourceKind { /// The external source has been loaded already. - Present(String), + Present(Lrc), /// No attempt has been made to load the external source. AbsentOk, /// A failed attempt has been made to load the external source. AbsentErr, - /// No external source has to be loaded, since the `SourceFile` represents a local crate. Unneeded, } impl ExternalSource { pub fn is_absent(&self) -> bool { - match *self { - ExternalSource::Present(_) => false, + match self { + ExternalSource::Foreign { kind: ExternalSourceKind::Present(_), .. } => false, _ => true, } } - pub fn get_source(&self) -> Option<&str> { - match *self { - ExternalSource::Present(ref src) => Some(src), + pub fn get_source(&self) -> Option<&Lrc> { + match self { + ExternalSource::Foreign { kind: ExternalSourceKind::Present(ref src), .. } => Some(src), _ => None, } } @@ -868,6 +993,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 { @@ -880,12 +1069,10 @@ pub struct SourceFile { /// The unmapped path of the file that the source came from. /// Set to `None` if the `SourceFile` was imported from an external crate. pub unmapped_path: Option, - /// Indicates which crate this `SourceFile` was imported from. - pub crate_of_origin: u32, /// 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, @@ -903,6 +1090,8 @@ pub struct SourceFile { pub normalized_pos: Vec, /// A hash of the filename, used for speeding up hashing in incremental compilation. pub name_hash: u128, + /// Indicates which crate this `SourceFile` was imported from. + pub cnum: CrateNum, } impl Encodable for SourceFile { @@ -969,7 +1158,8 @@ impl Encodable for SourceFile { s.emit_struct_field("multibyte_chars", 6, |s| self.multibyte_chars.encode(s))?; s.emit_struct_field("non_narrow_chars", 7, |s| self.non_narrow_chars.encode(s))?; s.emit_struct_field("name_hash", 8, |s| self.name_hash.encode(s))?; - s.emit_struct_field("normalized_pos", 9, |s| self.normalized_pos.encode(s)) + s.emit_struct_field("normalized_pos", 9, |s| self.normalized_pos.encode(s))?; + s.emit_struct_field("cnum", 10, |s| self.cnum.encode(s)) }) } } @@ -980,7 +1170,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))?; @@ -1019,24 +1210,24 @@ impl Decodable for SourceFile { let name_hash: u128 = d.read_struct_field("name_hash", 8, |d| Decodable::decode(d))?; let normalized_pos: Vec = d.read_struct_field("normalized_pos", 9, |d| Decodable::decode(d))?; + let cnum: CrateNum = d.read_struct_field("cnum", 10, |d| Decodable::decode(d))?; Ok(SourceFile { name, name_was_remapped, unmapped_path: None, - // `crate_of_origin` has to be set by the importer. - // This value matches up with `rustc_hir::def_id::INVALID_CRATE`. - // That constant is not available here, unfortunately. - crate_of_origin: std::u32::MAX - 1, start_pos, end_pos, src: None, src_hash, - external_src: Lock::new(ExternalSource::AbsentOk), + // Unused - the metadata decoder will construct + // a new SourceFile, filling in `external_src` properly + external_src: Lock::new(ExternalSource::Unneeded), lines, multibyte_chars, non_narrow_chars, normalized_pos, name_hash, + cnum, }) }) } @@ -1055,21 +1246,19 @@ 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); hasher.finish::() }; let end_pos = start_pos.to_usize() + src.len(); - assert!(end_pos <= u32::max_value() as usize); + assert!(end_pos <= u32::MAX as usize); let (lines, multibyte_chars, non_narrow_chars) = analyze_source_file::analyze_source_file(&src[..], start_pos); @@ -1078,7 +1267,6 @@ impl SourceFile { name, name_was_remapped, unmapped_path: Some(unmapped_path), - crate_of_origin: 0, src: Some(Lrc::new(src)), src_hash, external_src: Lock::new(ExternalSource::Unneeded), @@ -1089,6 +1277,7 @@ impl SourceFile { non_narrow_chars, normalized_pos, name_hash, + cnum: LOCAL_CRATE, } } @@ -1106,21 +1295,26 @@ impl SourceFile { where F: FnOnce() -> Option, { - if *self.external_src.borrow() == ExternalSource::AbsentOk { + if matches!( + *self.external_src.borrow(), + ExternalSource::Foreign { kind: ExternalSourceKind::AbsentOk, .. } + ) { let src = get_src(); let mut external_src = self.external_src.borrow_mut(); // Check that no-one else have provided the source while we were getting it - if *external_src == ExternalSource::AbsentOk { - if let Some(src) = src { - let mut hasher: StableHasher = StableHasher::new(); - hasher.write(src.as_bytes()); - - if hasher.finish::() == self.src_hash { - *external_src = ExternalSource::Present(src); + if let ExternalSource::Foreign { + kind: src_kind @ ExternalSourceKind::AbsentOk, .. + } = &mut *external_src + { + if let Some(mut src) = src { + // The src_hash needs to be computed on the pre-normalized src. + if self.src_hash.matches(&src) { + normalize_src(&mut src, BytePos::from_usize(0)); + *src_kind = ExternalSourceKind::Present(Lrc::new(src)); return true; } } else { - *external_src = ExternalSource::AbsentErr; + *src_kind = ExternalSourceKind::AbsentErr; } false @@ -1531,7 +1725,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 353f7b3f52bc3..4b5bce1db2628 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) } @@ -98,6 +86,8 @@ impl FileLoader for RealFileLoader { #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] pub struct StableSourceFileId(u128); +// FIXME: we need a more globally consistent approach to the problem solved by +// StableSourceFileId, perhaps built atop source_file.name_hash. impl StableSourceFileId { pub fn new(source_file: &SourceFile) -> StableSourceFileId { StableSourceFileId::new_from_pieces( @@ -107,14 +97,21 @@ impl StableSourceFileId { ) } - pub fn new_from_pieces( + fn new_from_pieces( name: &FileName, name_was_remapped: bool, unmapped_path: Option<&FileName>, ) -> StableSourceFileId { let mut hasher = StableHasher::new(); - name.hash(&mut hasher); + if let FileName::Real(real_name) = name { + // rust-lang/rust#70924: Use the stable (virtualized) name when + // available. (We do not want artifacts from transient file system + // paths for libstd to leak into our build artifacts.) + real_name.stable_name().hash(&mut hasher) + } else { + name.hash(&mut hasher); + } name_was_remapped.hash(&mut hasher); unmapped_path.hash(&mut hasher); @@ -141,27 +138,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, } } @@ -243,7 +244,7 @@ impl SourceMap { fn try_new_source_file( &self, - filename: FileName, + mut filename: FileName, src: String, ) -> Result, OffsetOverflowError> { // The path is used to determine the directory for loading submodules and @@ -253,13 +254,22 @@ impl SourceMap { // be empty, so the working directory will be used. let unmapped_path = filename.clone(); - let (filename, was_remapped) = match filename { - FileName::Real(filename) => { - let (filename, was_remapped) = self.path_mapping.map_prefix(filename); - (FileName::Real(filename), was_remapped) + let was_remapped; + if let FileName::Real(real_filename) = &mut filename { + match real_filename { + RealFileName::Named(path_to_be_remapped) + | RealFileName::Devirtualized { + local_path: path_to_be_remapped, + virtual_name: _, + } => { + let mapped = self.path_mapping.map_prefix(path_to_be_remapped.clone()); + was_remapped = mapped.1; + *path_to_be_remapped = mapped.0; + } } - other => (other, false), - }; + } else { + was_remapped = false; + } let file_id = StableSourceFileId::new_from_pieces(&filename, was_remapped, Some(&unmapped_path)); @@ -275,6 +285,7 @@ impl SourceMap { unmapped_path, src, Pos::from_usize(start_pos), + self.hash_kind, )); let mut files = self.files.borrow_mut(); @@ -296,14 +307,16 @@ impl SourceMap { &self, filename: FileName, name_was_remapped: bool, - crate_of_origin: u32, - src_hash: u128, + src_hash: SourceFileHash, name_hash: u128, source_len: usize, + cnum: CrateNum, mut file_local_lines: Vec, mut file_local_multibyte_chars: Vec, mut file_local_non_narrow_chars: Vec, mut file_local_normalized_pos: Vec, + original_start_pos: BytePos, + original_end_pos: BytePos, ) -> Lrc { let start_pos = self .allocate_address_space(source_len) @@ -332,10 +345,13 @@ impl SourceMap { name: filename, name_was_remapped, unmapped_path: None, - crate_of_origin, src: None, src_hash, - external_src: Lock::new(ExternalSource::AbsentOk), + external_src: Lock::new(ExternalSource::Foreign { + kind: ExternalSourceKind::AbsentOk, + original_start_pos, + original_end_pos, + }), start_pos, end_pos, lines: file_local_lines, @@ -343,6 +359,7 @@ impl SourceMap { non_narrow_chars: file_local_non_narrow_chars, normalized_pos: file_local_normalized_pos, name_hash, + cnum, }); let mut files = self.files.borrow_mut(); @@ -362,16 +379,16 @@ impl SourceMap { // If there is a doctest offset, applies it to the line. pub fn doctest_offset_line(&self, file: &FileName, orig: usize) -> usize { - return match file { + match file { FileName::DocTest(_, offset) => { - return if *offset >= 0 { - orig + *offset as usize - } else { + if *offset < 0 { orig - (-(*offset)) as usize - }; + } else { + orig + *offset as usize + } } _ => orig, - }; + } } /// Looks up source information about a `BytePos`. @@ -529,6 +546,10 @@ impl SourceMap { let (lo, hi) = self.is_valid_span(sp)?; assert!(hi.line >= lo.line); + if sp.is_dummy() { + return Ok(FileLines { file: lo.file, lines: Vec::new() }); + } + let mut lines = Vec::with_capacity(hi.line - lo.line + 1); // The span starts partway through the first line, @@ -539,6 +560,9 @@ impl SourceMap { // and to the end of the line. Be careful because the line // numbers in Loc are 1-based, so we subtract 1 to get 0-based // lines. + // + // FIXME: now that we handle DUMMY_SP up above, we should consider + // asserting that the line numbers here are all indeed 1-based. let hi_line = hi.line.saturating_sub(1); for line_index in lo.line.saturating_sub(1)..hi_line { let line_len = lo.file.get_line(line_index).map(|s| s.chars().count()).unwrap_or(0); @@ -563,10 +587,10 @@ impl SourceMap { let local_end = self.lookup_byte_offset(sp.hi()); if local_begin.sf.start_pos != local_end.sf.start_pos { - return Err(SpanSnippetError::DistinctSources(DistinctSources { + Err(SpanSnippetError::DistinctSources(DistinctSources { begin: (local_begin.sf.name.clone(), local_begin.sf.start_pos), end: (local_end.sf.name.clone(), local_end.sf.start_pos), - })); + })) } else { self.ensure_source_file_source_present(local_begin.sf.clone()); @@ -584,13 +608,11 @@ impl SourceMap { } if let Some(ref src) = local_begin.sf.src { - return extract_source(src, start_index, end_index); + extract_source(src, start_index, end_index) } else if let Some(src) = local_begin.sf.external_src.borrow().get_source() { - return extract_source(src, start_index, end_index); + extract_source(src, start_index, end_index) } else { - return Err(SpanSnippetError::SourceNotAvailable { - filename: local_begin.sf.name.clone(), - }); + Err(SpanSnippetError::SourceNotAvailable { filename: local_begin.sf.name.clone() }) } } } @@ -722,7 +744,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, '{') } @@ -790,9 +819,7 @@ impl SourceMap { // Disregard indexes that are at the start or end of their spans, they can't fit bigger // characters. - if (!forwards && end_index == usize::min_value()) - || (forwards && start_index == usize::max_value()) - { + if (!forwards && end_index == usize::MIN) || (forwards && start_index == usize::MAX) { debug!("find_width_of_character_at_span: start or end of span, cannot be multibyte"); return 1; } @@ -899,14 +926,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 @@ -978,7 +1014,7 @@ impl SourceMap { } pub fn ensure_source_file_source_present(&self, source_file: Lrc) -> bool { source_file.add_external_src(|| match source_file.name { - FileName::Real(ref name) => self.file_loader.read_file(name).ok(), + FileName::Real(ref name) => self.file_loader.read_file(name.local_path()).ok(), _ => None, }) } 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 bca4bfee85ad8..fa1368b104c7e 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -2,7 +2,7 @@ //! allows bidirectional lookup; i.e., given a value, one can easily find the //! type, and vice versa. -use arena::DroplessArena; +use rustc_arena::DroplessArena; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_macros::{symbols, HashStable_Generic}; @@ -120,6 +120,7 @@ symbols! { abi_unadjusted, abi_vectorcall, abi_x86_interrupt, + abi_avr_interrupt, abort, aborts, address, @@ -144,8 +145,10 @@ symbols! { any, arbitrary_enum_discriminant, arbitrary_self_types, + Arc, Arguments, ArgumentV1, + arith_offset, arm_target_feature, asm, assert, @@ -156,9 +159,11 @@ symbols! { assume_init, async_await, async_closure, + atomics, attr, attributes, attr_literals, + att_syntax, augmented_assignments, automatically_derived, avx512_target_feature, @@ -182,6 +187,7 @@ symbols! { caller_location, cdylib, cfg, + cfg_accessible, cfg_attr, cfg_attr_multi, cfg_doctest, @@ -190,6 +196,7 @@ symbols! { cfg_target_has_atomic, cfg_target_thread_local, cfg_target_vendor, + cfg_version, char, clippy, clone, @@ -221,6 +228,7 @@ symbols! { const_loop, const_mut_refs, const_panic, + const_precise_live_drops, const_raw_ptr_deref, const_raw_ptr_to_usize_cast, const_transmute, @@ -233,6 +241,7 @@ symbols! { copy_closures, core, core_intrinsics, + count_code_region, crate_id, crate_in_paths, crate_local, @@ -252,6 +261,7 @@ symbols! { debug_trait, declare_lint_pass, decl_macro, + debug, Debug, Decodable, Default, @@ -317,6 +327,8 @@ symbols! { f32, f64, feature, + ffi_const, + ffi_pure, ffi_returns_twice, field, field_init_shorthand, @@ -335,6 +347,7 @@ symbols! { from_method, from_ok, from_usize, + from_trait, fundamental, future, Future, @@ -345,6 +358,7 @@ symbols! { generators, generic_associated_types, generic_param_attrs, + get_context, global_allocator, global_asm, globs, @@ -371,6 +385,8 @@ symbols! { if_let, if_while_or_patterns, ignore, + inlateout, + inout, impl_header_lifetime_elision, impl_lint_pass, impl_trait_in_bindings, @@ -406,6 +422,7 @@ symbols! { label_break_value, lang, lang_items, + lateout, let_chains, lhs, lib, @@ -422,6 +439,7 @@ symbols! { LintPass, lint_reasons, literal, + llvm_asm, local_inner_macros, log_syntax, loop_break_value, @@ -453,6 +471,7 @@ symbols! { min_align_of, min_const_fn, min_const_unsafe_fn, + min_specialization, mips_target_feature, mmx_target_feature, module, @@ -470,6 +489,7 @@ symbols! { needs_drop, needs_panic_runtime, negate_unsigned, + negative_impls, never, never_type, never_type_fallback, @@ -487,18 +507,22 @@ 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, not, note, object_safe_for_dispatch, + offset, Ok, omit_gdb_pretty_printer_section, on, @@ -511,11 +535,13 @@ symbols! { option, Option, option_env, + options, opt_out_copy, or, or_patterns, Ord, Ordering, + out, Output, overlapping_marker_traits, packed, @@ -541,13 +567,14 @@ symbols! { plugin, plugin_registrar, plugins, + poll, Poll, - poll_with_tls_context, powerpc_target_feature, precise_pointer_size_matching, pref_align_of, prelude, prelude_import, + preserves_flags, primitive, proc_dash_macro: "proc-macro", proc_macro, @@ -561,9 +588,13 @@ symbols! { proc_macro_mod, proc_macro_non_items, proc_macro_path_invoc, + profiler_builtins, profiler_runtime, + ptr_guaranteed_eq, + ptr_guaranteed_ne, ptr_offset_from, pub_restricted, + pure, pushpop_unsafe, quad_precision_float, question_mark, @@ -577,6 +608,8 @@ symbols! { raw_dylib, raw_identifiers, raw_ref_op, + Rc, + readonly, Ready, reason, recursion_limit, @@ -598,6 +631,7 @@ symbols! { Result, Return, rhs, + riscv_target_feature, rlib, rotate_left, rotate_right, @@ -647,6 +681,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, @@ -654,6 +689,8 @@ symbols! { rustc_proc_macro_decls, rustc_promotable, rustc_regions, + rustc_unsafe_specialization_marker, + rustc_specialization_trait, rustc_stable, rustc_std_internal_symbol, rustc_symbol_name, @@ -710,12 +747,15 @@ 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, task, + _task_context, tbm_target_feature, termination_trait, termination_trait_test, @@ -776,6 +816,7 @@ symbols! { unmarked_api, unreachable_code, unrestricted_attribute_tokens, + unsafe_block_in_unsafe_fn, unsafe_no_drop_flag, unsized_locals, unsized_tuple_coercion, @@ -793,6 +834,7 @@ symbols! { var, vec, Vec, + version, vis, visible_private_types, volatile, @@ -853,12 +895,12 @@ impl Ident { } /// "Normalize" ident for use in comparisons using "item hygiene". - /// Identifiers with same string value become same if they came from the same "modern" macro + /// Identifiers with same string value become same if they came from the same macro 2.0 macro /// (e.g., `macro` item, but not `macro_rules` item) and stay different if they came from - /// different "modern" macros. + /// different macro 2.0 macros. /// Technically, this operation strips all non-opaque marks from ident's syntactic context. - pub fn modern(self) -> Ident { - Ident::new(self.name, self.span.modern()) + pub fn normalize_to_macros_2_0(self) -> Ident { + Ident::new(self.name, self.span.normalize_to_macros_2_0()) } /// "Normalize" ident for use in comparisons using "local variable hygiene". @@ -866,8 +908,8 @@ impl Ident { /// macro (e.g., `macro` or `macro_rules!` items) and stay different if they came from different /// non-transparent macros. /// Technically, this operation strips all transparent marks from ident's syntactic context. - pub fn modern_and_legacy(self) -> Ident { - Ident::new(self.name, self.span.modern_and_legacy()) + pub fn normalize_to_macro_rules(self) -> Ident { + Ident::new(self.name, self.span.normalize_to_macro_rules()) } /// Convert the name to a `SymbolStr`. This is a slowish operation because @@ -979,6 +1021,31 @@ impl fmt::Display for IdentPrinter { } } +/// An newtype around `Ident` that calls [Ident::normalize_to_macro_rules] on +/// construction. +// FIXME(matthewj, petrochenkov) Use this more often, add a similar +// `ModernIdent` struct and use that as well. +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct MacroRulesNormalizedIdent(Ident); + +impl MacroRulesNormalizedIdent { + pub fn new(ident: Ident) -> Self { + Self(ident.normalize_to_macro_rules()) + } +} + +impl fmt::Debug for MacroRulesNormalizedIdent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl fmt::Display for MacroRulesNormalizedIdent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + /// An interned string. /// /// Internally, a `Symbol` is implemented as an index, and all operations @@ -997,7 +1064,7 @@ rustc_index::newtype_index! { impl Symbol { const fn new(n: u32) -> Self { - Symbol(SymbolIndex::from_u32_const(n)) + Symbol(SymbolIndex::from_u32(n)) } /// Maps a string to its interned representation. @@ -1118,12 +1185,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; @@ -1138,8 +1213,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 new file mode 100644 index 0000000000000..d670ababe9f12 --- /dev/null +++ b/src/librustc_symbol_mangling/Cargo.toml @@ -0,0 +1,23 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_symbol_mangling" +version = "0.0.0" +edition = "2018" + +[lib] +name = "rustc_symbol_mangling" +path = "lib.rs" +doctest = false + +[dependencies] +log = "0.4" +punycode = "0.4.0" +rustc-demangle = "0.1.16" + +rustc_ast = { path = "../librustc_ast" } +rustc_span = { path = "../librustc_span" } +rustc_middle = { path = "../librustc_middle" } +rustc_hir = { path = "../librustc_hir" } +rustc_target = { path = "../librustc_target" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_session = { path = "../librustc_session" } diff --git a/src/librustc_codegen_utils/symbol_names/legacy.rs b/src/librustc_symbol_mangling/legacy.rs similarity index 94% rename from src/librustc_codegen_utils/symbol_names/legacy.rs rename to src/librustc_symbol_mangling/legacy.rs index 0dedda9bb6b73..3038b0c6bd7eb 100644 --- a/src/librustc_codegen_utils/symbol_names/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; @@ -59,10 +59,14 @@ pub(super) fn mangle( .print_def_path(def_id, &[]) .unwrap(); - if instance.is_vtable_shim() { + if let ty::InstanceDef::VtableShim(..) = instance.def { let _ = printer.write_str("{{vtable-shim}}"); } + if let ty::InstanceDef::ReifyShim(..) = instance.def { + let _ = printer.write_str("{{reify-shim}}"); + } + printer.path.finish(hash) } @@ -123,7 +127,8 @@ fn get_symbol_hash<'tcx>( } // We want to avoid accidental collision between different types of instances. - // Especially, VtableShim may overlap with its original instance without this. + // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original + // instances without this. discriminant(&instance.def).hash_stable(&mut hcx, &mut hasher); }); @@ -211,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), @@ -259,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() => @@ -303,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 new file mode 100644 index 0000000000000..5a1c6491f86d8 --- /dev/null +++ b/src/librustc_symbol_mangling/lib.rs @@ -0,0 +1,270 @@ +//! The Rust Linkage Model and Symbol Names +//! ======================================= +//! +//! The semantic model of Rust linkage is, broadly, that "there's no global +//! namespace" between crates. Our aim is to preserve the illusion of this +//! model despite the fact that it's not *quite* possible to implement on +//! modern linkers. We initially didn't use system linkers at all, but have +//! been convinced of their utility. +//! +//! There are a few issues to handle: +//! +//! - Linkers operate on a flat namespace, so we have to flatten names. +//! We do this using the C++ namespace-mangling technique. Foo::bar +//! symbols and such. +//! +//! - Symbols for distinct items with the same *name* need to get different +//! linkage-names. Examples of this are monomorphizations of functions or +//! items within anonymous scopes that end up having the same path. +//! +//! - Symbols in different crates but with same names "within" the crate need +//! to get different linkage-names. +//! +//! - Symbol names should be deterministic: Two consecutive runs of the +//! compiler over the same code base should produce the same symbol names for +//! the same items. +//! +//! - Symbol names should not depend on any global properties of the code base, +//! so that small modifications to the code base do not result in all symbols +//! changing. In previous versions of the compiler, symbol names incorporated +//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be +//! infeasible when used in conjunction with incremental compilation because +//! small code changes would invalidate all symbols generated previously. +//! +//! - Even symbols from different versions of the same crate should be able to +//! live next to each other without conflict. +//! +//! In order to fulfill the above requirements the following scheme is used by +//! the compiler: +//! +//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit +//! hash value into every exported symbol name. Anything that makes a difference +//! to the symbol being named, but does not show up in the regular path needs to +//! be fed into this hash: +//! +//! - Different monomorphizations of the same item have the same path but differ +//! in their concrete type parameters, so these parameters are part of the +//! data being digested for the symbol hash. +//! +//! - Rust allows items to be defined in anonymous scopes, such as in +//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have +//! the path `foo::bar`, since the anonymous scopes do not contribute to the +//! path of an item. The compiler already handles this case via so-called +//! disambiguating `DefPaths` which use indices to distinguish items with the +//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` +//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation +//! information into the symbol name too, these indices are fed into the +//! symbol hash, so that the above two symbols would end up with different +//! hash values. +//! +//! The two measures described above suffice to avoid intra-crate conflicts. In +//! order to also avoid inter-crate conflicts two more measures are taken: +//! +//! - The name of the crate containing the symbol is prepended to the symbol +//! name, i.e., symbols are "crate qualified". For example, a function `foo` in +//! module `bar` in crate `baz` would get a symbol name like +//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids +//! simple conflicts between functions from different crates. +//! +//! - In order to be able to also use symbols from two versions of the same +//! crate (which naturally also have the same name), a stronger measure is +//! required: The compiler accepts an arbitrary "disambiguator" value via the +//! `-C metadata` command-line argument. This disambiguator is then fed into +//! the symbol hash of every exported item. Consequently, the symbols in two +//! identical crates but with different disambiguators are not in conflict +//! with each other. This facility is mainly intended to be used by build +//! tools like Cargo. +//! +//! A note on symbol name stability +//! ------------------------------- +//! Previous versions of the compiler resorted to feeding NodeIds into the +//! symbol hash in order to disambiguate between items with the same path. The +//! current version of the name generation algorithm takes great care not to do +//! that, since NodeIds are notoriously unstable: A small change to the +//! code base will offset all NodeIds after the change and thus, much as using +//! the SVH in the hash, invalidate an unbounded number of symbol names. This +//! makes re-using previously compiled code for incremental compilation +//! virtually impossible. Thus, symbol hash generation exclusively relies on +//! DefPaths which are much more robust in the face of changes to the code base. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(never_type)] +#![feature(nll)] +#![feature(or_patterns)] +#![feature(in_band_lifetimes)] +#![recursion_limit = "256"] + +#[macro_use] +extern crate rustc_middle; + +use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_hir::Node; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_session::config::SymbolManglingVersion; + +use rustc_span::symbol::Symbol; + +use log::debug; + +mod legacy; +mod v0; + +pub mod test; + +/// This function computes the symbol name for the given `instance` and the +/// given instantiating crate. That is, if you know that instance X is +/// instantiated in crate Y, this is the symbol name this instance would have. +pub fn symbol_name_for_instance_in_crate( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + instantiating_crate: CrateNum, +) -> String { + compute_symbol_name(tcx, instance, || instantiating_crate) +} + +pub fn provide(providers: &mut Providers<'_>) { + *providers = Providers { symbol_name: symbol_name_provider, ..*providers }; +} + +// The `symbol_name` query provides the symbol name for calling a given +// instance from the local crate. In particular, it will also look up the +// correct symbol name of instances from upstream crates. +fn symbol_name_provider(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::SymbolName { + let symbol_name = compute_symbol_name(tcx, instance, || { + // This closure determines the instantiating crate for instances that + // need an instantiating-crate-suffix for their symbol name, in order + // to differentiate between local copies. + if is_generic(instance.substs) { + // For generics we might find re-usable upstream instances. If there + // is one, we rely on the symbol being instantiated locally. + instance.upstream_monomorphization(tcx).unwrap_or(LOCAL_CRATE) + } else { + // For non-generic things that need to avoid naming conflicts, we + // always instantiate a copy in the local crate. + LOCAL_CRATE + } + }); + + ty::SymbolName { name: Symbol::intern(&symbol_name) } +} + +/// Computes the symbol name for the given instance. This function will call +/// `compute_instantiating_crate` if it needs to factor the instantiating crate +/// into the symbol name. +fn compute_symbol_name( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + compute_instantiating_crate: impl FnOnce() -> CrateNum, +) -> String { + let def_id = instance.def_id(); + let substs = instance.substs; + + debug!("symbol_name(def_id={:?}, substs={:?})", def_id, substs); + + // FIXME(eddyb) Precompute a custom symbol name based on attributes. + let is_foreign = if let Some(def_id) = def_id.as_local() { + if tcx.plugin_registrar_fn(LOCAL_CRATE) == Some(def_id.to_def_id()) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_plugin_registrar_symbol(disambiguator); + } + if tcx.proc_macro_decls_static(LOCAL_CRATE) == Some(def_id.to_def_id()) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_proc_macro_decls_symbol(disambiguator); + } + let hir_id = tcx.hir().as_local_hir_id(def_id); + match tcx.hir().get(hir_id) { + Node::ForeignItem(_) => true, + _ => false, + } + } else { + tcx.is_foreign_item(def_id) + }; + + let attrs = tcx.codegen_fn_attrs(def_id); + + // Foreign items by default use no mangling for their symbol name. There's a + // few exceptions to this rule though: + // + // * This can be overridden with the `#[link_name]` attribute + // + // * On the wasm32 targets there is a bug (or feature) in LLD [1] where the + // same-named symbol when imported from different wasm modules will get + // hooked up incorrectly. As a result foreign symbols, on the wasm target, + // with a wasm import module, get mangled. Additionally our codegen will + // deduplicate symbols based purely on the symbol name, but for wasm this + // isn't quite right because the same-named symbol on wasm can come from + // different modules. For these reasons if `#[link(wasm_import_module)]` + // is present we mangle everything on wasm because the demangled form will + // show up in the `wasm-import-name` custom attribute in LLVM IR. + // + // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 + if is_foreign { + if tcx.sess.target.target.arch != "wasm32" + || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id) + { + if let Some(name) = attrs.link_name { + return name.to_string(); + } + return tcx.item_name(def_id).to_string(); + } + } + + if let Some(name) = attrs.export_name { + // Use provided name + return name.to_string(); + } + + if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { + // Don't mangle + return tcx.item_name(def_id).to_string(); + } + + let avoid_cross_crate_conflicts = + // If this is an instance of a generic function, we also hash in + // the ID of the instantiating crate. This avoids symbol conflicts + // in case the same instances is emitted in two crates of the same + // project. + is_generic(substs) || + + // If we're dealing with an instance of a function that's inlined from + // another crate but we're marking it as globally shared to our + // compliation (aka we're not making an internal copy in each of our + // codegen units) then this symbol may become an exported (but hidden + // visibility) symbol. This means that multiple crates may do the same + // and we want to be sure to avoid any symbol conflicts here. + match MonoItem::Fn(instance).instantiation_mode(tcx) { + InstantiationMode::GloballyShared { may_conflict: true } => true, + _ => false, + }; + + let instantiating_crate = + if avoid_cross_crate_conflicts { Some(compute_instantiating_crate()) } else { None }; + + // Pick the crate responsible for the symbol mangling version, which has to: + // 1. be stable for each instance, whether it's being defined or imported + // 2. obey each crate's own `-Z symbol-mangling-version`, as much as possible + // We solve these as follows: + // 1. because symbol names depend on both `def_id` and `instantiating_crate`, + // both their `CrateNum`s are stable for any given instance, so we can pick + // either and have a stable choice of symbol mangling version + // 2. we favor `instantiating_crate` where possible (i.e. when `Some`) + let mangling_version_crate = instantiating_crate.unwrap_or(def_id.krate); + let mangling_version = if mangling_version_crate == LOCAL_CRATE { + tcx.sess.opts.debugging_opts.symbol_mangling_version + } else { + tcx.symbol_mangling_version(mangling_version_crate) + }; + + match mangling_version { + SymbolManglingVersion::Legacy => legacy::mangle(tcx, instance, instantiating_crate), + SymbolManglingVersion::V0 => v0::mangle(tcx, instance, instantiating_crate), + } +} + +fn is_generic(substs: SubstsRef<'_>) -> bool { + substs.non_erasable_generics().next().is_some() +} diff --git a/src/librustc_symbol_mangling/test.rs b/src/librustc_symbol_mangling/test.rs new file mode 100644 index 0000000000000..5175b692e17b6 --- /dev/null +++ b/src/librustc_symbol_mangling/test.rs @@ -0,0 +1,70 @@ +//! Walks the crate looking for items/impl-items/trait-items that have +//! either a `rustc_symbol_name` or `rustc_def_path` attribute and +//! generates an error giving, respectively, the symbol name or +//! def-path. This is used for unit testing the code that generates +//! paths etc in all kinds of annoying scenarios. + +use rustc_hir as hir; +use rustc_middle::ty::{Instance, TyCtxt}; +use rustc_span::symbol::{sym, Symbol}; + +const SYMBOL_NAME: Symbol = sym::rustc_symbol_name; +const DEF_PATH: Symbol = sym::rustc_def_path; + +pub fn report_symbol_names(tcx: TyCtxt<'_>) { + // if the `rustc_attrs` feature is not enabled, then the + // attributes we are interested in cannot be present anyway, so + // skip the walk. + if !tcx.features().rustc_attrs { + return; + } + + tcx.dep_graph.with_ignore(|| { + let mut visitor = SymbolNamesTest { tcx }; + tcx.hir().krate().visit_all_item_likes(&mut visitor); + }) +} + +struct SymbolNamesTest<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl SymbolNamesTest<'tcx> { + fn process_attrs(&mut self, hir_id: hir::HirId) { + let tcx = self.tcx; + let def_id = tcx.hir().local_def_id(hir_id); + for attr in tcx.get_attrs(def_id.to_def_id()).iter() { + if attr.check_name(SYMBOL_NAME) { + // for now, can only use on monomorphic names + let instance = Instance::mono(tcx, def_id.to_def_id()); + let mangled = self.tcx.symbol_name(instance); + tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled)); + if let Ok(demangling) = rustc_demangle::try_demangle(&mangled.name.as_str()) { + tcx.sess.span_err(attr.span, &format!("demangling({})", demangling)); + tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); + } + } else if attr.check_name(DEF_PATH) { + let path = tcx.def_path_str(def_id.to_def_id()); + tcx.sess.span_err(attr.span, &format!("def-path({})", path)); + } + + // (*) The formatting of `tag({})` is chosen so that tests can elect + // to test the entirety of the string, if they choose, or else just + // some subset. + } + } +} + +impl hir::itemlikevisit::ItemLikeVisitor<'tcx> for SymbolNamesTest<'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + self.process_attrs(item.hir_id); + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { + self.process_attrs(trait_item.hir_id); + } + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { + self.process_attrs(impl_item.hir_id); + } +} diff --git a/src/librustc_codegen_utils/symbol_names/v0.rs b/src/librustc_symbol_mangling/v0.rs similarity index 96% rename from src/librustc_codegen_utils/symbol_names/v0.rs rename to src/librustc_symbol_mangling/v0.rs index ce6d0d9dc5ba8..7d117b77cf5e5 100644 --- a/src/librustc_codegen_utils/symbol_names/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; @@ -34,8 +34,17 @@ pub(super) fn mangle( binders: vec![], out: String::from(prefix), }; - cx = if instance.is_vtable_shim() { - cx.path_append_ns(|cx| cx.print_def_path(def_id, substs), 'S', 0, "").unwrap() + + // Append `::{shim:...#0}` to shims that can coexist with a non-shim instance. + let shim_kind = match instance.def { + ty::InstanceDef::VtableShim(_) => Some("vtable"), + ty::InstanceDef::ReifyShim(_) => Some("reify"), + + _ => None, + }; + + cx = if let Some(shim_kind) = shim_kind { + cx.path_append_ns(|cx| cx.print_def_path(def_id, substs), 'S', 0, shim_kind).unwrap() } else { cx.print_def_path(def_id, substs).unwrap() }; @@ -144,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("_"); } _ => {} @@ -336,7 +345,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { ty::Never => "z", // Placeholders (should be demangled as `_`). - ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error => "p", + ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => "p", _ => "", }; @@ -358,7 +367,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { ty::Tuple(_) if ty.is_unit() => unreachable!(), // Placeholders, also handled as part of basic types. - ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error => { + ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => { unreachable!() } @@ -404,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)?; @@ -469,7 +477,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { predicates: &'tcx ty::List>, ) -> Result { for predicate in predicates { - match *predicate { + match predicate { ty::ExistentialPredicate::Trait(trait_ref) => { // Use a type that can't appear in defaults of type parameters. let dummy_self = self.tcx.mk_ty_infer(ty::FreshTy(0)); diff --git a/src/librustc_target/Cargo.toml b/src/librustc_target/Cargo.toml index 0e234036879e5..c73490e451320 100644 --- a/src/librustc_target/Cargo.toml +++ b/src/librustc_target/Cargo.toml @@ -13,6 +13,6 @@ bitflags = "1.2.1" log = "0.4" rustc_data_structures = { path = "../librustc_data_structures" } rustc_macros = { path = "../librustc_macros" } -rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_serialize = { path = "../librustc_serialize" } rustc_span = { path = "../librustc_span" } rustc_index = { path = "../librustc_index" } 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/avr.rs b/src/librustc_target/abi/call/avr.rs new file mode 100644 index 0000000000000..c1f7a1e3af586 --- /dev/null +++ b/src/librustc_target/abi/call/avr.rs @@ -0,0 +1,59 @@ +//! LLVM-frontend specific AVR calling convention implementation. +//! +//! # Current calling convention ABI +//! +//! Inherited from Clang's `clang::DefaultABIInfo` implementation - self described +//! as +//! +//! > the default implementation for ABI specific details. This implementation +//! > provides information which results in +//! > self-consistent and sensible LLVM IR generation, but does not +//! > conform to any particular ABI. +//! > +//! > - Doxygen Doxumentation of `clang::DefaultABIInfo` +//! +//! This calling convention may not match AVR-GCC in all cases. +//! +//! In the future, an AVR-GCC compatible argument classification ABI should be +//! adopted in both Rust and Clang. +//! +//! *NOTE*: Currently, this module implements the same calling convention +//! that clang with AVR currently does - the default, simple, unspecialized +//! ABI implementation available to all targets. This ABI is not +//! binary-compatible with AVR-GCC. Once LLVM [PR46140](https://bugs.llvm.org/show_bug.cgi?id=46140) +//! is completed, this module should be updated to match so that both Clang +//! and Rust emit code to the same AVR-GCC compatible ABI. +//! +//! In particular, both Clang and Rust may not have the same semantics +//! when promoting arguments to indirect references as AVR-GCC. It is important +//! to note that the core AVR ABI implementation within LLVM itself is ABI +//! compatible with AVR-GCC - Rust and AVR-GCC only differ in the small amount +//! of compiler frontend specific calling convention logic implemented here. + +use crate::abi::call::{ArgAbi, FnAbi}; + +fn classify_ret_ty(ret: &mut ArgAbi<'_, Ty>) { + if ret.layout.is_aggregate() { + ret.make_indirect(); + } +} + +fn classify_arg_ty(arg: &mut ArgAbi<'_, Ty>) { + if arg.layout.is_aggregate() { + arg.make_indirect(); + } +} + +pub fn compute_abi_info(fty: &mut FnAbi<'_, Ty>) { + if !fty.ret.is_ignore() { + classify_ret_ty(&mut fty.ret); + } + + for arg in &mut fty.args { + if arg.is_ignore() { + continue; + } + + classify_arg_ty(arg); + } +} 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..8f7e2bba5aa6d 100644 --- a/src/librustc_target/abi/call/mod.rs +++ b/src/librustc_target/abi/call/mod.rs @@ -1,10 +1,11 @@ -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; mod amdgpu; mod arm; +mod avr; mod hexagon; mod mips; mod mips64; @@ -264,7 +265,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 +285,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 +309,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 +329,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 +408,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 +417,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()) } } @@ -522,6 +526,8 @@ pub enum Conv { X86_64Win64, AmdGpuKernel, + AvrInterrupt, + AvrNonBlockingInterrupt, } /// Metadata describing how the arguments to a native function @@ -546,13 +552,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" => { @@ -575,6 +583,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "aarch64" => aarch64::compute_abi_info(cx, self), "amdgpu" => amdgpu::compute_abi_info(cx, self), "arm" => arm::compute_abi_info(cx, self), + "avr" => avr::compute_abi_info(self), "mips" => mips::compute_abi_info(cx, self), "mips64" => mips64::compute_abi_info(cx, self), "powerpc" => powerpc::compute_abi_info(self), 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 2f8bbd66c322b..c79e9bb289008 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,35 +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(_) => Size::ZERO, - FieldPlacement::Array { stride, count } => { - let i = i as u64; + FieldsShape::Primitive => { + unreachable!("FieldsShape::offset: `Primitive`s have no fields") + } + FieldsShape::Union(count) => { + assert!( + i < count.get(), + "tried to access field {} of union with {} fields", + i, + count + ); + Size::ZERO + } + FieldsShape::Array { stride, count } => { + let i = u64::try_from(i).unwrap(); assert!(i < count); stride * i } - 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 @@ -689,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; @@ -703,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 { @@ -748,7 +777,7 @@ impl Abi { Primitive::Int(_, signed) => signed, _ => false, }, - _ => false, + _ => panic!("`is_signed` on non-scalar ABI {:?}", self), } } @@ -780,25 +809,30 @@ pub enum Variants { /// Single enum variants, structs/tuples, unions, and all non-ADTs. Single { index: VariantIdx }, - /// Enum-likes with more than one inhabited variant: for each case there is - /// a struct, and they all have space reserved for the discriminant. - /// For enums this is the sole field of the layout. + /// Enum-likes with more than one inhabited variant: each variant comes with + /// a *discriminant* (usually the same as the variant index but the user can + /// assign explicit discriminant values). That discriminant is encoded + /// as a *tag* on the machine. The layout of each variant is + /// a struct, and they all have space reserved for the tag. + /// For enums, the tag is the sole field of the layout. Multiple { - discr: Scalar, - discr_kind: DiscriminantKind, - discr_index: usize, - variants: IndexVec, + tag: Scalar, + tag_encoding: TagEncoding, + tag_field: usize, + variants: IndexVec, }, } #[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] -pub enum DiscriminantKind { - /// Integer tag holding the discriminant value itself. - Tag, +pub enum TagEncoding { + /// The tag directly stores the discriminant, but possibly with a smaller layout + /// (so converting the tag to the discriminant can require sign extension). + Direct, /// Niche (values invalid for a type) encoding the discriminant: - /// the variant `dataful_variant` contains a niche at an arbitrary - /// offset (field `discr_index` of the enum), which for a variant with + /// Discriminant and variant index coincide. + /// The variant `dataful_variant` contains a niche at an arbitrary + /// offset (field `tag_field` of the enum), which for a variant with /// discriminant `d` is set to /// `(d - niche_variants.start).wrapping_add(niche_start)`. /// @@ -870,10 +904,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 @@ -901,14 +934,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, @@ -917,38 +950,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 { @@ -1002,30 +1035,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) @@ -1033,14 +1066,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() @@ -1067,8 +1100,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 { @@ -1094,7 +1127,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/hexagon.rs b/src/librustc_target/asm/hexagon.rs new file mode 100644 index 0000000000000..d41941d0b4cd7 --- /dev/null +++ b/src/librustc_target/asm/hexagon.rs @@ -0,0 +1,93 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + Hexagon HexagonInlineAsmRegClass { + reg, + } +} + +impl HexagonInlineAsmRegClass { + 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 => types! { _: I8, I16, I32, F32; }, + } + } +} + +def_regs! { + Hexagon HexagonInlineAsmReg HexagonInlineAsmRegClass { + r0: reg = ["r0"], + r1: reg = ["r1"], + r2: reg = ["r2"], + r3: reg = ["r3"], + r4: reg = ["r4"], + r5: reg = ["r5"], + r6: reg = ["r6"], + r7: reg = ["r7"], + r8: reg = ["r8"], + r9: reg = ["r9"], + r10: reg = ["r10"], + r11: reg = ["r11"], + r12: reg = ["r12"], + r13: reg = ["r13"], + r14: reg = ["r14"], + r15: reg = ["r15"], + r16: reg = ["r16"], + r17: reg = ["r17"], + r18: reg = ["r18"], + r19: reg = ["r19"], + r20: reg = ["r20"], + r21: reg = ["r21"], + r22: reg = ["r22"], + r23: reg = ["r23"], + r24: reg = ["r24"], + r25: reg = ["r25"], + r26: reg = ["r26"], + r27: reg = ["r27"], + r28: reg = ["r28"], + #error = ["r29", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r30", "fr"] => + "the frame register cannot be used as an operand for inline asm", + #error = ["r31", "lr"] => + "the link register cannot be used as an operand for inline asm", + } +} + +impl HexagonInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option, + ) -> fmt::Result { + out.write_str(self.name()) + } + + pub fn overlapping_regs(self, mut _cb: impl FnMut(HexagonInlineAsmReg)) {} +} diff --git a/src/librustc_target/asm/mod.rs b/src/librustc_target/asm/mod.rs new file mode 100644 index 0000000000000..834d7c6d381a3 --- /dev/null +++ b/src/librustc_target/asm/mod.rs @@ -0,0 +1,572 @@ +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, + )* + }) => { + #[allow(unreachable_code)] + #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] + #[allow(non_camel_case_types)] + pub enum $arch_reg { + $($reg,)* + } + + impl $arch_reg { + pub fn name(self) -> &'static str { + match self { + $(Self::$reg => $reg_name,)* + } + } + + pub fn reg_class(self) -> $arch_regclass { + match self { + $(Self::$reg => $arch_regclass::$class,)* + } + } + + pub fn parse( + _arch: super::InlineAsmArch, + mut _has_feature: impl FnMut(&str) -> bool, + 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, + >, + ) { + #[allow(unused_imports)] + 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 hexagon; +mod nvptx; +mod riscv; +mod x86; + +pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; +pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; +pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; +pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; +pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; +pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; + +#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash)] +pub enum InlineAsmArch { + X86, + X86_64, + Arm, + AArch64, + RiscV32, + RiscV64, + Nvptx64, + Hexagon, +} + +impl FromStr for InlineAsmArch { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "x86" => Ok(Self::X86), + "x86_64" => Ok(Self::X86_64), + "arm" => Ok(Self::Arm), + "aarch64" => Ok(Self::AArch64), + "riscv32" => Ok(Self::RiscV32), + "riscv64" => Ok(Self::RiscV64), + "nvptx64" => Ok(Self::Nvptx64), + "hexagon" => Ok(Self::Hexagon), + _ => Err(()), + } + } +} + +#[derive( + Copy, + Clone, + RustcEncodable, + RustcDecodable, + Debug, + Eq, + PartialEq, + Hash, + HashStable_Generic +)] +pub enum InlineAsmReg { + X86(X86InlineAsmReg), + Arm(ArmInlineAsmReg), + AArch64(AArch64InlineAsmReg), + RiscV(RiscVInlineAsmReg), + Nvptx(NvptxInlineAsmReg), + Hexagon(HexagonInlineAsmReg), +} + +impl InlineAsmReg { + pub fn name(self) -> &'static str { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + Self::Hexagon(r) => r.name(), + } + } + + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::X86(r) => InlineAsmRegClass::X86(r.reg_class()), + Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()), + Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()), + Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), + Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()), + } + } + + pub fn parse( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + name: Symbol, + ) -> Result { + // FIXME: use direct symbol comparison for register names + // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`. + let name = name.as_str(); + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmReg::parse(arch, has_feature, &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)?) + } + InlineAsmArch::Nvptx64 => { + Self::Nvptx(NvptxInlineAsmReg::parse(arch, has_feature, &name)?) + } + InlineAsmArch::Hexagon => { + Self::Hexagon(HexagonInlineAsmReg::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), + Self::Hexagon(r) => r.emit(out, arch, modifier), + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) { + match self { + Self::X86(r) => r.overlapping_regs(|r| cb(Self::X86(r))), + Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))), + Self::AArch64(_) => cb(self), + Self::RiscV(_) => cb(self), + Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), + } + } +} + +#[derive( + Copy, + Clone, + RustcEncodable, + RustcDecodable, + Debug, + Eq, + PartialEq, + Hash, + HashStable_Generic +)] +pub enum InlineAsmRegClass { + X86(X86InlineAsmRegClass), + Arm(ArmInlineAsmRegClass), + AArch64(AArch64InlineAsmRegClass), + RiscV(RiscVInlineAsmRegClass), + Nvptx(NvptxInlineAsmRegClass), + Hexagon(HexagonInlineAsmRegClass), +} + +impl InlineAsmRegClass { + pub fn name(self) -> &'static str { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + Self::Nvptx(r) => r.name(), + Self::Hexagon(r) => r.name(), + } + } + + /// Returns a suggested register class to use for this type. This is called + /// after type checking via `supported_types` fails to give a better error + /// message to the user. + pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option { + match self { + Self::X86(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::X86), + Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm), + Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64), + Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV), + Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx), + Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon), + } + } + + /// Returns a suggested template modifier to use for this type and an + /// example of a register named formatted with it. + /// + /// Such suggestions are useful if a type smaller than the full register + /// size is used and a modifier can be used to point to the subregister of + /// the correct size. + pub fn suggest_modifier( + self, + arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.suggest_modifier(arch, ty), + Self::Arm(r) => r.suggest_modifier(arch, ty), + Self::AArch64(r) => r.suggest_modifier(arch, ty), + Self::RiscV(r) => r.suggest_modifier(arch, ty), + Self::Nvptx(r) => r.suggest_modifier(arch, ty), + Self::Hexagon(r) => r.suggest_modifier(arch, ty), + } + } + + /// Returns the default modifier for this register and an example of a + /// register named formatted with it. + /// + /// This is only needed when the register class can suggest a modifier, so + /// that the user can be shown how to get the default behavior without a + /// warning. + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.default_modifier(arch), + Self::Arm(r) => r.default_modifier(arch), + Self::AArch64(r) => r.default_modifier(arch), + Self::RiscV(r) => r.default_modifier(arch), + Self::Nvptx(r) => r.default_modifier(arch), + Self::Hexagon(r) => r.default_modifier(arch), + } + } + + /// Returns a list of supported types for this register class, each with a + /// options target feature required to use this type. + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::X86(r) => r.supported_types(arch), + Self::Arm(r) => r.supported_types(arch), + Self::AArch64(r) => r.supported_types(arch), + Self::RiscV(r) => r.supported_types(arch), + Self::Nvptx(r) => r.supported_types(arch), + Self::Hexagon(r) => r.supported_types(arch), + } + } + + pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { + // FIXME: use direct symbol comparison for register class names + name.with(|name| { + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::AArch64 => { + Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Hexagon => { + Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?) + } + }) + }) + } + + /// Returns the list of template modifiers that can be used with this + /// register class. + pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] { + match self { + Self::X86(r) => r.valid_modifiers(arch), + Self::Arm(r) => r.valid_modifiers(arch), + Self::AArch64(r) => r.valid_modifiers(arch), + Self::RiscV(r) => r.valid_modifiers(arch), + Self::Nvptx(r) => r.valid_modifiers(arch), + Self::Hexagon(r) => r.valid_modifiers(arch), + } + } +} + +#[derive( + Copy, + Clone, + RustcEncodable, + RustcDecodable, + Debug, + Eq, + PartialEq, + Hash, + HashStable_Generic +)] +pub enum InlineAsmRegOrRegClass { + Reg(InlineAsmReg), + RegClass(InlineAsmRegClass), +} + +impl InlineAsmRegOrRegClass { + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::Reg(r) => r.reg_class(), + Self::RegClass(r) => r, + } + } +} + +impl fmt::Display for InlineAsmRegOrRegClass { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Reg(r) => write!(f, "\"{}\"", r.name()), + Self::RegClass(r) => f.write_str(r.name()), + } + } +} + +/// Set of types which can be used with a particular register class. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum InlineAsmType { + I8, + I16, + I32, + I64, + I128, + F32, + F64, + VecI8(u64), + VecI16(u64), + VecI32(u64), + VecI64(u64), + VecI128(u64), + VecF32(u64), + VecF64(u64), +} + +impl InlineAsmType { + pub fn is_integer(self) -> bool { + match self { + Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 => true, + _ => false, + } + } + + pub fn size(self) -> Size { + Size::from_bytes(match self { + Self::I8 => 1, + Self::I16 => 2, + Self::I32 => 4, + Self::I64 => 8, + Self::I128 => 16, + Self::F32 => 4, + Self::F64 => 8, + Self::VecI8(n) => n * 1, + Self::VecI16(n) => n * 2, + Self::VecI32(n) => n * 4, + Self::VecI64(n) => n * 8, + Self::VecI128(n) => n * 16, + Self::VecF32(n) => n * 4, + Self::VecF64(n) => n * 8, + }) + } +} + +impl fmt::Display for InlineAsmType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::I8 => f.write_str("i8"), + Self::I16 => f.write_str("i16"), + Self::I32 => f.write_str("i32"), + Self::I64 => f.write_str("i64"), + Self::I128 => f.write_str("i128"), + Self::F32 => f.write_str("f32"), + Self::F64 => f.write_str("f64"), + Self::VecI8(n) => write!(f, "i8x{}", n), + Self::VecI16(n) => write!(f, "i16x{}", n), + Self::VecI32(n) => write!(f, "i32x{}", n), + Self::VecI64(n) => write!(f, "i64x{}", n), + Self::VecI128(n) => write!(f, "i128x{}", n), + Self::VecF32(n) => write!(f, "f32x{}", n), + Self::VecF64(n) => write!(f, "f64x{}", n), + } + } +} + +/// Returns the full set of allocatable registers for a given architecture. +/// +/// The registers are structured as a map containing the set of allocatable +/// registers in each register class. A particular register may be allocatable +/// from multiple register classes, in which case it will appear multiple times +/// in the map. +// NOTE: This function isn't used at the moment, but is needed to support +// falling back to an external assembler. +pub fn allocatable_registers( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, +) -> 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 + } + InlineAsmArch::Nvptx64 => { + let mut map = nvptx::regclass_map(); + nvptx::fill_reg_map(arch, has_feature, &mut map); + map + } + InlineAsmArch::Hexagon => { + let mut map = hexagon::regclass_map(); + hexagon::fill_reg_map(arch, has_feature, &mut map); + map + } + } +} diff --git a/src/librustc_target/asm/nvptx.rs b/src/librustc_target/asm/nvptx.rs new file mode 100644 index 0000000000000..43d16ae0f5d10 --- /dev/null +++ b/src/librustc_target/asm/nvptx.rs @@ -0,0 +1,49 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; + +def_reg_class! { + Nvptx NvptxInlineAsmRegClass { + reg16, + reg32, + reg64, + } +} + +impl NvptxInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: 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::reg16 => types! { _: I8, I16; }, + Self::reg32 => types! { _: I8, I16, I32, F32; }, + Self::reg64 => types! { _: I8, I16, I32, F32, I64, F64; }, + } + } +} + +def_regs! { + // Registers in PTX are declared in the assembly. + // There are no predefined registers that one can use. + Nvptx NvptxInlineAsmReg NvptxInlineAsmRegClass {} +} 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 3c397eb444d1d..c2cdd2fd3ecf6 100644 --- a/src/librustc_target/lib.rs +++ b/src/librustc_target/lib.rs @@ -9,18 +9,26 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(bool_to_option)] +#![feature(const_if_match)] +#![feature(const_fn)] +#![feature(const_panic)] #![feature(nll)] #![feature(never_type)] #![feature(associated_type_bounds)] #![feature(exhaustive_patterns)] +// FIXME(#56935): Work around ICEs during cross-compilation. +#[allow(unused)] +extern crate rustc_macros; + #[macro_use] extern crate log; pub mod abi; +pub mod asm; pub mod spec; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in librustc. +/// 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..1447716ca8484 100644 --- a/src/librustc_target/spec/aarch64_apple_ios.rs +++ b/src/librustc_target/spec/aarch64_apple_ios.rs @@ -15,10 +15,22 @@ pub fn target() -> TargetResult { target_vendor: "apple".to_string(), linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { - features: "+neon,+fp-armv8,+cyclone".to_string(), + features: "+neon,+fp-armv8,+apple-a7".to_string(), 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..21f660ac8b839 100644 --- a/src/librustc_target/spec/aarch64_apple_tvos.rs +++ b/src/librustc_target/spec/aarch64_apple_tvos.rs @@ -15,10 +15,11 @@ pub fn target() -> TargetResult { target_vendor: "apple".to_string(), linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { - features: "+neon,+fp-armv8,+cyclone".to_string(), + features: "+neon,+fp-armv8,+apple-a7".to_string(), 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/abi.rs b/src/librustc_target/spec/abi.rs index ac4c561402b67..a5c874bb4ac05 100644 --- a/src/librustc_target/spec/abi.rs +++ b/src/librustc_target/spec/abi.rs @@ -5,23 +5,21 @@ use rustc_macros::HashStable_Generic; #[cfg(test)] mod tests; -#[derive( - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - RustcEncodable, - RustcDecodable, - Clone, - Copy, - Debug, - HashStable_Generic -)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Clone, Copy, Debug)] +#[derive(HashStable_Generic)] pub enum Abi { // N.B., this ordering MUST match the AbiDatas array below. // (This is ensured by the test indices_are_correct().) + // Multiplatform / generic ABIs + // + // These ABIs come first because every time we add a new ABI, we + // have to re-bless all the hashing tests. These are used in many + // places, so giving them stable values reduces test churn. The + // specific values are meaningless. + Rust = 0, + C = 1, + // Single platform ABIs Cdecl, Stdcall, @@ -36,10 +34,10 @@ pub enum Abi { X86Interrupt, AmdGpuKernel, EfiApi, + AvrInterrupt, + AvrNonBlockingInterrupt, // Multiplatform / generic ABIs - Rust, - C, System, RustIntrinsic, RustCall, @@ -60,6 +58,9 @@ pub struct AbiData { #[allow(non_upper_case_globals)] const AbiDatas: &[AbiData] = &[ + // Cross-platform ABIs + AbiData { abi: Abi::Rust, name: "Rust", generic: true }, + AbiData { abi: Abi::C, name: "C", generic: true }, // Platform-specific ABIs AbiData { abi: Abi::Cdecl, name: "cdecl", generic: false }, AbiData { abi: Abi::Stdcall, name: "stdcall", generic: false }, @@ -74,9 +75,13 @@ const AbiDatas: &[AbiData] = &[ AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt", generic: false }, AbiData { abi: Abi::AmdGpuKernel, name: "amdgpu-kernel", generic: false }, AbiData { abi: Abi::EfiApi, name: "efiapi", generic: false }, + AbiData { abi: Abi::AvrInterrupt, name: "avr-interrupt", generic: false }, + AbiData { + abi: Abi::AvrNonBlockingInterrupt, + name: "avr-non-blocking-interrupt", + generic: false, + }, // Cross-platform ABIs - AbiData { abi: Abi::Rust, name: "Rust", generic: true }, - AbiData { abi: Abi::C, name: "C", generic: true }, AbiData { abi: Abi::System, name: "system", generic: true }, AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true }, AbiData { abi: Abi::RustCall, name: "rust-call", generic: true }, diff --git a/src/librustc_target/spec/apple_base.rs b/src/librustc_target/spec/apple_base.rs index d116ddf952aaa..bdd5a893d34e2 100644 --- a/src/librustc_target/spec/apple_base.rs +++ b/src/librustc_target/spec/apple_base.rs @@ -31,6 +31,17 @@ pub fn opts() -> TargetOptions { has_elf_tls: version >= (10, 7), abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, + + // This environment variable is pretty magical but is intended for + // producing deterministic builds. This was first discovered to be used + // by the `ar` tool as a way to control whether or not mtime entries in + // the archive headers were set to zero or not. It appears that + // eventually the linker got updated to do the same thing and now reads + // this environment variable too in recent versions. + // + // For some more info see the commentary on #47086 + link_env: vec![("ZERO_AR_DATE".to_string(), "1".to_string())], + ..Default::default() } } @@ -57,7 +68,7 @@ pub fn macos_link_env_remove() -> Vec { let mut env_remove = Vec::with_capacity(2); // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which // may occur when we're linking a custom build script while targeting iOS for example. - if let Some(sdkroot) = env::var("SDKROOT").ok() { + if let Ok(sdkroot) = env::var("SDKROOT") { if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform") { env_remove.push("SDKROOT".to_string()) } diff --git a/src/librustc_target/spec/apple_sdk_base.rs b/src/librustc_target/spec/apple_sdk_base.rs index 513754352fbfb..0d0a0da9d1c4c 100644 --- a/src/librustc_target/spec/apple_sdk_base.rs +++ b/src/librustc_target/spec/apple_sdk_base.rs @@ -43,40 +43,26 @@ pub fn get_sdk_root(sdk_name: &str) -> Result { // to allow the SDK path to be set. (For clang, xcrun sets // SDKROOT; for rustc, the user or build system can set it, or we // can fall back to checking for xcrun on PATH.) - if let Some(sdkroot) = env::var("SDKROOT").ok() { + if let Ok(sdkroot) = env::var("SDKROOT") { let p = Path::new(&sdkroot); match sdk_name { // Ignore `SDKROOT` if it's clearly set for the wrong platform. "appletvos" if sdkroot.contains("TVSimulator.platform") - || sdkroot.contains("MacOSX.platform") => - { - () - } + || sdkroot.contains("MacOSX.platform") => {} "appletvsimulator" - if sdkroot.contains("TVOS.platform") || sdkroot.contains("MacOSX.platform") => - { - () - } + if sdkroot.contains("TVOS.platform") || sdkroot.contains("MacOSX.platform") => {} "iphoneos" if sdkroot.contains("iPhoneSimulator.platform") - || sdkroot.contains("MacOSX.platform") => - { - () - } + || sdkroot.contains("MacOSX.platform") => {} "iphonesimulator" - if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => - { - () + if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => { } "macosx10.15" if sdkroot.contains("iPhoneOS.platform") - || sdkroot.contains("iPhoneSimulator.platform") => - { - () - } + || sdkroot.contains("iPhoneSimulator.platform") => {} // Ignore `SDKROOT` if it's not a valid path. - _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => (), + _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} _ => return Ok(sdkroot), } } @@ -136,7 +122,7 @@ fn target_cpu(arch: Arch) -> String { match arch { Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher Armv7s => "cortex-a9", - Arm64 => "cyclone", + Arm64 => "apple-a7", I386 => "yonah", X86_64 => "core2", X86_64_macabi => "core2", @@ -155,7 +141,6 @@ pub fn opts(arch: Arch, os: AppleOS) -> Result { let pre_link_args = build_pre_link_args(arch, os)?; Ok(TargetOptions { cpu: target_cpu(arch), - dynamic_linking: false, executables: true, pre_link_args, link_env_remove: link_env_remove(arch), 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/avr_unknown_unknown.rs b/src/librustc_target/spec/avr_unknown_unknown.rs new file mode 100644 index 0000000000000..f90a8def0aa2f --- /dev/null +++ b/src/librustc_target/spec/avr_unknown_unknown.rs @@ -0,0 +1,17 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "avr-unknown-unknown".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "16".to_string(), + data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".to_string(), + arch: "avr".to_string(), + linker_flavor: LinkerFlavor::Gcc, + target_os: "unknown".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + target_c_int_width: 16.to_string(), + options: super::freestanding_base::opts(), + }) +} diff --git a/src/librustc_target/spec/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/freestanding_base.rs b/src/librustc_target/spec/freestanding_base.rs new file mode 100644 index 0000000000000..5402ea074fae1 --- /dev/null +++ b/src/librustc_target/spec/freestanding_base.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; +use std::default::Default; + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + + args.insert( + LinkerFlavor::Gcc, + vec![ + // We want to be able to strip as much executable code as possible + // from the linker command line, and this flag indicates to the + // linker that it can avoid linking in dynamic libraries that don't + // actually satisfy any symbols up to that point (as with many other + // resolutions the linker does). This option only applies to all + // following libraries so we're sure to pass it as one of the first + // arguments. + "-Wl,--as-needed".to_string(), + ], + ); + + TargetOptions { + dynamic_linking: false, + executables: true, + linker_is_gnu: true, + has_rpath: false, + pre_link_args: args, + position_independent_executables: false, + ..Default::default() + } +} diff --git a/src/librustc_target/spec/fuchsia_base.rs b/src/librustc_target/spec/fuchsia_base.rs index 046388e9be8f4..dd55788b664ea 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(); @@ -10,7 +9,14 @@ pub fn opts() -> TargetOptions { "--eh-frame-hdr".to_string(), "--hash-style=gnu".to_string(), "-z".to_string(), + "max-page-size=4096".to_string(), + "-z".to_string(), + "now".to_string(), + "-z".to_string(), "rodynamic".to_string(), + "-z".to_string(), + "separate-loadable-segments".to_string(), + "--pack-dyn-relocs=relr".to_string(), ], ); @@ -24,7 +30,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..29250f21383be 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; @@ -52,26 +54,29 @@ mod arm_base; mod cloudabi_base; mod dragonfly_base; mod freebsd_base; +mod freestanding_base; mod fuchsia_base; mod haiku_base; mod hermit_base; mod hermit_kernel_base; +mod illumos_base; mod l4re_base; mod linux_base; mod linux_kernel_base; mod linux_musl_base; +mod msvc_base; mod netbsd_base; mod openbsd_base; mod redox_base; mod riscv_base; mod solaris_base; mod thumb_base; -mod uefi_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 +269,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 +475,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()); } )+ } @@ -423,6 +580,8 @@ supported_targets! { ("aarch64-fuchsia", aarch64_fuchsia), ("x86_64-fuchsia", x86_64_fuchsia), + ("avr-unknown-unknown", avr_unknown_unknown), + ("x86_64-unknown-l4re-uclibc", x86_64_unknown_l4re_uclibc), ("aarch64-unknown-redox", aarch64_unknown_redox), @@ -447,6 +606,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), @@ -460,6 +621,7 @@ supported_targets! { ("i686-uwp-windows-msvc", i686_uwp_windows_msvc), ("i586-pc-windows-msvc", i586_pc_windows_msvc), ("thumbv7a-pc-windows-msvc", thumbv7a_pc_windows_msvc), + ("thumbv7a-uwp-windows-msvc", thumbv7a_uwp_windows_msvc), ("asmjs-unknown-emscripten", asmjs_unknown_emscripten), ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), @@ -512,6 +674,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 +702,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 +731,25 @@ 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 + pub pre_link_args: LinkArgs, + /// Objects to link before and after all other object code. + pub pre_link_objects: CrtObjects, + pub post_link_objects: CrtObjects, + /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the + /// target's native gcc and fall back to the "self-contained" mode and pull them manually. + /// See `crt_objects.rs` for some more detailed documentation. + pub pre_link_objects_fallback: CrtObjects, + pub post_link_objects_fallback: CrtObjects, + /// Which logic to use to determine whether to fall back to the "self-contained" mode or not. + pub crt_objects_fallback: Option, + /// Linker arguments that are unconditionally passed after any - /// user-defined but before post_link_objects. Standard platform + /// 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 +758,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 +789,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. @@ -686,6 +858,8 @@ pub struct TargetOptions { /// the functions in the executable are not randomized and can be used /// during an exploit of a vulnerability in any code. pub position_independent_executables: bool, + /// Executables that are both statically linked and position-independent are supported. + pub static_position_independent_executables: bool, /// Determines if the target always requires using the PLT for indirect /// library calls or not. This controls the default value of the `-Z plt` flag. pub needs_plt: bool, @@ -711,11 +885,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 +944,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 +984,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 { @@ -825,17 +999,17 @@ impl Default for TargetOptions { linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()), lld_flavor: LldFlavor::Ld, 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, @@ -858,13 +1032,14 @@ impl Default for TargetOptions { has_rpath: false, no_default_libraries: true, position_independent_executables: false, + static_position_independent_executables: false, needs_plt: false, relro_level: RelroLevel::None, - pre_link_objects_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 +1050,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 +1069,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 +1079,7 @@ impl Default for TargetOptions { llvm_abiname: "".to_string(), relax_elf_relocations: false, llvm_args: vec![], + use_ctors_section: false, } } } @@ -993,20 +1169,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 +1198,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 +1261,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 +1306,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 +1392,17 @@ 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 +1411,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); @@ -1184,6 +1436,7 @@ impl Target { key!(has_rpath, bool); key!(no_default_libraries, bool); key!(position_independent_executables, bool); + key!(static_position_independent_executables, bool); key!(needs_plt, bool); key!(relro_level, RelroLevel)?; key!(archive_format); @@ -1191,7 +1444,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 +1462,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 +1472,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 +1623,17 @@ 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); @@ -1413,6 +1667,7 @@ impl ToJson for Target { target_option_val!(has_rpath); target_option_val!(no_default_libraries); target_option_val!(position_independent_executables); + target_option_val!(static_position_independent_executables); target_option_val!(needs_plt); target_option_val!(relro_level); target_option_val!(archive_format); @@ -1420,7 +1675,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 +1693,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 +1703,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..d7b3e7e15307a 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,10 +22,9 @@ 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, ..Default::default() }, }) diff --git a/src/librustc_target/spec/riscv32imac_unknown_none_elf.rs b/src/librustc_target/spec/riscv32imac_unknown_none_elf.rs index 6ec49410aebe2..b93b6fcf8002a 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,10 +22,9 @@ 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, ..Default::default() }, }) diff --git a/src/librustc_target/spec/riscv32imc_unknown_none_elf.rs b/src/librustc_target/spec/riscv32imc_unknown_none_elf.rs index 70346347fb38f..a16e7e31c6619 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,10 +22,9 @@ 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, ..Default::default() }, }) 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..e5147a12ed320 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,11 +22,10 @@ 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, ..Default::default() }, }) diff --git a/src/librustc_target/spec/riscv64imac_unknown_none_elf.rs b/src/librustc_target/spec/riscv64imac_unknown_none_elf.rs index 614403fe02af0..dc056b55b3868 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,11 +22,10 @@ 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, ..Default::default() }, }) 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..b2ad62e1b260b --- /dev/null +++ b/src/librustc_target/spec/tests/tests_impl.rs @@ -0,0 +1,47 @@ +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.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); + } + } + assert!( + (self.options.pre_link_objects_fallback.is_empty() + && self.options.post_link_objects_fallback.is_empty()) + || self.options.crt_objects_fallback.is_some() + ); + } +} 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/thumbv7a_uwp_windows_msvc.rs b/src/librustc_target/spec/thumbv7a_uwp_windows_msvc.rs new file mode 100644 index 0000000000000..ff2e892100607 --- /dev/null +++ b/src/librustc_target/spec/thumbv7a_uwp_windows_msvc.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, 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(jordanrh): use PanicStrategy::Unwind when SEH is + // implemented for windows/arm in LLVM + base.panic_strategy = PanicStrategy::Abort; + + Ok(Target { + llvm_target: "thumbv7a-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + target_vendor: "uwp".to_string(), + linker_flavor: LinkerFlavor::Msvc, + options: TargetOptions { + features: "+vfp3,+neon".to_string(), + cpu: "generic".to_string(), + abi_blacklist: super::arm_base::abi_blacklist(), + ..base + }, + }) +} 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..777bb58d7db8f 100644 --- a/src/librustc_target/spec/vxworks_base.rs +++ b/src/librustc_target/spec/vxworks_base.rs @@ -1,9 +1,6 @@ use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; -use std::default::Default; pub fn opts() -> TargetOptions { - let mut args_crt = LinkArgs::new(); - args_crt.insert(LinkerFlavor::Gcc, vec!["--static-crt".to_string()]); let mut args = LinkArgs::new(); args.insert( LinkerFlavor::Gcc, @@ -30,7 +27,6 @@ pub fn opts() -> TargetOptions { pre_link_args: args, position_independent_executables: false, has_elf_tls: true, - pre_link_args_crt: args_crt, crt_static_default: true, crt_static_respected: true, crt_static_allows_dylibs: true, 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..680dbbad4b0a0 --- /dev/null +++ b/src/librustc_target/spec/windows_gnu_base.rs @@ -0,0 +1,97 @@ +use crate::spec::crt_objects::{self, CrtObjectsFallback}; +use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert( + LinkerFlavor::Gcc, + vec![ + // Tell GCC to avoid linker plugins, because we are not bundling + // them with Windows installer, and Rust does its own LTO anyways. + "-fno-use-linker-plugin".to_string(), + // Always enable DEP (NX bit) when it is available + "-Wl,--nxcompat".to_string(), + ], + ); + + let mut late_link_args = LinkArgs::new(); + let mut late_link_args_dynamic = LinkArgs::new(); + let mut late_link_args_static = LinkArgs::new(); + // Order of `late_link_args*` was found through trial and error to work with various + // mingw-w64 versions (not tested on the CI). It's expected to change from time to time. + late_link_args.insert( + LinkerFlavor::Gcc, + vec![ + "-lmsvcrt".to_string(), + "-lmingwex".to_string(), + "-lmingw32".to_string(), + // mingw's msvcrt is a weird hybrid import library and static library. + // And it seems that the linker fails to use import symbols from msvcrt + // that are required from functions in msvcrt in certain cases. For example + // `_fmode` that is used by an implementation of `__p__fmode` in x86_64. + // The library is purposely listed twice to fix that. + // + // See https://github.com/rust-lang/rust/pull/47483 for some more details. + "-lmsvcrt".to_string(), + "-luser32".to_string(), + "-lkernel32".to_string(), + ], + ); + late_link_args_dynamic.insert( + LinkerFlavor::Gcc, + vec![ + // If any of our crates are dynamically linked then we need to use + // the shared libgcc_s-dw2-1.dll. This is required to support + // unwinding across DLL boundaries. + "-lgcc_s".to_string(), + "-lgcc".to_string(), + "-lkernel32".to_string(), + ], + ); + late_link_args_static.insert( + LinkerFlavor::Gcc, + vec![ + // If all of our crates are statically linked then we can get away + // with statically linking the libgcc unwinding code. This allows + // binaries to be redistributed without the libgcc_s-dw2-1.dll + // dependency, but unfortunately break unwinding across DLL + // boundaries when unwinding across FFI boundaries. + "-lgcc_eh".to_string(), + "-l:libpthread.a".to_string(), + "-lgcc".to_string(), + // libpthread depends on libmsvcrt, so we need to link it *again*. + "-lmsvcrt".to_string(), + "-lkernel32".to_string(), + ], + ); + + TargetOptions { + // FIXME(#13846) this should be enabled for windows + function_sections: false, + linker: Some("gcc".to_string()), + dynamic_linking: true, + executables: true, + dll_prefix: String::new(), + dll_suffix: ".dll".to_string(), + exe_suffix: ".exe".to_string(), + staticlib_prefix: "lib".to_string(), + staticlib_suffix: ".a".to_string(), + target_family: Some("windows".to_string()), + is_like_windows: true, + allows_weak_linkage: false, + pre_link_args, + pre_link_objects: crt_objects::pre_mingw(), + post_link_objects: crt_objects::post_mingw(), + pre_link_objects_fallback: crt_objects::pre_mingw_fallback(), + post_link_objects_fallback: crt_objects::post_mingw_fallback(), + crt_objects_fallback: Some(CrtObjectsFallback::Mingw), + late_link_args, + late_link_args_dynamic, + late_link_args_static, + abi_return_struct_as_int: true, + emit_debug_gdb_scripts: false, + requires_uwtable: true, + + ..Default::default() + } +} diff --git a/src/librustc_target/spec/windows_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..81974769cafb8 100644 --- a/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs +++ b/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs @@ -61,14 +61,14 @@ pub fn target() -> Result { max_atomic_width: Some(64), panic_strategy: PanicStrategy::Unwind, cpu: "x86-64".into(), - features: "+rdrnd,+rdseed".into(), + features: "+rdrnd,+rdseed,+lvi-cfi,+lvi-load-hardening".into(), + llvm_args: vec!["--x86-experimental-lvi-inline-asm-hardening".into()], position_independent_executables: true, pre_link_args: iter::once(( LinkerFlavor::Lld(LldFlavor::Ld), PRE_LINK_ARGS.iter().cloned().map(String::from).collect(), )) .collect(), - post_link_objects: vec!["libunwind.a".into()], 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..2567ca47ef967 --- /dev/null +++ b/src/librustc_target/spec/x86_64_unknown_illumos.rs @@ -0,0 +1,25 @@ +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-p270:32:32-p271:32:32-p272:64:64-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_linux_musl.rs b/src/librustc_target/spec/x86_64_unknown_linux_musl.rs index 34c628e8f67bd..3a22290da6858 100644 --- a/src/librustc_target/spec/x86_64_unknown_linux_musl.rs +++ b/src/librustc_target/spec/x86_64_unknown_linux_musl.rs @@ -6,6 +6,7 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; + base.static_position_independent_executables = true; Ok(Target { llvm_target: "x86_64-unknown-linux-musl".to_string(), 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..fd11a85a9c406 100644 --- a/src/librustc_trait_selection/Cargo.toml +++ b/src/librustc_trait_selection/Cargo.toml @@ -10,10 +10,10 @@ path = "lib.rs" doctest = false [dependencies] -fmt_macros = { path = "../libfmt_macros" } +rustc_parse_format = { path = "../librustc_parse_format" } 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/autoderef.rs b/src/librustc_trait_selection/autoderef.rs new file mode 100644 index 0000000000000..d542e16d83f10 --- /dev/null +++ b/src/librustc_trait_selection/autoderef.rs @@ -0,0 +1,229 @@ +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::{self, TraitEngine}; +use rustc_errors::struct_span_err; +use rustc_hir as hir; +use rustc_infer::infer::InferCtxt; +use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness}; +use rustc_middle::ty::{ToPredicate, TypeFoldable}; +use rustc_session::DiagnosticMessageId; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +#[derive(Copy, Clone, Debug)] +pub enum AutoderefKind { + Builtin, + Overloaded, +} + +struct AutoderefSnapshot<'tcx> { + at_start: bool, + reached_recursion_limit: bool, + steps: Vec<(Ty<'tcx>, AutoderefKind)>, + cur_ty: Ty<'tcx>, + obligations: Vec>, +} + +pub struct Autoderef<'a, 'tcx> { + // Meta infos: + infcx: &'a InferCtxt<'a, 'tcx>, + span: Span, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + + // Current state: + state: AutoderefSnapshot<'tcx>, + + // Configurations: + include_raw_pointers: bool, + silence_errors: bool, +} + +impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { + type Item = (Ty<'tcx>, usize); + + fn next(&mut self) -> Option { + let tcx = self.infcx.tcx; + + debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty); + if self.state.at_start { + self.state.at_start = false; + debug!("autoderef stage #0 is {:?}", self.state.cur_ty); + return Some((self.state.cur_ty, 0)); + } + + // If we have reached the recursion limit, error gracefully. + if !tcx.sess.recursion_limit().value_within_limit(self.state.steps.len()) { + if !self.silence_errors { + report_autoderef_recursion_limit_error(tcx, self.span, self.state.cur_ty); + } + self.state.reached_recursion_limit = true; + return None; + } + + if self.state.cur_ty.is_ty_var() { + return None; + } + + // Otherwise, deref if type is derefable: + let (kind, new_ty) = + if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { + (AutoderefKind::Builtin, mt.ty) + } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { + (AutoderefKind::Overloaded, ty) + } else { + return None; + }; + + if new_ty.references_error() { + return None; + } + + self.state.steps.push((self.state.cur_ty, kind)); + debug!( + "autoderef stage #{:?} is {:?} from {:?}", + self.step_count(), + new_ty, + (self.state.cur_ty, kind) + ); + self.state.cur_ty = new_ty; + + Some((self.state.cur_ty, self.step_count())) + } +} + +impl<'a, 'tcx> Autoderef<'a, 'tcx> { + pub fn new( + infcx: &'a InferCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + span: Span, + base_ty: Ty<'tcx>, + ) -> Autoderef<'a, 'tcx> { + Autoderef { + infcx, + span, + body_id, + param_env, + state: AutoderefSnapshot { + steps: vec![], + cur_ty: infcx.resolve_vars_if_possible(&base_ty), + obligations: vec![], + at_start: true, + reached_recursion_limit: false, + }, + include_raw_pointers: false, + silence_errors: false, + } + } + + fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option> { + debug!("overloaded_deref_ty({:?})", ty); + + let tcx = self.infcx.tcx; + + // + let trait_ref = TraitRef { + def_id: tcx.lang_items().deref_trait()?, + substs: tcx.mk_substs_trait(ty, &[]), + }; + + let cause = traits::ObligationCause::misc(self.span, self.body_id); + + let obligation = traits::Obligation::new( + cause.clone(), + self.param_env, + trait_ref.without_const().to_predicate(tcx), + ); + if !self.infcx.predicate_may_hold(&obligation) { + debug!("overloaded_deref_ty: cannot match obligation"); + return None; + } + + let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot(); + let normalized_ty = fulfillcx.normalize_projection_type( + &self.infcx, + self.param_env, + ty::ProjectionTy::from_ref_and_name(tcx, trait_ref, Ident::from_str("Target")), + cause, + ); + if let Err(e) = fulfillcx.select_where_possible(&self.infcx) { + // This shouldn't happen, except for evaluate/fulfill mismatches, + // but that's not a reason for an ICE (`predicate_may_hold` is conservative + // by design). + debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", e); + return None; + } + let obligations = fulfillcx.pending_obligations(); + debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); + self.state.obligations.extend(obligations); + + Some(self.infcx.resolve_vars_if_possible(&normalized_ty)) + } + + /// Returns the final type we ended up with, which may be an inference + /// variable (we will resolve it first, if we want). + pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> { + if resolve { + self.infcx.resolve_vars_if_possible(&self.state.cur_ty) + } else { + self.state.cur_ty + } + } + + pub fn step_count(&self) -> usize { + self.state.steps.len() + } + + pub fn into_obligations(self) -> Vec> { + self.state.obligations + } + + pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { + &self.state.steps + } + + pub fn span(&self) -> Span { + self.span.clone() + } + + pub fn reached_recursion_limit(&self) -> bool { + self.state.reached_recursion_limit + } + + /// also dereference through raw pointer types + /// e.g., assuming ptr_to_Foo is the type `*const Foo` + /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] + /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] + pub fn include_raw_pointers(mut self) -> Self { + self.include_raw_pointers = true; + self + } + + pub fn silence_errors(mut self) -> Self { + self.silence_errors = true; + self + } +} + +pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { + // We've reached the recursion limit, error gracefully. + let suggested_limit = tcx.sess.recursion_limit() * 2; + let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty); + let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg); + let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); + if fresh { + struct_span_err!( + tcx.sess, + span, + E0055, + "reached the recursion limit while auto-dereferencing `{:?}`", + ty + ) + .span_label(span, "deref recursion limit reached") + .help(&format!( + "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)", + suggested_limit, tcx.crate_name, + )) + .emit(); + } +} diff --git a/src/librustc_trait_selection/infer.rs b/src/librustc_trait_selection/infer.rs index 7abcbf45277fb..dc895ad34a932 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,8 +43,8 @@ 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() { - return ty.is_copy_modulo_regions(self.tcx, param_env, span); + if !(param_env, ty).needs_infer() { + return ty.is_copy_modulo_regions(self.tcx.at(span), param_env); } let copy_def_id = self.tcx.require_lang_item(lang_items::CopyTraitLangItem, None); @@ -90,7 +90,7 @@ pub trait InferCtxtBuilderExt<'tcx> { where K: TypeFoldable<'tcx>, R: Debug + TypeFoldable<'tcx>, - Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable; + Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>; } impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { @@ -118,7 +118,7 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { where K: TypeFoldable<'tcx>, R: Debug + TypeFoldable<'tcx>, - Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable, + Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>, { self.enter_with_canonical( DUMMY_SP, diff --git a/src/librustc_trait_selection/lib.rs b/src/librustc_trait_selection/lib.rs index 739aff4fb94c9..4692fa04ed587 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,7 @@ #![feature(drain_filter)] #![feature(in_band_lifetimes)] #![feature(crate_visibility_modifier)] +#![feature(or_patterns)] #![recursion_limit = "512"] // For rustdoc #[macro_use] @@ -25,8 +26,9 @@ extern crate rustc_data_structures; #[macro_use] extern crate log; #[macro_use] -extern crate rustc; +extern crate rustc_middle; +pub mod autoderef; pub mod infer; pub mod opaque_types; pub mod traits; diff --git a/src/librustc_trait_selection/opaque_types.rs b/src/librustc_trait_selection/opaque_types.rs index 6cf1302783c0b..adccdd0b2617a 100644 --- a/src/librustc_trait_selection/opaque_types.rs +++ b/src/librustc_trait_selection/opaque_types.rs @@ -1,18 +1,18 @@ use crate::infer::InferCtxtExt as _; use crate::traits::{self, PredicateObligation}; -use rustc::session::config::nightly_options; -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, Ty, TyCtxt}; +use rustc_session::config::nightly_options; use rustc_span::Span; pub type OpaqueTypeMap<'tcx> = DefIdMap>; @@ -133,9 +133,9 @@ pub trait InferCtxtExt<'tcx> { fn generate_member_constraint( &self, concrete_ty: Ty<'tcx>, - opaque_type_generics: &ty::Generics, opaque_defn: &OpaqueTypeDecl<'tcx>, opaque_type_def_id: DefId, + first_own_region_index: usize, ); /*private*/ @@ -405,7 +405,24 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { debug!("constrain_opaque_type: concrete_ty={:?}", concrete_ty); - let opaque_type_generics = tcx.generics_of(def_id); + let first_own_region = match opaque_defn.origin { + hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => { + // We lower + // + // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> + // + // into + // + // type foo::<'p0..'pn>::Foo<'q0..'qm> + // fn foo() -> foo::<'static..'static>::Foo<'l0..'lm>. + // + // For these types we onlt iterate over `'l0..lm` below. + tcx.generics_of(def_id).parent_count + } + // These opaque type inherit all lifetime parameters from their + // parent, so we have to check them all. + hir::OpaqueTyOrigin::Binding | hir::OpaqueTyOrigin::Misc => 0, + }; let span = tcx.def_span(def_id); @@ -418,22 +435,16 @@ 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 { concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, op: |r| self.sub_regions(infer::CallReturn(span), required_region, r), }); } if let GenerateMemberConstraints::IfNoStaticBound = mode { - self.generate_member_constraint( - concrete_ty, - opaque_type_generics, - opaque_defn, - def_id, - ); + self.generate_member_constraint(concrete_ty, opaque_defn, def_id, first_own_region); } return; } @@ -446,29 +457,27 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // `['a]` for the first impl trait and `'b` for the // second. let mut least_region = None; - for param in &opaque_type_generics.params { - match param.kind { - GenericParamDefKind::Lifetime => {} - _ => continue, - } - // Get the value supplied for this region from the substs. - let subst_arg = opaque_defn.substs.region_at(param.index as usize); + for subst_arg in &opaque_defn.substs[first_own_region..] { + let subst_region = match subst_arg.unpack() { + GenericArgKind::Lifetime(r) => r, + GenericArgKind::Type(_) | GenericArgKind::Const(_) => continue, + }; // Compute the least upper bound of it with the other regions. debug!("constrain_opaque_types: least_region={:?}", least_region); - debug!("constrain_opaque_types: subst_arg={:?}", subst_arg); + debug!("constrain_opaque_types: subst_region={:?}", subst_region); match least_region { - None => least_region = Some(subst_arg), + None => least_region = Some(subst_region), Some(lr) => { - if free_region_relations.sub_free_regions(self.tcx, lr, subst_arg) { + if free_region_relations.sub_free_regions(self.tcx, lr, subst_region) { // keep the current least region - } else if free_region_relations.sub_free_regions(self.tcx, subst_arg, lr) { - // switch to `subst_arg` - least_region = Some(subst_arg); + } else if free_region_relations.sub_free_regions(self.tcx, subst_region, lr) { + // switch to `subst_region` + least_region = Some(subst_region); } else { // There are two regions (`lr` and - // `subst_arg`) which are not relatable. We + // `subst_region`) which are not relatable. We // can't find a best choice. Therefore, // instead of creating a single bound like // `'r: 'a` (which is our preferred choice), @@ -477,13 +486,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // regions that appear in the impl trait. // For now, enforce a feature gate outside of async functions. - self.member_constraint_feature_gate(opaque_defn, def_id, lr, subst_arg); + self.member_constraint_feature_gate(opaque_defn, def_id, lr, subst_region); return self.generate_member_constraint( concrete_ty, - opaque_type_generics, opaque_defn, def_id, + first_own_region, ); } } @@ -495,16 +504,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let GenerateMemberConstraints::IfNoStaticBound = mode { if least_region != tcx.lifetimes.re_static { - self.generate_member_constraint( - concrete_ty, - opaque_type_generics, - opaque_defn, - def_id, - ); + self.generate_member_constraint(concrete_ty, opaque_defn, def_id, first_own_region); } } concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, op: |r| self.sub_regions(infer::CallReturn(span), least_region, r), }); } @@ -520,28 +523,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn generate_member_constraint( &self, concrete_ty: Ty<'tcx>, - opaque_type_generics: &ty::Generics, opaque_defn: &OpaqueTypeDecl<'tcx>, opaque_type_def_id: DefId, + first_own_region: usize, ) { // Create the set of choice regions: each region in the hidden // type can be equal to any of the region parameters of the // opaque type definition. let choice_regions: Lrc>> = Lrc::new( - opaque_type_generics - .params + opaque_defn.substs[first_own_region..] .iter() - .filter(|param| match param.kind { - GenericParamDefKind::Lifetime => true, - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => false, + .filter_map(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => Some(r), + GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, }) - .map(|param| opaque_defn.substs.region_at(param.index as usize)) .chain(std::iter::once(self.tcx.lifetimes.re_static)) .collect(), ); concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { - tcx: self.tcx, op: |r| { self.member_constraint( opaque_type_def_id, @@ -577,7 +577,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { hir::OpaqueTyOrigin::AsyncFn => return false, // Otherwise, generate the label we'll use in the error message. - hir::OpaqueTyOrigin::TypeAlias + hir::OpaqueTyOrigin::Binding | hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::Misc => "impl Trait", }; @@ -650,7 +650,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // shifting. let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id); let map: FxHashMap, GenericArg<'tcx>> = - substs.iter().enumerate().map(|(index, subst)| (*subst, id_substs[index])).collect(); + substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect(); // Convert the type from the function into a type valid outside // the function, by replacing invalid regions with 'static, @@ -673,7 +673,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // `least_region`. We cannot use `push_outlives_components` because regions in // closure signatures are not included in their outlives components. We need to // ensure all regions outlive the given bound so that we don't end up with, -// say, `ReScope` appearing in a return type and causing ICEs when other +// say, `ReVar` appearing in a return type and causing ICEs when other // functions end up with region constraints involving regions from other // functions. // @@ -682,15 +682,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // // We ignore any type parameters because impl trait values are assumed to // capture all the in-scope type parameters. -struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP> -where - OP: FnMut(ty::Region<'tcx>), -{ - tcx: TyCtxt<'tcx>, +struct ConstrainOpaqueTypeRegionVisitor { op: OP, } -impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor where OP: FnMut(ty::Region<'tcx>), { @@ -717,27 +713,27 @@ where } match ty.kind { - ty::Closure(def_id, ref substs) => { + ty::Closure(_, ref substs) => { // Skip lifetime parameters of the enclosing item(s) - for upvar_ty in substs.as_closure().upvar_tys(def_id, self.tcx) { + for upvar_ty in substs.as_closure().upvar_tys() { upvar_ty.visit_with(self); } - substs.as_closure().sig_ty(def_id, self.tcx).visit_with(self); + substs.as_closure().sig_as_fn_ptr_ty().visit_with(self); } - ty::Generator(def_id, ref substs, _) => { + ty::Generator(_, ref substs, _) => { // Skip lifetime parameters of the enclosing item(s) // Also skip the witness type, because that has no free regions. - for upvar_ty in substs.as_generator().upvar_tys(def_id, self.tcx) { + for upvar_ty in substs.as_generator().upvar_tys() { upvar_ty.visit_with(self); } - substs.as_generator().return_ty(def_id, self.tcx).visit_with(self); - substs.as_generator().yield_ty(def_id, self.tcx).visit_with(self); - substs.as_generator().resume_ty(def_id, self.tcx).visit_with(self); + substs.as_generator().return_ty().visit_with(self); + substs.as_generator().yield_ty().visit_with(self); + substs.as_generator().resume_ty().visit_with(self); } _ => { ty.super_visit_with(self); @@ -823,11 +819,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { // The regions that we expect from borrow checking. ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {} - ty::ReEmpty(_) - | ty::RePlaceholder(_) - | ty::ReVar(_) - | ty::ReScope(_) - | ty::ReClosureBound(_) => { + ty::ReEmpty(_) | ty::RePlaceholder(_) | ty::ReVar(_) => { // All of the regions in the type should either have been // erased by writeback, or mapped back to named regions by // borrow checking. @@ -846,7 +838,6 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { if let Some(hidden_ty) = self.hidden_ty.take() { unexpected_hidden_region_diagnostic( self.tcx, - None, self.tcx.def_span(self.opaque_type_def_id), hidden_ty, r, @@ -902,7 +893,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { // during codegen. let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, &kind)| { + let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { if index < generics.parent_count { // Accommodate missing regions in the parent kinds... self.fold_kind_mapping_missing_regions_to_empty(kind) @@ -917,7 +908,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { ty::Generator(def_id, substs, movability) => { let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, &kind)| { + let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { if index < generics.parent_count { // Accommodate missing regions in the parent kinds... self.fold_kind_mapping_missing_regions_to_empty(kind) @@ -950,7 +941,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { ) .emit(); - self.tcx().types.err + self.tcx().ty_error() } } } @@ -983,7 +974,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { ) .emit(); - self.tcx().consts.err + self.tcx().const_error(ct.ty) } } } @@ -1011,7 +1002,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { tcx, ty_op: |ty| { if ty.references_error() { - return tcx.types.err; + return tcx.ty_error(); } else if let ty::Opaque(def_id, substs) = ty.kind { // Check that this is `impl Trait` type is // declared by `parent_def_id` -- i.e., one whose @@ -1047,11 +1038,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 { @@ -1067,25 +1060,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), - hir::OpaqueTyOrigin::TypeAlias, - ), - _ => (def_scope_default(), hir::OpaqueTyOrigin::TypeAlias), + _ => (def_scope_default(), hir::OpaqueTyOrigin::Misc), }, _ => bug!( - "expected (impl) item, found {}", + "expected item, found {}", tcx.hir().node_to_string(opaque_hir_id), ), }; 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!( @@ -1138,7 +1128,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 @@ -1168,7 +1159,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!("instantiate_opaque_types: ty_var={:?}", ty_var); for predicate in &bounds.predicates { - if let ty::Predicate::Projection(projection) = &predicate { + if let ty::PredicateKind::Projection(projection) = predicate.kind() { if projection.skip_binder().ty.references_error() { // No point on adding these obligations since there's a type error involved. return ty_var; @@ -1211,11 +1202,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); @@ -1250,33 +1245,29 @@ pub fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: DefId, opaque_hir_id: hir /// /// Requires that trait definitions have been processed so that we can /// elaborate predicates and walk supertraits. -// -// FIXME: callers may only have a `&[Predicate]`, not a `Vec`, so that's -// what this code should accept. 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 { - ty::Predicate::Projection(..) - | ty::Predicate::Trait(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::RegionOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => None, - ty::Predicate::TypeOutlives(predicate) => { + .filter_map(|obligation| { + debug!("required_region_bounds(obligation={:?})", obligation); + match obligation.predicate.kind() { + ty::PredicateKind::Projection(..) + | ty::PredicateKind::Trait(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => None, + ty::PredicateKind::TypeOutlives(predicate) => { // Search for a bound of the form `erased_self_ty // : 'a`, but be wary of something like `for<'a> // erased_self_ty : 'a` (we interpret a diff --git a/src/librustc_trait_selection/traits/auto_trait.rs b/src/librustc_trait_selection/traits/auto_trait.rs index d221d6886e9fb..da945a1918524 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}; @@ -96,7 +96,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { )); match result { - Ok(Some(Vtable::VtableImpl(_))) => { + Ok(Some(ImplSource::ImplSourceUserDefined(_))) => { debug!( "find_auto_trait_generics({:?}): \ manual impl found, bailing out", @@ -113,7 +113,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { return AutoTraitResult::ExplicitImpl; } - return tcx.infer_ctxt().enter(|infcx| { + tcx.infer_ctxt().enter(|infcx| { let mut fresh_preds = FxHashSet::default(); // Due to the way projections are handled by SelectionContext, we need to run @@ -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(); @@ -219,8 +213,8 @@ impl<'tcx> AutoTraitFinder<'tcx> { let info = AutoTraitInfo { full_user_env, region_data, vid_to_region }; - return AutoTraitResult::PositiveImpl(auto_trait_callback(&infcx, info)); - }); + AutoTraitResult::PositiveImpl(auto_trait_callback(&infcx, info)) + }) } } @@ -287,12 +281,11 @@ impl AutoTraitFinder<'tcx> { }, })); - let mut computed_preds: FxHashSet<_> = param_env.caller_bounds.iter().cloned().collect(); - let mut user_computed_preds: FxHashSet<_> = - user_env.caller_bounds.iter().cloned().collect(); + let computed_preds = param_env.caller_bounds.iter(); + let mut user_computed_preds: FxHashSet<_> = user_env.caller_bounds.iter().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(); @@ -311,25 +304,27 @@ impl AutoTraitFinder<'tcx> { let result = select.select(&obligation); match &result { - &Ok(Some(ref vtable)) => { + &Ok(Some(ref impl_source)) => { // 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 ImplSource::ImplSourceUserDefined(ImplSourceUserDefinedData { + impl_def_id, + .. + }) = impl_source + { + // 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(); + let obligations = impl_source.clone().nested_obligations().into_iter(); if !self.evaluate_nested_obligations( ty, @@ -349,7 +344,8 @@ impl AutoTraitFinder<'tcx> { already_visited.remove(&pred); self.add_user_pred( &mut user_computed_preds, - ty::Predicate::Trait(pred, hir::Constness::NotConst), + ty::PredicateKind::Trait(pred, hir::Constness::NotConst) + .to_predicate(self.tcx), ); predicates.push_back(pred); } else { @@ -366,9 +362,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); } @@ -384,7 +382,7 @@ impl AutoTraitFinder<'tcx> { ty, trait_did, new_env, final_user_env ); - return Some((new_env, final_user_env)); + Some((new_env, final_user_env)) } /// This method is designed to work around the following issue: @@ -417,82 +415,81 @@ 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::PredicateKind::Trait(new_trait, _), + ty::PredicateKind::Trait(old_trait, _), + ) = (new_pred.kind(), old_pred.kind()) + { + 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 + } + _ => {} } } } - _ => {} } - return true; + true }); if should_add_new { @@ -591,15 +588,15 @@ impl AutoTraitFinder<'tcx> { } fn is_param_no_infer(&self, substs: SubstsRef<'_>) -> bool { - return self.is_of_param(substs.type_at(0)) && !substs.types().any(|t| t.has_infer_types()); + self.is_of_param(substs.type_at(0)) && !substs.types().any(|t| t.has_infer_types()) } pub fn is_of_param(&self, ty: Ty<'_>) -> bool { - return match ty.kind { + match ty.kind { ty::Param(_) => true, ty::Projection(p) => self.is_of_param(p.self_ty()), _ => false, - }; + } } fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { @@ -619,7 +616,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)); @@ -639,8 +636,8 @@ impl AutoTraitFinder<'tcx> { // // We check this by calling is_of_param on the relevant types // from the various possible predicates - match &predicate { - &ty::Predicate::Trait(p, _) => { + match predicate.kind() { + &ty::PredicateKind::Trait(p, _) => { if self.is_param_no_infer(p.skip_binder().trait_ref.substs) && !only_projections && is_new_pred @@ -649,7 +646,7 @@ impl AutoTraitFinder<'tcx> { } predicates.push_back(p); } - &ty::Predicate::Projection(p) => { + &ty::PredicateKind::Projection(p) => { debug!( "evaluate_nested_obligations: examining projection predicate {:?}", predicate @@ -749,7 +746,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, @@ -774,12 +771,12 @@ impl AutoTraitFinder<'tcx> { } } } - &ty::Predicate::RegionOutlives(ref binder) => { + &ty::PredicateKind::RegionOutlives(binder) => { if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() { return false; } } - &ty::Predicate::TypeOutlives(ref binder) => { + &ty::PredicateKind::TypeOutlives(binder) => { match ( binder.no_bound_vars(), binder.map_bound_ref(|pred| pred.0).no_bound_vars(), @@ -804,7 +801,7 @@ impl AutoTraitFinder<'tcx> { _ => panic!("Unexpected predicate {:?} {:?}", ty, predicate), }; } - return true; + true } pub fn clean_pred( 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..cbbff82d35f73 --- /dev/null +++ b/src/librustc_trait_selection/traits/chalk_fulfill.rs @@ -0,0 +1,266 @@ +//! 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"); + + // FIXME(chalk): this has problems because of late-bound regions + //inputs.extend(trait_ref.substs.iter().flat_map(|arg| arg.walk())); + inputs.extend(trait_ref.substs.iter()); + } + + // 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(), + // FIXME(chalk): this is hit in ui/where-clauses/where-clause-constraints-are-local-for-trait-impl + // and ui/generics/generic-static-methods + _ => 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..cf575d3eca9c2 100644 --- a/src/librustc_trait_selection/traits/codegen/mod.rs +++ b/src/librustc_trait_selection/traits/codegen/mod.rs @@ -5,13 +5,14 @@ use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::traits::{ - FulfillmentContext, Obligation, ObligationCause, SelectionContext, TraitEngine, Vtable, + FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, }; -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 +/// Attempts to resolve an obligation to a `ImplSource`. The result is +/// a shallow `ImplSource` resolution, meaning that we do not /// (necessarily) resolve all nested obligations on the impl. Note /// that type check should guarantee to us that all nested /// obligations *could be* resolved if we wanted to. @@ -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) @@ -68,14 +69,14 @@ pub fn codegen_fulfill_obligation<'tcx>( // all nested obligations. This is because they can inform the // inference of the impl's type parameters. let mut fulfill_cx = FulfillmentContext::new(); - let vtable = selection.map(|predicate| { + let impl_source = selection.map(|predicate| { debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); fulfill_cx.register_predicate_obligation(&infcx, predicate); }); - let vtable = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, &vtable); + let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, &impl_source); - info!("Cache miss: {:?} => {:?}", trait_ref, vtable); - Some(vtable) + info!("Cache miss: {:?} => {:?}", trait_ref, impl_source); + Ok(impl_source) }) } diff --git a/src/librustc_trait_selection/traits/coherence.rs b/src/librustc_trait_selection/traits/coherence.rs index 5f542e7e13be5..3ec7fe2bf25c6 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. @@ -119,12 +120,13 @@ fn overlap<'cx, 'tcx>( debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id); selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| { - overlap_within_probe(selcx, a_def_id, b_def_id, snapshot) + overlap_within_probe(selcx, skip_leak_check, a_def_id, b_def_id, snapshot) }) } fn overlap_within_probe( selcx: &mut SelectionContext<'cx, 'tcx>, + skip_leak_check: SkipLeakCheck, a_def_id: DefId, b_def_id: DefId, snapshot: &CombinedSnapshot<'_, 'tcx>, @@ -179,6 +181,13 @@ fn overlap_within_probe( return None; } + if !skip_leak_check.is_yes() { + if let Err(_) = infcx.leak_check(true, snapshot) { + debug!("overlap: leak check failed"); + return None; + } + } + let impl_header = selcx.infcx().resolve_vars_if_possible(&a_impl_header); let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes(); debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); @@ -221,10 +230,10 @@ pub fn trait_ref_is_knowable<'tcx>( // we are an owner. if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() { debug!("trait_ref_is_knowable: orphan check passed"); - return None; + None } else { debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned"); - return Some(Conflict::Upstream); + Some(Conflict::Upstream) } } @@ -326,12 +335,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 +387,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 +435,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 +493,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); @@ -530,11 +573,10 @@ fn ty_is_non_local_constructor(ty: Ty<'_>, in_crate: InCrate) -> Option> } } - ty::Error => None, + 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 abd9638bfa78b..fd0c1a54d27ad 100644 --- a/src/librustc_trait_selection/traits/error_reporting/mod.rs +++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs @@ -11,22 +11,22 @@ 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::session::DiagnosticMessageId; -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_span::source_map::SourceMap; -use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::Node; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{ + self, fast_reject, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, + TypeFoldable, WithConstness, +}; +use rustc_session::DiagnosticMessageId; +use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP}; use std::fmt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -66,7 +66,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 +127,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 @@ -148,9 +148,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { continue; } - if self.error_implies(&error2.predicate, &error.predicate) + if self.error_implies(error2.predicate, error.predicate) && !(error2.index >= error.index - && self.error_implies(&error.predicate, &error2.predicate)) + && self.error_implies(error.predicate, error2.predicate)) { info!("skipping {:?} (implied by {:?})", error, error2); is_suppressed[index] = true; @@ -254,8 +254,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .emit(); return; } - match obligation.predicate { - ty::Predicate::Trait(ref trait_predicate, _) => { + match obligation.predicate.kind() { + ty::PredicateKind::Trait(ref trait_predicate, _) => { let trait_predicate = self.resolve_vars_if_possible(trait_predicate); if self.tcx.sess.has_errors() && trait_predicate.references_error() { @@ -285,15 +285,17 @@ 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!( "`?` couldn't convert the error to `{}`", - trait_ref.self_ty(), + trait_ref.skip_binder().self_ty(), )), 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(), ), ) @@ -308,11 +310,45 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { "{}", message.unwrap_or_else(|| format!( "the trait bound `{}` is not satisfied{}", - trait_ref.without_const().to_predicate(), + trait_ref.without_const().to_predicate(tcx), post_message, )) ); + 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.skip_binder().self_ty() + ), + ); + } + } + let explanation = if obligation.cause.code == ObligationCauseCode::MainFunctionType { "consider using `()`, or a `Result`".to_owned() @@ -321,7 +357,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { "{}the trait `{}` is not implemented for `{}`", pre_message, trait_ref.print_only_trait_path(), - trait_ref.self_ty(), + trait_ref.skip_binder().self_ty(), ) }; @@ -359,22 +395,40 @@ 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()); } + self.suggest_dereferences(&obligation, &mut err, &trait_ref, points_at_arg); self.suggest_borrow_on_unsized_slice(&obligation.cause.code, &mut err); self.suggest_fn_call(&obligation, &mut err, &trait_ref, points_at_arg); self.suggest_remove_reference(&obligation, &mut err, &trait_ref); self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref); self.note_version_mismatch(&mut err, &trait_ref); + + if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() { + self.suggest_await_before_try(&mut err, &obligation, &trait_ref, span); + } + if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) { err.emit(); return; } + if is_unsize { + // If the obligation failed due to a missing implementation of the + // `Unsize` trait, give a pointer to why that might be the case + err.note( + "all implementations of `Unsize` are provided \ + automatically by the compiler, see \ + \ + for more information", + ); + } + // 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 +442,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 +451,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 @@ -420,10 +478,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_pred }); let unit_obligation = Obligation { - predicate: ty::Predicate::Trait( + predicate: ty::PredicateKind::Trait( predicate, hir::Constness::NotConst, - ), + ) + .to_predicate(self.tcx), ..obligation.clone() }; if self.predicate_may_hold(&unit_obligation) { @@ -441,17 +500,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err } - ty::Predicate::Subtype(ref predicate) => { + ty::PredicateKind::Subtype(ref predicate) => { // Errors for Subtype predicates show up as // `FulfillmentErrorCode::CodeSubtypeError`, // not selection error. span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) } - ty::Predicate::RegionOutlives(ref predicate) => { + ty::PredicateKind::RegionOutlives(ref predicate) => { let predicate = self.resolve_vars_if_possible(predicate); let err = self - .region_outlives_predicate(&obligation.cause, &predicate) + .region_outlives_predicate(&obligation.cause, predicate) .err() .unwrap(); struct_span_err!( @@ -464,7 +523,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) } - ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => { + ty::PredicateKind::Projection(..) | ty::PredicateKind::TypeOutlives(..) => { let predicate = self.resolve_vars_if_possible(&obligation.predicate); struct_span_err!( self.tcx.sess, @@ -475,19 +534,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ) } - ty::Predicate::ObjectSafe(trait_def_id) => { + &ty::PredicateKind::ObjectSafe(trait_def_id) => { let violations = self.tcx.object_safety_violations(trait_def_id); report_object_safety_error(self.tcx, span, trait_def_id, violations) } - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - let found_kind = self.closure_kind(closure_def_id, 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(); + &ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => { + let found_kind = self.closure_kind(closure_substs).unwrap(); + let closure_span = + self.tcx.sess.source_map().guess_head_span( + self.tcx.hir().span_if_local(closure_def_id).unwrap(), + ); + let hir_id = self.tcx.hir().as_local_hir_id(closure_def_id.expect_local()); let mut err = struct_span_err!( self.tcx.sess, closure_span, @@ -540,16 +598,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - 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); + ty::PredicateKind::WellFormed(ty) => { + if !self.tcx.sess.opts.debugging_opts.chalk { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); + } else { + // FIXME: we'll need a better message which takes into account + // which bounds actually failed to hold. + self.tcx.sess.struct_span_err( + span, + &format!("the type `{}` is not well-formed (chalk)", ty), + ) + } } - ty::Predicate::ConstEvaluatable(..) => { + ty::PredicateKind::ConstEvaluatable(..) => { // Errors for `ConstEvaluatable` predicates show up as // `SelectionError::ConstEvalFailure`, // not `Unimplemented`. @@ -559,6 +626,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation ) } + + ty::PredicateKind::ConstEquate(..) => { + // Errors for `ConstEquate` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-equate requirement gave wrong error: `{:?}`", + obligation + ) + } } } @@ -570,7 +648,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - let found_trait_ty = found_trait_ref.self_ty(); + let found_trait_ty = match found_trait_ref.self_ty().no_bound_vars() { + Some(ty) => ty, + None => return, + }; let found_did = match found_trait_ty.kind { ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did), @@ -580,7 +661,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 +694,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 +735,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,48 +764,43 @@ 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 { span, - kind: hir::ImplItemKind::Method(ref sig, _), + kind: hir::ImplItemKind::Fn(ref sig, _), .. }) | Node::TraitItem(&hir::TraitItem { @@ -723,7 +808,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 +822,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 @@ -815,11 +896,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // For example, if `expected_args_length` is 2, suggest `|_, _|`. if found_args.is_empty() && is_closure { let underscores = vec!["_"; expected_args.len()].join(", "); - err.span_suggestion( + err.span_suggestion_verbose( pipe_span, &format!( "consider changing the closure to take and ignore the expected argument{}", - if expected_args.len() < 2 { "" } else { "s" } + pluralize!(expected_args.len()) ), format!("|{}|", underscores), Applicability::MachineApplicable, @@ -833,7 +914,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .map(|(name, _)| name.to_owned()) .collect::>() .join(", "); - err.span_suggestion( + err.span_suggestion_verbose( found_span, "change the closure to take multiple arguments instead of a single tuple", format!("|{}|", sugg), @@ -870,7 +951,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { String::new() }, ); - err.span_suggestion( + err.span_suggestion_verbose( found_span, "change the closure to accept a tuple instead of individual arguments", sugg, @@ -887,7 +968,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait InferCtxtPrivExt<'tcx> { // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. - fn error_implies(&self, cond: &ty::Predicate<'tcx>, error: &ty::Predicate<'tcx>) -> bool; + fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; fn report_fulfillment_error( &self, @@ -932,12 +1013,15 @@ trait InferCtxtPrivExt<'tcx> { trait_ref: &ty::PolyTraitRef<'tcx>, ); - fn mk_obligation_for_def_id( + /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the + /// `trait_ref`. + /// + /// For this to work, `new_self_ty` must have no escaping bound variables. + fn mk_trait_obligation_with_new_self_ty( &self, - def_id: DefId, - output_ty: Ty<'tcx>, - cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, + trait_ref: &ty::PolyTraitRef<'tcx>, + new_self_ty: Ty<'tcx>, ) -> PredicateObligation<'tcx>; fn maybe_report_ambiguity( @@ -954,13 +1038,13 @@ trait InferCtxtPrivExt<'tcx> { fn note_obligation_cause( &self, - err: &mut DiagnosticBuilder<'_>, + err: &mut DiagnosticBuilder<'tcx>, obligation: &PredicateObligation<'tcx>, ); fn suggest_unsized_bound_if_applicable( &self, - err: &mut DiagnosticBuilder<'_>, + err: &mut DiagnosticBuilder<'tcx>, obligation: &PredicateObligation<'tcx>, ); @@ -974,21 +1058,21 @@ trait InferCtxtPrivExt<'tcx> { impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. - fn error_implies(&self, cond: &ty::Predicate<'tcx>, error: &ty::Predicate<'tcx>) -> bool { + fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { if cond == error { return true; } - let (cond, error) = match (cond, error) { - (&ty::Predicate::Trait(..), &ty::Predicate::Trait(ref error, _)) => (cond, error), + let (cond, error) = match (cond.kind(), error.kind()) { + (ty::PredicateKind::Trait(..), ty::PredicateKind::Trait(error, _)) => (cond, error), _ => { // FIXME: make this work in other cases too. return false; } }; - for 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::PredicateKind::Trait(implication, _) = obligation.predicate.kind() { let error = error.to_poly_trait_ref(); let implication = implication.to_poly_trait_ref(); // FIXME: I'm just not taking associated types at all here. @@ -1036,6 +1120,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(); + } } } @@ -1059,7 +1152,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // // this can fail if the problem was higher-ranked, in which // cause I have no idea for a good error message. - if let ty::Predicate::Projection(ref data) = predicate { + if let ty::PredicateKind::Projection(ref data) = predicate.kind() { let mut selcx = SelectionContext::new(self); let (data, _) = self.replace_bound_vars_with_fresh_vars( obligation.cause.span, @@ -1155,8 +1248,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ty::Generator(..) => Some(18), 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"), + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } @@ -1188,8 +1280,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 +1288,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(), } } @@ -1280,11 +1368,15 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { ) { let get_trait_impl = |trait_def_id| { let mut trait_impl = None; - self.tcx.for_each_relevant_impl(trait_def_id, trait_ref.self_ty(), |impl_def_id| { - if trait_impl.is_none() { - trait_impl = Some(impl_def_id); - } - }); + self.tcx.for_each_relevant_impl( + trait_def_id, + trait_ref.skip_binder().self_ty(), + |impl_def_id| { + if trait_impl.is_none() { + trait_impl = Some(impl_def_id); + } + }, + ); trait_impl }; let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); @@ -1308,16 +1400,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } } - fn mk_obligation_for_def_id( + fn mk_trait_obligation_with_new_self_ty( &self, - def_id: DefId, - output_ty: Ty<'tcx>, - cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, + trait_ref: &ty::PolyTraitRef<'tcx>, + new_self_ty: Ty<'tcx>, ) -> PredicateObligation<'tcx> { - let new_trait_ref = - ty::TraitRef { def_id, substs: self.tcx.mk_substs_trait(output_ty, &[]) }; - Obligation::new(cause, param_env, new_trait_ref.without_const().to_predicate()) + assert!(!new_self_ty.has_escaping_bound_vars()); + + let trait_ref = trait_ref.map_bound_ref(|tr| ty::TraitRef { + substs: self.tcx.mk_substs_trait(new_self_ty, &tr.substs[1..]), + ..*tr + }); + + Obligation::new( + ObligationCause::dummy(), + param_env, + trait_ref.without_const().to_predicate(self.tcx), + ) } fn maybe_report_ambiguity( @@ -1344,10 +1444,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - let mut err = match predicate { - ty::Predicate::Trait(ref data, _) => { + let mut err = match predicate.kind() { + ty::PredicateKind::Trait(ref data, _) => { let trait_ref = data.to_poly_trait_ref(); - let self_ty = trait_ref.self_ty(); + let self_ty = trait_ref.skip_binder().self_ty(); debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref); if predicate.references_error() { @@ -1388,7 +1488,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 +1498,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,19 +1518,18 @@ 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( - span, + err.span_suggestion_verbose( + span.shrink_to_hi(), &format!( "consider specifying the type argument{} in the function call", - if generics.params.len() > 1 { "s" } else { "" }, + pluralize!(generics.params.len()), ), format!( - "{}::<{}>", - snippet, + "::<{}>", generics .params .iter() @@ -1443,16 +1544,27 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { err } - ty::Predicate::WellFormed(ty) => { + ty::PredicateKind::WellFormed(arg) => { // Same hacky approach as above to avoid deluging user // with error messages. - if ty.references_error() || self.tcx.sess.has_errors() { + if arg.references_error() || self.tcx.sess.has_errors() { return; } - self.need_type_info_err(body_id, span, ty, ErrorCode::E0282) + + match arg.unpack() { + GenericArgKind::Lifetime(lt) => { + span_bug!(span, "unexpected well formed predicate: {:?}", lt) + } + GenericArgKind::Type(ty) => { + self.need_type_info_err(body_id, span, ty, ErrorCode::E0282) + } + GenericArgKind::Const(ct) => { + self.need_type_info_err_const(body_id, span, ct, ErrorCode::E0282) + } + } } - ty::Predicate::Subtype(ref data) => { + ty::PredicateKind::Subtype(ref data) => { if data.references_error() || self.tcx.sess.has_errors() { // no need to overload user in such cases return; @@ -1462,15 +1574,29 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { assert!(a.is_ty_var() && b.is_ty_var()); self.need_type_info_err(body_id, span, a, ErrorCode::E0282) } - ty::Predicate::Projection(ref data) => { + ty::PredicateKind::Projection(ref data) => { let trait_ref = data.to_poly_trait_ref(self.tcx); - let self_ty = trait_ref.self_ty(); + let self_ty = trait_ref.skip_binder().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 + } } _ => { @@ -1481,10 +1607,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 } }; @@ -1541,7 +1667,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { let obligation = Obligation::new( ObligationCause::dummy(), param_env, - cleaned_pred.without_const().to_predicate(), + cleaned_pred.without_const().to_predicate(selcx.tcx()), ); self.predicate_may_hold(&obligation) @@ -1550,7 +1676,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { fn note_obligation_cause( &self, - err: &mut DiagnosticBuilder<'_>, + err: &mut DiagnosticBuilder<'tcx>, obligation: &PredicateObligation<'tcx>, ) { // First, attempt to add note to this error with an async-await-specific @@ -1568,38 +1694,98 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { fn suggest_unsized_bound_if_applicable( &self, - err: &mut DiagnosticBuilder<'_>, + err: &mut DiagnosticBuilder<'tcx>, obligation: &PredicateObligation<'tcx>, ) { - if let ( - ty::Predicate::Trait(pred, _), - ObligationCauseCode::BindingObligation(item_def_id, span), - ) = (&obligation.predicate, &obligation.cause.code) - { - if let (Some(generics), true) = ( - self.tcx.hir().get_if_local(*item_def_id).as_ref().and_then(|n| n.generics()), - Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), - ) { - for param in generics.params { - if param.span == *span - && !param.bounds.iter().any(|bound| { - bound.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(), " + "), - }; - err.span_suggestion( - span, - "consider relaxing the implicit `Sized` restriction", - format!("{} ?Sized", separator), - Applicability::MachineApplicable, + let (pred, item_def_id, span) = + match (obligation.predicate.kind(), &obligation.cause.code.peel_derives()) { + ( + ty::PredicateKind::Trait(pred, _), + ObligationCauseCode::BindingObligation(item_def_id, span), + ) => (pred, item_def_id, span), + _ => return, + }; + + let node = match ( + self.tcx.hir().get_if_local(*item_def_id), + Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), + ) { + (Some(node), true) => node, + _ => return, + }; + let generics = match node.generics() { + Some(generics) => generics, + None => return, + }; + for param in generics.params { + if param.span != *span + || param.bounds.iter().any(|bound| { + bound.trait_ref().and_then(|trait_ref| trait_ref.trait_def_id()) + == self.tcx.lang_items().sized_trait() + }) + { + continue; + } + match node { + hir::Node::Item( + item + @ + hir::Item { + kind: + hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..), + .. + }, + ) => { + // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a + // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S(T);` + // is not. + let mut visitor = FindTypeParam { + param: param.name.ident().name, + invalid_spans: vec![], + nested: false, + }; + visitor.visit_item(item); + if !visitor.invalid_spans.is_empty() { + let mut multispan: MultiSpan = param.span.into(); + multispan.push_span_label( + param.span, + format!("this could be changed to `{}: ?Sized`...", param.name.ident()), + ); + for sp in visitor.invalid_spans { + multispan.push_span_label( + sp, + format!( + "...if indirection was used here: `Box<{}>`", + param.name.ident(), + ), + ); + } + err.span_help( + multispan, + &format!( + "you could relax the implicit `Sized` bound on `{T}` if it were \ + used through indirection like `&{T}` or `Box<{T}>`", + T = param.name.ident(), + ), ); return; } } + _ => {} } + let (span, separator) = match param.bounds { + [] => (span.shrink_to_hi(), ":"), + [.., bound] => (bound.span().shrink_to_hi(), " +"), + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{} ?Sized", separator), + Applicability::MachineApplicable, + ); + return; } } @@ -1619,27 +1805,88 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } } +/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting +/// `param: ?Sized` would be a valid constraint. +struct FindTypeParam { + param: rustc_span::Symbol, + invalid_spans: Vec, + nested: bool, +} + +impl<'v> Visitor<'v> for FindTypeParam { + type Map = rustc_hir::intravisit::ErasedMap<'v>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { + hir::intravisit::NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + // We collect the spans of all uses of the "bare" type param, like in `field: T` or + // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be + // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` + // obligations like `Box` and `Vec`, but we perform no extra analysis for those cases + // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors + // in that case should make what happened clear enough. + match ty.kind { + hir::TyKind::Ptr(_) | hir::TyKind::Rptr(..) | hir::TyKind::TraitObject(..) => {} + hir::TyKind::Path(hir::QPath::Resolved(None, path)) + if path.segments.len() == 1 && path.segments[0].ident.name == self.param => + { + if !self.nested { + self.invalid_spans.push(ty.span); + } + } + hir::TyKind::Path(_) => { + let prev = self.nested; + self.nested = true; + hir::intravisit::walk_ty(self, ty); + self.nested = prev; + } + _ => { + hir::intravisit::walk_ty(self, ty); + } + } + } +} + pub fn recursive_type_with_infinite_size_error( tcx: TyCtxt<'tcx>, type_def_id: DefId, -) -> DiagnosticBuilder<'tcx> { + spans: Vec, +) { assert!(type_def_id.is_local()); let span = tcx.hir().span_if_local(type_def_id).unwrap(); - let span = tcx.sess.source_map().def_span(span); - let mut err = struct_span_err!( - tcx.sess, - span, - E0072, - "recursive type `{}` has infinite size", - tcx.def_path_str(type_def_id) - ); + let span = tcx.sess.source_map().guess_head_span(span); + let path = tcx.def_path_str(type_def_id); + let mut err = + struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size", path); err.span_label(span, "recursive type has infinite size"); - err.help(&format!( - "insert indirection (e.g., a `Box`, `Rc`, or `&`) \ - at some point to make `{}` representable", - tcx.def_path_str(type_def_id) - )); - err + for &span in &spans { + err.span_label(span, "recursive without indirection"); + } + let msg = format!( + "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `{}` representable", + path, + ); + if spans.len() <= 4 { + err.multipart_suggestion( + &msg, + spans + .iter() + .flat_map(|&span| { + vec![ + (span.shrink_to_lo(), "Box<".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ] + .into_iter() + }) + .collect(), + Applicability::HasPlaceholders, + ); + } else { + err.help(&msg); + } + err.emit(); } /// Summarizes information @@ -1672,229 +1919,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 6e3074cd3ca98..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,25 +82,23 @@ 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", }) }) } hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(_, hir::TraitMethod::Provided(body_id)), + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)), .. }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")), hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Method(sig, body_id), + 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 { @@ -111,11 +109,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }), hir::Node::Expr(hir::Expr { .. }) => { let parent_hid = hir.get_parent_node(hir_id); - if parent_hid != hir_id { - return self.describe_enclosure(parent_hid); - } else { - None - } + if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None } } _ => None, } @@ -138,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. @@ -146,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. @@ -223,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 656c3c7e6138f..176bd90303ddd 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -1,37 +1,60 @@ use super::{ EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, + SelectionContext, }; +use crate::autoderef::Autoderef; 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::{ + self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty, + TyCtxt, TypeFoldable, WithConstness, +}; +use rustc_middle::ty::{TypeAndMut, TypeckTables}; +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<'tcx>, body_id: hir::HirId, ); fn suggest_borrow_on_unsized_slice( &self, code: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'_>, + ); + + fn suggest_dereferences( + &self, + obligation: &PredicateObligation<'tcx>, err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::PolyTraitRef<'tcx>, + points_at_arg: bool, ); fn get_closure_name( @@ -52,7 +75,7 @@ crate trait InferCtxtExt<'tcx> { fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::Binder>, points_at_arg: bool, has_custom_message: bool, @@ -61,14 +84,14 @@ crate trait InferCtxtExt<'tcx> { fn suggest_remove_reference( &self, obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::Binder>, ); fn suggest_change_mut( &self, obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::Binder>, points_at_arg: bool, ); @@ -76,14 +99,16 @@ crate trait InferCtxtExt<'tcx> { fn suggest_semicolon_removal( &self, obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, span: Span, trait_ref: &ty::Binder>, ); + fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option; + fn suggest_impl_trait( &self, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, span: Span, obligation: &PredicateObligation<'tcx>, trait_ref: &ty::Binder>, @@ -91,7 +116,7 @@ crate trait InferCtxtExt<'tcx> { fn point_at_returns_when_relevant( &self, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, ); @@ -120,15 +145,13 @@ 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, - trait_ref: ty::TraitRef<'_>, + interior_or_upvar_span: GeneratorInteriorOrUpvar, + interior_extra_info: Option<(Option, Span, Option, Option)>, + inner_generator_body: Option<&hir::Body<'tcx>>, + outer_generator: Option, + trait_ref: ty::TraitRef<'tcx>, target_ty: Ty<'tcx>, - tables: &ty::TypeckTables<'_>, + tables: &ty::TypeckTables<'tcx>, obligation: &PredicateObligation<'tcx>, next_code: Option<&ObligationCauseCode<'tcx>>, ); @@ -143,126 +166,273 @@ 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.tail_span_for_suggestion(), + 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( + tcx: TyCtxt<'tcx>, + generics: &hir::Generics<'tcx>, + msg: &str, + err: &mut DiagnosticBuilder<'_>, + fn_sig: Option<&hir::FnSig<'_>>, + projection: Option<&ty::ProjectionTy<'_>>, + trait_ref: ty::PolyTraitRef<'tcx>, + 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(tcx).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(tcx).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<'tcx>, body_id: hir::HirId, ) { - let self_ty = trait_ref.self_ty(); + let self_ty = trait_ref.skip_binder().self_ty(); let (param_ty, projection) = match &self_ty.kind { ty::Param(_) => (true, None), ty::Projection(projection) => (false, Some(projection)), _ => 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( + self.tcx, + &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( + self.tcx, &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::Method(..), + 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( + self.tcx, + &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( + self.tcx, + &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 +444,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; @@ -291,12 +459,68 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } + /// When after several dereferencing, the reference satisfies the trait + /// binding. This function provides dereference suggestion for this + /// specific situation. + fn suggest_dereferences( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + trait_ref: &ty::PolyTraitRef<'tcx>, + points_at_arg: bool, + ) { + // It only make sense when suggesting dereferences for arguments + if !points_at_arg { + return; + } + let param_env = obligation.param_env; + let body_id = obligation.cause.body_id; + let span = obligation.cause.span; + let real_trait_ref = match &obligation.cause.code { + ObligationCauseCode::ImplDerivedObligation(cause) + | ObligationCauseCode::DerivedObligation(cause) + | ObligationCauseCode::BuiltinDerivedObligation(cause) => &cause.parent_trait_ref, + _ => trait_ref, + }; + let real_ty = match real_trait_ref.self_ty().no_bound_vars() { + Some(ty) => ty, + None => return, + }; + + if let ty::Ref(region, base_ty, mutbl) = real_ty.kind { + let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty); + if let Some(steps) = autoderef.find_map(|(ty, steps)| { + // Re-add the `&` + let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); + let obligation = + self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_ref, ty); + Some(steps).filter(|_| self.predicate_may_hold(&obligation)) + }) { + if steps > 0 { + if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) { + // Don't care about `&mut` because `DerefMut` is used less + // often and user will not expect autoderef happens. + if src.starts_with("&") && !src.starts_with("&mut ") { + let derefs = "*".repeat(steps); + err.span_suggestion( + span, + "consider adding dereference here", + format!("&{}{}", derefs, &src[1..]), + Applicability::MachineApplicable, + ); + } + } + } + } + } + } + /// When encountering an assignment of an unsized trait, like `let x = ""[..];`, provide a /// suggestion to borrow the initializer in order to use have a slice instead. fn suggest_borrow_on_unsized_slice( &self, code: &ObligationCauseCode<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, ) { if let &ObligationCauseCode::VariableType(hir_id) = code { let parent_node = self.tcx.hir().get_parent_node(hir_id); @@ -342,7 +566,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), .. })) => { @@ -351,7 +575,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Different to previous arm because one is `&hir::Local` and the other // is `P`. Some(hir::Node::Local(local)) => get_name(err, &local.pat.kind), - _ => return None, + _ => None, } } @@ -365,32 +589,39 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref: &ty::Binder>, points_at_arg: bool, ) { - let self_ty = trait_ref.self_ty(); + let self_ty = match trait_ref.self_ty().no_bound_vars() { + None => return, + Some(ty) => ty, + }; + let (def_id, output_ty, callable) = match self_ty.kind { - ty::Closure(def_id, substs) => { - (def_id, self.closure_sig(def_id, substs).output(), "closure") - } + ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"), ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"), _ => return, }; let msg = format!("use parentheses to call the {}", callable); - let obligation = self.mk_obligation_for_def_id( - trait_ref.def_id(), - output_ty.skip_binder(), - obligation.cause.clone(), - obligation.param_env, - ); + // `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound + // variables, so bail out if we have any. + let output_ty = match output_ty.no_bound_vars() { + Some(ty) => ty, + None => return, + }; + + let new_obligation = + self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_ref, output_ty); - match self.evaluate_obligation(&obligation) { - Ok(EvaluationResult::EvaluatedToOk) - | Ok(EvaluationResult::EvaluatedToOkModuloRegions) - | Ok(EvaluationResult::EvaluatedToAmbig) => {} + match self.evaluate_obligation(&new_obligation) { + Ok( + EvaluationResult::EvaluatedToOk + | EvaluationResult::EvaluatedToOkModuloRegions + | EvaluationResult::EvaluatedToAmbig, + ) => {} _ => return, } let hir = self.tcx.hir(); // Get the name of the callable and the arguments to be used in the suggestion. - let snippet = match hir.get_if_local(def_id) { + let (snippet, sugg) = match hir.get_if_local(def_id) { Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, decl, _, span, ..), .. @@ -401,7 +632,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { None => return, }; let args = decl.inputs.iter().map(|_| "_").collect::>().join(", "); - format!("{}({})", name, args) + let sugg = format!("({})", args); + (format!("{}{}", name, sugg), sugg) } Some(hir::Node::Item(hir::Item { ident, @@ -422,7 +654,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }) .collect::>() .join(", "); - format!("{}({})", ident, args) + let sugg = format!("({})", args); + (format!("{}{}", ident, sugg), sugg) } _ => return, }; @@ -431,10 +664,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // an argument, the `obligation.cause.span` points at the expression // of the argument, so we can provide a suggestion. This is signaled // by `points_at_arg`. Otherwise, we give a more general note. - err.span_suggestion( - obligation.cause.span, + err.span_suggestion_verbose( + obligation.cause.span.shrink_to_hi(), &msg, - snippet, + sugg, Applicability::HasPlaceholders, ); } else { @@ -445,7 +678,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::Binder>, points_at_arg: bool, has_custom_message: bool, @@ -468,8 +701,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let new_obligation = Obligation::new( ObligationCause::dummy(), param_env, - new_trait_ref.without_const().to_predicate(), + new_trait_ref.without_const().to_predicate(self.tcx), ); + if self.predicate_must_hold_modulo_regions(&new_obligation) { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { // We have a very specific type of error, where just borrowing this argument @@ -477,6 +711,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // original type obligation, not the last one that failed, which is arbitrary. // Because of this, we modify the error to refer to the original obligation and // return early in the caller. + let msg = format!( "the trait bound `{}: {}` is not satisfied", found, @@ -499,12 +734,23 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation.parent_trait_ref.skip_binder().print_only_trait_path(), ), ); - err.span_suggestion( - span, - "consider borrowing here", - format!("&{}", snippet), - Applicability::MaybeIncorrect, - ); + + // This if is to prevent a special edge-case + if !span.from_expansion() { + // We don't want a borrowing suggestion on the fields in structs, + // ``` + // struct Foo { + // the_foos: Vec + // } + // ``` + + err.span_suggestion( + span, + "consider borrowing here", + format!("&{}", snippet), + Applicability::MaybeIncorrect, + ); + } return true; } } @@ -517,10 +763,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn suggest_remove_reference( &self, obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::Binder>, ) { - let trait_ref = trait_ref.skip_binder(); let span = obligation.cause.span; if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { @@ -531,17 +776,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - let mut trait_type = trait_ref.self_ty(); + let mut suggested_ty = match trait_ref.self_ty().no_bound_vars() { + Some(ty) => ty, + None => return, + }; for refs_remaining in 0..refs_number { - if let ty::Ref(_, t_type, _) = trait_type.kind { - trait_type = t_type; + if let ty::Ref(_, inner_ty, _) = suggested_ty.kind { + suggested_ty = inner_ty; - let new_obligation = self.mk_obligation_for_def_id( - trait_ref.def_id, - trait_type, - ObligationCause::dummy(), + let new_obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, + trait_ref, + suggested_ty, ); if self.predicate_may_hold(&new_obligation) { @@ -579,7 +826,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn suggest_change_mut( &self, obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, trait_ref: &ty::Binder>, points_at_arg: bool, ) { @@ -599,27 +846,36 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind { - let trait_type = match mutability { + 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 suggested_ty = match mutability { hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type), hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type), }; - let new_obligation = self.mk_obligation_for_def_id( - trait_ref.skip_binder().def_id, - trait_type, - ObligationCause::dummy(), + let new_obligation = self.mk_trait_obligation_with_new_self_ty( obligation.param_env, + &trait_ref, + suggested_ty, ); - - if self.evaluate_obligation_no_overflow(&new_obligation).must_apply_modulo_regions() - { + let suggested_ty_would_satisfy_obligation = self + .evaluate_obligation_no_overflow(&new_obligation) + .must_apply_modulo_regions(); + if suggested_ty_would_satisfy_obligation { let sp = self .tcx .sess .source_map() .span_take_while(span, |c| c.is_whitespace() || *c == '&'); if points_at_arg && mutability == hir::Mutability::Not && refs_number > 0 { - err.span_suggestion( + err.span_suggestion_verbose( sp, "consider changing this borrow's mutability", "&mut ".to_string(), @@ -629,7 +885,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.note(&format!( "`{}` is implemented for `{:?}`, but not for `{:?}`", trait_ref.print_only_trait_path(), - trait_type, + suggested_ty, trait_ref.skip_binder().self_ty(), )); } @@ -641,10 +897,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn suggest_semicolon_removal( &self, obligation: &PredicateObligation<'tcx>, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, span: Span, trait_ref: &ty::Binder>, ) { + let is_empty_tuple = + |ty: ty::Binder>| ty.skip_binder().kind == ty::Tuple(ty::List::empty()); + let hir = self.tcx.hir(); let parent_node = hir.get_parent_node(obligation.cause.body_id); let node = hir.find(parent_node); @@ -656,7 +915,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let hir::ExprKind::Block(blk, _) = &body.value.kind { if sig.decl.output.span().overlaps(span) && blk.expr.is_none() - && "()" == &trait_ref.self_ty().to_string() + && is_empty_tuple(trait_ref.self_ty()) { // FIXME(estebank): When encountering a method with a trait // bound not satisfied in the return type with a body that has @@ -671,12 +930,23 @@ 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. fn suggest_impl_trait( &self, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, span: Span, obligation: &PredicateObligation<'tcx>, trait_ref: &ty::Binder>, @@ -737,12 +1007,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 &= + !matches!(ty.kind, ty::Error(_)) + && last_ty.map_or(true, |last_ty| { + // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes + // *after* in the dependency graph. + match (&ty.kind, &last_ty.kind) { + (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 +1037,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 +1052,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 +1077,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 +1103,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 +1130,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); @@ -850,7 +1142,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn point_at_returns_when_relevant( &self, - err: &mut DiagnosticBuilder<'tcx>, + err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, ) { match obligation.cause.code.peel_derives() { @@ -947,7 +1239,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 +1254,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 +1269,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 +1303,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,23 +1328,25 @@ 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. - let (mut trait_ref, mut target_ty) = match obligation.predicate { - ty::Predicate::Trait(p, _) => { + // the type. The last generator (`outer_generator` below) has information about where the + // bound was introduced. At least one generator should be present for this diagnostic to be + // modified. + let (mut trait_ref, mut target_ty) = match obligation.predicate.kind() { + ty::PredicateKind::Trait(p, _) => { (Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty())) } _ => (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(); + let ty = derived_obligation.parent_trait_ref.skip_binder().self_ty(); debug!( "maybe_note_obligation_cause_for_async_await: \ parent_trait_ref={:?} self_ty.kind={:?}", @@ -1062,7 +1356,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 +1388,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; } @@ -1105,68 +1399,112 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let generator_did_root = self.tcx.closure_base_def_id(generator_did); debug!( "maybe_note_obligation_cause_for_async_await: generator_did={:?} \ - generator_did_root={:?} in_progress_tables.local_id_root={:?} span={:?}", + generator_did_root={:?} in_progress_tables.hir_owner={:?} span={:?}", generator_did, generator_did_root, - in_progress_tables.as_ref().map(|t| t.local_id_root), + in_progress_tables.as_ref().map(|t| t.hir_owner), span ); let query_tables; let tables: &TypeckTables<'tcx> = match &in_progress_tables { - Some(t) if t.local_id_root == Some(generator_did_root) => t, + 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_mentioned(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,39 +1522,25 @@ 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, - trait_ref: ty::TraitRef<'_>, + interior_or_upvar_span: GeneratorInteriorOrUpvar, + interior_extra_info: Option<(Option, Span, Option, Option)>, + inner_generator_body: Option<&hir::Body<'tcx>>, + outer_generator: Option, + trait_ref: ty::TraitRef<'tcx>, target_ty: Ty<'tcx>, - tables: &ty::TypeckTables<'_>, + tables: &ty::TypeckTables<'tcx>, obligation: &PredicateObligation<'tcx>, next_code: Option<&ObligationCauseCode<'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 +1553,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 +1591,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_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 +1763,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 +1774,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 +1806,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 +1867,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`"); } @@ -1488,7 +1882,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.note(&format!("required because it appears within the type `{}`", ty)); obligated_types.push(ty); - let parent_predicate = parent_trait_ref.without_const().to_predicate(); + let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); if !self.is_recursive_obligation(obligated_types, &data.parent_code) { self.note_obligation_cause_code( err, @@ -1505,7 +1899,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { parent_trait_ref.print_only_trait_path(), parent_trait_ref.skip_binder().self_ty() )); - let parent_predicate = parent_trait_ref.without_const().to_predicate(); + let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); + self.note_obligation_cause_code( + err, + &parent_predicate, + &data.parent_code, + obligated_types, + ); + } + ObligationCauseCode::DerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); self.note_obligation_cause_code( err, &parent_predicate, @@ -1527,6 +1931,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,38 +1947,123 @@ 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"); - } - } } } fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) { - let current_limit = self.tcx.sess.recursion_limit.get(); + let current_limit = self.tcx.sess.recursion_limit(); let suggested_limit = current_limit * 2; err.help(&format!( "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)", 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_before_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()); + + // Do not check on infer_types to avoid panic in evaluate_obligation. + if self_ty.has_infer_types() { + return; + } + let self_ty = self.tcx.erase_regions(&self_ty); + + let impls_future = self.tcx.type_implements_trait(( + future_trait, + self_ty.skip_binder(), + 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().skip_binder(), + 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_before_try: normalized_projection_type {:?}", + self.resolve_vars_if_possible(&normalized_ty) + ); + let try_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_ref, + normalized_ty, + ); + debug!("suggest_await_before_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. /// Used to point at the return spans when we want to suggest some change to them. #[derive(Default)] -struct ReturnsVisitor<'v> { - returns: Vec<&'v hir::Expr<'v>>, +pub struct ReturnsVisitor<'v> { + pub returns: Vec<&'v hir::Expr<'v>>, in_block_tail: bool, } impl<'v> Visitor<'v> for ReturnsVisitor<'v> { - type Map = rustc::hir::map::Map<'v>; + type Map = hir::intravisit::ErasedMap<'v>; fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap { hir::intravisit::NestedVisitorMap::None @@ -1615,3 +2111,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<'_>, + 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 5def77ce7324c..32ab63458e752 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, ShallowResolver}; -use rustc::ty::error::ExpectedFound; -use rustc::ty::{self, ToPolyTraitRef, Ty, TypeFoldable}; +use crate::infer::{InferCtxt, TyOrConstInferVar}; use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; +use rustc_errors::ErrorReported; use rustc_infer::traits::{TraitEngine, TraitEngineExt as _}; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::{self, Const, ToPolyTraitRef, Ty, TypeFoldable}; use std::marker::PhantomData; use super::project; @@ -73,12 +75,16 @@ pub struct FulfillmentContext<'tcx> { #[derive(Clone, Debug)] pub struct PendingPredicateObligation<'tcx> { pub obligation: PredicateObligation<'tcx>, - pub stalled_on: Vec, + // This is far more often read than modified, meaning that we + // should mostly optimize for reading speed, while modifying is not as relevant. + // + // For whatever reason using a boxed slice is slower than using a `Vec` here. + pub stalled_on: Vec>, } // `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -static_assert_size!(PendingPredicateObligation<'_>, 136); +static_assert_size!(PendingPredicateObligation<'_>, 72); impl<'a, 'tcx> FulfillmentContext<'tcx> { /// Creates a new fulfillment context. @@ -131,7 +137,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { // FIXME: if we kept the original cache key, we could mark projection // obligations as complete for the projection cache here. - errors.extend(outcome.errors.into_iter().map(|e| to_fulfillment_error(e))); + errors.extend(outcome.errors.into_iter().map(to_fulfillment_error)); // If nothing new was added, no need to keep looping. if outcome.stalled { @@ -214,7 +220,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { .predicates .to_errors(CodeAmbiguity) .into_iter() - .map(|e| to_fulfillment_error(e)) + .map(to_fulfillment_error) .collect(); if errors.is_empty() { Ok(()) } else { Err(errors) } } @@ -266,8 +272,8 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { // Match arms are in order of frequency, which matters because this // code is so hot. 1 and 0 dominate; 2+ is fairly rare. 1 => { - let infer = pending_obligation.stalled_on[0]; - ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(infer) + let infer_var = pending_obligation.stalled_on[0]; + self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) } 0 => { // In this case we haven't changed, but wish to make a change. @@ -277,8 +283,8 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { // This `for` loop was once a call to `all()`, but this lower-level // form was a perf win. See #64545 for details. (|| { - for &infer in &pending_obligation.stalled_on { - if ShallowResolver::new(self.selcx.infcx()).shallow_resolve_changed(infer) { + for &infer_var in &pending_obligation.stalled_on { + if self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) { return true; } } @@ -309,21 +315,16 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { debug!("process_obligation: obligation = {:?} cause = {:?}", obligation, obligation.cause); - fn infer_ty(ty: Ty<'tcx>) -> ty::InferTy { - match ty.kind { - ty::Infer(infer) => infer, - _ => panic!(), - } - } + let infcx = self.selcx.infcx(); - match obligation.predicate { - ty::Predicate::Trait(ref data, _) => { - let trait_obligation = obligation.with(data.clone()); + match obligation.predicate.kind() { + ty::PredicateKind::Trait(ref data, _) => { + let trait_obligation = obligation.with(*data); - if data.is_global() { + if obligation.predicate.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 @@ -333,12 +334,12 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } match self.selcx.select(&trait_obligation) { - Ok(Some(vtable)) => { + Ok(Some(impl_source)) => { debug!( "selecting trait `{:?}` at depth {} yielded Ok(Some)", data, obligation.recursion_depth ); - ProcessResult::Changed(mk_pending(vtable.nested_obligations())) + ProcessResult::Changed(mk_pending(impl_source.nested_obligations())) } Ok(None) => { debug!( @@ -351,11 +352,11 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { // trait selection is because we don't have enough // information about the types in the trait. pending_obligation.stalled_on = - trait_ref_type_vars(self.selcx, data.to_poly_trait_ref()); + trait_ref_infer_vars(self.selcx, data.to_poly_trait_ref()); 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 ); @@ -372,14 +373,14 @@ 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) { + &ty::PredicateKind::RegionOutlives(binder) => { + match infcx.region_outlives_predicate(&obligation.cause, binder) { Ok(()) => ProcessResult::Changed(vec![]), Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), } } - ty::Predicate::TypeOutlives(ref binder) => { + ty::PredicateKind::TypeOutlives(ref binder) => { // Check if there are higher-ranked vars. match binder.no_bound_vars() { // If there are, inspect the underlying type further. @@ -423,13 +424,13 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } } - ty::Predicate::Projection(ref data) => { - let project_obligation = obligation.with(data.clone()); + ty::PredicateKind::Projection(ref data) => { + let project_obligation = obligation.with(*data); match project::poly_project_and_unify_type(self.selcx, &project_obligation) { Ok(None) => { let tcx = self.selcx.tcx(); pending_obligation.stalled_on = - trait_ref_type_vars(self.selcx, data.to_poly_trait_ref(tcx)); + trait_ref_infer_vars(self.selcx, data.to_poly_trait_ref(tcx)); ProcessResult::Unchanged } Ok(Some(os)) => ProcessResult::Changed(mk_pending(os)), @@ -437,7 +438,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } } - ty::Predicate::ObjectSafe(trait_def_id) => { + &ty::PredicateKind::ObjectSafe(trait_def_id) => { if !self.selcx.tcx().is_object_safe(trait_def_id) { ProcessResult::Error(CodeSelectionError(Unimplemented)) } else { @@ -445,8 +446,8 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } } - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - match self.selcx.infcx().closure_kind(closure_def_id, closure_substs) { + &ty::PredicateKind::ClosureKind(_, closure_substs, kind) => { + match self.selcx.infcx().closure_kind(closure_substs) { Some(closure_kind) => { if closure_kind.extends(kind) { ProcessResult::Changed(vec![]) @@ -458,23 +459,24 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } } - ty::Predicate::WellFormed(ty) => { + &ty::PredicateKind::WellFormed(arg) => { match wf::obligations( self.selcx.infcx(), obligation.param_env, obligation.cause.body_id, - ty, + arg, obligation.cause.span, ) { None => { - pending_obligation.stalled_on = vec![infer_ty(ty)]; + pending_obligation.stalled_on = + vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()]; ProcessResult::Unchanged } Some(os) => ProcessResult::Changed(mk_pending(os)), } } - ty::Predicate::Subtype(ref subtype) => { + &ty::PredicateKind::Subtype(subtype) => { match self.selcx.infcx().subtype_predicate( &obligation.cause, obligation.param_env, @@ -483,8 +485,8 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { None => { // None means that both are unresolved. pending_obligation.stalled_on = vec![ - infer_ty(subtype.skip_binder().a), - infer_ty(subtype.skip_binder().b), + TyOrConstInferVar::maybe_from_ty(subtype.skip_binder().a).unwrap(), + TyOrConstInferVar::maybe_from_ty(subtype.skip_binder().b).unwrap(), ]; ProcessResult::Unchanged } @@ -503,7 +505,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } } - ty::Predicate::ConstEvaluatable(def_id, substs) => { + &ty::PredicateKind::ConstEvaluatable(def_id, substs) => { match self.selcx.infcx().const_eval_resolve( obligation.param_env, def_id, @@ -515,6 +517,68 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))), } } + + ty::PredicateKind::ConstEquate(c1, c2) => { + debug!("equating consts: c1={:?} c2={:?}", c1, c2); + + let stalled_on = &mut pending_obligation.stalled_on; + + let mut evaluate = |c: &'tcx Const<'tcx>| { + if let ty::ConstKind::Unevaluated(def_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 + } + } + } } } @@ -534,20 +598,22 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } } -/// Returns the set of type variables contained in a trait ref -fn trait_ref_type_vars<'a, 'tcx>( +/// Returns the set of inference variables contained in a trait ref. +fn trait_ref_infer_vars<'a, 'tcx>( selcx: &mut SelectionContext<'a, 'tcx>, - t: ty::PolyTraitRef<'tcx>, -) -> Vec { - t.skip_binder() // ok b/c this check doesn't care about regions - .input_types() - .map(|t| selcx.infcx().resolve_vars_if_possible(&t)) - .filter(|t| t.has_infer_types()) - .flat_map(|t| t.walk()) - .filter_map(|t| match t.kind { - ty::Infer(infer) => Some(infer), - _ => None, - }) + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Vec> { + selcx + .infcx() + .resolve_vars_if_possible(&trait_ref) + .skip_binder() // ok b/c this check doesn't care about regions + .substs + .iter() + // FIXME(eddyb) try using `skip_current_subtree` to skip everything that + // doesn't contain inference variables, not just the outermost level. + .filter(|arg| arg.has_infer_types_or_consts()) + .flat_map(|arg| arg.walk()) + .filter_map(TyOrConstInferVar::maybe_from_generic_arg) .collect() } diff --git a/src/librustc_trait_selection/traits/misc.rs b/src/librustc_trait_selection/traits/misc.rs index d500cff67c64b..61567aeb57cb0 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; @@ -48,7 +48,7 @@ pub fn can_type_implement_copy( continue; } let span = tcx.def_span(field.did); - let cause = ObligationCause { span, ..ObligationCause::dummy() }; + let cause = ObligationCause::dummy_with_span(span); let ctx = traits::FulfillmentContext::new(); match traits::fully_normalize(&infcx, ctx, cause, param_env, &ty) { Ok(ty) => { diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs index 7b93982db974b..9ab87e6b6ca01 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; @@ -21,24 +22,25 @@ mod util; pub mod wf; use crate::infer::outlives::env::OutlivesEnvironment; -use crate::infer::{InferCtxt, SuppressRegionErrors, TyCtxtInferExt}; +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::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::{ + self, GenericParamDefKind, ParamEnv, ToPredicate, Ty, TyCtxt, WithConstness, +}; +use rustc_span::Span; use std::fmt::Debug; pub use self::FulfillmentErrorCode::*; +pub use self::ImplSource::*; pub use self::ObligationCauseCode::*; pub use self::SelectionError::*; -pub use self::Vtable::*; pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; pub use self::coherence::{OrphanCheckErr, OverlapResult}; @@ -54,23 +56,22 @@ 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}; pub use self::structural_match::search_for_structural_match_violation; -pub use self::structural_match::type_marked_structural; 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 +113,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,9 +139,9 @@ 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(), + predicate: trait_ref.without_const().to_predicate(infcx.tcx), }; let result = infcx.predicate_must_hold_modulo_regions(&obligation); @@ -165,7 +166,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); @@ -234,17 +235,14 @@ fn do_normalize_predicates<'tcx>( debug!("do_normalize_predictes: normalized predicates = {:?}", predicates); - let region_scope_tree = region::ScopeTree::default(); - // We can use the `elaborated_env` here; the region code only // cares about declarations like `'a: 'b`. let outlives_env = OutlivesEnvironment::new(elaborated_env); infcx.resolve_regions_and_report_errors( region_context, - ®ion_scope_tree, &outlives_env, - SuppressRegionErrors::default(), + RegionckMode::default(), ); let predicates = match infcx.fully_resolve(&predicates) { @@ -261,8 +259,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 +297,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()) + .map(|obligation| obligation.predicate) + .collect(); debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); @@ -328,8 +328,8 @@ pub fn normalize_param_env_or_error<'tcx>( // This works fairly well because trait matching does not actually care about param-env // TypeOutlives predicates - these are normally used by regionck. let outlives_predicates: Vec<_> = predicates - .drain_filter(|predicate| match predicate { - ty::Predicate::TypeOutlives(..) => true, + .drain_filter(|predicate| match predicate.kind() { + ty::PredicateKind::TypeOutlives(..) => true, _ => false, }) .collect(); @@ -475,7 +475,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,14 +520,46 @@ 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 + ); + + let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) }; + + let obligation = Obligation { + cause: ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: trait_ref.without_const().to_predicate(tcx), + }; + tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) +} + pub fn provide(providers: &mut ty::query::Providers<'_>) { object_safety::provide(providers); + structural_match::provide(providers); *providers = ty::query::Providers { specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, vtable_methods, 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 d0d41f3ae32ad..5befc797a517a 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; @@ -39,7 +40,7 @@ pub fn astconv_object_safety_violations( let violations = traits::supertrait_def_ids(tcx, trait_def_id) .map(|def_id| predicates_reference_self(tcx, def_id, true)) .filter(|spans| !spans.is_empty()) - .map(|spans| ObjectSafetyViolation::SupertraitSelf(spans)) + .map(ObjectSafetyViolation::SupertraitSelf) .collect(); debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations); @@ -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) @@ -185,37 +208,18 @@ fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> .filter_map(|pred| { match pred { hir::WherePredicate::BoundPredicate(pred) - if pred.bounded_ty.hir_id.owner_def_id() == trait_def_id => + if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id => { // 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,22 +239,22 @@ 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() .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) .filter_map(|(predicate, &sp)| { - match predicate { - ty::Predicate::Trait(ref data, _) => { + match predicate.kind() { + ty::PredicateKind::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 } } - ty::Predicate::Projection(ref data) => { + ty::PredicateKind::Projection(ref data) => { // And similarly for projections. This should be redundant with // the previous check because any projection should have a // matching `Trait` predicate with the same inputs, but we do @@ -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) @@ -276,13 +276,14 @@ fn predicates_reference_self( None } } - ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::TypeOutlives(..) - | ty::Predicate::RegionOutlives(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::ConstEvaluatable(..) => None, + ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => None, } }) .collect() @@ -303,18 +304,22 @@ 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 { - ty::Predicate::Trait(ref trait_pred, _) => { - trait_pred.def_id() == sized_def_id && trait_pred.skip_binder().self_ty().is_param(0) + elaborate_predicates(tcx, predicates.into_iter()).any(|obligation| { + match obligation.predicate.kind() { + ty::PredicateKind::Trait(ref trait_pred, _) => { + trait_pred.def_id() == sized_def_id + && trait_pred.skip_binder().self_ty().is_param(0) + } + ty::PredicateKind::Projection(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => false, } - ty::Predicate::Projection(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::RegionOutlives(..) - | ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::TypeOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => false, }) } @@ -363,7 +368,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 +426,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 +622,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 = @@ -634,7 +639,7 @@ fn receiver_is_dispatchable<'tcx>( substs: tcx.mk_substs_trait(tcx.types.self_param, &[unsized_self_ty.into()]), } .without_const() - .to_predicate(); + .to_predicate(tcx); // U: Trait let trait_predicate = { @@ -647,13 +652,12 @@ fn receiver_is_dispatchable<'tcx>( } }); - ty::TraitRef { def_id: unsize_did, substs }.without_const().to_predicate() + ty::TraitRef { def_id: unsize_did, substs }.without_const().to_predicate(tcx) }; let caller_bounds: Vec> = param_env .caller_bounds .iter() - .cloned() .chain(iter::once(unsize_predicate)) .chain(iter::once(trait_predicate)) .collect(); @@ -670,7 +674,7 @@ fn receiver_is_dispatchable<'tcx>( substs: tcx.mk_substs_trait(receiver_ty, &[unsized_receiver_ty.into()]), } .without_const() - .to_predicate(); + .to_predicate(tcx); Obligation::new(ObligationCause::dummy(), param_env, predicate) }; @@ -725,52 +729,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..a1dfa838e7af3 100644 --- a/src/librustc_trait_selection/traits/on_unimplemented.rs +++ b/src/librustc_trait_selection/traits/on_unimplemented.rs @@ -1,13 +1,10 @@ -use fmt_macros::{Parser, Piece, Position}; - -use rustc::ty::{self, GenericParamDefKind, TyCtxt}; -use rustc::util::common::ErrorReported; - 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_parse_format::{ParseMode, Parser, Piece, Position}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; @@ -83,7 +80,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 +207,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 +271,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 +349,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 dde78aa4357e9..f71b3fcf129da 100644 --- a/src/librustc_trait_selection/traits/project.rs +++ b/src/librustc_trait_selection/traits/project.rs @@ -11,21 +11,28 @@ use super::PredicateObligation; use super::Selection; use super::SelectionContext; use super::SelectionError; +use super::{ + ImplSourceClosureData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData, + ImplSourceGeneratorData, ImplSourceUserDefinedData, +}; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; -use super::{VtableClosureData, 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_hir::lang_items::{FnOnceOutputLangItem, FnOnceTraitLangItem, GeneratorTraitLangItem}; +use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_span::symbol::sym; 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>>; @@ -142,15 +149,12 @@ pub fn poly_project_and_unify_type<'cx, 'tcx>( debug!("poly_project_and_unify_type(obligation={:?})", obligation); let infcx = selcx.infcx(); - infcx.commit_if_ok(|snapshot| { - let (placeholder_predicate, placeholder_map) = + infcx.commit_if_ok(|_snapshot| { + let (placeholder_predicate, _) = infcx.replace_bound_vars_with_placeholders(&obligation.predicate); let placeholder_obligation = obligation.with(placeholder_predicate); let result = project_and_unify_type(selcx, &placeholder_obligation)?; - infcx - .leak_check(false, &placeholder_map, snapshot) - .map_err(|err| MismatchedProjectionTypes { err })?; Ok(result) }) } @@ -260,7 +264,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, @@ -326,11 +330,11 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { Reveal::UserFacing => ty, Reveal::All => { - let recursion_limit = *self.tcx().sess.recursion_limit.get(); - if self.depth >= recursion_limit { + let recursion_limit = self.tcx().sess.recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { let obligation = Obligation::with_depth( self.cause.clone(), - recursion_limit, + recursion_limit.0, self.param_env, ty, ); @@ -354,7 +358,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { // handle normalization within binders because // otherwise we wind up a need to normalize when doing // trait matching (since you can have a trait - // obligation like `for<'a> T::B : Fn(&'a int)`), but + // obligation like `for<'a> T::B: Fn(&'a i32)`), but // we can't normalize with bound regions in scope. So // far now we just ignore binders but only normalize // if all bound regions are gone (and then we still @@ -386,7 +390,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) + } } } @@ -425,7 +434,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>( }); let projection = ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, ty: ty_var }); let obligation = - Obligation::with_depth(cause, depth + 1, param_env, projection.to_predicate()); + Obligation::with_depth(cause, depth + 1, param_env, projection.to_predicate(tcx)); obligations.push(obligation); ty_var }) @@ -469,7 +478,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) => { @@ -509,9 +518,9 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( ); // But for now, let's classify this as an overflow: - let recursion_limit = *selcx.tcx().sess.recursion_limit.get(); + let recursion_limit = selcx.tcx().sess.recursion_limit(); let obligation = - Obligation::with_depth(cause, recursion_limit, param_env, projection_ty); + Obligation::with_depth(cause, recursion_limit.0, param_env, projection_ty); selcx.infcx().report_overflow_error(&obligation, false); } Err(ProjectionCacheEntry::NormalizedTy(ty)) => { @@ -535,7 +544,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); @@ -602,7 +611,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) } @@ -613,7 +622,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) } @@ -622,7 +631,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(_)) => { @@ -632,7 +641,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) @@ -654,7 +663,7 @@ fn prune_cache_value_obligations<'a, 'tcx>( let mut obligations: Vec<_> = result .obligations .iter() - .filter(|obligation| match obligation.predicate { + .filter(|obligation| match obligation.predicate.kind() { // We found a `T: Foo` predicate, let's check // if `U` references any unresolved type // variables. In principle, we only care if this @@ -664,7 +673,9 @@ fn prune_cache_value_obligations<'a, 'tcx>( // indirect obligations (e.g., we project to `?0`, // but we have `T: Foo` and `?1: Bar`). - ty::Predicate::Projection(ref data) => infcx.unresolved_type_vars(&data.ty()).is_some(), + ty::PredicateKind::Projection(ref data) => { + infcx.unresolved_type_vars(&data.ty()).is_some() + } // We are only interested in `T: Foo` predicates, whre // `U` references one of `unresolved_type_vars`. =) @@ -713,7 +724,7 @@ fn get_paranoid_cache_value_obligation<'a, 'tcx>( cause, recursion_depth: depth, param_env, - predicate: trait_ref.without_const().to_predicate(), + predicate: trait_ref.without_const().to_predicate(infcx.tcx), } } @@ -748,7 +759,7 @@ fn normalize_to_error<'a, 'tcx>( cause, recursion_depth: depth, param_env, - predicate: trait_ref.without_const().to_predicate(), + predicate: trait_ref.without_const().to_predicate(selcx.tcx()), }; let tcx = selcx.infcx().tcx; let def_id = projection_ty.item_def_id; @@ -771,7 +782,7 @@ struct Progress<'tcx> { impl<'tcx> Progress<'tcx> { fn error(tcx: TyCtxt<'tcx>) -> Self { - Progress { ty: tcx.types.err, obligations: vec![] } + Progress { ty: tcx.ty_error(), obligations: vec![] } } fn with_addl_obligations(mut self, mut obligations: Vec>) -> Self { @@ -801,8 +812,7 @@ fn project_type<'cx, 'tcx>( ) -> Result, ProjectionTyError<'tcx>> { debug!("project(obligation={:?})", obligation); - let recursion_limit = *selcx.tcx().sess.recursion_limit.get(); - if obligation.recursion_depth >= recursion_limit { + if !selcx.tcx().sess.recursion_limit().value_within_limit(obligation.recursion_depth) { debug!("project: overflow!"); return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow)); } @@ -859,7 +869,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>( obligation_trait_ref, candidate_set, ProjectionTyCandidate::ParamEnv, - obligation.param_env.caller_bounds.iter().cloned(), + obligation.param_env.caller_bounds.iter(), ); } @@ -883,9 +893,12 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( let tcx = selcx.tcx(); // Check whether the self-type is itself a projection. - let (def_id, substs) = match obligation_trait_ref.self_ty().kind { - ty::Projection(ref data) => (data.trait_ref(tcx).def_id, data.substs), - ty::Opaque(def_id, substs) => (def_id, substs), + // If so, extract what we know from the trait and try to come up with a good answer. + let bounds = match obligation_trait_ref.self_ty().kind { + ty::Projection(ref data) => { + tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) + } + ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs), ty::Infer(ty::TyVar(_)) => { // If the self-type is an inference variable, then it MAY wind up // being a projected type, so induce an ambiguity. @@ -895,35 +908,29 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( _ => return, }; - // 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); assemble_candidates_from_predicates( selcx, obligation, obligation_trait_ref, candidate_set, ProjectionTyCandidate::TraitDef, - bounds, + bounds.iter(), ) } -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 { debug!("assemble_candidates_from_predicates: predicate={:?}", predicate); - if let ty::Predicate::Projection(data) = predicate { + if let &ty::PredicateKind::Projection(data) = predicate.kind() { let same_def_id = data.projection_def_id() == obligation.predicate.item_def_id; let is_match = same_def_id @@ -964,8 +971,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( let poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate()); let _ = selcx.infcx().commit_if_ok(|_| { - let vtable = match selcx.select(&trait_obligation) { - Ok(Some(vtable)) => vtable, + let impl_source = match selcx.select(&trait_obligation) { + Ok(Some(impl_source)) => impl_source, Ok(None) => { candidate_set.mark_ambiguous(); return Err(()); @@ -977,16 +984,16 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } }; - let eligible = match &vtable { - super::VtableClosure(_) - | super::VtableGenerator(_) - | super::VtableFnPointer(_) - | super::VtableObject(_) - | super::VtableTraitAlias(_) => { - debug!("assemble_candidates_from_impls: vtable={:?}", vtable); + let eligible = match &impl_source { + super::ImplSourceClosure(_) + | super::ImplSourceGenerator(_) + | super::ImplSourceFnPointer(_) + | super::ImplSourceObject(_) + | super::ImplSourceTraitAlias(_) => { + debug!("assemble_candidates_from_impls: impl_source={:?}", impl_source); true } - super::VtableImpl(impl_data) => { + super::ImplSourceUserDefined(impl_data) => { // We have to be careful when projecting out of an // impl because of specialization. If we are not in // codegen (i.e., projection mode is not "any"), and the @@ -1008,53 +1015,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); - - 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 - } 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, + assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id) + .map_err(|ErrorReported| ())?; + if node_item.is_final() { + // Non-specializable items are always projectable. + true + } else { // Only reveal a specializable default if we're past type-checking // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. - 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={}", @@ -1065,7 +1045,47 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } } } - super::VtableParam(..) => { + super::ImplSourceDiscriminantKind(..) => { + // 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::ImplSourceParam(..) => { // This case tell us nothing about the value of an // associated type. Consider: // @@ -1093,18 +1113,18 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // in `assemble_candidates_from_param_env`. false } - super::VtableAutoImpl(..) | super::VtableBuiltin(..) => { + super::ImplSourceAutoImpl(..) | super::ImplSourceBuiltin(..) => { // These traits have no associated types. span_bug!( obligation.cause.span, "Cannot project an associated type from `{:?}`", - vtable + impl_source ); } }; if eligible { - if candidate_set.push_candidate(ProjectionTyCandidate::Select(vtable)) { + if candidate_set.push_candidate(ProjectionTyCandidate::Select(impl_source)) { Ok(()) } else { Err(()) @@ -1123,40 +1143,54 @@ fn confirm_candidate<'cx, 'tcx>( ) -> Progress<'tcx> { debug!("confirm_candidate(candidate={:?}, obligation={:?})", candidate, obligation); - match candidate { + let mut progress = match candidate { ProjectionTyCandidate::ParamEnv(poly_projection) | ProjectionTyCandidate::TraitDef(poly_projection) => { confirm_param_env_candidate(selcx, obligation, poly_projection) } - ProjectionTyCandidate::Select(vtable) => { - confirm_select_candidate(selcx, obligation, obligation_trait_ref, vtable) + ProjectionTyCandidate::Select(impl_source) => { + confirm_select_candidate(selcx, obligation, obligation_trait_ref, impl_source) } + }; + // When checking for cycle during evaluation, we compare predicates with + // "syntactic" equality. Since normalization generally introduces a type + // with new region variables, we need to resolve them to existing variables + // when possible for this to work. See `auto-trait-projection-recursion.rs` + // for a case where this matters. + if progress.ty.has_infer_regions() { + progress.ty = OpportunisticRegionResolver::new(selcx.infcx()).fold_ty(progress.ty); } + progress } fn confirm_select_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, obligation_trait_ref: &ty::TraitRef<'tcx>, - vtable: Selection<'tcx>, + impl_source: Selection<'tcx>, ) -> Progress<'tcx> { - match vtable { - super::VtableImpl(data) => confirm_impl_candidate(selcx, obligation, data), - 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::VtableObject(_) => confirm_object_candidate(selcx, obligation, obligation_trait_ref), - super::VtableAutoImpl(..) - | super::VtableParam(..) - | super::VtableBuiltin(..) - | super::VtableTraitAlias(..) => + match impl_source { + super::ImplSourceUserDefined(data) => confirm_impl_candidate(selcx, obligation, data), + super::ImplSourceGenerator(data) => confirm_generator_candidate(selcx, obligation, data), + super::ImplSourceClosure(data) => confirm_closure_candidate(selcx, obligation, data), + super::ImplSourceFnPointer(data) => confirm_fn_pointer_candidate(selcx, obligation, data), + super::ImplSourceDiscriminantKind(data) => { + confirm_discriminant_kind_candidate(selcx, obligation, data) + } + super::ImplSourceObject(_) => { + confirm_object_candidate(selcx, obligation, obligation_trait_ref) + } + super::ImplSourceAutoImpl(..) + | super::ImplSourceParam(..) + | super::ImplSourceBuiltin(..) + | super::ImplSourceTraitAlias(..) => // we don't create Select candidates with this kind of resolution { span_bug!( obligation.cause.span, "Cannot project an associated type from `{:?}`", - vtable + impl_source ) } } @@ -1180,20 +1214,17 @@ fn confirm_object_candidate<'cx, 'tcx>( }; let env_predicates = data .projection_bounds() - .map(|p| p.with_self_ty(selcx.tcx(), object_ty).to_predicate()) - .collect(); + .map(|p| p.with_self_ty(selcx.tcx(), object_ty).to_predicate(selcx.tcx())); 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 { - ty::Predicate::Projection(data) => { - if data.projection_def_id() == obligation.predicate.item_def_id { - Some(data) - } else { - None - } + let env_predicates = env_predicates.filter_map(|o| match o.predicate.kind() { + &ty::PredicateKind::Projection(data) + if data.projection_def_id() == obligation.predicate.item_def_id => + { + Some(data) } _ => None, }); @@ -1233,9 +1264,9 @@ fn confirm_object_candidate<'cx, 'tcx>( fn confirm_generator_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - vtable: VtableGeneratorData<'tcx, PredicateObligation<'tcx>>, + impl_source: ImplSourceGeneratorData<'tcx, PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let gen_sig = vtable.substs.as_generator().poly_sig(vtable.generator_def_id, selcx.tcx()); + let gen_sig = impl_source.substs.as_generator().poly_sig(); let Normalized { value: gen_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -1251,7 +1282,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, @@ -1279,16 +1310,47 @@ fn confirm_generator_candidate<'cx, 'tcx>( }); confirm_param_env_candidate(selcx, obligation, predicate) - .with_addl_obligations(vtable.nested) + .with_addl_obligations(impl_source.nested) .with_addl_obligations(obligations) } +fn confirm_discriminant_kind_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + _: ImplSourceDiscriminantKindData, +) -> 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>, - fn_pointer_vtable: VtableFnPointerData<'tcx, PredicateObligation<'tcx>>, + fn_pointer_impl_source: ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let fn_type = selcx.infcx().shallow_resolve(fn_pointer_vtable.fn_ty); + let fn_type = selcx.infcx().shallow_resolve(fn_pointer_impl_source.fn_ty); let sig = fn_type.fn_sig(selcx.tcx()); let Normalized { value: sig, obligations } = normalize_with_depth( selcx, @@ -1299,19 +1361,16 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( ); confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes) - .with_addl_obligations(fn_pointer_vtable.nested) + .with_addl_obligations(fn_pointer_impl_source.nested) .with_addl_obligations(obligations) } fn confirm_closure_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>, + impl_source: ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let tcx = selcx.tcx(); - let infcx = selcx.infcx(); - let closure_sig_ty = vtable.substs.as_closure().sig_ty(vtable.closure_def_id, tcx); - let closure_sig = infcx.shallow_resolve(closure_sig_ty).fn_sig(tcx); + let closure_sig = impl_source.substs.as_closure().sig(); let Normalized { value: closure_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -1326,7 +1385,7 @@ fn confirm_closure_candidate<'cx, 'tcx>( ); confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) - .with_addl_obligations(vtable.nested) + .with_addl_obligations(impl_source.nested) .with_addl_obligations(obligations) } @@ -1340,8 +1399,8 @@ 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 fn_once_output_def_id = tcx.require_lang_item(FnOnceOutputLangItem, None); let predicate = super::util::closure_trait_ref_and_return_type( tcx, @@ -1351,11 +1410,10 @@ fn confirm_callable_candidate<'cx, 'tcx>( flag, ) .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - Ident::with_dummy_span(rustc_hir::FN_OUTPUT_NAME), - ), + projection_ty: ty::ProjectionTy { + substs: trait_ref.substs, + item_def_id: fn_once_output_def_id, + }, ty: ret_type, }); @@ -1387,8 +1445,8 @@ fn confirm_param_env_candidate<'cx, 'tcx>( obligation, poly_cache_entry, e, ); debug!("confirm_param_env_candidate: {}", msg); - infcx.tcx.sess.delay_span_bug(obligation.cause.span, &msg); - Progress { ty: infcx.tcx.types.err, obligations: vec![] } + let err = infcx.tcx.ty_error_with_message(obligation.cause.span, &msg); + Progress { ty: err, obligations: vec![] } } } } @@ -1396,16 +1454,19 @@ fn confirm_param_env_candidate<'cx, 'tcx>( fn confirm_impl_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - impl_vtable: VtableImplData<'tcx, PredicateObligation<'tcx>>, + impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); - let VtableImplData { impl_def_id, substs, nested } = impl_vtable; + let ImplSourceUserDefinedData { impl_def_id, substs, nested } = impl_impl_source; let assoc_item_id = obligation.predicate.item_def_id; let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); let param_env = obligation.param_env; - let assoc_ty = assoc_ty_def(selcx, impl_def_id, assoc_item_id); + let assoc_ty = match assoc_ty_def(selcx, impl_def_id, assoc_item_id) { + Ok(assoc_ty) => assoc_ty, + Err(ErrorReported) => return Progress { ty: tcx.ty_error(), obligations: nested }, + }; if !assoc_ty.item.defaultness.has_value() { // This means that the impl is missing a definition for the @@ -1416,20 +1477,24 @@ fn confirm_impl_candidate<'cx, 'tcx>( "confirm_impl_candidate: no associated type {:?} for {:?}", assoc_ty.item.ident, obligation.predicate ); - return Progress { ty: tcx.types.err, obligations: nested }; + return Progress { ty: tcx.ty_error(), obligations: nested }; } + // If we're trying to normalize ` as X>::A` using + //`impl X for Vec { type A = Box; }`, then: + // + // * `obligation.predicate.substs` is `[Vec, S]` + // * `substs` is `[u32]` + // * `substs` ends up as `[u32, S]` 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 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) - } else { - tcx.type_of(assoc_ty.item.def_id) - }; + let substs = + translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node); + let ty = tcx.type_of(assoc_ty.item.def_id); if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { - tcx.sess - .delay_span_bug(DUMMY_SP, "impl item and trait item have different parameter counts"); - Progress { ty: tcx.types.err, obligations: nested } + let err = tcx.ty_error_with_message( + DUMMY_SP, + "impl item and trait item have different parameter counts", + ); + Progress { ty: err, obligations: nested } } else { Progress { ty: ty.subst(tcx, substs), obligations: nested } } @@ -1444,34 +1509,34 @@ fn assoc_ty_def( selcx: &SelectionContext<'_, '_>, impl_def_id: DefId, assoc_ty_def_id: DefId, -) -> specialization_graph::NodeItem { +) -> 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; let trait_def = tcx.trait_def(trait_def_id); // This function may be called while we are still building the - // specialization graph that is queried below (via TraidDef::ancestors()), + // specialization graph that is queried below (via TraitDef::ancestors()), // so, in order to avoid unnecessary infinite recursion, we manually look // for the associated item at the given impl. // If there is no such item in that impl, this function will fail with a // cycle error if the specialization graph is currently being built. let impl_node = specialization_graph::Node::Impl(impl_def_id); for item in impl_node.items(tcx) { - if matches!(item.kind, ty::AssocKind::Type | ty::AssocKind::OpaqueTy) + if matches!(item.kind, ty::AssocKind::Type) && tcx.hygienic_eq(item.ident, assoc_ty_name, trait_def_id) { - return 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) }, + }); } } - if let Some(assoc_item) = - trait_def.ancestors(tcx, impl_def_id).leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) - { - assoc_item + let ancestors = trait_def.ancestors(tcx, impl_def_id)?; + if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) { + Ok(assoc_item) } else { // This is saying that neither the trait nor // the impl contain a definition for this @@ -1486,14 +1551,14 @@ fn assoc_ty_def( crate trait ProjectionCacheKeyExt<'tcx>: Sized { fn from_poly_projection_predicate( selcx: &mut SelectionContext<'cx, 'tcx>, - predicate: &ty::PolyProjectionPredicate<'tcx>, + predicate: ty::PolyProjectionPredicate<'tcx>, ) -> Option; } impl<'tcx> ProjectionCacheKeyExt<'tcx> for ProjectionCacheKey<'tcx> { fn from_poly_projection_predicate( selcx: &mut SelectionContext<'cx, 'tcx>, - predicate: &ty::PolyProjectionPredicate<'tcx>, + predicate: ty::PolyProjectionPredicate<'tcx>, ) -> Option { let infcx = selcx.infcx(); // We don't do cross-snapshot caching of obligations with escaping regions, diff --git a/src/librustc_trait_selection/traits/query/dropck_outlives.rs b/src/librustc_trait_selection/traits/query/dropck_outlives.rs index 40a21b5a6ed4a..d07c95270e004 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>>; @@ -101,7 +101,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::Ref(..) | ty::Str | ty::Foreign(..) - | ty::Error => true, + | ty::Error(_) => true, // [T; N] and [T] have same properties as T. ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty), @@ -109,8 +109,8 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { // (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(def_id, ref substs) => { - substs.as_closure().upvar_tys(def_id, tcx).all(|t| trivial_dropck_outlives(tcx, t)) + ty::Closure(_, ref substs) => { + substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t)) } ty::Adt(def, _) => { @@ -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 adec2ddb25322..ca49ff5884f98 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 }) } } } @@ -96,11 +108,11 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { Reveal::UserFacing => ty, Reveal::All => { - let recursion_limit = *self.tcx().sess.recursion_limit.get(); - if self.anon_depth >= recursion_limit { + let recursion_limit = self.tcx().sess.recursion_limit(); + if !recursion_limit.value_within_limit(self.anon_depth) { let obligation = Obligation::with_depth( self.cause.clone(), - recursion_limit, + recursion_limit.0, self.param_env, ty, ); @@ -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 } @@ -133,7 +145,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { // handle normalization within binders because // otherwise we wind up a need to normalize when doing // trait matching (since you can have a trait - // obligation like `for<'a> T::B : Fn(&'a int)`), but + // obligation like `for<'a> T::B: Fn(&'a i32)`), but // we can't normalize with bound regions in scope. So // far now we just ignore binders but only normalize // if all bound regions are gone (and then we still @@ -169,12 +181,12 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { debug!("QueryNormalizer: result = {:#?}", result); debug!("QueryNormalizer: obligations = {:#?}", obligations); self.obligations.extend(obligations); - return result.normalized_ty; + result.normalized_ty } Err(_) => { self.error = true; - return ty; + 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..5c8719da14e6f 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::{self, ParamEnvAnd, 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 = (); @@ -15,7 +15,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { // `&T`, accounts for about 60% percentage of the predicates // we have to prove. No need to canonicalize and all that for // such cases. - if let Predicate::Trait(trait_ref, _) = key.value.predicate { + if let ty::PredicateKind::Trait(trait_ref, _) = key.value.predicate.kind() { if let Some(sized_def_id) = tcx.lang_items().sized_trait() { if trait_ref.def_id() == sized_def_id { if trait_ref.skip_binder().self_ty().is_trivially_sized(tcx) { 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 deleted file mode 100644 index ab3214d8d2d23..0000000000000 --- a/src/librustc_trait_selection/traits/select.rs +++ /dev/null @@ -1,3790 +0,0 @@ -// ignore-tidy-filelength - -//! Candidate selection. See the [rustc dev guide] for more information on how this works. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection - -use self::EvaluationResult::*; -use self::SelectionCandidate::*; - -use super::coherence::{self, Conflict}; -use super::project; -use super::project::{normalize_with_depth, normalize_with_depth_to}; -use super::util; -use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; -use super::wf; -use super::DerivedObligationCause; -use super::Selection; -use super::SelectionResult; -use super::TraitNotObjectSafe; -use super::TraitQueryMode; -use super::{BuiltinDerivedObligation, ImplDerivedObligation, ObligationCauseCode}; -use super::{Normalized, ProjectionCacheKey}; -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, -}; -use super::{ - VtableAutoImplData, VtableBuiltinData, VtableClosureData, 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_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_index::bit_set::GrowableBitSet; -use rustc_span::symbol::sym; -use rustc_target::spec::abi::Abi; - -use std::cell::{Cell, RefCell}; -use std::cmp; -use std::fmt::{self, Display}; -use std::iter; -use std::rc::Rc; - -pub use rustc::traits::select::*; - -pub struct SelectionContext<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, - - /// Freshener used specifically for entries on the obligation - /// stack. This ensures that all entries on the stack at one time - /// will have the same set of placeholder entries, which is - /// important for checking for trait bounds that recursively - /// require themselves. - freshener: TypeFreshener<'cx, 'tcx>, - - /// If `true`, indicates that the evaluation should be conservative - /// and consider the possibility of types outside this crate. - /// This comes up primarily when resolving ambiguity. Imagine - /// there is some trait reference `$0: Bar` where `$0` is an - /// inference variable. If `intercrate` is true, then we can never - /// say for sure that this reference is not implemented, even if - /// there are *no impls at all for `Bar`*, because `$0` could be - /// bound to some type that in a downstream crate that implements - /// `Bar`. This is the suitable mode for coherence. Elsewhere, - /// though, we set this to false, because we are only interested - /// in types that the user could actually have written --- in - /// other words, we consider `$0: Bar` to be unimplemented if - /// there is no type that the user could *actually name* that - /// would satisfy it. This avoids crippling inference, basically. - intercrate: bool, - - intercrate_ambiguity_causes: Option>, - - /// Controls whether or not to filter out negative impls when selecting. - /// This is used in librustdoc to distinguish between the lack of an impl - /// and a negative impl - allow_negative_impls: bool, - - /// The mode that trait queries run in, which informs our error handling - /// policy. In essence, canonicalized queries need their errors propagated - /// rather than immediately reported because we do not have accurate spans. - query_mode: TraitQueryMode, -} - -// A stack that walks back up the stack frame. -struct TraitObligationStack<'prev, 'tcx> { - obligation: &'prev TraitObligation<'tcx>, - - /// The trait ref from `obligation` but "freshened" with the - /// selection-context's freshener. Used to check for recursion. - fresh_trait_ref: ty::PolyTraitRef<'tcx>, - - /// Starts out equal to `depth` -- if, during evaluation, we - /// encounter a cycle, then we will set this flag to the minimum - /// depth of that cycle for all participants in the cycle. These - /// participants will then forego caching their results. This is - /// not the most efficient solution, but it addresses #60010. The - /// problem we are trying to prevent: - /// - /// - If you have `A: AutoTrait` requires `B: AutoTrait` and `C: NonAutoTrait` - /// - `B: AutoTrait` requires `A: AutoTrait` (coinductive cycle, ok) - /// - `C: NonAutoTrait` requires `A: AutoTrait` (non-coinductive cycle, not ok) - /// - /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait` - /// is `EvaluatedToOk`; this is because they were only considered - /// ok on the premise that if `A: AutoTrait` held, but we indeed - /// encountered a problem (later on) with `A: AutoTrait. So we - /// currently set a flag on the stack node for `B: AutoTrait` (as - /// well as the second instance of `A: AutoTrait`) to suppress - /// caching. - /// - /// This is a simple, targeted fix. A more-performant fix requires - /// deeper changes, but would permit more caching: we could - /// basically defer caching until we have fully evaluated the - /// tree, and then cache the entire tree at once. In any case, the - /// performance impact here shouldn't be so horrible: every time - /// this is hit, we do cache at least one trait, so we only - /// evaluate each member of a cycle up to N times, where N is the - /// length of the cycle. This means the performance impact is - /// bounded and we shouldn't have any terrible worst-cases. - reached_depth: Cell, - - previous: TraitObligationStackList<'prev, 'tcx>, - - /// The number of parent frames plus one (thus, the topmost frame has depth 1). - depth: usize, - - /// The depth-first number of this node in the search graph -- a - /// pre-order index. Basically, a freshly incremented counter. - dfn: usize, -} - -struct SelectionCandidateSet<'tcx> { - // A list of candidates that definitely apply to the current - // obligation (meaning: types unify). - vec: Vec>, - - // If `true`, then there were candidates that might or might - // not have applied, but we couldn't tell. This occurs when some - // of the input types are type variables, in which case there are - // various "builtin" rules that might or might not trigger. - ambiguous: bool, -} - -#[derive(PartialEq, Eq, Debug, Clone)] -struct EvaluatedCandidate<'tcx> { - candidate: SelectionCandidate<'tcx>, - evaluation: EvaluationResult, -} - -/// When does the builtin impl for `T: Trait` apply? -enum BuiltinImplConditions<'tcx> { - /// The impl is conditional on `T1, T2, ...: Trait`. - Where(ty::Binder>>), - /// There is no built-in impl. There may be some other - /// candidate (a where-clause or user-defined impl). - None, - /// It is unknown whether there is an impl. - Ambiguous, -} - -impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: false, - intercrate_ambiguity_causes: None, - allow_negative_impls: false, - query_mode: TraitQueryMode::Standard, - } - } - - pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: true, - intercrate_ambiguity_causes: None, - allow_negative_impls: false, - query_mode: TraitQueryMode::Standard, - } - } - - pub fn with_negative( - infcx: &'cx InferCtxt<'cx, 'tcx>, - allow_negative_impls: bool, - ) -> SelectionContext<'cx, 'tcx> { - debug!("with_negative({:?})", allow_negative_impls); - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: false, - intercrate_ambiguity_causes: None, - allow_negative_impls, - query_mode: TraitQueryMode::Standard, - } - } - - pub fn with_query_mode( - infcx: &'cx InferCtxt<'cx, 'tcx>, - query_mode: TraitQueryMode, - ) -> SelectionContext<'cx, 'tcx> { - debug!("with_query_mode({:?})", query_mode); - SelectionContext { - infcx, - freshener: infcx.freshener(), - intercrate: false, - intercrate_ambiguity_causes: None, - allow_negative_impls: false, - query_mode, - } - } - - /// Enables tracking of intercrate ambiguity causes. These are - /// used in coherence to give improved diagnostics. We don't do - /// this until we detect a coherence error because it can lead to - /// false overflow results (#47139) and because it costs - /// computation time. - pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { - assert!(self.intercrate); - assert!(self.intercrate_ambiguity_causes.is_none()); - self.intercrate_ambiguity_causes = Some(vec![]); - debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); - } - - /// Gets the intercrate ambiguity causes collected since tracking - /// was enabled and disables tracking at the same time. If - /// tracking is not enabled, just returns an empty vector. - pub fn take_intercrate_ambiguity_causes(&mut self) -> Vec { - assert!(self.intercrate); - self.intercrate_ambiguity_causes.take().unwrap_or(vec![]) - } - - pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'tcx> { - self.infcx - } - - pub fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - pub fn closure_typer(&self) -> &'cx InferCtxt<'cx, 'tcx> { - self.infcx - } - - /////////////////////////////////////////////////////////////////////////// - // Selection - // - // The selection phase tries to identify *how* an obligation will - // be resolved. For example, it will identify which impl or - // parameter bound is to be used. The process can be inconclusive - // if the self type in the obligation is not fully inferred. Selection - // can result in an error in one of two ways: - // - // 1. If no applicable impl or parameter bound can be found. - // 2. If the output type parameters in the obligation do not match - // those specified by the impl/bound. For example, if the obligation - // is `Vec: Iterable`, but the impl specifies - // `impl Iterable for Vec`, than an error would result. - - /// Attempts to satisfy the obligation. If successful, this will affect the surrounding - /// type environment by performing unification. - pub fn select( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> SelectionResult<'tcx, Selection<'tcx>> { - debug!("select({:?})", obligation); - debug_assert!(!obligation.predicate.has_escaping_bound_vars()); - - let pec = &ProvisionalEvaluationCache::default(); - let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); - - let candidate = match self.candidate_from_obligation(&stack) { - Err(SelectionError::Overflow) => { - // In standard mode, overflow must have been caught and reported - // earlier. - assert!(self.query_mode == TraitQueryMode::Canonical); - return Err(SelectionError::Overflow); - } - Err(e) => { - return Err(e); - } - Ok(None) => { - return Ok(None); - } - Ok(Some(candidate)) => candidate, - }; - - match self.confirm_candidate(obligation, candidate) { - Err(SelectionError::Overflow) => { - assert!(self.query_mode == TraitQueryMode::Canonical); - Err(SelectionError::Overflow) - } - Err(e) => Err(e), - Ok(candidate) => Ok(Some(candidate)), - } - } - - /////////////////////////////////////////////////////////////////////////// - // EVALUATION - // - // Tests whether an obligation can be selected or whether an impl - // can be applied to particular types. It skips the "confirmation" - // step and hence completely ignores output type parameters. - // - // The result is "true" if the obligation *may* hold and "false" if - // we can be sure it does not. - - /// Evaluates whether the obligation `obligation` can be satisfied (by any means). - pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool { - debug!("predicate_may_hold_fatal({:?})", obligation); - - // This fatal query is a stopgap that should only be used in standard mode, - // where we do not expect overflow to be propagated. - assert!(self.query_mode == TraitQueryMode::Standard); - - self.evaluate_root_obligation(obligation) - .expect("Overflow should be caught earlier in standard query mode") - .may_apply() - } - - /// Evaluates whether the obligation `obligation` can be satisfied - /// and returns an `EvaluationResult`. This is meant for the - /// *initial* call. - pub fn evaluate_root_obligation( - &mut self, - obligation: &PredicateObligation<'tcx>, - ) -> Result { - self.evaluation_probe(|this| { - this.evaluate_predicate_recursively( - TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), - obligation.clone(), - ) - }) - } - - fn evaluation_probe( - &mut self, - op: impl FnOnce(&mut Self) -> Result, - ) -> Result { - self.infcx.probe(|snapshot| -> Result { - let result = op(self)?; - match self.infcx.region_constraints_added_in_snapshot(snapshot) { - None => Ok(result), - Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)), - } - }) - } - - /// Evaluates the predicates in `predicates` recursively. Note that - /// this applies projections in the predicates, and therefore - /// is run within an inference probe. - fn evaluate_predicates_recursively<'o, I>( - &mut self, - stack: TraitObligationStackList<'o, 'tcx>, - predicates: I, - ) -> Result - where - I: IntoIterator>, - { - let mut result = EvaluatedToOk; - for obligation in predicates { - let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; - debug!("evaluate_predicate_recursively({:?}) = {:?}", obligation, eval); - if let EvaluatedToErr = eval { - // fast-path - EvaluatedToErr is the top of the lattice, - // so we don't need to look on the other predicates. - return Ok(EvaluatedToErr); - } else { - result = cmp::max(result, eval); - } - } - Ok(result) - } - - fn evaluate_predicate_recursively<'o>( - &mut self, - previous_stack: TraitObligationStackList<'o, 'tcx>, - obligation: PredicateObligation<'tcx>, - ) -> Result { - debug!( - "evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})", - previous_stack.head(), - obligation - ); - - // `previous_stack` stores a `TraitObligatiom`, while `obligation` is - // a `PredicateObligation`. These are distinct types, so we can't - // use any `Option` combinator method that would force them to be - // the same. - match previous_stack.head() { - Some(h) => self.check_recursion_limit(&obligation, h.obligation)?, - None => self.check_recursion_limit(&obligation, &obligation)?, - } - - match obligation.predicate { - ty::Predicate::Trait(ref t, _) => { - debug_assert!(!t.has_escaping_bound_vars()); - let obligation = obligation.with(t.clone()); - self.evaluate_trait_predicate_recursively(previous_stack, obligation) - } - - ty::Predicate::Subtype(ref p) => { - // Does this code ever run? - match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { - Some(Ok(InferOk { mut obligations, .. })) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively( - previous_stack, - obligations.into_iter(), - ) - } - Some(Err(_)) => Ok(EvaluatedToErr), - None => Ok(EvaluatedToAmbig), - } - } - - ty::Predicate::WellFormed(ty) => match wf::obligations( - self.infcx, - obligation.param_env, - obligation.cause.body_id, - ty, - obligation.cause.span, - ) { - Some(mut obligations) => { - self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively(previous_stack, obligations.into_iter()) - } - None => Ok(EvaluatedToAmbig), - }, - - ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) => { - // We do not consider region relationships when evaluating trait matches. - Ok(EvaluatedToOkModuloRegions) - } - - ty::Predicate::ObjectSafe(trait_def_id) => { - if self.tcx().is_object_safe(trait_def_id) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } - } - - ty::Predicate::Projection(ref data) => { - let project_obligation = obligation.with(data.clone()); - match project::poly_project_and_unify_type(self, &project_obligation) { - Ok(Some(mut subobligations)) => { - self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); - let result = self.evaluate_predicates_recursively( - previous_stack, - subobligations.into_iter(), - ); - if let Some(key) = - ProjectionCacheKey::from_poly_projection_predicate(self, data) - { - self.infcx.inner.borrow_mut().projection_cache.complete(key); - } - result - } - Ok(None) => Ok(EvaluatedToAmbig), - Err(_) => Ok(EvaluatedToErr), - } - } - - ty::Predicate::ClosureKind(closure_def_id, closure_substs, kind) => { - match self.infcx.closure_kind(closure_def_id, closure_substs) { - Some(closure_kind) => { - if closure_kind.extends(kind) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } - } - None => Ok(EvaluatedToAmbig), - } - } - - ty::Predicate::ConstEvaluatable(def_id, substs) => { - match self.tcx().const_eval_resolve( - obligation.param_env, - def_id, - substs, - None, - None, - ) { - Ok(_) => Ok(EvaluatedToOk), - Err(_) => Ok(EvaluatedToErr), - } - } - } - } - - fn evaluate_trait_predicate_recursively<'o>( - &mut self, - previous_stack: TraitObligationStackList<'o, 'tcx>, - mut obligation: TraitObligation<'tcx>, - ) -> Result { - debug!("evaluate_trait_predicate_recursively({:?})", obligation); - - if !self.intercrate - && obligation.is_global() - && obligation.param_env.caller_bounds.iter().all(|bound| bound.needs_subst()) - { - // If a param env has no global bounds, global obligations do not - // depend on its particular value in order to work, so we can clear - // out the param env and get better caching. - debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation); - obligation.param_env = obligation.param_env.without_caller_bounds(); - } - - let stack = self.push_stack(previous_stack, &obligation); - let fresh_trait_ref = stack.fresh_trait_ref; - if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) { - debug!("CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); - return Ok(result); - } - - if let Some(result) = stack.cache().get_provisional(fresh_trait_ref) { - debug!("PROVISIONAL CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); - stack.update_reached_depth(stack.cache().current_reached_depth()); - return Ok(result); - } - - // Check if this is a match for something already on the - // stack. If so, we don't want to insert the result into the - // main cache (it is cycle dependent) nor the provisional - // cache (which is meant for things that have completed but - // for a "backedge" -- this result *is* the backedge). - if let Some(cycle_result) = self.check_evaluation_cycle(&stack) { - return Ok(cycle_result); - } - - let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack)); - let result = result?; - - if !result.must_apply_modulo_regions() { - stack.cache().on_failure(stack.dfn); - } - - let reached_depth = stack.reached_depth.get(); - if reached_depth >= stack.depth { - debug!("CACHE MISS: EVAL({:?})={:?}", fresh_trait_ref, result); - self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result); - - stack.cache().on_completion(stack.depth, |fresh_trait_ref, provisional_result| { - self.insert_evaluation_cache( - obligation.param_env, - fresh_trait_ref, - dep_node, - provisional_result.max(result), - ); - }); - } else { - debug!("PROVISIONAL: {:?}={:?}", fresh_trait_ref, result); - debug!( - "evaluate_trait_predicate_recursively: caching provisionally because {:?} \ - is a cycle participant (at depth {}, reached depth {})", - fresh_trait_ref, stack.depth, reached_depth, - ); - - stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_ref, result); - } - - Ok(result) - } - - /// If there is any previous entry on the stack that precisely - /// matches this obligation, then we can assume that the - /// obligation is satisfied for now (still all other conditions - /// must be met of course). One obvious case this comes up is - /// marker traits like `Send`. Think of a linked list: - /// - /// struct List { data: T, next: Option>> } - /// - /// `Box>` will be `Send` if `T` is `Send` and - /// `Option>>` is `Send`, and in turn - /// `Option>>` is `Send` if `Box>` is - /// `Send`. - /// - /// Note that we do this comparison using the `fresh_trait_ref` - /// fields. Because these have all been freshened using - /// `self.freshener`, we can be sure that (a) this will not - /// affect the inferencer state and (b) that if we see two - /// fresh regions with the same index, they refer to the same - /// unbound type variable. - fn check_evaluation_cycle( - &mut self, - stack: &TraitObligationStack<'_, 'tcx>, - ) -> Option { - if let Some(cycle_depth) = stack - .iter() - .skip(1) // Skip top-most frame. - .find(|prev| { - stack.obligation.param_env == prev.obligation.param_env - && stack.fresh_trait_ref == prev.fresh_trait_ref - }) - .map(|stack| stack.depth) - { - debug!( - "evaluate_stack({:?}) --> recursive at depth {}", - stack.fresh_trait_ref, cycle_depth, - ); - - // If we have a stack like `A B C D E A`, where the top of - // the stack is the final `A`, then this will iterate over - // `A, E, D, C, B` -- i.e., all the participants apart - // from the cycle head. We mark them as participating in a - // cycle. This suppresses caching for those nodes. See - // `in_cycle` field for more details. - stack.update_reached_depth(cycle_depth); - - // Subtle: when checking for a coinductive cycle, we do - // not compare using the "freshened trait refs" (which - // have erased regions) but rather the fully explicit - // trait refs. This is important because it's only a cycle - // if the regions match exactly. - let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth); - let cycle = cycle.map(|stack| { - ty::Predicate::Trait(stack.obligation.predicate, hir::Constness::NotConst) - }); - if self.coinductive_match(cycle) { - debug!("evaluate_stack({:?}) --> recursive, coinductive", stack.fresh_trait_ref); - Some(EvaluatedToOk) - } else { - debug!("evaluate_stack({:?}) --> recursive, inductive", stack.fresh_trait_ref); - Some(EvaluatedToRecur) - } - } else { - None - } - } - - fn evaluate_stack<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> Result { - // In intercrate mode, whenever any of the types are unbound, - // there can always be an impl. Even if there are no impls in - // this crate, perhaps the type would be unified with - // something from another crate that does provide an impl. - // - // In intra mode, we must still be conservative. The reason is - // that we want to avoid cycles. Imagine an impl like: - // - // impl Eq for Vec - // - // and a trait reference like `$0 : Eq` where `$0` is an - // unbound variable. When we evaluate this trait-reference, we - // will unify `$0` with `Vec<$1>` (for some fresh variable - // `$1`), on the condition that `$1 : Eq`. We will then wind - // up with many candidates (since that are other `Eq` impls - // that apply) and try to winnow things down. This results in - // a recursive evaluation that `$1 : Eq` -- as you can - // imagine, this is just where we started. To avoid that, we - // check for unbound variables and return an ambiguous (hence possible) - // match if we've seen this trait before. - // - // This suffices to allow chains like `FnMut` implemented in - // terms of `Fn` etc, but we could probably make this more - // precise still. - let unbound_input_types = - stack.fresh_trait_ref.skip_binder().input_types().any(|ty| ty.is_fresh()); - // This check was an imperfect workaround for a bug in the old - // intercrate mode; it should be removed when that goes away. - if unbound_input_types && self.intercrate { - debug!( - "evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous", - stack.fresh_trait_ref - ); - // Heuristics: show the diagnostics when there are no candidates in crate. - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - if let Ok(candidate_set) = self.assemble_candidates(stack) { - if !candidate_set.ambiguous && candidate_set.vec.is_empty() { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let cause = IntercrateAmbiguityCause::DownstreamCrate { - trait_desc: trait_ref.print_only_trait_path().to_string(), - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, - }; - debug!("evaluate_stack: pushing cause = {:?}", cause); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); - } - } - } - return Ok(EvaluatedToAmbig); - } - if unbound_input_types - && stack.iter().skip(1).any(|prev| { - stack.obligation.param_env == prev.obligation.param_env - && self.match_fresh_trait_refs( - &stack.fresh_trait_ref, - &prev.fresh_trait_ref, - prev.obligation.param_env, - ) - }) - { - debug!( - "evaluate_stack({:?}) --> unbound argument, recursive --> giving up", - stack.fresh_trait_ref - ); - return Ok(EvaluatedToUnknown); - } - - match self.candidate_from_obligation(stack) { - Ok(Some(c)) => self.evaluate_candidate(stack, &c), - Ok(None) => Ok(EvaluatedToAmbig), - Err(Overflow) => Err(OverflowError), - Err(..) => Ok(EvaluatedToErr), - } - } - - /// For defaulted traits, we use a co-inductive strategy to solve, so - /// that recursion is ok. This routine returns `true` if the top of the - /// stack (`cycle[0]`): - /// - /// - is a defaulted trait, - /// - it also appears in the backtrace at some position `X`, - /// - all the predicates at positions `X..` between `X` and the top are - /// also defaulted traits. - pub fn coinductive_match(&mut self, cycle: I) -> bool - where - I: Iterator>, - { - let mut cycle = cycle; - cycle.all(|predicate| self.coinductive_predicate(predicate)) - } - - fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { - let result = match predicate { - ty::Predicate::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()), - _ => false, - }; - debug!("coinductive_predicate({:?}) = {:?}", predicate, result); - result - } - - /// Further evaluates `candidate` to decide whether all type parameters match and whether nested - /// obligations are met. Returns whether `candidate` remains viable after this further - /// scrutiny. - fn evaluate_candidate<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - candidate: &SelectionCandidate<'tcx>, - ) -> Result { - debug!( - "evaluate_candidate: depth={} candidate={:?}", - stack.obligation.recursion_depth, candidate - ); - let result = self.evaluation_probe(|this| { - let candidate = (*candidate).clone(); - match this.confirm_candidate(stack.obligation, candidate) { - Ok(selection) => this.evaluate_predicates_recursively( - stack.list(), - selection.nested_obligations().into_iter(), - ), - Err(..) => Ok(EvaluatedToErr), - } - })?; - debug!( - "evaluate_candidate: depth={} result={:?}", - stack.obligation.recursion_depth, result - ); - Ok(result) - } - - fn check_evaluation_cache( - &self, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Option { - let tcx = self.tcx(); - if self.can_use_global_caches(param_env) { - let cache = tcx.evaluation_cache.hashmap.borrow(); - if let Some(cached) = cache.get(¶m_env.and(trait_ref)) { - return Some(cached.get(tcx)); - } - } - self.infcx - .evaluation_cache - .hashmap - .borrow() - .get(¶m_env.and(trait_ref)) - .map(|v| v.get(tcx)) - } - - fn insert_evaluation_cache( - &mut self, - param_env: ty::ParamEnv<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - dep_node: DepNodeIndex, - result: EvaluationResult, - ) { - // Avoid caching results that depend on more than just the trait-ref - // - the stack can create recursion. - if result.is_stack_dependent() { - return; - } - - if self.can_use_global_caches(param_env) { - if !trait_ref.has_local_value() { - debug!( - "insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", - trait_ref, result, - ); - // This may overwrite the cache with the same value - // FIXME: Due to #50507 this overwrites the different values - // This should be changed to use HashMapExt::insert_same - // when that is fixed - self.tcx() - .evaluation_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); - return; - } - } - - debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,); - self.infcx - .evaluation_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); - } - - /// For various reasons, it's possible for a subobligation - /// to have a *lower* recursion_depth than the obligation used to create it. - /// Projection sub-obligations may be returned from the projection cache, - /// which results in obligations with an 'old' `recursion_depth`. - /// Additionally, methods like `wf::obligations` and - /// `InferCtxt.subtype_predicate` produce subobligations without - /// taking in a 'parent' depth, causing the generated subobligations - /// to have a `recursion_depth` of `0`. - /// - /// To ensure that obligation_depth never decreasees, we force all subobligations - /// to have at least the depth of the original obligation. - fn add_depth>>( - &self, - it: I, - min_depth: usize, - ) { - it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); - } - - /// Checks that the recursion limit has not been exceeded. - /// - /// The weird return type of this function allows it to be used with the `try` (`?`) - /// operator within certain functions. - fn check_recursion_limit, V: Display + TypeFoldable<'tcx>>( - &self, - obligation: &Obligation<'tcx, T>, - error_obligation: &Obligation<'tcx, V>, - ) -> Result<(), OverflowError> { - let recursion_limit = *self.infcx.tcx.sess.recursion_limit.get(); - if obligation.recursion_depth >= recursion_limit { - match self.query_mode { - TraitQueryMode::Standard => { - self.infcx().report_overflow_error(error_obligation, true); - } - TraitQueryMode::Canonical => { - return Err(OverflowError); - } - } - } - Ok(()) - } - - /////////////////////////////////////////////////////////////////////////// - // CANDIDATE ASSEMBLY - // - // The selection process begins by examining all in-scope impls, - // caller obligations, and so forth and assembling a list of - // candidates. See the [rustc dev guide] for more details. - // - // [rustc dev guide]: - // https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly - - fn candidate_from_obligation<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - // Watch out for overflow. This intentionally bypasses (and does - // not update) the cache. - self.check_recursion_limit(&stack.obligation, &stack.obligation)?; - - // Check the cache. Note that we freshen the trait-ref - // 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()); - debug!( - "candidate_from_obligation(cache_fresh_trait_pred={:?}, obligation={:?})", - cache_fresh_trait_pred, stack - ); - debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); - - if let Some(c) = - self.check_candidate_cache(stack.obligation.param_env, &cache_fresh_trait_pred) - { - debug!("CACHE HIT: SELECT({:?})={:?}", cache_fresh_trait_pred, c); - return c; - } - - // If no match, compute result and insert into cache. - // - // FIXME(nikomatsakis) -- this cache is not taking into - // account cycles that may have occurred in forming the - // candidate. I don't know of any specific problems that - // result but it seems awfully suspicious. - let (candidate, dep_node) = - self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); - - debug!("CACHE MISS: SELECT({:?})={:?}", cache_fresh_trait_pred, candidate); - self.insert_candidate_cache( - stack.obligation.param_env, - cache_fresh_trait_pred, - dep_node, - candidate.clone(), - ); - candidate - } - - fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) - where - OP: FnOnce(&mut Self) -> R, - { - let (result, dep_node) = - self.tcx().dep_graph.with_anon_task(DepKind::TraitSelect, || op(self)); - self.tcx().dep_graph.read_index(dep_node); - (result, dep_node) - } - - // Treat negative impls as unimplemented, and reservation impls as ambiguity. - fn filter_negative_and_reservation_impls( - &mut self, - candidate: SelectionCandidate<'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if let ImplCandidate(def_id) = candidate { - let tcx = self.tcx(); - match tcx.impl_polarity(def_id) { - ty::ImplPolarity::Negative if !self.allow_negative_impls => { - return Err(Unimplemented); - } - ty::ImplPolarity::Reservation => { - if let Some(intercrate_ambiguity_clauses) = - &mut self.intercrate_ambiguity_causes - { - let attrs = tcx.get_attrs(def_id); - let attr = attr::find_by_name(&attrs, sym::rustc_reservation_impl); - let value = attr.and_then(|a| a.value_str()); - if let Some(value) = value { - debug!( - "filter_negative_and_reservation_impls: \ - reservation impl ambiguity on {:?}", - def_id - ); - intercrate_ambiguity_clauses.push( - IntercrateAmbiguityCause::ReservationImpl { - message: value.to_string(), - }, - ); - } - } - return Ok(None); - } - _ => {} - }; - } - Ok(Some(candidate)) - } - - fn candidate_from_obligation_no_cache<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if stack.obligation.predicate.references_error() { - // If we encounter a `Error`, we generally prefer the - // most "optimistic" result in response -- that is, the - // one least likely to report downstream errors. But - // because this routine is shared by coherence and by - // trait selection, there isn't an obvious "right" choice - // here in that respect, so we opt to just return - // ambiguity and let the upstream clients sort it out. - return Ok(None); - } - - if let Some(conflict) = self.is_knowable(stack) { - debug!("coherence stage: not knowable"); - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - // Heuristics: show the diagnostics when there are no candidates in crate. - if let Ok(candidate_set) = self.assemble_candidates(stack) { - let mut no_candidates_apply = true; - { - let evaluated_candidates = - candidate_set.vec.iter().map(|c| self.evaluate_candidate(stack, &c)); - - for ec in evaluated_candidates { - match ec { - Ok(c) => { - if c.may_apply() { - no_candidates_apply = false; - break; - } - } - Err(e) => return Err(e.into()), - } - } - } - - if !candidate_set.ambiguous && no_candidates_apply { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }; - let cause = if let Conflict::Upstream = conflict { - IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } - } else { - IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } - }; - debug!("evaluate_stack: pushing cause = {:?}", cause); - self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); - } - } - } - return Ok(None); - } - - let candidate_set = self.assemble_candidates(stack)?; - - if candidate_set.ambiguous { - debug!("candidate set contains ambig"); - return Ok(None); - } - - let mut candidates = candidate_set.vec; - - debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - // At this point, we know that each of the entries in the - // candidate set is *individually* applicable. Now we have to - // figure out if they contain mutual incompatibilities. This - // frequently arises if we have an unconstrained input type -- - // for example, we are looking for `$0: Eq` where `$0` is some - // unconstrained type variable. In that case, we'll get a - // candidate which assumes $0 == int, one that assumes `$0 == - // usize`, etc. This spells an ambiguity. - - // If there is more than one candidate, first winnow them down - // by considering extra conditions (nested obligations and so - // forth). We don't winnow if there is exactly one - // candidate. This is a relatively minor distinction but it - // can lead to better inference and error-reporting. An - // example would be if there was an impl: - // - // impl Vec { fn push_clone(...) { ... } } - // - // and we were to see some code `foo.push_clone()` where `boo` - // is a `Vec` and `Bar` does not implement `Clone`. If - // we were to winnow, we'd wind up with zero candidates. - // Instead, we select the right impl now but report "`Bar` does - // not implement `Clone`". - if candidates.len() == 1 { - return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); - } - - // Winnow, but record the exact outcome of evaluation, which - // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates - .into_iter() - .map(|c| match self.evaluate_candidate(stack, &c) { - Ok(eval) if eval.may_apply() => { - Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) - } - Ok(_) => Ok(None), - Err(OverflowError) => Err(Overflow), - }) - .flat_map(Result::transpose) - .collect::, _>>()?; - - debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); - - let needs_infer = stack.obligation.predicate.needs_infer(); - - // If there are STILL multiple candidates, we can further - // reduce the list by dropping duplicates -- including - // resolving specializations. - if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { - self.candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - needs_infer, - ) - }); - if is_dup { - debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - candidates.swap_remove(i); - } else { - debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); - i += 1; - - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - if i > 1 { - debug!("multiple matches, ambig"); - return Ok(None); - } - } - } - } - - // If there are *NO* candidates, then there are no impls -- - // that we know of, anyway. Note that in the case where there - // are unbound type variables within the obligation, it might - // be the case that you could still satisfy the obligation - // from another crate by instantiating the type variables with - // a type from another crate that does have an impl. This case - // is checked for in `evaluate_stack` (and hence users - // who might care about this case, like coherence, should use - // that function). - if candidates.is_empty() { - return Err(Unimplemented); - } - - // Just one candidate left. - self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) - } - - fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { - debug!("is_knowable(intercrate={:?})", self.intercrate); - - if !self.intercrate { - return None; - } - - let obligation = &stack.obligation; - let predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); - - // Okay to skip binder because of the nature of the - // trait-ref-is-knowable check, which does not care about - // bound regions. - let trait_ref = predicate.skip_binder().trait_ref; - - coherence::trait_ref_is_knowable(self.tcx(), trait_ref) - } - - /// Returns `true` if the global caches can be used. - /// Do note that if the type itself is not in the - /// global tcx, the local caches will be used. - fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool { - // If there are any e.g. 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() { - return false; - } - - // Avoid using the master cache during coherence and just rely - // on the local cache. This effectively disables caching - // during coherence. It is really just a simplification to - // avoid us having to fear that coherence results "pollute" - // the master cache. Since coherence executes pretty quickly, - // it's not worth going to more trouble to increase the - // hit-rate, I don't think. - if self.intercrate { - return false; - } - - // Otherwise, we can use the global cache. - true - } - - fn check_candidate_cache( - &mut self, - param_env: ty::ParamEnv<'tcx>, - cache_fresh_trait_pred: &ty::PolyTraitPredicate<'tcx>, - ) -> Option>> { - let tcx = self.tcx(); - let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref; - if self.can_use_global_caches(param_env) { - let cache = tcx.selection_cache.hashmap.borrow(); - if let Some(cached) = cache.get(¶m_env.and(*trait_ref)) { - return Some(cached.get(tcx)); - } - } - self.infcx - .selection_cache - .hashmap - .borrow() - .get(¶m_env.and(*trait_ref)) - .map(|v| v.get(tcx)) - } - - /// Determines whether can we safely cache the result - /// of selecting an obligation. This is almost always `true`, - /// except when dealing with certain `ParamCandidate`s. - /// - /// Ordinarily, a `ParamCandidate` will contain no inference variables, - /// since it was usually produced directly from a `DefId`. However, - /// certain cases (currently only librustdoc's blanket impl finder), - /// a `ParamEnv` may be explicitly constructed with inference types. - /// When this is the case, we do *not* want to cache the resulting selection - /// candidate. This is due to the fact that it might not always be possible - /// to equate the obligation's trait ref and the candidate's trait ref, - /// if more constraints end up getting added to an inference variable. - /// - /// Because of this, we always want to re-run the full selection - /// process for our obligation the next time we see it, since - /// we might end up picking a different `SelectionCandidate` (or none at all). - fn can_cache_candidate( - &self, - result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, - ) -> bool { - match result { - Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => { - !trait_ref.skip_binder().input_types().any(|t| t.walk().any(|t_| t_.is_ty_infer())) - } - _ => true, - } - } - - fn insert_candidate_cache( - &mut self, - param_env: ty::ParamEnv<'tcx>, - cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, - dep_node: DepNodeIndex, - candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, - ) { - let tcx = self.tcx(); - let trait_ref = cache_fresh_trait_pred.skip_binder().trait_ref; - - if !self.can_cache_candidate(&candidate) { - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?} -\ - candidate is not cacheable", - trait_ref, candidate - ); - return; - } - - if self.can_use_global_caches(param_env) { - if let Err(Overflow) = candidate { - // Don't cache overflow globally; we only produce this in certain modes. - } else if !trait_ref.has_local_value() { - if !candidate.has_local_value() { - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?}) global", - trait_ref, candidate, - ); - // This may overwrite the cache with the same value. - tcx.selection_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); - return; - } - } - } - - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?}) local", - trait_ref, candidate, - ); - self.infcx - .selection_cache - .hashmap - .borrow_mut() - .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); - } - - fn assemble_candidates<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> Result, SelectionError<'tcx>> { - let TraitObligationStack { obligation, .. } = *stack; - let obligation = &Obligation { - param_env: obligation.param_env, - cause: obligation.cause.clone(), - recursion_depth: obligation.recursion_depth, - predicate: self.infcx().resolve_vars_if_possible(&obligation.predicate), - }; - - if obligation.predicate.skip_binder().self_ty().is_ty_var() { - // Self is a type variable (e.g., `_: AsRef`). - // - // This is somewhat problematic, as the current scheme can't really - // handle it turning to be a projection. This does end up as truly - // ambiguous in most cases anyway. - // - // Take the fast path out - this also improves - // performance by preventing assemble_candidates_from_impls from - // matching every impl for this trait. - return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true }); - } - - let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; - - self.assemble_candidates_for_trait_alias(obligation, &mut candidates)?; - - // Other bounds. Consider both in-scope bounds from fn decl - // and applicable impls. There is a certain set of precedence rules here. - let def_id = obligation.predicate.def_id(); - let lang_items = self.tcx().lang_items(); - - if lang_items.copy_trait() == Some(def_id) { - debug!("obligation self ty is {:?}", obligation.predicate.skip_binder().self_ty()); - - // User-defined copy impls are permitted, but only for - // structs and enums. - self.assemble_candidates_from_impls(obligation, &mut candidates)?; - - // 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.sized_trait() == Some(def_id) { - // Sized is never implementable by end-users, it is - // always automatically computed. - let sized_conditions = self.sized_conditions(obligation); - self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?; - } else if lang_items.unsize_trait() == Some(def_id) { - self.assemble_candidates_for_unsizing(obligation, &mut candidates); - } else { - if lang_items.clone_trait() == Some(def_id) { - // Same builtin conditions as `Copy`, i.e., every type which has builtin support - // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` - // types have builtin support for `Clone`. - let clone_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; - } - - self.assemble_generator_candidates(obligation, &mut candidates)?; - self.assemble_closure_candidates(obligation, &mut candidates)?; - self.assemble_fn_pointer_candidates(obligation, &mut candidates)?; - self.assemble_candidates_from_impls(obligation, &mut candidates)?; - self.assemble_candidates_from_object_ty(obligation, &mut candidates); - } - - self.assemble_candidates_from_projected_tys(obligation, &mut candidates); - self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; - // Auto implementations have lower priority, so we only - // consider triggering a default if there is no other impl that can apply. - if candidates.vec.is_empty() { - self.assemble_candidates_from_auto_impls(obligation, &mut candidates)?; - } - debug!("candidate list size: {}", candidates.vec.len()); - Ok(candidates) - } - - fn assemble_candidates_from_projected_tys( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) { - debug!("assemble_candidates_for_projected_tys({:?})", obligation); - - // Before we go into the whole placeholder thing, just - // quickly check if the self-type is a projection at all. - match obligation.predicate.skip_binder().trait_ref.self_ty().kind { - ty::Projection(_) | ty::Opaque(..) => {} - ty::Infer(ty::TyVar(_)) => { - span_bug!( - obligation.cause.span, - "Self=_ should have been handled by assemble_candidates" - ); - } - _ => return, - } - - let result = self.infcx.probe(|snapshot| { - self.match_projection_obligation_against_definition_bounds(obligation, snapshot) - }); - - if result { - candidates.vec.push(ProjectionCandidate); - } - } - - fn match_projection_obligation_against_definition_bounds( - &mut self, - obligation: &TraitObligation<'tcx>, - snapshot: &CombinedSnapshot<'_, 'tcx>, - ) -> bool { - let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); - let (placeholder_trait_predicate, placeholder_map) = - self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); - debug!( - "match_projection_obligation_against_definition_bounds: \ - placeholder_trait_predicate={:?}", - placeholder_trait_predicate, - ); - - let (def_id, substs) = match placeholder_trait_predicate.trait_ref.self_ty().kind { - ty::Projection(ref data) => (data.trait_ref(self.tcx()).def_id, data.substs), - ty::Opaque(def_id, substs) => (def_id, substs), - _ => { - span_bug!( - obligation.cause.span, - "match_projection_obligation_against_definition_bounds() called \ - but self-ty is not a projection: {:?}", - placeholder_trait_predicate.trait_ref.self_ty() - ); - } - }; - debug!( - "match_projection_obligation_against_definition_bounds: \ - def_id={:?}, substs={:?}", - def_id, substs - ); - - let predicates_of = self.tcx().predicates_of(def_id); - let bounds = predicates_of.instantiate(self.tcx(), substs); - debug!( - "match_projection_obligation_against_definition_bounds: \ - bounds={:?}", - bounds - ); - - let elaborated_predicates = util::elaborate_predicates(self.tcx(), bounds.predicates); - 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(), - &placeholder_map, - snapshot, - ) - }) - }); - - debug!( - "match_projection_obligation_against_definition_bounds: \ - matching_bound={:?}", - matching_bound - ); - match matching_bound { - None => false, - Some(bound) => { - // Repeat the successful match, if any, this time outside of a probe. - let result = self.match_projection( - obligation, - bound, - placeholder_trait_predicate.trait_ref.clone(), - &placeholder_map, - snapshot, - ); - - assert!(result); - true - } - } - } - - fn match_projection( - &mut self, - obligation: &TraitObligation<'tcx>, - trait_bound: ty::PolyTraitRef<'tcx>, - placeholder_trait_ref: ty::TraitRef<'tcx>, - placeholder_map: &PlaceholderMap<'tcx>, - snapshot: &CombinedSnapshot<'_, 'tcx>, - ) -> bool { - debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); - self.infcx - .at(&obligation.cause, obligation.param_env) - .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) - .is_ok() - && self.infcx.leak_check(false, placeholder_map, snapshot).is_ok() - } - - /// Given an obligation like ``, searches the obligations that the caller - /// supplied to find out whether it is listed among them. - /// - /// Never affects the inference environment. - fn assemble_candidates_from_caller_bounds<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - debug!("assemble_candidates_from_caller_bounds({:?})", stack.obligation); - - let all_bounds = stack - .obligation - .param_env - .caller_bounds - .iter() - .filter_map(|o| o.to_opt_poly_trait_ref()); - - // Micro-optimization: filter out predicates relating to different traits. - let matching_bounds = - all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id()); - - // 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())?; - if wc.may_apply() { - param_candidates.push(ParamCandidate(bound)); - } - } - - candidates.vec.extend(param_candidates); - - Ok(()) - } - - fn evaluate_where_clause<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - where_clause_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result { - self.evaluation_probe(|this| { - match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { - Ok(obligations) => { - this.evaluate_predicates_recursively(stack.list(), obligations.into_iter()) - } - Err(()) => Ok(EvaluatedToErr), - } - }) - } - - fn assemble_generator_candidates( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) { - return Ok(()); - } - - // Okay to skip binder because the substs on generator types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = *obligation.self_ty().skip_binder(); - match self_ty.kind { - ty::Generator(..) => { - debug!( - "assemble_generator_candidates: self_ty={:?} obligation={:?}", - self_ty, obligation - ); - - candidates.vec.push(GeneratorCandidate); - } - ty::Infer(ty::TyVar(_)) => { - debug!("assemble_generator_candidates: ambiguous self-type"); - candidates.ambiguous = true; - } - _ => {} - } - - Ok(()) - } - - /// Checks for the artificial impl that the compiler will create for an obligation like `X : - /// FnMut<..>` where `X` is a closure type. - /// - /// Note: the type parameters on a closure candidate are modeled as *output* type - /// parameters and hence do not affect whether this trait is a match or not. They will be - /// unified during the confirmation step. - fn assemble_closure_candidates( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - let kind = match self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) { - Some(k) => k, - None => { - return Ok(()); - } - }; - - // Okay to skip binder because the substs on closure types never - // touch bound regions, they just capture the in-scope - // type/region parameters - match obligation.self_ty().skip_binder().kind { - ty::Closure(closure_def_id, closure_substs) => { - debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation); - match self.infcx.closure_kind(closure_def_id, closure_substs) { - Some(closure_kind) => { - debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); - if closure_kind.extends(kind) { - candidates.vec.push(ClosureCandidate); - } - } - None => { - debug!("assemble_unboxed_candidates: closure_kind not yet known"); - candidates.vec.push(ClosureCandidate); - } - } - } - ty::Infer(ty::TyVar(_)) => { - debug!("assemble_unboxed_closure_candidates: ambiguous self-type"); - candidates.ambiguous = true; - } - _ => {} - } - - Ok(()) - } - - /// Implements one of the `Fn()` family for a fn pointer. - fn assemble_fn_pointer_candidates( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - // We provide impl of all fn traits for fn pointers. - if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() { - return Ok(()); - } - - // Okay to skip binder because what we are inspecting doesn't involve bound regions. - let self_ty = *obligation.self_ty().skip_binder(); - match self_ty.kind { - ty::Infer(ty::TyVar(_)) => { - debug!("assemble_fn_pointer_candidates: ambiguous self-type"); - candidates.ambiguous = true; // Could wind up being a fn() type. - } - // Provide an impl, but only for suitable `fn` pointers. - ty::FnDef(..) | ty::FnPtr(_) => { - if let ty::FnSig { - unsafety: hir::Unsafety::Normal, - abi: Abi::Rust, - c_variadic: false, - .. - } = self_ty.fn_sig(self.tcx()).skip_binder() - { - candidates.vec.push(FnPointerCandidate); - } - } - _ => {} - } - - Ok(()) - } - - /// Searches for impls that might apply to `obligation`. - fn assemble_candidates_from_impls( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - debug!("assemble_candidates_from_impls(obligation={:?})", obligation); - - self.tcx().for_each_relevant_impl( - obligation.predicate.def_id(), - obligation.predicate.skip_binder().trait_ref.self_ty(), - |impl_def_id| { - self.infcx.probe(|snapshot| { - if let Ok(_substs) = self.match_impl(impl_def_id, obligation, snapshot) { - candidates.vec.push(ImplCandidate(impl_def_id)); - } - }); - }, - ); - - Ok(()) - } - - fn assemble_candidates_from_auto_impls( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - // Okay to skip binder here because the tests we do below do not involve bound regions. - let self_ty = *obligation.self_ty().skip_binder(); - debug!("assemble_candidates_from_auto_impls(self_ty={:?})", self_ty); - - let def_id = obligation.predicate.def_id(); - - if self.tcx().trait_is_auto(def_id) { - match self_ty.kind { - ty::Dynamic(..) => { - // For object types, we don't know what the closed - // over types are. This means we conservatively - // say nothing; a candidate may be added by - // `assemble_candidates_from_object_ty`. - } - ty::Foreign(..) => { - // Since the contents of foreign types is unknown, - // we don't add any `..` impl. Default traits could - // still be provided by a manual implementation for - // this trait and type. - } - ty::Param(..) | ty::Projection(..) => { - // In these cases, we don't know what the actual - // type is. Therefore, we cannot break it down - // into its constituent types. So we don't - // consider the `..` impl but instead just add no - // candidates: this means that typeck will only - // succeed if there is another reason to believe - // that this obligation holds. That could be a - // where-clause or, in the case of an object type, - // it could be that the object type lists the - // trait (e.g., `Foo+Send : Send`). See - // `compile-fail/typeck-default-trait-impl-send-param.rs` - // for an example of a test case that exercises - // this path. - } - ty::Infer(ty::TyVar(_)) => { - // The auto impl might apply; we don't know. - candidates.ambiguous = true; - } - ty::Generator(_, _, movability) - if self.tcx().lang_items().unpin_trait() == Some(def_id) => - { - match movability { - hir::Movability::Static => { - // Immovable generators are never `Unpin`, so - // suppress the normal auto-impl candidate for it. - } - hir::Movability::Movable => { - // Movable generators are always `Unpin`, so add an - // unconditional builtin candidate. - candidates.vec.push(BuiltinCandidate { has_nested: false }); - } - } - } - - _ => candidates.vec.push(AutoImplCandidate(def_id)), - } - } - - Ok(()) - } - - /// Searches for impls that might apply to `obligation`. - fn assemble_candidates_from_object_ty( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) { - debug!( - "assemble_candidates_from_object_ty(self_ty={:?})", - obligation.self_ty().skip_binder() - ); - - self.infcx.probe(|_snapshot| { - // The code below doesn't care about regions, and the - // self-ty here doesn't escape this probe, so just erase - // any LBR. - let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty()); - let poly_trait_ref = match self_ty.kind { - ty::Dynamic(ref data, ..) => { - if data.auto_traits().any(|did| did == obligation.predicate.def_id()) { - debug!( - "assemble_candidates_from_object_ty: matched builtin bound, \ - pushing candidate" - ); - candidates.vec.push(BuiltinObjectCandidate); - return; - } - - if let Some(principal) = data.principal() { - if !self.infcx.tcx.features().object_safe_for_dispatch { - principal.with_self_ty(self.tcx(), self_ty) - } else if self.tcx().is_object_safe(principal.def_id()) { - principal.with_self_ty(self.tcx(), self_ty) - } else { - return; - } - } else { - // Only auto trait bounds exist. - return; - } - } - ty::Infer(ty::TyVar(_)) => { - debug!("assemble_candidates_from_object_ty: ambiguous"); - candidates.ambiguous = true; // could wind up being an object type - return; - } - _ => return, - }; - - debug!("assemble_candidates_from_object_ty: poly_trait_ref={:?}", poly_trait_ref); - - // Count only those upcast versions that match the trait-ref - // we are looking for. Specifically, do not only check for the - // correct trait, but also the correct type parameters. - // For example, we may be trying to upcast `Foo` to `Bar`, - // but `Foo` is declared as `trait Foo: Bar`. - let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref) - .filter(|upcast_trait_ref| { - self.infcx - .probe(|_| self.match_poly_trait_ref(obligation, *upcast_trait_ref).is_ok()) - }) - .count(); - - if upcast_trait_refs > 1 { - // Can be upcast in many ways; need more type information. - candidates.ambiguous = true; - } else if upcast_trait_refs == 1 { - candidates.vec.push(ObjectCandidate); - } - }) - } - - /// Searches for unsizing that might apply to `obligation`. - fn assemble_candidates_for_unsizing( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) { - // We currently never consider higher-ranked obligations e.g. - // `for<'a> &'a T: Unsize` to be implemented. This is not - // because they are a priori invalid, and we could potentially add support - // for them later, it's just that there isn't really a strong need for it. - // A `T: Unsize` obligation is always used as part of a `T: CoerceUnsize` - // impl, and those are generally applied to concrete types. - // - // That said, one might try to write a fn with a where clause like - // for<'a> Foo<'a, T>: Unsize> - // where the `'a` is kind of orthogonal to the relevant part of the `Unsize`. - // Still, you'd be more likely to write that where clause as - // T: Trait - // so it seems ok if we (conservatively) fail to accept that `Unsize` - // obligation above. Should be possible to extend this in the future. - let source = match obligation.self_ty().no_bound_vars() { - Some(t) => t, - None => { - // Don't add any candidates if there are bound regions. - return; - } - }; - let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); - - debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target); - - let may_apply = match (&source.kind, &target.kind) { - // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { - // Upcasts permit two things: - // - // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` - // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` - // - // Note that neither of these changes requires any - // change at runtime. Eventually this will be - // generalized. - // - // We always upcast when we can because of reason - // #2 (region bounds). - data_a.principal_def_id() == data_b.principal_def_id() - && data_b - .auto_traits() - // All of a's auto traits need to be in b's auto traits. - .all(|b| data_a.auto_traits().any(|a| a == b)) - } - - // `T` -> `Trait` - (_, &ty::Dynamic(..)) => true, - - // Ambiguous handling is below `T` -> `Trait`, because inference - // variables can still implement `Unsize` and nested - // obligations will have the final say (likely deferred). - (&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => { - debug!("assemble_candidates_for_unsizing: ambiguous"); - candidates.ambiguous = true; - false - } - - // `[T; n]` -> `[T]` - (&ty::Array(..), &ty::Slice(_)) => true, - - // `Struct` -> `Struct` - (&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => { - def_id_a == def_id_b - } - - // `(.., T)` -> `(.., U)` - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(), - - _ => false, - }; - - if may_apply { - candidates.vec.push(BuiltinUnsizeCandidate); - } - } - - fn assemble_candidates_for_trait_alias( - &mut self, - obligation: &TraitObligation<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - // Okay to skip binder here because the tests we do below do not involve bound regions. - let self_ty = *obligation.self_ty().skip_binder(); - debug!("assemble_candidates_for_trait_alias(self_ty={:?})", self_ty); - - let def_id = obligation.predicate.def_id(); - - if self.tcx().is_trait_alias(def_id) { - candidates.vec.push(TraitAliasCandidate(def_id)); - } - - Ok(()) - } - - /////////////////////////////////////////////////////////////////////////// - // WINNOW - // - // Winnowing is the process of attempting to resolve ambiguity by - // probing further. During the winnowing process, we unify all - // type variables and then we also attempt to evaluate recursive - // bounds to see if they are satisfied. - - /// Returns `true` if `victim` should be dropped in favor of - /// `other`. Generally speaking we will drop duplicate - /// candidates and prefer where-clause candidates. - /// - /// See the comment for "SelectionCandidate" for more details. - fn candidate_should_be_dropped_in_favor_of( - &mut self, - victim: &EvaluatedCandidate<'tcx>, - other: &EvaluatedCandidate<'tcx>, - needs_infer: bool, - ) -> bool { - if victim.candidate == other.candidate { - return true; - } - - // Check if a bound would previously have been removed when normalizing - // the param_env so that it can be given the lowest priority. See - // #50825 for the motivation for this. - let is_global = - |cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions(); - - 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, - ParamCandidate(ref cand) => match victim.candidate { - AutoImplCandidate(..) => { - bug!( - "default implementations shouldn't be recorded \ - 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, - ImplCandidate(..) - | ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { .. } - | TraitAliasCandidate(..) => { - // Global bounds from the where clause should be ignored - // here (see issue #50825). Otherwise, we have a where - // clause so don't go around looking for impls. - !is_global(cand) - } - ObjectCandidate | ProjectionCandidate => { - // Arbitrarily give param candidates priority - // over projection and object candidates. - !is_global(cand) - } - ParamCandidate(..) => false, - }, - ObjectCandidate | ProjectionCandidate => match victim.candidate { - AutoImplCandidate(..) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other valid candidates" - ); - } - // 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, - ImplCandidate(..) - | ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { .. } - | TraitAliasCandidate(..) => true, - ObjectCandidate | ProjectionCandidate => { - // Arbitrarily give param candidates priority - // over projection and object candidates. - true - } - ParamCandidate(ref cand) => is_global(cand), - }, - ImplCandidate(other_def) => { - // See if we can toss out `victim` based on specialization. - // This requires us to know *for sure* that the `other` impl applies - // i.e., `EvaluatedToOk`. - if other.evaluation.must_apply_modulo_regions() { - match victim.candidate { - ImplCandidate(victim_def) => { - let tcx = self.tcx(); - if tcx.specializes((other_def, victim_def)) { - return true; - } - return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { - Some(ty::ImplOverlapKind::Permitted { marker: true }) => { - // Subtle: If the predicate we are evaluating has inference - // variables, do *not* allow discarding candidates due to - // marker trait impls. - // - // Without this restriction, we could end up accidentally - // constrainting inference variables based on an arbitrarily - // chosen trait impl. - // - // Imagine we have the following code: - // - // ```rust - // #[marker] trait MyTrait {} - // impl MyTrait for u8 {} - // impl MyTrait for bool {} - // ``` - // - // And we are evaluating the predicate `<_#0t as MyTrait>`. - // - // During selection, we will end up with one candidate for each - // impl of `MyTrait`. If we were to discard one impl in favor - // of the other, we would be left with one candidate, causing - // us to "successfully" select the predicate, unifying - // _#0t with (for example) `u8`. - // - // However, we have no reason to believe that this unification - // is correct - we've essentially just picked an arbitrary - // *possibility* for _#0t, and required that this be the *only* - // possibility. - // - // Eventually, we will either: - // 1) Unify all inference variables in the predicate through - // some other means (e.g. type-checking of a function). We will - // then be in a position to drop marker trait candidates - // without constraining inference variables (since there are - // none left to constrin) - // 2) Be left with some unconstrained inference variables. We - // will then correctly report an inference error, since the - // existence of multiple marker trait impls tells us nothing - // about which one should actually apply. - !needs_infer - } - Some(_) => true, - None => false, - }; - } - ParamCandidate(ref cand) => { - // Prefer the impl to a global where clause candidate. - return is_global(cand); - } - _ => (), - } - } - - false - } - ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { has_nested: true } => { - match victim.candidate { - ParamCandidate(ref cand) => { - // Prefer these to a global where-clause bound - // (see issue #50825). - is_global(cand) && other.evaluation.must_apply_modulo_regions() - } - _ => false, - } - } - _ => false, - } - } - - /////////////////////////////////////////////////////////////////////////// - // BUILTIN BOUNDS - // - // These cover the traits that are built-in to the language - // itself: `Copy`, `Clone` and `Sized`. - - fn assemble_builtin_bound_candidates( - &mut self, - conditions: BuiltinImplConditions<'tcx>, - candidates: &mut SelectionCandidateSet<'tcx>, - ) -> Result<(), SelectionError<'tcx>> { - match conditions { - BuiltinImplConditions::Where(nested) => { - debug!("builtin_bound: nested={:?}", nested); - candidates - .vec - .push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() }); - } - BuiltinImplConditions::None => {} - BuiltinImplConditions::Ambiguous => { - debug!("assemble_builtin_bound_candidates: ambiguous builtin"); - candidates.ambiguous = true; - } - } - - Ok(()) - } - - fn sized_conditions( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - - match self_ty.kind { - ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::RawPtr(..) - | ty::Char - | ty::Ref(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Array(..) - | ty::Closure(..) - | ty::Never - | ty::Error => { - // safe for everything - Where(ty::Binder::dummy(Vec::new())) - } - - ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None, - - ty::Tuple(tys) => { - Where(ty::Binder::bind(tys.last().into_iter().map(|k| k.expect_ty()).collect())) - } - - ty::Adt(def, substs) => { - let sized_crit = def.sized_constraint(self.tcx()); - // (*) binder moved here - Where(ty::Binder::bind( - sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect(), - )) - } - - ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None, - ty::Infer(ty::TyVar(_)) => Ambiguous, - - ty::UnnormalizedProjection(..) - | ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); - } - } - } - - fn copy_clone_conditions( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - - match self_ty.kind { - ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Error => Where(ty::Binder::dummy(Vec::new())), - - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) => { - // Implementations provided in libcore - None - } - - ty::Dynamic(..) - | ty::Str - | ty::Slice(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Foreign(..) - | ty::Ref(_, _, hir::Mutability::Mut) => None, - - ty::Array(element_ty, _) => { - // (*) binder moved here - Where(ty::Binder::bind(vec![element_ty])) - } - - ty::Tuple(tys) => { - // (*) binder moved here - Where(ty::Binder::bind(tys.iter().map(|k| k.expect_ty()).collect())) - } - - ty::Closure(def_id, substs) => { - // (*) binder moved here - Where(ty::Binder::bind(substs.as_closure().upvar_tys(def_id, self.tcx()).collect())) - } - - ty::Adt(..) | ty::Projection(..) | ty::Param(..) | ty::Opaque(..) => { - // Fallback to whatever user-defined impls exist in this case. - None - } - - ty::Infer(ty::TyVar(_)) => { - // Unbound type variable. Might or might not have - // applicable impls and so forth, depending on what - // those type variables wind up being bound to. - Ambiguous - } - - ty::UnnormalizedProjection(..) - | ty::Placeholder(..) - | ty::Bound(..) - | ty::Infer(ty::FreshTy(_)) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); - } - } - } - - /// For default impls, we need to break apart a type into its - /// "constituent types" -- meaning, the types that it contains. - /// - /// Here are some (simple) examples: - /// - /// ``` - /// (i32, u32) -> [i32, u32] - /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32] - /// Bar where struct Bar { x: T, y: u32 } -> [i32, u32] - /// Zed where enum Zed { A(T), B(u32) } -> [i32, u32] - /// ``` - fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec> { - match t.kind { - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Str - | ty::Error - | ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::Never - | ty::Char => Vec::new(), - - ty::UnnormalizedProjection(..) - | 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(_)) => { - bug!("asked to assemble constituent types of unexpected type: {:?}", t); - } - - ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { - vec![element_ty] - } - - ty::Array(element_ty, _) | ty::Slice(element_ty) => vec![element_ty], - - ty::Tuple(ref tys) => { - // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - tys.iter().map(|k| k.expect_ty()).collect() - } - - ty::Closure(def_id, ref substs) => { - substs.as_closure().upvar_tys(def_id, self.tcx()).collect() - } - - ty::Generator(def_id, ref substs, _) => { - let witness = substs.as_generator().witness(def_id, self.tcx()); - substs - .as_generator() - .upvar_tys(def_id, self.tcx()) - .chain(iter::once(witness)) - .collect() - } - - ty::GeneratorWitness(types) => { - // This is sound because no regions in the witness can refer to - // the binder outside the witness. So we'll effectivly reuse - // the implicit binder around the witness. - types.skip_binder().to_vec() - } - - // For `PhantomData`, we pass `T`. - ty::Adt(def, substs) if def.is_phantom_data() => substs.types().collect(), - - ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(self.tcx(), substs)).collect(), - - ty::Opaque(def_id, substs) => { - // We can resolve the `impl Trait` to its concrete type, - // which enforces a DAG between the functions requiring - // the auto trait bounds in question. - vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)] - } - } - } - - fn collect_predicates_for_types( - &mut self, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - recursion_depth: usize, - trait_def_id: DefId, - types: ty::Binder>>, - ) -> Vec> { - // Because the types were potentially derived from - // higher-ranked obligations they may reference late-bound - // regions. For example, `for<'a> Foo<&'a int> : Copy` would - // yield a type like `for<'a> &'a int`. In general, we - // maintain the invariant that we never manipulate bound - // regions, so we have to process these bound regions somehow. - // - // The strategy is to: - // - // 1. Instantiate those regions to placeholder regions (e.g., - // `for<'a> &'a int` becomes `&0 int`. - // 2. Produce something like `&'0 int : Copy` - // 3. Re-bind the regions back to `for<'a> &'a int : Copy` - - types - .skip_binder() - .iter() - .flat_map(|ty| { - // binder moved -\ - let ty: ty::Binder> = ty::Binder::bind(ty); // <----/ - - 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, - ); - let skol_obligation = predicate_for_trait_def( - self.tcx(), - param_env, - cause.clone(), - trait_def_id, - recursion_depth, - normalized_ty, - &[], - ); - obligations.push(skol_obligation); - obligations - }) - }) - .collect() - } - - /////////////////////////////////////////////////////////////////////////// - // CONFIRMATION - // - // Confirmation unifies the output type parameters of the trait - // with the values found in the obligation, possibly yielding a - // type error. See the [rustc dev guide] for more details. - // - // [rustc dev guide]: - // https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation - - fn confirm_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - candidate: SelectionCandidate<'tcx>, - ) -> Result, SelectionError<'tcx>> { - debug!("confirm_candidate({:?}, {:?})", obligation, candidate); - - match candidate { - BuiltinCandidate { has_nested } => { - let data = self.confirm_builtin_candidate(obligation, has_nested); - Ok(VtableBuiltin(data)) - } - - ParamCandidate(param) => { - let obligations = self.confirm_param_candidate(obligation, param); - Ok(VtableParam(obligations)) - } - - ImplCandidate(impl_def_id) => { - Ok(VtableImpl(self.confirm_impl_candidate(obligation, impl_def_id))) - } - - AutoImplCandidate(trait_def_id) => { - let data = self.confirm_auto_impl_candidate(obligation, trait_def_id); - Ok(VtableAutoImpl(data)) - } - - ProjectionCandidate => { - self.confirm_projection_candidate(obligation); - Ok(VtableParam(Vec::new())) - } - - ClosureCandidate => { - let vtable_closure = self.confirm_closure_candidate(obligation)?; - Ok(VtableClosure(vtable_closure)) - } - - GeneratorCandidate => { - let vtable_generator = self.confirm_generator_candidate(obligation)?; - Ok(VtableGenerator(vtable_generator)) - } - - FnPointerCandidate => { - let data = self.confirm_fn_pointer_candidate(obligation)?; - Ok(VtableFnPointer(data)) - } - - TraitAliasCandidate(alias_def_id) => { - let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); - Ok(VtableTraitAlias(data)) - } - - ObjectCandidate => { - let data = self.confirm_object_candidate(obligation); - Ok(VtableObject(data)) - } - - BuiltinObjectCandidate => { - // This indicates something like `Trait + Send: Send`. In this case, we know that - // this holds because that's what the object type is telling us, and there's really - // no additional obligations to prove and no types in particular to unify, etc. - Ok(VtableParam(Vec::new())) - } - - BuiltinUnsizeCandidate => { - let data = self.confirm_builtin_unsize_candidate(obligation)?; - Ok(VtableBuiltin(data)) - } - } - } - - fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) { - self.infcx.commit_unconditionally(|snapshot| { - let result = - self.match_projection_obligation_against_definition_bounds(obligation, snapshot); - assert!(result); - }) - } - - fn confirm_param_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - param: ty::PolyTraitRef<'tcx>, - ) -> Vec> { - debug!("confirm_param_candidate({:?},{:?})", obligation, param); - - // During evaluation, we already checked that this - // 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()) { - Ok(obligations) => obligations, - Err(()) => { - bug!( - "Where clause `{:?}` was applicable to `{:?}` but now is not", - param, - obligation - ); - } - } - } - - fn confirm_builtin_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - has_nested: bool, - ) -> VtableBuiltinData> { - debug!("confirm_builtin_candidate({:?}, {:?})", obligation, has_nested); - - let lang_items = self.tcx().lang_items(); - let obligations = if has_nested { - let trait_def = obligation.predicate.def_id(); - let conditions = if Some(trait_def) == lang_items.sized_trait() { - self.sized_conditions(obligation) - } else if Some(trait_def) == lang_items.copy_trait() { - self.copy_clone_conditions(obligation) - } else if Some(trait_def) == lang_items.clone_trait() { - self.copy_clone_conditions(obligation) - } else { - bug!("unexpected builtin trait {:?}", trait_def) - }; - let nested = match conditions { - BuiltinImplConditions::Where(nested) => nested, - _ => bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation), - }; - - let cause = obligation.derived_cause(BuiltinDerivedObligation); - self.collect_predicates_for_types( - obligation.param_env, - cause, - obligation.recursion_depth + 1, - trait_def, - nested, - ) - } else { - vec![] - }; - - debug!("confirm_builtin_candidate: obligations={:?}", obligations); - - VtableBuiltinData { nested: obligations } - } - - /// This handles the case where a `auto trait Foo` impl is being used. - /// The idea is that the impl applies to `X : Foo` if the following conditions are met: - /// - /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds - /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds. - fn confirm_auto_impl_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - trait_def_id: DefId, - ) -> VtableAutoImplData> { - debug!("confirm_auto_impl_candidate({:?}, {:?})", obligation, trait_def_id); - - let types = obligation.predicate.map_bound(|inner| { - let self_ty = self.infcx.shallow_resolve(inner.self_ty()); - self.constituent_types_for_ty(self_ty) - }); - self.vtable_auto_impl(obligation, trait_def_id, types) - } - - /// See `confirm_auto_impl_candidate`. - fn vtable_auto_impl( - &mut self, - obligation: &TraitObligation<'tcx>, - trait_def_id: DefId, - nested: ty::Binder>>, - ) -> VtableAutoImplData> { - debug!("vtable_auto_impl: nested={:?}", 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, - ) - }); - - // 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); - - VtableAutoImplData { trait_def_id, nested: obligations } - } - - fn confirm_impl_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - impl_def_id: DefId, - ) -> VtableImplData<'tcx, PredicateObligation<'tcx>> { - debug!("confirm_impl_candidate({:?},{:?})", obligation, impl_def_id); - - // First, create the substitutions by matching the impl again, - // this time not in a probe. - self.infcx.commit_unconditionally(|snapshot| { - 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, - ) - }) - } - - fn vtable_impl( - &mut self, - impl_def_id: DefId, - mut substs: Normalized<'tcx, SubstsRef<'tcx>>, - cause: ObligationCause<'tcx>, - recursion_depth: usize, - param_env: ty::ParamEnv<'tcx>, - ) -> VtableImplData<'tcx, PredicateObligation<'tcx>> { - debug!( - "vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={})", - impl_def_id, substs, recursion_depth, - ); - - let mut impl_obligations = self.impl_or_trait_obligations( - cause, - recursion_depth, - param_env, - impl_def_id, - &substs.value, - ); - - debug!( - "vtable_impl: impl_def_id={:?} impl_obligations={:?}", - impl_def_id, impl_obligations - ); - - // Because of RFC447, the impl-trait-ref and obligations - // are sufficient to determine the impl substs, without - // relying on projections in the impl-trait-ref. - // - // e.g., `impl> Foo<::T> for V` - impl_obligations.append(&mut substs.obligations); - - VtableImplData { impl_def_id, substs: substs.value, nested: impl_obligations } - } - - fn confirm_object_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> VtableObjectData<'tcx, PredicateObligation<'tcx>> { - debug!("confirm_object_candidate({:?})", obligation); - - // FIXME(nmatsakis) skipping binder here seems wrong -- we should - // probably flatten the binder from the obligation and the binder - // from the object. Have to try to make a broken test case that - // results. - let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let poly_trait_ref = match self_ty.kind { - ty::Dynamic(ref data, ..) => data - .principal() - .unwrap_or_else(|| { - span_bug!(obligation.cause.span, "object candidate with no principal") - }) - .with_self_ty(self.tcx(), self_ty), - _ => span_bug!(obligation.cause.span, "object candidate with non-object"), - }; - - let mut upcast_trait_ref = None; - let mut nested = vec![]; - let vtable_base; - - { - let tcx = self.tcx(); - - // We want to find the first supertrait in the list of - // supertraits that we can unify with, and do that - // unification. We know that there is exactly one in the list - // where we can unify, because otherwise select would have - // reported an ambiguity. (When we do find a match, also - // record it for later.) - let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(|&t| { - match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) { - Ok(obligations) => { - upcast_trait_ref = Some(t); - nested.extend(obligations); - false - } - Err(_) => true, - } - }); - - // Additionally, for each of the non-matching predicates that - // we pass over, we sum up the set of number of vtable - // entries, so that we can compute the offset for the selected - // trait. - vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum(); - } - - VtableObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested } - } - - fn confirm_fn_pointer_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - debug!("confirm_fn_pointer_candidate({:?})", obligation); - - // Okay to skip binder; it is reintroduced below. - let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let sig = self_ty.fn_sig(self.tcx()); - let trait_ref = closure_trait_ref_and_return_type( - self.tcx(), - obligation.predicate.def_id(), - self_ty, - sig, - util::TupleArgumentsFlag::Yes, - ) - .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, - ); - - self.confirm_poly_trait_refs( - obligation.cause.clone(), - obligation.param_env, - obligation.predicate.to_poly_trait_ref(), - trait_ref, - )?; - Ok(VtableFnPointerData { fn_ty: self_ty, nested: obligations }) - } - - fn confirm_trait_alias_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - alias_def_id: DefId, - ) -> VtableTraitAliasData<'tcx, PredicateObligation<'tcx>> { - debug!("confirm_trait_alias_candidate({:?}, {:?})", obligation, alias_def_id); - - self.infcx.commit_unconditionally(|_| { - let (predicate, _) = - self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); - let trait_ref = predicate.trait_ref; - let trait_def_id = trait_ref.def_id; - let substs = trait_ref.substs; - - let trait_obligations = self.impl_or_trait_obligations( - obligation.cause.clone(), - obligation.recursion_depth, - obligation.param_env, - trait_def_id, - &substs, - ); - - debug!( - "confirm_trait_alias_candidate: trait_def_id={:?} trait_obligations={:?}", - trait_def_id, trait_obligations - ); - - VtableTraitAliasData { alias_def_id, substs: substs, nested: trait_obligations } - }) - } - - fn confirm_generator_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - // Okay to skip binder because the substs on generator types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let (generator_def_id, substs) = match self_ty.kind { - ty::Generator(id, substs, _) => (id, substs), - _ => bug!("closure candidate for non-closure {:?}", obligation), - }; - - debug!("confirm_generator_candidate({:?},{:?},{:?})", obligation, generator_def_id, substs); - - let trait_ref = self.generator_trait_ref_unnormalized(obligation, generator_def_id, substs); - let Normalized { value: trait_ref, mut obligations } = normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &trait_ref, - ); - - debug!( - "confirm_generator_candidate(generator_def_id={:?}, \ - trait_ref={:?}, obligations={:?})", - generator_def_id, trait_ref, obligations - ); - - obligations.extend(self.confirm_poly_trait_refs( - obligation.cause.clone(), - obligation.param_env, - obligation.predicate.to_poly_trait_ref(), - trait_ref, - )?); - - Ok(VtableGeneratorData { generator_def_id, substs, nested: obligations }) - } - - fn confirm_closure_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - debug!("confirm_closure_candidate({:?})", obligation); - - let kind = self - .tcx() - .fn_trait_kind_from_lang_item(obligation.predicate.def_id()) - .unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation)); - - // Okay to skip binder because the substs on closure types never - // touch bound regions, they just capture the in-scope - // type/region parameters. - let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); - let (closure_def_id, substs) = match self_ty.kind { - ty::Closure(id, substs) => (id, substs), - _ => bug!("closure candidate for non-closure {:?}", obligation), - }; - - let trait_ref = self.closure_trait_ref_unnormalized(obligation, closure_def_id, substs); - let Normalized { value: trait_ref, mut obligations } = 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={:?})", - closure_def_id, trait_ref, obligations - ); - - obligations.extend(self.confirm_poly_trait_refs( - obligation.cause.clone(), - obligation.param_env, - obligation.predicate.to_poly_trait_ref(), - trait_ref, - )?); - - 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 }) - } - - /// In the case of closure types and fn pointers, - /// we currently treat the input type parameters on the trait as - /// outputs. This means that when we have a match we have only - /// considered the self type, so we have to go back and make sure - /// to relate the argument types too. This is kind of wrong, but - /// since we control the full set of impls, also not that wrong, - /// and it DOES yield better error messages (since we don't report - /// errors as if there is no applicable impl, but rather report - /// errors are about mismatched argument types. - /// - /// Here is an example. Imagine we have a closure expression - /// and we desugared it so that the type of the expression is - /// `Closure`, and `Closure` expects an int as argument. Then it - /// is "as if" the compiler generated this impl: - /// - /// impl Fn(int) for Closure { ... } - /// - /// Now imagine our obligation is `Fn(usize) for Closure`. So far - /// we have matched the self type `Closure`. At this point we'll - /// compare the `int` to `usize` and generate an error. - /// - /// Note that this checking occurs *after* the impl has selected, - /// because these output type parameters should not affect the - /// selection of the impl. Therefore, if there is a mismatch, we - /// report an error to the user. - fn confirm_poly_trait_refs( - &mut self, - obligation_cause: ObligationCause<'tcx>, - obligation_param_env: ty::ParamEnv<'tcx>, - obligation_trait_ref: ty::PolyTraitRef<'tcx>, - expected_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - self.infcx - .at(&obligation_cause, obligation_param_env) - .sup(obligation_trait_ref, expected_trait_ref) - .map(|InferOk { obligations, .. }| obligations) - .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) - } - - fn confirm_builtin_unsize_candidate( - &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result>, SelectionError<'tcx>> { - let tcx = self.tcx(); - - // `assemble_candidates_for_unsizing` should ensure there are no late-bound - // regions here. See the comment there for more details. - let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); - let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); - let target = self.infcx.shallow_resolve(target); - - debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target); - - let mut nested = vec![]; - match (&source.kind, &target.kind) { - // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { - // See `assemble_candidates_for_unsizing` for more info. - let existential_predicates = data_a.map_bound(|data_a| { - let iter = data_a - .principal() - .map(|x| ty::ExistentialPredicate::Trait(x)) - .into_iter() - .chain( - data_a - .projection_bounds() - .map(|x| ty::ExistentialPredicate::Projection(x)), - ) - .chain(data_b.auto_traits().map(ty::ExistentialPredicate::AutoTrait)); - tcx.mk_existential_predicates(iter) - }); - let source_trait = tcx.mk_dynamic(existential_predicates, r_b); - - // Require that the traits involved in this upcast are **equal**; - // only the **lifetime bound** is changed. - // - // FIXME: This condition is arguably too strong -- it would - // suffice for the source trait to be a *subtype* of the target - // trait. In particular, changing from something like - // `for<'a, 'b> Foo<'a, 'b>` to `for<'a> Foo<'a, 'a>` should be - // permitted. And, indeed, in the in commit - // 904a0bde93f0348f69914ee90b1f8b6e4e0d7cbc, this - // condition was loosened. However, when the leak check was - // added back, using subtype here actually guides the coercion - // code in such a way that it accepts `old-lub-glb-object.rs`. - // This is probably a good thing, but I've modified this to `.eq` - // because I want to continue rejecting that test (as we have - // done for quite some time) before we are firmly comfortable - // with what our behavior should be there. -nikomatsakis - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(target, source_trait) // FIXME -- see below - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - - // Register one obligation for 'a: 'b. - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - ObjectCastObligation(target), - ); - let outlives = ty::OutlivesPredicate(r_a, r_b); - nested.push(Obligation::with_depth( - cause, - obligation.recursion_depth + 1, - obligation.param_env, - ty::Binder::bind(outlives).to_predicate(), - )); - } - - // `T` -> `Trait` - (_, &ty::Dynamic(ref data, r)) => { - let mut object_dids = data.auto_traits().chain(data.principal_def_id()); - if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) { - return Err(TraitNotObjectSafe(did)); - } - - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - ObjectCastObligation(target), - ); - - let predicate_to_obligation = |predicate| { - Obligation::with_depth( - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - ) - }; - - // Create obligations: - // - Casting `T` to `Trait` - // - For all the various builtin bounds attached to the object cast. (In other - // words, if the object type is `Foo + Send`, this would create an obligation for - // the `Send` check.) - // - Projection predicates - nested.extend( - data.iter().map(|predicate| { - predicate_to_obligation(predicate.with_self_ty(tcx, source)) - }), - ); - - // We can only make objects from sized types. - let tr = ty::TraitRef::new( - tcx.require_lang_item(lang_items::SizedTraitLangItem, None), - tcx.mk_substs_trait(source, &[]), - ); - nested.push(predicate_to_obligation(tr.without_const().to_predicate())); - - // If the type is `Foo + 'a`, ensure that the type - // being cast to `Foo + 'a` outlives `'a`: - let outlives = ty::OutlivesPredicate(source, r); - nested.push(predicate_to_obligation(ty::Binder::dummy(outlives).to_predicate())); - } - - // `[T; n]` -> `[T]` - (&ty::Array(a, _), &ty::Slice(b)) => { - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(b, a) - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - } - - // `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 mut ty_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); - found = true; - } - } - if !found { - 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); - } - } - - // Extract `Field` and `Field` from `Struct` and `Struct`. - let inner_source = field.subst(tcx, substs_a); - let inner_target = field.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)); - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(target, new_struct) - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - - // Construct the nested `Field: 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()], - )); - } - - // `(.., T)` -> `(.., U)` - (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { - assert_eq!(tys_a.len(), tys_b.len()); - - // The last field of the tuple has to exist. - let (&a_last, a_mid) = if let Some(x) = tys_a.split_last() { - x - } else { - return Err(Unimplemented); - }; - let &b_last = tys_b.last().unwrap(); - - // Check that the source tuple with the target's - // last element is equal to the target. - let new_tuple = tcx.mk_tup( - a_mid.iter().map(|k| k.expect_ty()).chain(iter::once(b_last.expect_ty())), - ); - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(target, new_tuple) - .map_err(|_| Unimplemented)?; - 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], - )); - } - - _ => bug!(), - }; - - Ok(VtableBuiltinData { nested }) - } - - /////////////////////////////////////////////////////////////////////////// - // Matching - // - // Matching is a common path used for both evaluation and - // confirmation. It basically unifies types that appear in impls - // and traits. This does affect the surrounding environment; - // therefore, when used during evaluation, match routines must be - // run inside of a `probe()` so that their side-effects are - // contained. - - fn rematch_impl( - &mut self, - impl_def_id: DefId, - obligation: &TraitObligation<'tcx>, - snapshot: &CombinedSnapshot<'_, 'tcx>, - ) -> Normalized<'tcx, SubstsRef<'tcx>> { - match self.match_impl(impl_def_id, obligation, snapshot) { - Ok(substs) => substs, - Err(()) => { - bug!( - "Impl {:?} was matchable against {:?} but now is not", - impl_def_id, - obligation - ); - } - } - } - - fn match_impl( - &mut self, - impl_def_id: DefId, - obligation: &TraitObligation<'tcx>, - snapshot: &CombinedSnapshot<'_, 'tcx>, - ) -> Result>, ()> { - let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); - - // Before we create the substitutions and everything, first - // consider a "quick reject". This avoids creating more types - // and so forth that we need to. - if self.fast_reject_trait_refs(obligation, &impl_trait_ref) { - return Err(()); - } - - let (skol_obligation, placeholder_map) = - self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); - let skol_obligation_trait_ref = skol_obligation.trait_ref; - - let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id); - - let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs); - - let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = - project::normalize_with_depth( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - &impl_trait_ref, - ); - - debug!( - "match_impl(impl_def_id={:?}, obligation={:?}, \ - impl_trait_ref={:?}, skol_obligation_trait_ref={:?})", - impl_def_id, obligation, impl_trait_ref, skol_obligation_trait_ref - ); - - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(skol_obligation_trait_ref, impl_trait_ref) - .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?; - nested_obligations.extend(obligations); - - if let Err(e) = self.infcx.leak_check(false, &placeholder_map, snapshot) { - debug!("match_impl: failed leak check due to `{}`", e); - return Err(()); - } - - if !self.intercrate - && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation - { - debug!("match_impl: reservation impls only apply in intercrate mode"); - return Err(()); - } - - debug!("match_impl: success impl_substs={:?}", impl_substs); - Ok(Normalized { value: impl_substs, obligations: nested_obligations }) - } - - fn fast_reject_trait_refs( - &mut self, - obligation: &TraitObligation<'_>, - impl_trait_ref: &ty::TraitRef<'_>, - ) -> bool { - // We can avoid creating type variables and doing the full - // substitution if we find that any of the input types, when - // simplified, do not match. - - obligation.predicate.skip_binder().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 - }, - ) - } - - /// Normalize `where_clause_trait_ref` and try to match it against - /// `obligation`. If successful, return any predicates that - /// result from the normalization. Normalization is necessary - /// because where-clauses are stored in the parameter environment - /// unnormalized. - fn match_where_clause_trait_ref( - &mut self, - obligation: &TraitObligation<'tcx>, - where_clause_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result>, ()> { - self.match_poly_trait_ref(obligation, where_clause_trait_ref) - } - - /// Returns `Ok` if `poly_trait_ref` being true implies that the - /// obligation is satisfied. - fn match_poly_trait_ref( - &mut self, - obligation: &TraitObligation<'tcx>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Result>, ()> { - debug!( - "match_poly_trait_ref: obligation={:?} poly_trait_ref={:?}", - obligation, poly_trait_ref - ); - - self.infcx - .at(&obligation.cause, obligation.param_env) - .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref) - .map(|InferOk { obligations, .. }| obligations) - .map_err(|_| ()) - } - - /////////////////////////////////////////////////////////////////////////// - // Miscellany - - fn match_fresh_trait_refs( - &self, - previous: &ty::PolyTraitRef<'tcx>, - current: &ty::PolyTraitRef<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - let mut matcher = ty::_match::Match::new(self.tcx(), param_env); - matcher.relate(previous, current).is_ok() - } - - fn push_stack<'o>( - &mut self, - previous_stack: TraitObligationStackList<'o, 'tcx>, - obligation: &'o TraitObligation<'tcx>, - ) -> TraitObligationStack<'o, 'tcx> { - let fresh_trait_ref = - obligation.predicate.to_poly_trait_ref().fold_with(&mut self.freshener); - - let dfn = previous_stack.cache.next_dfn(); - let depth = previous_stack.depth() + 1; - TraitObligationStack { - obligation, - fresh_trait_ref, - reached_depth: Cell::new(depth), - previous: previous_stack, - dfn, - depth, - } - } - - fn closure_trait_ref_unnormalized( - &mut self, - obligation: &TraitObligation<'tcx>, - closure_def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> ty::PolyTraitRef<'tcx> { - debug!( - "closure_trait_ref_unnormalized(obligation={:?}, closure_def_id={:?}, substs={:?})", - obligation, closure_def_id, substs, - ); - let closure_type = self.infcx.closure_sig(closure_def_id, substs); - - debug!("closure_trait_ref_unnormalized: closure_type = {:?}", closure_type); - - // (1) Feels icky to skip the binder here, but OTOH we know - // that the self-type is an unboxed closure type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). Still probably some - // refactoring could make this nicer. - closure_trait_ref_and_return_type( - self.tcx(), - obligation.predicate.def_id(), - obligation.predicate.skip_binder().self_ty(), // (1) - closure_type, - util::TupleArgumentsFlag::No, - ) - .map_bound(|(trait_ref, _)| trait_ref) - } - - fn generator_trait_ref_unnormalized( - &mut self, - obligation: &TraitObligation<'tcx>, - closure_def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> ty::PolyTraitRef<'tcx> { - let gen_sig = substs.as_generator().poly_sig(closure_def_id, self.tcx()); - - // (1) Feels icky to skip the binder here, but OTOH we know - // that the self-type is an generator type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). Still probably some - // refactoring could make this nicer. - - super::util::generator_trait_ref_and_outputs( - self.tcx(), - obligation.predicate.def_id(), - obligation.predicate.skip_binder().self_ty(), // (1) - gen_sig, - ) - .map_bound(|(trait_ref, ..)| trait_ref) - } - - /// Returns the obligations that are implied by instantiating an - /// impl or trait. The obligations are substituted and fully - /// normalized. This is used when confirming an impl or default - /// impl. - fn impl_or_trait_obligations( - &mut self, - cause: ObligationCause<'tcx>, - recursion_depth: usize, - param_env: ty::ParamEnv<'tcx>, - def_id: DefId, // of impl or trait - substs: SubstsRef<'tcx>, // for impl or trait - ) -> Vec> { - debug!("impl_or_trait_obligations(def_id={:?})", def_id); - let tcx = self.tcx(); - - // To allow for one-pass evaluation of the nested obligation, - // each predicate must be preceded by the obligations required - // to normalize it. - // for example, if we have: - // impl, V: Iterator> Foo for V - // the impl will have the following predicates: - // ::Item = U, - // U: Iterator, U: Sized, - // V: Iterator, V: Sized, - // ::Item: Copy - // When we substitute, say, `V => IntoIter, U => $0`, the last - // obligation will normalize to `<$0 as Iterator>::Item = $1` and - // `$1: Copy`, so we must ensure the obligations are emitted in - // that order. - let predicates = tcx.predicates_of(def_id); - assert_eq!(predicates.parent, None); - let mut obligations = Vec::with_capacity(predicates.predicates.len()); - for (predicate, _) in predicates.predicates { - let predicate = normalize_with_depth_to( - self, - param_env, - cause.clone(), - recursion_depth, - &predicate.subst(tcx, substs), - &mut obligations, - ); - obligations.push(Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate, - }); - } - - // We are performing deduplication here to avoid exponential blowups - // (#38528) from happening, but the real cause of the duplication is - // unknown. What we know is that the deduplication avoids exponential - // amount of predicates being propagated when processing deeply nested - // types. - // - // This code is hot enough that it's worth avoiding the allocation - // required for the FxHashSet when possible. Special-casing lengths 0, - // 1 and 2 covers roughly 75-80% of the cases. - if obligations.len() <= 1 { - // No possibility of duplicates. - } else if obligations.len() == 2 { - // Only two elements. Drop the second if they are equal. - if obligations[0] == obligations[1] { - obligations.truncate(1); - } - } else { - // Three or more elements. Use a general deduplication process. - let mut seen = FxHashSet::default(); - obligations.retain(|i| seen.insert(i.clone())); - } - - obligations - } -} - -trait TraitObligationExt<'tcx> { - fn derived_cause( - &self, - variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, - ) -> ObligationCause<'tcx>; -} - -impl<'tcx> TraitObligationExt<'tcx> for TraitObligation<'tcx> { - #[allow(unused_comparisons)] - fn derived_cause( - &self, - variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, - ) -> ObligationCause<'tcx> { - /*! - * Creates a cause for obligations that are derived from - * `obligation` by a recursive search (e.g., for a builtin - * bound, or eventually a `auto trait Foo`). If `obligation` - * is itself a derived obligation, this is just a clone, but - * otherwise we create a "derived obligation" cause so as to - * keep track of the original root obligation for error - * reporting. - */ - - let obligation = self; - - // NOTE(flaper87): As of now, it keeps track of the whole error - // chain. Ideally, we should have a way to configure this either - // by using -Z verbose or just a CLI argument. - let derived_cause = DerivedObligationCause { - parent_trait_ref: obligation.predicate.to_poly_trait_ref(), - parent_code: Rc::new(obligation.cause.code.clone()), - }; - let derived_code = variant(derived_cause); - ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code) - } -} - -impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { - fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { - TraitObligationStackList::with(self) - } - - fn cache(&self) -> &'o ProvisionalEvaluationCache<'tcx> { - self.previous.cache - } - - fn iter(&'o self) -> TraitObligationStackList<'o, 'tcx> { - self.list() - } - - /// Indicates that attempting to evaluate this stack entry - /// required accessing something from the stack at depth `reached_depth`. - fn update_reached_depth(&self, reached_depth: usize) { - assert!( - self.depth > reached_depth, - "invoked `update_reached_depth` with something under this stack: \ - self.depth={} reached_depth={}", - self.depth, - reached_depth, - ); - debug!("update_reached_depth(reached_depth={})", reached_depth); - let mut p = self; - while reached_depth < p.depth { - debug!("update_reached_depth: marking {:?} as cycle participant", p.fresh_trait_ref); - p.reached_depth.set(p.reached_depth.get().min(reached_depth)); - p = p.previous.head.unwrap(); - } - } -} - -/// The "provisional evaluation cache" is used to store intermediate cache results -/// when solving auto traits. Auto traits are unusual in that they can support -/// cycles. So, for example, a "proof tree" like this would be ok: -/// -/// - `Foo: Send` :- -/// - `Bar: Send` :- -/// - `Foo: Send` -- cycle, but ok -/// - `Baz: Send` -/// -/// Here, to prove `Foo: Send`, we have to prove `Bar: Send` and -/// `Baz: Send`. Proving `Bar: Send` in turn required `Foo: Send`. -/// For non-auto traits, this cycle would be an error, but for auto traits (because -/// they are coinductive) it is considered ok. -/// -/// However, there is a complication: at the point where we have -/// "proven" `Bar: Send`, we have in fact only proven it -/// *provisionally*. In particular, we proved that `Bar: Send` -/// *under the assumption* that `Foo: Send`. But what if we later -/// find out this assumption is wrong? Specifically, we could -/// encounter some kind of error proving `Baz: Send`. In that case, -/// `Bar: Send` didn't turn out to be true. -/// -/// In Issue #60010, we found a bug in rustc where it would cache -/// these intermediate results. This was fixed in #60444 by disabling -/// *all* caching for things involved in a cycle -- in our example, -/// that would mean we don't cache that `Bar: Send`. But this led -/// to large slowdowns. -/// -/// Specifically, imagine this scenario, where proving `Baz: Send` -/// first requires proving `Bar: Send` (which is true: -/// -/// - `Foo: Send` :- -/// - `Bar: Send` :- -/// - `Foo: Send` -- cycle, but ok -/// - `Baz: Send` -/// - `Bar: Send` -- would be nice for this to be a cache hit! -/// - `*const T: Send` -- but what if we later encounter an error? -/// -/// The *provisional evaluation cache* resolves this issue. It stores -/// cache results that we've proven but which were involved in a cycle -/// in some way. We track the minimal stack depth (i.e., the -/// farthest from the top of the stack) that we are dependent on. -/// The idea is that the cache results within are all valid -- so long as -/// none of the nodes in between the current node and the node at that minimum -/// depth result in an error (in which case the cached results are just thrown away). -/// -/// During evaluation, we consult this provisional cache and rely on -/// it. Accessing a cached value is considered equivalent to accessing -/// a result at `reached_depth`, so it marks the *current* solution as -/// provisional as well. If an error is encountered, we toss out any -/// provisional results added from the subtree that encountered the -/// error. When we pop the node at `reached_depth` from the stack, we -/// can commit all the things that remain in the provisional cache. -struct ProvisionalEvaluationCache<'tcx> { - /// next "depth first number" to issue -- just a counter - dfn: Cell, - - /// Stores the "coldest" depth (bottom of stack) reached by any of - /// the evaluation entries. The idea here is that all things in the provisional - /// cache are always dependent on *something* that is colder in the stack: - /// therefore, if we add a new entry that is dependent on something *colder still*, - /// we have to modify the depth for all entries at once. - /// - /// Example: - /// - /// Imagine we have a stack `A B C D E` (with `E` being the top of - /// the stack). We cache something with depth 2, which means that - /// it was dependent on C. Then we pop E but go on and process a - /// new node F: A B C D F. Now F adds something to the cache with - /// depth 1, meaning it is dependent on B. Our original cache - /// entry is also dependent on B, because there is a path from E - /// to C and then from C to F and from F to B. - reached_depth: Cell, - - /// Map from cache key to the provisionally evaluated thing. - /// The cache entries contain the result but also the DFN in which they - /// were added. The DFN is used to clear out values on failure. - /// - /// Imagine we have a stack like: - /// - /// - `A B C` and we add a cache for the result of C (DFN 2) - /// - Then we have a stack `A B D` where `D` has DFN 3 - /// - We try to solve D by evaluating E: `A B D E` (DFN 4) - /// - `E` generates various cache entries which have cyclic dependices on `B` - /// - `A B D E F` and so forth - /// - the DFN of `F` for example would be 5 - /// - then we determine that `E` is in error -- we will then clear - /// all cache values whose DFN is >= 4 -- in this case, that - /// means the cached value for `F`. - map: RefCell, ProvisionalEvaluation>>, -} - -/// A cache value for the provisional cache: contains the depth-first -/// number (DFN) and result. -#[derive(Copy, Clone, Debug)] -struct ProvisionalEvaluation { - from_dfn: usize, - result: EvaluationResult, -} - -impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { - fn default() -> Self { - Self { - dfn: Cell::new(0), - reached_depth: Cell::new(std::usize::MAX), - map: Default::default(), - } - } -} - -impl<'tcx> ProvisionalEvaluationCache<'tcx> { - /// Get the next DFN in sequence (basically a counter). - fn next_dfn(&self) -> usize { - let result = self.dfn.get(); - self.dfn.set(result + 1); - result - } - - /// Check the provisional cache for any result for - /// `fresh_trait_ref`. If there is a hit, then you must consider - /// it an access to the stack slots at depth - /// `self.current_reached_depth()` and above. - fn get_provisional(&self, fresh_trait_ref: ty::PolyTraitRef<'tcx>) -> Option { - debug!( - "get_provisional(fresh_trait_ref={:?}) = {:#?} with reached-depth {}", - fresh_trait_ref, - self.map.borrow().get(&fresh_trait_ref), - self.reached_depth.get(), - ); - Some(self.map.borrow().get(&fresh_trait_ref)?.result) - } - - /// Current value of the `reached_depth` counter -- all the - /// provisional cache entries are dependent on the item at this - /// depth. - fn current_reached_depth(&self) -> usize { - self.reached_depth.get() - } - - /// Insert a provisional result into the cache. The result came - /// from the node with the given DFN. It accessed a minimum depth - /// of `reached_depth` to compute. It evaluated `fresh_trait_ref` - /// and resulted in `result`. - fn insert_provisional( - &self, - from_dfn: usize, - reached_depth: usize, - fresh_trait_ref: ty::PolyTraitRef<'tcx>, - result: EvaluationResult, - ) { - debug!( - "insert_provisional(from_dfn={}, reached_depth={}, fresh_trait_ref={:?}, result={:?})", - from_dfn, reached_depth, fresh_trait_ref, result, - ); - let r_d = self.reached_depth.get(); - self.reached_depth.set(r_d.min(reached_depth)); - - debug!("insert_provisional: reached_depth={:?}", self.reached_depth.get()); - - self.map.borrow_mut().insert(fresh_trait_ref, ProvisionalEvaluation { from_dfn, result }); - } - - /// Invoked when the node with dfn `dfn` does not get a successful - /// result. This will clear out any provisional cache entries - /// that were added since `dfn` was created. This is because the - /// provisional entries are things which must assume that the - /// things on the stack at the time of their creation succeeded -- - /// since the failing node is presently at the top of the stack, - /// these provisional entries must either depend on it or some - /// ancestor of it. - fn on_failure(&self, dfn: usize) { - debug!("on_failure(dfn={:?})", dfn,); - self.map.borrow_mut().retain(|key, eval| { - if !eval.from_dfn >= dfn { - debug!("on_failure: removing {:?}", key); - false - } else { - true - } - }); - } - - /// Invoked when the node at depth `depth` completed without - /// depending on anything higher in the stack (if that completion - /// was a failure, then `on_failure` should have been invoked - /// already). The callback `op` will be invoked for each - /// provisional entry that we can now confirm. - fn on_completion( - &self, - depth: usize, - mut op: impl FnMut(ty::PolyTraitRef<'tcx>, EvaluationResult), - ) { - debug!("on_completion(depth={}, reached_depth={})", depth, self.reached_depth.get(),); - - if self.reached_depth.get() < depth { - debug!("on_completion: did not yet reach depth to complete"); - return; - } - - for (fresh_trait_ref, eval) in self.map.borrow_mut().drain() { - debug!("on_completion: fresh_trait_ref={:?} eval={:?}", fresh_trait_ref, eval,); - - op(fresh_trait_ref, eval.result); - } - - self.reached_depth.set(std::usize::MAX); - } -} - -#[derive(Copy, Clone)] -struct TraitObligationStackList<'o, 'tcx> { - cache: &'o ProvisionalEvaluationCache<'tcx>, - head: Option<&'o TraitObligationStack<'o, 'tcx>>, -} - -impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> { - fn empty(cache: &'o ProvisionalEvaluationCache<'tcx>) -> TraitObligationStackList<'o, 'tcx> { - TraitObligationStackList { cache, head: None } - } - - fn with(r: &'o TraitObligationStack<'o, 'tcx>) -> TraitObligationStackList<'o, 'tcx> { - TraitObligationStackList { cache: r.cache(), head: Some(r) } - } - - fn head(&self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { - self.head - } - - fn depth(&self) -> usize { - if let Some(head) = self.head { head.depth } else { 0 } - } -} - -impl<'o, 'tcx> Iterator for TraitObligationStackList<'o, 'tcx> { - type Item = &'o TraitObligationStack<'o, 'tcx>; - - fn next(&mut self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { - match self.head { - Some(o) => { - *self = o.previous; - Some(o) - } - None => None, - } - } -} - -impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TraitObligationStack({:?})", self.obligation) - } -} diff --git a/src/librustc_trait_selection/traits/select/candidate_assembly.rs b/src/librustc_trait_selection/traits/select/candidate_assembly.rs new file mode 100644 index 0000000000000..4dab5814f7b7e --- /dev/null +++ b/src/librustc_trait_selection/traits/select/candidate_assembly.rs @@ -0,0 +1,621 @@ +//! Candidate assembly. +//! +//! The selection process begins by examining all in-scope impls, +//! caller obligations, and so forth and assembling a list of +//! candidates. See the [rustc dev guide] for more details. +//! +//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly +use rustc_hir as hir; +use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; +use rustc_middle::ty::{self, TypeFoldable}; +use rustc_target::spec::abi::Abi; + +use crate::traits::{util, SelectionResult}; + +use super::BuiltinImplConditions; +use super::SelectionCandidate::{self, *}; +use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; + +impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + pub(super) fn candidate_from_obligation<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + // Watch out for overflow. This intentionally bypasses (and does + // not update) the cache. + self.check_recursion_limit(&stack.obligation, &stack.obligation)?; + + // Check the cache. Note that we freshen the trait-ref + // 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); + debug!( + "candidate_from_obligation(cache_fresh_trait_pred={:?}, obligation={:?})", + cache_fresh_trait_pred, stack + ); + debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); + + if let Some(c) = + self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred) + { + debug!("CACHE HIT: SELECT({:?})={:?}", cache_fresh_trait_pred, c); + return c; + } + + // If no match, compute result and insert into cache. + // + // FIXME(nikomatsakis) -- this cache is not taking into + // account cycles that may have occurred in forming the + // candidate. I don't know of any specific problems that + // result but it seems awfully suspicious. + let (candidate, dep_node) = + self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); + + debug!("CACHE MISS: SELECT({:?})={:?}", cache_fresh_trait_pred, candidate); + self.insert_candidate_cache( + stack.obligation.param_env, + cache_fresh_trait_pred, + dep_node, + candidate.clone(), + ); + candidate + } + + pub(super) fn assemble_candidates<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> Result, SelectionError<'tcx>> { + let TraitObligationStack { obligation, .. } = *stack; + let obligation = &Obligation { + param_env: obligation.param_env, + cause: obligation.cause.clone(), + recursion_depth: obligation.recursion_depth, + predicate: self.infcx().resolve_vars_if_possible(&obligation.predicate), + }; + + if obligation.predicate.skip_binder().self_ty().is_ty_var() { + // Self is a type variable (e.g., `_: AsRef`). + // + // This is somewhat problematic, as the current scheme can't really + // handle it turning to be a projection. This does end up as truly + // ambiguous in most cases anyway. + // + // Take the fast path out - this also improves + // performance by preventing assemble_candidates_from_impls from + // matching every impl for this trait. + return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true }); + } + + let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; + + self.assemble_candidates_for_trait_alias(obligation, &mut candidates)?; + + // Other bounds. Consider both in-scope bounds from fn decl + // and applicable impls. There is a certain set of precedence rules here. + let def_id = obligation.predicate.def_id(); + let lang_items = self.tcx().lang_items(); + + if lang_items.copy_trait() == Some(def_id) { + debug!("obligation self ty is {:?}", obligation.predicate.skip_binder().self_ty()); + + // User-defined copy impls are permitted, but only for + // structs and enums. + self.assemble_candidates_from_impls(obligation, &mut candidates)?; + + // 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. + let sized_conditions = self.sized_conditions(obligation); + self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?; + } else if lang_items.unsize_trait() == Some(def_id) { + self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } else { + if lang_items.clone_trait() == Some(def_id) { + // Same builtin conditions as `Copy`, i.e., every type which has builtin support + // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` + // types have builtin support for `Clone`. + let clone_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; + } + + self.assemble_generator_candidates(obligation, &mut candidates)?; + self.assemble_closure_candidates(obligation, &mut candidates)?; + self.assemble_fn_pointer_candidates(obligation, &mut candidates)?; + self.assemble_candidates_from_impls(obligation, &mut candidates)?; + self.assemble_candidates_from_object_ty(obligation, &mut candidates); + } + + self.assemble_candidates_from_projected_tys(obligation, &mut candidates); + self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; + // Auto implementations have lower priority, so we only + // consider triggering a default if there is no other impl that can apply. + if candidates.vec.is_empty() { + self.assemble_candidates_from_auto_impls(obligation, &mut candidates)?; + } + debug!("candidate list size: {}", candidates.vec.len()); + Ok(candidates) + } + + fn assemble_candidates_from_projected_tys( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + debug!("assemble_candidates_for_projected_tys({:?})", obligation); + + // Before we go into the whole placeholder thing, just + // quickly check if the self-type is a projection at all. + match obligation.predicate.skip_binder().trait_ref.self_ty().kind { + ty::Projection(_) | ty::Opaque(..) => {} + ty::Infer(ty::TyVar(_)) => { + span_bug!( + obligation.cause.span, + "Self=_ should have been handled by assemble_candidates" + ); + } + _ => return, + } + + let result = self + .infcx + .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); + + if result { + candidates.vec.push(ProjectionCandidate); + } + } + + /// Given an obligation like ``, searches the obligations that the caller + /// supplied to find out whether it is listed among them. + /// + /// Never affects the inference environment. + fn assemble_candidates_from_caller_bounds<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + debug!("assemble_candidates_from_caller_bounds({:?})", stack.obligation); + + let all_bounds = stack + .obligation + .param_env + .caller_bounds + .iter() + .filter_map(|o| o.to_opt_poly_trait_ref()); + + // Micro-optimization: filter out predicates relating to different traits. + let matching_bounds = + all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id()); + + // 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)?; + if wc.may_apply() { + param_candidates.push(ParamCandidate(bound)); + } + } + + candidates.vec.extend(param_candidates); + + Ok(()) + } + + fn assemble_generator_candidates( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) { + return Ok(()); + } + + // Okay to skip binder because the substs on generator types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = *obligation.self_ty().skip_binder(); + match self_ty.kind { + ty::Generator(..) => { + debug!( + "assemble_generator_candidates: self_ty={:?} obligation={:?}", + self_ty, obligation + ); + + candidates.vec.push(GeneratorCandidate); + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_generator_candidates: ambiguous self-type"); + candidates.ambiguous = true; + } + _ => {} + } + + Ok(()) + } + + /// Checks for the artificial impl that the compiler will create for an obligation like `X : + /// FnMut<..>` where `X` is a closure type. + /// + /// Note: the type parameters on a closure candidate are modeled as *output* type + /// parameters and hence do not affect whether this trait is a match or not. They will be + /// unified during the confirmation step. + fn assemble_closure_candidates( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + let kind = match self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) { + Some(k) => k, + None => { + return Ok(()); + } + }; + + // Okay to skip binder because the substs on closure types never + // touch bound regions, they just capture the in-scope + // type/region parameters + match obligation.self_ty().skip_binder().kind { + ty::Closure(_, closure_substs) => { + debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation); + match self.infcx.closure_kind(closure_substs) { + Some(closure_kind) => { + debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); + if closure_kind.extends(kind) { + candidates.vec.push(ClosureCandidate); + } + } + None => { + debug!("assemble_unboxed_candidates: closure_kind not yet known"); + candidates.vec.push(ClosureCandidate); + } + } + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_unboxed_closure_candidates: ambiguous self-type"); + candidates.ambiguous = true; + } + _ => {} + } + + Ok(()) + } + + /// Implements one of the `Fn()` family for a fn pointer. + fn assemble_fn_pointer_candidates( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + // We provide impl of all fn traits for fn pointers. + if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() { + return Ok(()); + } + + // Okay to skip binder because what we are inspecting doesn't involve bound regions. + let self_ty = *obligation.self_ty().skip_binder(); + match self_ty.kind { + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_fn_pointer_candidates: ambiguous self-type"); + candidates.ambiguous = true; // Could wind up being a fn() type. + } + // Provide an impl, but only for suitable `fn` pointers. + ty::FnDef(..) | ty::FnPtr(_) => { + if let ty::FnSig { + unsafety: hir::Unsafety::Normal, + abi: Abi::Rust, + c_variadic: false, + .. + } = self_ty.fn_sig(self.tcx()).skip_binder() + { + candidates.vec.push(FnPointerCandidate); + } + } + _ => {} + } + + Ok(()) + } + + /// Searches for impls that might apply to `obligation`. + fn assemble_candidates_from_impls( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + debug!("assemble_candidates_from_impls(obligation={:?})", obligation); + + // Essentially any user-written impl will match with an error type, + // so creating `ImplCandidates` isn't useful. However, we might + // end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized) + // This helps us avoid overflow: see issue #72839 + // Since compilation is already guaranteed to fail, this is just + // to try to show the 'nicest' possible errors to the user. + if obligation.references_error() { + return Ok(()); + } + + self.tcx().for_each_relevant_impl( + obligation.predicate.def_id(), + obligation.predicate.skip_binder().trait_ref.self_ty(), + |impl_def_id| { + self.infcx.probe(|_| { + if let Ok(_substs) = self.match_impl(impl_def_id, obligation) { + candidates.vec.push(ImplCandidate(impl_def_id)); + } + }); + }, + ); + + Ok(()) + } + + fn assemble_candidates_from_auto_impls( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + // Okay to skip binder here because the tests we do below do not involve bound regions. + let self_ty = *obligation.self_ty().skip_binder(); + debug!("assemble_candidates_from_auto_impls(self_ty={:?})", self_ty); + + let def_id = obligation.predicate.def_id(); + + if self.tcx().trait_is_auto(def_id) { + match self_ty.kind { + ty::Dynamic(..) => { + // For object types, we don't know what the closed + // over types are. This means we conservatively + // say nothing; a candidate may be added by + // `assemble_candidates_from_object_ty`. + } + ty::Foreign(..) => { + // Since the contents of foreign types is unknown, + // we don't add any `..` impl. Default traits could + // still be provided by a manual implementation for + // this trait and type. + } + ty::Param(..) | ty::Projection(..) => { + // In these cases, we don't know what the actual + // type is. Therefore, we cannot break it down + // into its constituent types. So we don't + // consider the `..` impl but instead just add no + // candidates: this means that typeck will only + // succeed if there is another reason to believe + // that this obligation holds. That could be a + // where-clause or, in the case of an object type, + // it could be that the object type lists the + // trait (e.g., `Foo+Send : Send`). See + // `compile-fail/typeck-default-trait-impl-send-param.rs` + // for an example of a test case that exercises + // this path. + } + ty::Infer(ty::TyVar(_)) => { + // The auto impl might apply; we don't know. + candidates.ambiguous = true; + } + ty::Generator(_, _, movability) + if self.tcx().lang_items().unpin_trait() == Some(def_id) => + { + match movability { + hir::Movability::Static => { + // Immovable generators are never `Unpin`, so + // suppress the normal auto-impl candidate for it. + } + hir::Movability::Movable => { + // Movable generators are always `Unpin`, so add an + // unconditional builtin candidate. + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + } + } + + _ => candidates.vec.push(AutoImplCandidate(def_id)), + } + } + + Ok(()) + } + + /// Searches for impls that might apply to `obligation`. + fn assemble_candidates_from_object_ty( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + debug!( + "assemble_candidates_from_object_ty(self_ty={:?})", + obligation.self_ty().skip_binder() + ); + + self.infcx.probe(|_snapshot| { + // The code below doesn't care about regions, and the + // self-ty here doesn't escape this probe, so just erase + // any LBR. + let self_ty = self.tcx().erase_late_bound_regions(&obligation.self_ty()); + let poly_trait_ref = match self_ty.kind { + ty::Dynamic(ref data, ..) => { + if data.auto_traits().any(|did| did == obligation.predicate.def_id()) { + debug!( + "assemble_candidates_from_object_ty: matched builtin bound, \ + pushing candidate" + ); + candidates.vec.push(BuiltinObjectCandidate); + return; + } + + if let Some(principal) = data.principal() { + if !self.infcx.tcx.features().object_safe_for_dispatch { + principal.with_self_ty(self.tcx(), self_ty) + } else if self.tcx().is_object_safe(principal.def_id()) { + principal.with_self_ty(self.tcx(), self_ty) + } else { + return; + } + } else { + // Only auto trait bounds exist. + return; + } + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_candidates_from_object_ty: ambiguous"); + candidates.ambiguous = true; // could wind up being an object type + return; + } + _ => return, + }; + + debug!("assemble_candidates_from_object_ty: poly_trait_ref={:?}", poly_trait_ref); + + // Count only those upcast versions that match the trait-ref + // we are looking for. Specifically, do not only check for the + // correct trait, but also the correct type parameters. + // For example, we may be trying to upcast `Foo` to `Bar`, + // but `Foo` is declared as `trait Foo: Bar`. + let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref) + .filter(|upcast_trait_ref| { + self.infcx + .probe(|_| self.match_poly_trait_ref(obligation, *upcast_trait_ref).is_ok()) + }) + .count(); + + if upcast_trait_refs > 1 { + // Can be upcast in many ways; need more type information. + candidates.ambiguous = true; + } else if upcast_trait_refs == 1 { + candidates.vec.push(ObjectCandidate); + } + }) + } + + /// Searches for unsizing that might apply to `obligation`. + fn assemble_candidates_for_unsizing( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // We currently never consider higher-ranked obligations e.g. + // `for<'a> &'a T: Unsize` to be implemented. This is not + // because they are a priori invalid, and we could potentially add support + // for them later, it's just that there isn't really a strong need for it. + // A `T: Unsize` obligation is always used as part of a `T: CoerceUnsize` + // impl, and those are generally applied to concrete types. + // + // That said, one might try to write a fn with a where clause like + // for<'a> Foo<'a, T>: Unsize> + // where the `'a` is kind of orthogonal to the relevant part of the `Unsize`. + // Still, you'd be more likely to write that where clause as + // T: Trait + // so it seems ok if we (conservatively) fail to accept that `Unsize` + // obligation above. Should be possible to extend this in the future. + let source = match obligation.self_ty().no_bound_vars() { + Some(t) => t, + None => { + // Don't add any candidates if there are bound regions. + return; + } + }; + let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); + + debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target); + + let may_apply = match (&source.kind, &target.kind) { + // Trait+Kx+'a -> Trait+Ky+'b (upcasts). + (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { + // Upcasts permit two things: + // + // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` + // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` + // + // Note that neither of these changes requires any + // change at runtime. Eventually this will be + // generalized. + // + // We always upcast when we can because of reason + // #2 (region bounds). + data_a.principal_def_id() == data_b.principal_def_id() + && data_b + .auto_traits() + // All of a's auto traits need to be in b's auto traits. + .all(|b| data_a.auto_traits().any(|a| a == b)) + } + + // `T` -> `Trait` + (_, &ty::Dynamic(..)) => true, + + // Ambiguous handling is below `T` -> `Trait`, because inference + // variables can still implement `Unsize` and nested + // obligations will have the final say (likely deferred). + (&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => { + debug!("assemble_candidates_for_unsizing: ambiguous"); + candidates.ambiguous = true; + false + } + + // `[T; n]` -> `[T]` + (&ty::Array(..), &ty::Slice(_)) => true, + + // `Struct` -> `Struct` + (&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => { + def_id_a == def_id_b + } + + // `(.., T)` -> `(.., U)` + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => tys_a.len() == tys_b.len(), + + _ => false, + }; + + if may_apply { + candidates.vec.push(BuiltinUnsizeCandidate); + } + } + + fn assemble_candidates_for_trait_alias( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + // Okay to skip binder here because the tests we do below do not involve bound regions. + let self_ty = *obligation.self_ty().skip_binder(); + debug!("assemble_candidates_for_trait_alias(self_ty={:?})", self_ty); + + let def_id = obligation.predicate.def_id(); + + if self.tcx().is_trait_alias(def_id) { + candidates.vec.push(TraitAliasCandidate(def_id)); + } + + Ok(()) + } + + /// Assembles the trait which are built-in to the language itself: + /// `Copy`, `Clone` and `Sized`. + fn assemble_builtin_bound_candidates( + &mut self, + conditions: BuiltinImplConditions<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + match conditions { + BuiltinImplConditions::Where(nested) => { + debug!("builtin_bound: nested={:?}", nested); + candidates + .vec + .push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() }); + } + BuiltinImplConditions::None => {} + BuiltinImplConditions::Ambiguous => { + debug!("assemble_builtin_bound_candidates: ambiguous builtin"); + candidates.ambiguous = true; + } + } + + Ok(()) + } +} diff --git a/src/librustc_trait_selection/traits/select/confirmation.rs b/src/librustc_trait_selection/traits/select/confirmation.rs new file mode 100644 index 0000000000000..834bf17227d2e --- /dev/null +++ b/src/librustc_trait_selection/traits/select/confirmation.rs @@ -0,0 +1,810 @@ +//! Confirmation. +//! +//! Confirmation unifies the output type parameters of the trait +//! with the values found in the obligation, possibly yielding a +//! type error. See the [rustc dev guide] for more details. +//! +//! [rustc dev guide]: +//! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_hir::lang_items; +use rustc_index::bit_set::GrowableBitSet; +use rustc_infer::infer::InferOk; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; +use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{ToPolyTraitRef, ToPredicate, WithConstness}; +use rustc_span::def_id::DefId; + +use crate::traits::project::{self, normalize_with_depth}; +use crate::traits::select::TraitObligationExt; +use crate::traits::util; +use crate::traits::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; +use crate::traits::Normalized; +use crate::traits::OutputTypeParameterMismatch; +use crate::traits::Selection; +use crate::traits::TraitNotObjectSafe; +use crate::traits::{BuiltinDerivedObligation, ImplDerivedObligation}; +use crate::traits::{ + ImplSourceAutoImpl, ImplSourceBuiltin, ImplSourceClosure, ImplSourceDiscriminantKind, + ImplSourceFnPointer, ImplSourceGenerator, ImplSourceObject, ImplSourceParam, + ImplSourceTraitAlias, ImplSourceUserDefined, +}; +use crate::traits::{ + ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, + ImplSourceDiscriminantKindData, ImplSourceFnPointerData, ImplSourceGeneratorData, + ImplSourceObjectData, ImplSourceTraitAliasData, ImplSourceUserDefinedData, +}; +use crate::traits::{ObjectCastObligation, PredicateObligation, TraitObligation}; +use crate::traits::{Obligation, ObligationCause}; +use crate::traits::{SelectionError, Unimplemented}; + +use super::BuiltinImplConditions; +use super::SelectionCandidate::{self, *}; +use super::SelectionContext; + +use std::iter; + +impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + pub(super) fn confirm_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + candidate: SelectionCandidate<'tcx>, + ) -> Result, SelectionError<'tcx>> { + debug!("confirm_candidate({:?}, {:?})", obligation, candidate); + + match candidate { + BuiltinCandidate { has_nested } => { + let data = self.confirm_builtin_candidate(obligation, has_nested); + Ok(ImplSourceBuiltin(data)) + } + + ParamCandidate(param) => { + let obligations = self.confirm_param_candidate(obligation, param); + Ok(ImplSourceParam(obligations)) + } + + ImplCandidate(impl_def_id) => { + Ok(ImplSourceUserDefined(self.confirm_impl_candidate(obligation, impl_def_id))) + } + + AutoImplCandidate(trait_def_id) => { + let data = self.confirm_auto_impl_candidate(obligation, trait_def_id); + Ok(ImplSourceAutoImpl(data)) + } + + ProjectionCandidate => { + self.confirm_projection_candidate(obligation); + Ok(ImplSourceParam(Vec::new())) + } + + ClosureCandidate => { + let vtable_closure = self.confirm_closure_candidate(obligation)?; + Ok(ImplSourceClosure(vtable_closure)) + } + + GeneratorCandidate => { + let vtable_generator = self.confirm_generator_candidate(obligation)?; + Ok(ImplSourceGenerator(vtable_generator)) + } + + FnPointerCandidate => { + let data = self.confirm_fn_pointer_candidate(obligation)?; + Ok(ImplSourceFnPointer(data)) + } + + DiscriminantKindCandidate => { + Ok(ImplSourceDiscriminantKind(ImplSourceDiscriminantKindData)) + } + + TraitAliasCandidate(alias_def_id) => { + let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); + Ok(ImplSourceTraitAlias(data)) + } + + ObjectCandidate => { + let data = self.confirm_object_candidate(obligation); + Ok(ImplSourceObject(data)) + } + + BuiltinObjectCandidate => { + // This indicates something like `Trait + Send: Send`. In this case, we know that + // this holds because that's what the object type is telling us, and there's really + // no additional obligations to prove and no types in particular to unify, etc. + Ok(ImplSourceParam(Vec::new())) + } + + BuiltinUnsizeCandidate => { + let data = self.confirm_builtin_unsize_candidate(obligation)?; + Ok(ImplSourceBuiltin(data)) + } + } + } + + fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) { + self.infcx.commit_unconditionally(|_| { + let result = self.match_projection_obligation_against_definition_bounds(obligation); + assert!(result); + }) + } + + fn confirm_param_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + param: ty::PolyTraitRef<'tcx>, + ) -> Vec> { + debug!("confirm_param_candidate({:?},{:?})", obligation, param); + + // During evaluation, we already checked that this + // 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) { + Ok(obligations) => obligations, + Err(()) => { + bug!( + "Where clause `{:?}` was applicable to `{:?}` but now is not", + param, + obligation + ); + } + } + } + + fn confirm_builtin_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + has_nested: bool, + ) -> ImplSourceBuiltinData> { + debug!("confirm_builtin_candidate({:?}, {:?})", obligation, has_nested); + + let lang_items = self.tcx().lang_items(); + let obligations = if has_nested { + let trait_def = obligation.predicate.def_id(); + let conditions = if Some(trait_def) == lang_items.sized_trait() { + self.sized_conditions(obligation) + } else if Some(trait_def) == lang_items.copy_trait() { + self.copy_clone_conditions(obligation) + } else if Some(trait_def) == lang_items.clone_trait() { + self.copy_clone_conditions(obligation) + } else { + bug!("unexpected builtin trait {:?}", trait_def) + }; + let nested = match conditions { + BuiltinImplConditions::Where(nested) => nested, + _ => bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation), + }; + + let cause = obligation.derived_cause(BuiltinDerivedObligation); + ensure_sufficient_stack(|| { + self.collect_predicates_for_types( + obligation.param_env, + cause, + obligation.recursion_depth + 1, + trait_def, + nested, + ) + }) + } else { + vec![] + }; + + debug!("confirm_builtin_candidate: obligations={:?}", obligations); + + ImplSourceBuiltinData { nested: obligations } + } + + /// This handles the case where a `auto trait Foo` impl is being used. + /// The idea is that the impl applies to `X : Foo` if the following conditions are met: + /// + /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds + /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds. + fn confirm_auto_impl_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + trait_def_id: DefId, + ) -> ImplSourceAutoImplData> { + debug!("confirm_auto_impl_candidate({:?}, {:?})", obligation, trait_def_id); + + let types = obligation.predicate.map_bound(|inner| { + let self_ty = self.infcx.shallow_resolve(inner.self_ty()); + self.constituent_types_for_ty(self_ty) + }); + self.vtable_auto_impl(obligation, trait_def_id, types) + } + + /// See `confirm_auto_impl_candidate`. + fn vtable_auto_impl( + &mut self, + obligation: &TraitObligation<'tcx>, + trait_def_id: DefId, + nested: ty::Binder>>, + ) -> ImplSourceAutoImplData> { + 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 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); + + debug!("vtable_auto_impl: obligations={:?}", obligations); + + ImplSourceAutoImplData { trait_def_id, nested: obligations } + }) + } + + fn confirm_impl_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + impl_def_id: DefId, + ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> { + debug!("confirm_impl_candidate({:?},{:?})", obligation, impl_def_id); + + // First, create the substitutions by matching the impl again, + // this time not in a probe. + self.infcx.commit_unconditionally(|_| { + let substs = self.rematch_impl(impl_def_id, obligation); + debug!("confirm_impl_candidate: substs={:?}", substs); + let cause = obligation.derived_cause(ImplDerivedObligation); + ensure_sufficient_stack(|| { + self.vtable_impl( + impl_def_id, + substs, + cause, + obligation.recursion_depth + 1, + obligation.param_env, + ) + }) + }) + } + + fn vtable_impl( + &mut self, + impl_def_id: DefId, + mut substs: Normalized<'tcx, SubstsRef<'tcx>>, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> { + debug!( + "vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={})", + impl_def_id, substs, recursion_depth, + ); + + let mut impl_obligations = self.impl_or_trait_obligations( + cause, + recursion_depth, + param_env, + impl_def_id, + &substs.value, + ); + + debug!( + "vtable_impl: impl_def_id={:?} impl_obligations={:?}", + impl_def_id, impl_obligations + ); + + // Because of RFC447, the impl-trait-ref and obligations + // are sufficient to determine the impl substs, without + // relying on projections in the impl-trait-ref. + // + // e.g., `impl> Foo<::T> for V` + impl_obligations.append(&mut substs.obligations); + + ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: impl_obligations } + } + + fn confirm_object_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> ImplSourceObjectData<'tcx, PredicateObligation<'tcx>> { + debug!("confirm_object_candidate({:?})", obligation); + + // FIXME(nmatsakis) skipping binder here seems wrong -- we should + // probably flatten the binder from the obligation and the binder + // from the object. Have to try to make a broken test case that + // results. + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); + let poly_trait_ref = match self_ty.kind { + ty::Dynamic(ref data, ..) => data + .principal() + .unwrap_or_else(|| { + span_bug!(obligation.cause.span, "object candidate with no principal") + }) + .with_self_ty(self.tcx(), self_ty), + _ => span_bug!(obligation.cause.span, "object candidate with non-object"), + }; + + let mut upcast_trait_ref = None; + let mut nested = vec![]; + let vtable_base; + + { + let tcx = self.tcx(); + + // We want to find the first supertrait in the list of + // supertraits that we can unify with, and do that + // unification. We know that there is exactly one in the list + // where we can unify, because otherwise select would have + // reported an ambiguity. (When we do find a match, also + // record it for later.) + let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(|&t| { + match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) { + Ok(obligations) => { + upcast_trait_ref = Some(t); + nested.extend(obligations); + false + } + Err(_) => true, + } + }); + + // Additionally, for each of the non-matching predicates that + // we pass over, we sum up the set of number of vtable + // entries, so that we can compute the offset for the selected + // trait. + vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum(); + } + + ImplSourceObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested } + } + + fn confirm_fn_pointer_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result>, SelectionError<'tcx>> + { + debug!("confirm_fn_pointer_candidate({:?})", obligation); + + // Okay to skip binder; it is reintroduced below. + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); + let sig = self_ty.fn_sig(self.tcx()); + let trait_ref = closure_trait_ref_and_return_type( + self.tcx(), + obligation.predicate.def_id(), + self_ty, + sig, + util::TupleArgumentsFlag::Yes, + ) + .map_bound(|(trait_ref, _)| 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(), + obligation.param_env, + obligation.predicate.to_poly_trait_ref(), + trait_ref, + )?; + Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested: obligations }) + } + + fn confirm_trait_alias_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + alias_def_id: DefId, + ) -> ImplSourceTraitAliasData<'tcx, PredicateObligation<'tcx>> { + debug!("confirm_trait_alias_candidate({:?}, {:?})", obligation, alias_def_id); + + self.infcx.commit_unconditionally(|_| { + let (predicate, _) = + self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); + let trait_ref = predicate.trait_ref; + let trait_def_id = trait_ref.def_id; + let substs = trait_ref.substs; + + let trait_obligations = self.impl_or_trait_obligations( + obligation.cause.clone(), + obligation.recursion_depth, + obligation.param_env, + trait_def_id, + &substs, + ); + + debug!( + "confirm_trait_alias_candidate: trait_def_id={:?} trait_obligations={:?}", + trait_def_id, trait_obligations + ); + + ImplSourceTraitAliasData { alias_def_id, substs, nested: trait_obligations } + }) + } + + fn confirm_generator_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result>, SelectionError<'tcx>> + { + // Okay to skip binder because the substs on generator types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); + let (generator_def_id, substs) = match self_ty.kind { + ty::Generator(id, substs, _) => (id, substs), + _ => bug!("closure candidate for non-closure {:?}", obligation), + }; + + 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 } = 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={:?}, \ + trait_ref={:?}, obligations={:?})", + generator_def_id, trait_ref, obligations + ); + + obligations.extend(self.confirm_poly_trait_refs( + obligation.cause.clone(), + obligation.param_env, + obligation.predicate.to_poly_trait_ref(), + trait_ref, + )?); + + Ok(ImplSourceGeneratorData { generator_def_id, substs, nested: obligations }) + } + + fn confirm_closure_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result>, SelectionError<'tcx>> { + debug!("confirm_closure_candidate({:?})", obligation); + + let kind = self + .tcx() + .fn_trait_kind_from_lang_item(obligation.predicate.def_id()) + .unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation)); + + // Okay to skip binder because the substs on closure types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder()); + let (closure_def_id, substs) = match self_ty.kind { + ty::Closure(id, substs) => (id, substs), + _ => bug!("closure candidate for non-closure {:?}", obligation), + }; + + let trait_ref = self.closure_trait_ref_unnormalized(obligation, substs); + 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={:?})", + closure_def_id, trait_ref, obligations + ); + + obligations.extend(self.confirm_poly_trait_refs( + obligation.cause.clone(), + obligation.param_env, + obligation.predicate.to_poly_trait_ref(), + trait_ref, + )?); + + // FIXME: Chalk + + if !self.tcx().sess.opts.debugging_opts.chalk { + obligations.push(Obligation::new( + obligation.cause.clone(), + obligation.param_env, + ty::PredicateKind::ClosureKind(closure_def_id, substs, kind) + .to_predicate(self.tcx()), + )); + } + + Ok(ImplSourceClosureData { closure_def_id, substs, nested: obligations }) + } + + /// In the case of closure types and fn pointers, + /// we currently treat the input type parameters on the trait as + /// outputs. This means that when we have a match we have only + /// considered the self type, so we have to go back and make sure + /// to relate the argument types too. This is kind of wrong, but + /// since we control the full set of impls, also not that wrong, + /// and it DOES yield better error messages (since we don't report + /// errors as if there is no applicable impl, but rather report + /// errors are about mismatched argument types. + /// + /// Here is an example. Imagine we have a closure expression + /// and we desugared it so that the type of the expression is + /// `Closure`, and `Closure` expects `i32` as argument. Then it + /// is "as if" the compiler generated this impl: + /// + /// impl Fn(i32) for Closure { ... } + /// + /// Now imagine our obligation is `Closure: Fn(usize)`. So far + /// we have matched the self type `Closure`. At this point we'll + /// compare the `i32` to `usize` and generate an error. + /// + /// Note that this checking occurs *after* the impl has selected, + /// because these output type parameters should not affect the + /// selection of the impl. Therefore, if there is a mismatch, we + /// report an error to the user. + fn confirm_poly_trait_refs( + &mut self, + obligation_cause: ObligationCause<'tcx>, + obligation_param_env: ty::ParamEnv<'tcx>, + obligation_trait_ref: ty::PolyTraitRef<'tcx>, + expected_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result>, SelectionError<'tcx>> { + self.infcx + .at(&obligation_cause, obligation_param_env) + .sup(obligation_trait_ref, expected_trait_ref) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|e| OutputTypeParameterMismatch(expected_trait_ref, obligation_trait_ref, e)) + } + + fn confirm_builtin_unsize_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result>, SelectionError<'tcx>> { + let tcx = self.tcx(); + + // `assemble_candidates_for_unsizing` should ensure there are no late-bound + // regions here. See the comment there for more details. + let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); + let target = self.infcx.shallow_resolve(target); + + debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target); + + let mut nested = vec![]; + match (&source.kind, &target.kind) { + // Trait+Kx+'a -> Trait+Ky+'b (upcasts). + (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { + // See `assemble_candidates_for_unsizing` for more info. + let existential_predicates = data_a.map_bound(|data_a| { + let iter = data_a + .principal() + .map(ty::ExistentialPredicate::Trait) + .into_iter() + .chain(data_a.projection_bounds().map(ty::ExistentialPredicate::Projection)) + .chain(data_b.auto_traits().map(ty::ExistentialPredicate::AutoTrait)); + tcx.mk_existential_predicates(iter) + }); + let source_trait = tcx.mk_dynamic(existential_predicates, r_b); + + // Require that the traits involved in this upcast are **equal**; + // only the **lifetime bound** is changed. + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .sup(target, source_trait) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // Register one obligation for 'a: 'b. + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObjectCastObligation(target), + ); + let outlives = ty::OutlivesPredicate(r_a, r_b); + nested.push(Obligation::with_depth( + cause, + obligation.recursion_depth + 1, + obligation.param_env, + ty::Binder::bind(outlives).to_predicate(tcx), + )); + } + + // `T` -> `Trait` + (_, &ty::Dynamic(ref data, r)) => { + let mut object_dids = data.auto_traits().chain(data.principal_def_id()); + if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) { + return Err(TraitNotObjectSafe(did)); + } + + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObjectCastObligation(target), + ); + + let predicate_to_obligation = |predicate| { + Obligation::with_depth( + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + ) + }; + + // Create obligations: + // - Casting `T` to `Trait` + // - For all the various builtin bounds attached to the object cast. (In other + // words, if the object type is `Foo + Send`, this would create an obligation for + // the `Send` check.) + // - Projection predicates + nested.extend( + data.iter().map(|predicate| { + predicate_to_obligation(predicate.with_self_ty(tcx, source)) + }), + ); + + // We can only make objects from sized types. + let tr = ty::TraitRef::new( + tcx.require_lang_item(lang_items::SizedTraitLangItem, None), + tcx.mk_substs_trait(source, &[]), + ); + nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx))); + + // If the type is `Foo + 'a`, ensure that the type + // being cast to `Foo + 'a` outlives `'a`: + let outlives = ty::OutlivesPredicate(source, r); + nested.push(predicate_to_obligation(ty::Binder::dummy(outlives).to_predicate(tcx))); + } + + // `[T; n]` -> `[T]` + (&ty::Array(a, _), &ty::Slice(b)) => { + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(b, a) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + } + + // `Struct` -> `Struct` + (&ty::Adt(def, substs_a), &ty::Adt(_, substs_b)) => { + let maybe_unsizing_param_idx = |arg: GenericArg<'tcx>| match arg.unpack() { + GenericArgKind::Type(ty) => match ty.kind { + 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, + }, + }; + + // 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 arg in tail_field_ty.walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.insert(i); + found = true; + } + } + if !found { + 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 `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 + // 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) + .eq(target, new_struct) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // 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, + source_tail, + &[target_tail.into()], + )); + } + + // `(.., T)` -> `(.., U)` + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { + assert_eq!(tys_a.len(), tys_b.len()); + + // The last field of the tuple has to exist. + let (&a_last, a_mid) = tys_a.split_last().ok_or(Unimplemented)?; + let &b_last = tys_b.last().unwrap(); + + // Check that the source tuple with the target's + // last element is equal to the target. + let new_tuple = tcx.mk_tup( + a_mid.iter().map(|k| k.expect_ty()).chain(iter::once(b_last.expect_ty())), + ); + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(target, new_tuple) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // Construct the nested `T: Unsize` predicate. + 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!(), + }; + + Ok(ImplSourceBuiltinData { nested }) + } +} diff --git a/src/librustc_trait_selection/traits/select/mod.rs b/src/librustc_trait_selection/traits/select/mod.rs new file mode 100644 index 0000000000000..cff5efbfd0fd1 --- /dev/null +++ b/src/librustc_trait_selection/traits/select/mod.rs @@ -0,0 +1,2414 @@ +//! Candidate selection. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection + +use self::EvaluationResult::*; +use self::SelectionCandidate::*; + +use super::coherence::{self, Conflict}; +use super::project; +use super::project::normalize_with_depth_to; +use super::util; +use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; +use super::wf; +use super::DerivedObligationCause; +use super::Obligation; +use super::ObligationCauseCode; +use super::Selection; +use super::SelectionResult; +use super::TraitQueryMode; +use super::{Normalized, ProjectionCacheKey}; +use super::{ObligationCause, PredicateObligation, TraitObligation}; +use super::{Overflow, SelectionError, Unimplemented}; + +use crate::infer::{InferCtxt, InferOk, TypeFreshener}; +use crate::traits::error_reporting::InferCtxtExt; +use crate::traits::project::ProjectionCacheKeyExt; +use rustc_ast::attr; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::fast_reject; +use rustc_middle::ty::relate::TypeRelation; +use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef}; +use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable}; +use rustc_span::symbol::sym; + +use std::cell::{Cell, RefCell}; +use std::cmp; +use std::fmt::{self, Display}; +use std::iter; +use std::rc::Rc; + +pub use rustc_middle::traits::select::*; + +mod candidate_assembly; +mod confirmation; + +pub struct SelectionContext<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + + /// Freshener used specifically for entries on the obligation + /// stack. This ensures that all entries on the stack at one time + /// will have the same set of placeholder entries, which is + /// important for checking for trait bounds that recursively + /// require themselves. + freshener: TypeFreshener<'cx, 'tcx>, + + /// If `true`, indicates that the evaluation should be conservative + /// and consider the possibility of types outside this crate. + /// This comes up primarily when resolving ambiguity. Imagine + /// there is some trait reference `$0: Bar` where `$0` is an + /// inference variable. If `intercrate` is true, then we can never + /// say for sure that this reference is not implemented, even if + /// there are *no impls at all for `Bar`*, because `$0` could be + /// bound to some type that in a downstream crate that implements + /// `Bar`. This is the suitable mode for coherence. Elsewhere, + /// though, we set this to false, because we are only interested + /// in types that the user could actually have written --- in + /// other words, we consider `$0: Bar` to be unimplemented if + /// there is no type that the user could *actually name* that + /// would satisfy it. This avoids crippling inference, basically. + intercrate: bool, + + intercrate_ambiguity_causes: Option>, + + /// Controls whether or not to filter out negative impls when selecting. + /// This is used in librustdoc to distinguish between the lack of an impl + /// and a negative impl + allow_negative_impls: bool, + + /// The mode that trait queries run in, which informs our error handling + /// policy. In essence, canonicalized queries need their errors propagated + /// rather than immediately reported because we do not have accurate spans. + query_mode: TraitQueryMode, +} + +// A stack that walks back up the stack frame. +struct TraitObligationStack<'prev, 'tcx> { + obligation: &'prev TraitObligation<'tcx>, + + /// The trait ref from `obligation` but "freshened" with the + /// selection-context's freshener. Used to check for recursion. + fresh_trait_ref: ty::PolyTraitRef<'tcx>, + + /// Starts out equal to `depth` -- if, during evaluation, we + /// encounter a cycle, then we will set this flag to the minimum + /// depth of that cycle for all participants in the cycle. These + /// participants will then forego caching their results. This is + /// not the most efficient solution, but it addresses #60010. The + /// problem we are trying to prevent: + /// + /// - If you have `A: AutoTrait` requires `B: AutoTrait` and `C: NonAutoTrait` + /// - `B: AutoTrait` requires `A: AutoTrait` (coinductive cycle, ok) + /// - `C: NonAutoTrait` requires `A: AutoTrait` (non-coinductive cycle, not ok) + /// + /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait` + /// is `EvaluatedToOk`; this is because they were only considered + /// ok on the premise that if `A: AutoTrait` held, but we indeed + /// encountered a problem (later on) with `A: AutoTrait. So we + /// currently set a flag on the stack node for `B: AutoTrait` (as + /// well as the second instance of `A: AutoTrait`) to suppress + /// caching. + /// + /// This is a simple, targeted fix. A more-performant fix requires + /// deeper changes, but would permit more caching: we could + /// basically defer caching until we have fully evaluated the + /// tree, and then cache the entire tree at once. In any case, the + /// performance impact here shouldn't be so horrible: every time + /// this is hit, we do cache at least one trait, so we only + /// evaluate each member of a cycle up to N times, where N is the + /// length of the cycle. This means the performance impact is + /// bounded and we shouldn't have any terrible worst-cases. + reached_depth: Cell, + + previous: TraitObligationStackList<'prev, 'tcx>, + + /// The number of parent frames plus one (thus, the topmost frame has depth 1). + depth: usize, + + /// The depth-first number of this node in the search graph -- a + /// pre-order index. Basically, a freshly incremented counter. + dfn: usize, +} + +struct SelectionCandidateSet<'tcx> { + // A list of candidates that definitely apply to the current + // obligation (meaning: types unify). + vec: Vec>, + + // If `true`, then there were candidates that might or might + // not have applied, but we couldn't tell. This occurs when some + // of the input types are type variables, in which case there are + // various "builtin" rules that might or might not trigger. + ambiguous: bool, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +struct EvaluatedCandidate<'tcx> { + candidate: SelectionCandidate<'tcx>, + evaluation: EvaluationResult, +} + +/// When does the builtin impl for `T: Trait` apply? +enum BuiltinImplConditions<'tcx> { + /// The impl is conditional on `T1, T2, ...: Trait`. + Where(ty::Binder>>), + /// There is no built-in impl. There may be some other + /// candidate (a where-clause or user-defined impl). + None, + /// It is unknown whether there is an impl. + Ambiguous, +} + +impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: false, + intercrate_ambiguity_causes: None, + allow_negative_impls: false, + query_mode: TraitQueryMode::Standard, + } + } + + pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: true, + intercrate_ambiguity_causes: None, + allow_negative_impls: false, + query_mode: TraitQueryMode::Standard, + } + } + + pub fn with_negative( + infcx: &'cx InferCtxt<'cx, 'tcx>, + allow_negative_impls: bool, + ) -> SelectionContext<'cx, 'tcx> { + debug!("with_negative({:?})", allow_negative_impls); + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: false, + intercrate_ambiguity_causes: None, + allow_negative_impls, + query_mode: TraitQueryMode::Standard, + } + } + + pub fn with_query_mode( + infcx: &'cx InferCtxt<'cx, 'tcx>, + query_mode: TraitQueryMode, + ) -> SelectionContext<'cx, 'tcx> { + debug!("with_query_mode({:?})", query_mode); + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate: false, + intercrate_ambiguity_causes: None, + allow_negative_impls: false, + query_mode, + } + } + + /// Enables tracking of intercrate ambiguity causes. These are + /// used in coherence to give improved diagnostics. We don't do + /// this until we detect a coherence error because it can lead to + /// false overflow results (#47139) and because it costs + /// computation time. + pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { + assert!(self.intercrate); + assert!(self.intercrate_ambiguity_causes.is_none()); + self.intercrate_ambiguity_causes = Some(vec![]); + debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); + } + + /// Gets the intercrate ambiguity causes collected since tracking + /// was enabled and disables tracking at the same time. If + /// tracking is not enabled, just returns an empty vector. + pub fn take_intercrate_ambiguity_causes(&mut self) -> Vec { + assert!(self.intercrate); + self.intercrate_ambiguity_causes.take().unwrap_or(vec![]) + } + + pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'tcx> { + self.infcx + } + + pub fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + pub fn closure_typer(&self) -> &'cx InferCtxt<'cx, 'tcx> { + self.infcx + } + + /////////////////////////////////////////////////////////////////////////// + // Selection + // + // The selection phase tries to identify *how* an obligation will + // be resolved. For example, it will identify which impl or + // parameter bound is to be used. The process can be inconclusive + // if the self type in the obligation is not fully inferred. Selection + // can result in an error in one of two ways: + // + // 1. If no applicable impl or parameter bound can be found. + // 2. If the output type parameters in the obligation do not match + // those specified by the impl/bound. For example, if the obligation + // is `Vec: Iterable`, but the impl specifies + // `impl Iterable for Vec`, than an error would result. + + /// Attempts to satisfy the obligation. If successful, this will affect the surrounding + /// type environment by performing unification. + pub fn select( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> SelectionResult<'tcx, Selection<'tcx>> { + debug!("select({:?})", obligation); + debug_assert!(!obligation.predicate.has_escaping_bound_vars()); + + let pec = &ProvisionalEvaluationCache::default(); + let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); + + let candidate = match self.candidate_from_obligation(&stack) { + Err(SelectionError::Overflow) => { + // In standard mode, overflow must have been caught and reported + // earlier. + assert!(self.query_mode == TraitQueryMode::Canonical); + return Err(SelectionError::Overflow); + } + Err(e) => { + return Err(e); + } + Ok(None) => { + return Ok(None); + } + Ok(Some(candidate)) => candidate, + }; + + match self.confirm_candidate(obligation, candidate) { + Err(SelectionError::Overflow) => { + assert!(self.query_mode == TraitQueryMode::Canonical); + Err(SelectionError::Overflow) + } + Err(e) => Err(e), + Ok(candidate) => Ok(Some(candidate)), + } + } + + /////////////////////////////////////////////////////////////////////////// + // EVALUATION + // + // Tests whether an obligation can be selected or whether an impl + // can be applied to particular types. It skips the "confirmation" + // step and hence completely ignores output type parameters. + // + // The result is "true" if the obligation *may* hold and "false" if + // we can be sure it does not. + + /// Evaluates whether the obligation `obligation` can be satisfied (by any means). + pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool { + debug!("predicate_may_hold_fatal({:?})", obligation); + + // This fatal query is a stopgap that should only be used in standard mode, + // where we do not expect overflow to be propagated. + assert!(self.query_mode == TraitQueryMode::Standard); + + self.evaluate_root_obligation(obligation) + .expect("Overflow should be caught earlier in standard query mode") + .may_apply() + } + + /// Evaluates whether the obligation `obligation` can be satisfied + /// and returns an `EvaluationResult`. This is meant for the + /// *initial* call. + pub fn evaluate_root_obligation( + &mut self, + obligation: &PredicateObligation<'tcx>, + ) -> Result { + self.evaluation_probe(|this| { + this.evaluate_predicate_recursively( + TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), + obligation.clone(), + ) + }) + } + + fn evaluation_probe( + &mut self, + op: impl FnOnce(&mut Self) -> Result, + ) -> Result { + self.infcx.probe(|snapshot| -> Result { + let result = op(self)?; + + match self.infcx.leak_check(true, snapshot) { + Ok(()) => {} + Err(_) => return Ok(EvaluatedToErr), + } + + match self.infcx.region_constraints_added_in_snapshot(snapshot) { + None => Ok(result), + Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)), + } + }) + } + + /// Evaluates the predicates in `predicates` recursively. Note that + /// this applies projections in the predicates, and therefore + /// is run within an inference probe. + fn evaluate_predicates_recursively<'o, I>( + &mut self, + stack: TraitObligationStackList<'o, 'tcx>, + predicates: I, + ) -> Result + where + I: IntoIterator>, + { + let mut result = EvaluatedToOk; + for obligation in predicates { + let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; + debug!("evaluate_predicate_recursively({:?}) = {:?}", obligation, eval); + if let EvaluatedToErr = eval { + // fast-path - EvaluatedToErr is the top of the lattice, + // so we don't need to look on the other predicates. + return Ok(EvaluatedToErr); + } else { + result = cmp::max(result, eval); + } + } + Ok(result) + } + + fn evaluate_predicate_recursively<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + obligation: PredicateObligation<'tcx>, + ) -> Result { + debug!( + "evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})", + previous_stack.head(), + obligation + ); + + // `previous_stack` stores a `TraitObligatiom`, while `obligation` is + // a `PredicateObligation`. These are distinct types, so we can't + // use any `Option` combinator method that would force them to be + // the same. + match previous_stack.head() { + Some(h) => self.check_recursion_limit(&obligation, h.obligation)?, + None => self.check_recursion_limit(&obligation, &obligation)?, + } + + match obligation.predicate.kind() { + &ty::PredicateKind::Trait(t, _) => { + debug_assert!(!t.has_escaping_bound_vars()); + let obligation = obligation.with(t); + self.evaluate_trait_predicate_recursively(previous_stack, obligation) + } + + &ty::PredicateKind::Subtype(p) => { + // Does this code ever run? + match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { + Some(Ok(InferOk { mut obligations, .. })) => { + self.add_depth(obligations.iter_mut(), obligation.recursion_depth); + self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ) + } + Some(Err(_)) => Ok(EvaluatedToErr), + None => Ok(EvaluatedToAmbig), + } + } + + &ty::PredicateKind::WellFormed(arg) => match wf::obligations( + self.infcx, + obligation.param_env, + obligation.cause.body_id, + arg, + obligation.cause.span, + ) { + Some(mut obligations) => { + self.add_depth(obligations.iter_mut(), obligation.recursion_depth); + self.evaluate_predicates_recursively(previous_stack, obligations.into_iter()) + } + None => Ok(EvaluatedToAmbig), + }, + + ty::PredicateKind::TypeOutlives(..) | ty::PredicateKind::RegionOutlives(..) => { + // We do not consider region relationships when evaluating trait matches. + Ok(EvaluatedToOkModuloRegions) + } + + &ty::PredicateKind::ObjectSafe(trait_def_id) => { + if self.tcx().is_object_safe(trait_def_id) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToErr) + } + } + + &ty::PredicateKind::Projection(data) => { + let project_obligation = obligation.with(data); + match project::poly_project_and_unify_type(self, &project_obligation) { + Ok(Some(mut subobligations)) => { + self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); + let result = self.evaluate_predicates_recursively( + previous_stack, + subobligations.into_iter(), + ); + if let Some(key) = + ProjectionCacheKey::from_poly_projection_predicate(self, data) + { + self.infcx.inner.borrow_mut().projection_cache().complete(key); + } + result + } + Ok(None) => Ok(EvaluatedToAmbig), + Err(_) => Ok(EvaluatedToErr), + } + } + + &ty::PredicateKind::ClosureKind(_, closure_substs, kind) => { + match self.infcx.closure_kind(closure_substs) { + Some(closure_kind) => { + if closure_kind.extends(kind) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToErr) + } + } + None => Ok(EvaluatedToAmbig), + } + } + + &ty::PredicateKind::ConstEvaluatable(def_id, substs) => { + match self.tcx().const_eval_resolve( + obligation.param_env, + def_id, + substs, + None, + None, + ) { + Ok(_) => Ok(EvaluatedToOk), + Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig), + Err(_) => Ok(EvaluatedToErr), + } + } + + ty::PredicateKind::ConstEquate(c1, c2) => { + debug!("evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", c1, c2); + + let evaluate = |c: &'tcx ty::Const<'tcx>| { + if let ty::ConstKind::Unevaluated(def_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) + } + } + } + } + } + + fn evaluate_trait_predicate_recursively<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + mut obligation: TraitObligation<'tcx>, + ) -> Result { + debug!("evaluate_trait_predicate_recursively({:?})", obligation); + + if !self.intercrate + && obligation.is_global() + && obligation.param_env.caller_bounds.iter().all(|bound| bound.needs_subst()) + { + // If a param env has no global bounds, global obligations do not + // depend on its particular value in order to work, so we can clear + // out the param env and get better caching. + debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation); + obligation.param_env = obligation.param_env.without_caller_bounds(); + } + + let stack = self.push_stack(previous_stack, &obligation); + let fresh_trait_ref = stack.fresh_trait_ref; + if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) { + debug!("CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); + return Ok(result); + } + + if let Some(result) = stack.cache().get_provisional(fresh_trait_ref) { + debug!("PROVISIONAL CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); + stack.update_reached_depth(stack.cache().current_reached_depth()); + return Ok(result); + } + + // Check if this is a match for something already on the + // stack. If so, we don't want to insert the result into the + // main cache (it is cycle dependent) nor the provisional + // cache (which is meant for things that have completed but + // for a "backedge" -- this result *is* the backedge). + if let Some(cycle_result) = self.check_evaluation_cycle(&stack) { + return Ok(cycle_result); + } + + let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack)); + let result = result?; + + if !result.must_apply_modulo_regions() { + stack.cache().on_failure(stack.dfn); + } + + let reached_depth = stack.reached_depth.get(); + if reached_depth >= stack.depth { + debug!("CACHE MISS: EVAL({:?})={:?}", fresh_trait_ref, result); + self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result); + + stack.cache().on_completion(stack.depth, |fresh_trait_ref, provisional_result| { + self.insert_evaluation_cache( + obligation.param_env, + fresh_trait_ref, + dep_node, + provisional_result.max(result), + ); + }); + } else { + debug!("PROVISIONAL: {:?}={:?}", fresh_trait_ref, result); + debug!( + "evaluate_trait_predicate_recursively: caching provisionally because {:?} \ + is a cycle participant (at depth {}, reached depth {})", + fresh_trait_ref, stack.depth, reached_depth, + ); + + stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_ref, result); + } + + Ok(result) + } + + /// If there is any previous entry on the stack that precisely + /// matches this obligation, then we can assume that the + /// obligation is satisfied for now (still all other conditions + /// must be met of course). One obvious case this comes up is + /// marker traits like `Send`. Think of a linked list: + /// + /// struct List { data: T, next: Option>> } + /// + /// `Box>` will be `Send` if `T` is `Send` and + /// `Option>>` is `Send`, and in turn + /// `Option>>` is `Send` if `Box>` is + /// `Send`. + /// + /// Note that we do this comparison using the `fresh_trait_ref` + /// fields. Because these have all been freshened using + /// `self.freshener`, we can be sure that (a) this will not + /// affect the inferencer state and (b) that if we see two + /// fresh regions with the same index, they refer to the same + /// unbound type variable. + fn check_evaluation_cycle( + &mut self, + stack: &TraitObligationStack<'_, 'tcx>, + ) -> Option { + if let Some(cycle_depth) = stack + .iter() + .skip(1) // Skip top-most frame. + .find(|prev| { + stack.obligation.param_env == prev.obligation.param_env + && stack.fresh_trait_ref == prev.fresh_trait_ref + }) + .map(|stack| stack.depth) + { + debug!( + "evaluate_stack({:?}) --> recursive at depth {}", + stack.fresh_trait_ref, cycle_depth, + ); + + // If we have a stack like `A B C D E A`, where the top of + // the stack is the final `A`, then this will iterate over + // `A, E, D, C, B` -- i.e., all the participants apart + // from the cycle head. We mark them as participating in a + // cycle. This suppresses caching for those nodes. See + // `in_cycle` field for more details. + stack.update_reached_depth(cycle_depth); + + // Subtle: when checking for a coinductive cycle, we do + // not compare using the "freshened trait refs" (which + // have erased regions) but rather the fully explicit + // trait refs. This is important because it's only a cycle + // if the regions match exactly. + let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth); + let tcx = self.tcx(); + let cycle = cycle.map(|stack| { + ty::PredicateKind::Trait(stack.obligation.predicate, hir::Constness::NotConst) + .to_predicate(tcx) + }); + if self.coinductive_match(cycle) { + debug!("evaluate_stack({:?}) --> recursive, coinductive", stack.fresh_trait_ref); + Some(EvaluatedToOk) + } else { + debug!("evaluate_stack({:?}) --> recursive, inductive", stack.fresh_trait_ref); + Some(EvaluatedToRecur) + } + } else { + None + } + } + + fn evaluate_stack<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> Result { + // In intercrate mode, whenever any of the generics are unbound, + // there can always be an impl. Even if there are no impls in + // this crate, perhaps the type would be unified with + // something from another crate that does provide an impl. + // + // In intra mode, we must still be conservative. The reason is + // that we want to avoid cycles. Imagine an impl like: + // + // impl Eq for Vec + // + // and a trait reference like `$0 : Eq` where `$0` is an + // unbound variable. When we evaluate this trait-reference, we + // will unify `$0` with `Vec<$1>` (for some fresh variable + // `$1`), on the condition that `$1 : Eq`. We will then wind + // up with many candidates (since that are other `Eq` impls + // that apply) and try to winnow things down. This results in + // a recursive evaluation that `$1 : Eq` -- as you can + // imagine, this is just where we started. To avoid that, we + // check for unbound variables and return an ambiguous (hence possible) + // match if we've seen this trait before. + // + // This suffices to allow chains like `FnMut` implemented in + // terms of `Fn` etc, but we could probably make this more + // precise still. + let unbound_input_types = + stack.fresh_trait_ref.skip_binder().substs.types().any(|ty| ty.is_fresh()); + // This check was an imperfect workaround for a bug in the old + // intercrate mode; it should be removed when that goes away. + if unbound_input_types && self.intercrate { + debug!( + "evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous", + stack.fresh_trait_ref + ); + // Heuristics: show the diagnostics when there are no candidates in crate. + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + if let Ok(candidate_set) = self.assemble_candidates(stack) { + if !candidate_set.ambiguous && candidate_set.vec.is_empty() { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let cause = IntercrateAmbiguityCause::DownstreamCrate { + trait_desc: trait_ref.print_only_trait_path().to_string(), + self_desc: if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }, + }; + debug!("evaluate_stack: pushing cause = {:?}", cause); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } + } + } + return Ok(EvaluatedToAmbig); + } + if unbound_input_types + && stack.iter().skip(1).any(|prev| { + stack.obligation.param_env == prev.obligation.param_env + && self.match_fresh_trait_refs( + &stack.fresh_trait_ref, + &prev.fresh_trait_ref, + prev.obligation.param_env, + ) + }) + { + debug!( + "evaluate_stack({:?}) --> unbound argument, recursive --> giving up", + stack.fresh_trait_ref + ); + return Ok(EvaluatedToUnknown); + } + + match self.candidate_from_obligation(stack) { + Ok(Some(c)) => self.evaluate_candidate(stack, &c), + Ok(None) => Ok(EvaluatedToAmbig), + Err(Overflow) => Err(OverflowError), + Err(..) => Ok(EvaluatedToErr), + } + } + + /// For defaulted traits, we use a co-inductive strategy to solve, so + /// that recursion is ok. This routine returns `true` if the top of the + /// stack (`cycle[0]`): + /// + /// - is a defaulted trait, + /// - it also appears in the backtrace at some position `X`, + /// - all the predicates at positions `X..` between `X` and the top are + /// also defaulted traits. + pub fn coinductive_match(&mut self, cycle: I) -> bool + where + I: Iterator>, + { + let mut cycle = cycle; + cycle.all(|predicate| self.coinductive_predicate(predicate)) + } + + fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { + let result = match predicate.kind() { + ty::PredicateKind::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()), + _ => false, + }; + debug!("coinductive_predicate({:?}) = {:?}", predicate, result); + result + } + + /// Further evaluates `candidate` to decide whether all type parameters match and whether nested + /// obligations are met. Returns whether `candidate` remains viable after this further + /// scrutiny. + fn evaluate_candidate<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + candidate: &SelectionCandidate<'tcx>, + ) -> Result { + debug!( + "evaluate_candidate: depth={} candidate={:?}", + stack.obligation.recursion_depth, candidate + ); + let result = self.evaluation_probe(|this| { + let candidate = (*candidate).clone(); + match this.confirm_candidate(stack.obligation, candidate) { + Ok(selection) => this.evaluate_predicates_recursively( + stack.list(), + selection.nested_obligations().into_iter(), + ), + Err(..) => Ok(EvaluatedToErr), + } + })?; + debug!( + "evaluate_candidate: depth={} result={:?}", + stack.obligation.recursion_depth, result + ); + Ok(result) + } + + fn check_evaluation_cache( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Option { + let tcx = self.tcx(); + if self.can_use_global_caches(param_env) { + let cache = tcx.evaluation_cache.hashmap.borrow(); + if let Some(cached) = cache.get(¶m_env.and(trait_ref)) { + return Some(cached.get(tcx)); + } + } + self.infcx + .evaluation_cache + .hashmap + .borrow() + .get(¶m_env.and(trait_ref)) + .map(|v| v.get(tcx)) + } + + fn insert_evaluation_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + dep_node: DepNodeIndex, + result: EvaluationResult, + ) { + // Avoid caching results that depend on more than just the trait-ref + // - the stack can create recursion. + if result.is_stack_dependent() { + return; + } + + if self.can_use_global_caches(param_env) { + if !trait_ref.needs_infer() { + debug!( + "insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", + trait_ref, result, + ); + // This may overwrite the cache with the same value + // FIXME: Due to #50507 this overwrites the different values + // This should be changed to use HashMapExt::insert_same + // when that is fixed + self.tcx() + .evaluation_cache + .hashmap + .borrow_mut() + .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); + return; + } + } + + debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,); + self.infcx + .evaluation_cache + .hashmap + .borrow_mut() + .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, result)); + } + + /// For various reasons, it's possible for a subobligation + /// to have a *lower* recursion_depth than the obligation used to create it. + /// Projection sub-obligations may be returned from the projection cache, + /// which results in obligations with an 'old' `recursion_depth`. + /// Additionally, methods like `wf::obligations` and + /// `InferCtxt.subtype_predicate` produce subobligations without + /// taking in a 'parent' depth, causing the generated subobligations + /// to have a `recursion_depth` of `0`. + /// + /// To ensure that obligation_depth never decreasees, we force all subobligations + /// to have at least the depth of the original obligation. + fn add_depth>>( + &self, + it: I, + min_depth: usize, + ) { + it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); + } + + /// Checks that the recursion limit has not been exceeded. + /// + /// The weird return type of this function allows it to be used with the `try` (`?`) + /// operator within certain functions. + fn check_recursion_limit, V: Display + TypeFoldable<'tcx>>( + &self, + obligation: &Obligation<'tcx, T>, + error_obligation: &Obligation<'tcx, V>, + ) -> Result<(), OverflowError> { + if !self.infcx.tcx.sess.recursion_limit().value_within_limit(obligation.recursion_depth) { + match self.query_mode { + TraitQueryMode::Standard => { + self.infcx().report_overflow_error(error_obligation, true); + } + TraitQueryMode::Canonical => { + return Err(OverflowError); + } + } + } + Ok(()) + } + + fn in_task(&mut self, op: OP) -> (R, DepNodeIndex) + where + OP: FnOnce(&mut Self) -> R, + { + let (result, dep_node) = + self.tcx().dep_graph.with_anon_task(DepKind::TraitSelect, || op(self)); + self.tcx().dep_graph.read_index(dep_node); + (result, dep_node) + } + + // Treat negative impls as unimplemented, and reservation impls as ambiguity. + fn filter_negative_and_reservation_impls( + &mut self, + candidate: SelectionCandidate<'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let ImplCandidate(def_id) = candidate { + let tcx = self.tcx(); + match tcx.impl_polarity(def_id) { + ty::ImplPolarity::Negative if !self.allow_negative_impls => { + return Err(Unimplemented); + } + ty::ImplPolarity::Reservation => { + if let Some(intercrate_ambiguity_clauses) = + &mut self.intercrate_ambiguity_causes + { + let attrs = tcx.get_attrs(def_id); + let attr = attr::find_by_name(&attrs, sym::rustc_reservation_impl); + let value = attr.and_then(|a| a.value_str()); + if let Some(value) = value { + debug!( + "filter_negative_and_reservation_impls: \ + reservation impl ambiguity on {:?}", + def_id + ); + intercrate_ambiguity_clauses.push( + IntercrateAmbiguityCause::ReservationImpl { + message: value.to_string(), + }, + ); + } + } + return Ok(None); + } + _ => {} + }; + } + Ok(Some(candidate)) + } + + fn candidate_from_obligation_no_cache<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let Some(conflict) = self.is_knowable(stack) { + debug!("coherence stage: not knowable"); + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + // Heuristics: show the diagnostics when there are no candidates in crate. + if let Ok(candidate_set) = self.assemble_candidates(stack) { + let mut no_candidates_apply = true; + + for c in candidate_set.vec.iter() { + if self.evaluate_candidate(stack, &c)?.may_apply() { + no_candidates_apply = false; + break; + } + } + + if !candidate_set.ambiguous && no_candidates_apply { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let self_ty = trait_ref.self_ty(); + let trait_desc = trait_ref.print_only_trait_path().to_string(); + let self_desc = if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }; + let cause = if let Conflict::Upstream = conflict { + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } + } else { + IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } + }; + debug!("evaluate_stack: pushing cause = {:?}", cause); + self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); + } + } + } + return Ok(None); + } + + let candidate_set = self.assemble_candidates(stack)?; + + if candidate_set.ambiguous { + debug!("candidate set contains ambig"); + return Ok(None); + } + + let mut candidates = candidate_set.vec; + + debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + // At this point, we know that each of the entries in the + // candidate set is *individually* applicable. Now we have to + // figure out if they contain mutual incompatibilities. This + // frequently arises if we have an unconstrained input type -- + // for example, we are looking for `$0: Eq` where `$0` is some + // unconstrained type variable. In that case, we'll get a + // candidate which assumes $0 == int, one that assumes `$0 == + // usize`, etc. This spells an ambiguity. + + // If there is more than one candidate, first winnow them down + // by considering extra conditions (nested obligations and so + // forth). We don't winnow if there is exactly one + // candidate. This is a relatively minor distinction but it + // can lead to better inference and error-reporting. An + // example would be if there was an impl: + // + // impl Vec { fn push_clone(...) { ... } } + // + // and we were to see some code `foo.push_clone()` where `boo` + // is a `Vec` and `Bar` does not implement `Clone`. If + // we were to winnow, we'd wind up with zero candidates. + // Instead, we select the right impl now but report "`Bar` does + // not implement `Clone`". + if candidates.len() == 1 { + return self.filter_negative_and_reservation_impls(candidates.pop().unwrap()); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. Propagate overflow if it occurs. + let mut candidates = candidates + .into_iter() + .map(|c| match self.evaluate_candidate(stack, &c) { + Ok(eval) if eval.may_apply() => { + Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) + } + Ok(_) => Ok(None), + Err(OverflowError) => Err(Overflow), + }) + .flat_map(Result::transpose) + .collect::, _>>()?; + + debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + + let needs_infer = stack.obligation.predicate.needs_infer(); + + // If there are STILL multiple candidates, we can further + // reduce the list by dropping duplicates -- including + // resolving specializations. + if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + self.candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + needs_infer, + ) + }); + if is_dup { + debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + candidates.swap_remove(i); + } else { + debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + i += 1; + + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + if i > 1 { + debug!("multiple matches, ambig"); + return Ok(None); + } + } + } + } + + // If there are *NO* candidates, then there are no impls -- + // that we know of, anyway. Note that in the case where there + // are unbound type variables within the obligation, it might + // be the case that you could still satisfy the obligation + // from another crate by instantiating the type variables with + // a type from another crate that does have an impl. This case + // is checked for in `evaluate_stack` (and hence users + // who might care about this case, like coherence, should use + // that function). + if candidates.is_empty() { + // If there's an error type, 'downgrade' our result from + // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid + // emitting additional spurious errors, since we're guaranteed + // to have emitted at least one. + if stack.obligation.references_error() { + debug!("no results for error type, treating as ambiguous"); + return Ok(None); + } + return Err(Unimplemented); + } + + // Just one candidate left. + self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate) + } + + fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option { + debug!("is_knowable(intercrate={:?})", self.intercrate); + + if !self.intercrate { + return None; + } + + let obligation = &stack.obligation; + let predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); + + // Okay to skip binder because of the nature of the + // trait-ref-is-knowable check, which does not care about + // bound regions. + let trait_ref = predicate.skip_binder().trait_ref; + + coherence::trait_ref_is_knowable(self.tcx(), trait_ref) + } + + /// Returns `true` if the global caches can be used. + /// Do note that if the type itself is not in the + /// global tcx, the local caches will be used. + fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool { + // If there are any inference variables in the `ParamEnv`, then we + // always use a cache local to this particular scope. Otherwise, we + // switch to a global cache. + if param_env.needs_infer() { + return false; + } + + // Avoid using the master cache during coherence and just rely + // on the local cache. This effectively disables caching + // during coherence. It is really just a simplification to + // avoid us having to fear that coherence results "pollute" + // the master cache. Since coherence executes pretty quickly, + // it's not worth going to more trouble to increase the + // hit-rate, I don't think. + if self.intercrate { + return false; + } + + // Otherwise, we can use the global cache. + true + } + + fn check_candidate_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Option>> { + let tcx = self.tcx(); + let trait_ref = &cache_fresh_trait_pred.skip_binder().trait_ref; + if self.can_use_global_caches(param_env) { + let cache = tcx.selection_cache.hashmap.borrow(); + if let Some(cached) = cache.get(¶m_env.and(*trait_ref)) { + return Some(cached.get(tcx)); + } + } + self.infcx + .selection_cache + .hashmap + .borrow() + .get(¶m_env.and(*trait_ref)) + .map(|v| v.get(tcx)) + } + + /// Determines whether can we safely cache the result + /// of selecting an obligation. This is almost always `true`, + /// except when dealing with certain `ParamCandidate`s. + /// + /// Ordinarily, a `ParamCandidate` will contain no inference variables, + /// since it was usually produced directly from a `DefId`. However, + /// certain cases (currently only librustdoc's blanket impl finder), + /// a `ParamEnv` may be explicitly constructed with inference types. + /// When this is the case, we do *not* want to cache the resulting selection + /// candidate. This is due to the fact that it might not always be possible + /// to equate the obligation's trait ref and the candidate's trait ref, + /// if more constraints end up getting added to an inference variable. + /// + /// Because of this, we always want to re-run the full selection + /// process for our obligation the next time we see it, since + /// we might end up picking a different `SelectionCandidate` (or none at all). + fn can_cache_candidate( + &self, + result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, + ) -> bool { + match result { + Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.needs_infer(), + _ => true, + } + } + + fn insert_candidate_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + dep_node: DepNodeIndex, + candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, + ) { + let tcx = self.tcx(); + let trait_ref = cache_fresh_trait_pred.skip_binder().trait_ref; + + if !self.can_cache_candidate(&candidate) { + debug!( + "insert_candidate_cache(trait_ref={:?}, candidate={:?} -\ + candidate is not cacheable", + trait_ref, candidate + ); + return; + } + + if self.can_use_global_caches(param_env) { + if let Err(Overflow) = candidate { + // Don't cache overflow globally; we only produce this in certain modes. + } else if !trait_ref.needs_infer() { + if !candidate.needs_infer() { + debug!( + "insert_candidate_cache(trait_ref={:?}, candidate={:?}) global", + trait_ref, candidate, + ); + // This may overwrite the cache with the same value. + tcx.selection_cache + .hashmap + .borrow_mut() + .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); + return; + } + } + } + + debug!( + "insert_candidate_cache(trait_ref={:?}, candidate={:?}) local", + trait_ref, candidate, + ); + self.infcx + .selection_cache + .hashmap + .borrow_mut() + .insert(param_env.and(trait_ref), WithDepNode::new(dep_node, candidate)); + } + + fn match_projection_obligation_against_definition_bounds( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> bool { + let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); + let (placeholder_trait_predicate, _) = + self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); + debug!( + "match_projection_obligation_against_definition_bounds: \ + placeholder_trait_predicate={:?}", + placeholder_trait_predicate, + ); + + let tcx = self.infcx.tcx; + let predicates = match placeholder_trait_predicate.trait_ref.self_ty().kind { + ty::Projection(ref data) => { + tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) + } + ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs), + _ => { + span_bug!( + obligation.cause.span, + "match_projection_obligation_against_definition_bounds() called \ + but self-ty is not a projection: {:?}", + placeholder_trait_predicate.trait_ref.self_ty() + ); + } + }; + + let matching_bound = predicates.iter().find_map(|bound| { + if let ty::PredicateKind::Trait(bound, _) = bound.kind() { + let bound = bound.to_poly_trait_ref(); + if self.infcx.probe(|_| { + self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref) + }) { + return Some(bound); + } + } + None + }); + + debug!( + "match_projection_obligation_against_definition_bounds: \ + matching_bound={:?}", + matching_bound + ); + match matching_bound { + None => false, + Some(bound) => { + // Repeat the successful match, if any, this time outside of a probe. + let result = + self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref); + + assert!(result); + true + } + } + } + + fn match_projection( + &mut self, + obligation: &TraitObligation<'tcx>, + trait_bound: ty::PolyTraitRef<'tcx>, + placeholder_trait_ref: ty::TraitRef<'tcx>, + ) -> bool { + debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) + .is_ok() + } + + fn evaluate_where_clause<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + where_clause_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result { + self.evaluation_probe(|this| { + match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { + Ok(obligations) => { + this.evaluate_predicates_recursively(stack.list(), obligations.into_iter()) + } + Err(()) => Ok(EvaluatedToErr), + } + }) + } + + /////////////////////////////////////////////////////////////////////////// + // WINNOW + // + // Winnowing is the process of attempting to resolve ambiguity by + // probing further. During the winnowing process, we unify all + // type variables and then we also attempt to evaluate recursive + // bounds to see if they are satisfied. + + /// Returns `true` if `victim` should be dropped in favor of + /// `other`. Generally speaking we will drop duplicate + /// candidates and prefer where-clause candidates. + /// + /// See the comment for "SelectionCandidate" for more details. + fn candidate_should_be_dropped_in_favor_of( + &mut self, + victim: &EvaluatedCandidate<'tcx>, + other: &EvaluatedCandidate<'tcx>, + needs_infer: bool, + ) -> bool { + if victim.candidate == other.candidate { + return true; + } + + // Check if a bound would previously have been removed when normalizing + // the param_env so that it can be given the lowest priority. See + // #50825 for the motivation for this. + let is_global = + |cand: &ty::PolyTraitRef<'_>| cand.is_global() && !cand.has_late_bound_regions(); + + // (*) Prefer `BuiltinCandidate { has_nested: false }` and `DiscriminantKindCandidate` + // to anything else. + // + // This is a fix for #53123 and prevents winnowing from accidentally extending the + // lifetime of a variable. + match other.candidate { + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => true, + ParamCandidate(ref cand) => match victim.candidate { + AutoImplCandidate(..) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other valid candidates" + ); + } + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, + ImplCandidate(..) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { .. } + | TraitAliasCandidate(..) => { + // Global bounds from the where clause should be ignored + // here (see issue #50825). Otherwise, we have a where + // clause so don't go around looking for impls. + !is_global(cand) + } + ObjectCandidate | ProjectionCandidate => { + // Arbitrarily give param candidates priority + // over projection and object candidates. + !is_global(cand) + } + ParamCandidate(..) => false, + }, + ObjectCandidate | ProjectionCandidate => match victim.candidate { + AutoImplCandidate(..) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other valid candidates" + ); + } + // (*) + BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, + ImplCandidate(..) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { .. } + | TraitAliasCandidate(..) => true, + ObjectCandidate | ProjectionCandidate => { + // Arbitrarily give param candidates priority + // over projection and object candidates. + true + } + ParamCandidate(ref cand) => is_global(cand), + }, + ImplCandidate(other_def) => { + // See if we can toss out `victim` based on specialization. + // This requires us to know *for sure* that the `other` impl applies + // i.e., `EvaluatedToOk`. + if other.evaluation.must_apply_modulo_regions() { + match victim.candidate { + ImplCandidate(victim_def) => { + let tcx = self.tcx(); + if tcx.specializes((other_def, victim_def)) { + return true; + } + return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { + Some(ty::ImplOverlapKind::Permitted { marker: true }) => { + // Subtle: If the predicate we are evaluating has inference + // variables, do *not* allow discarding candidates due to + // marker trait impls. + // + // Without this restriction, we could end up accidentally + // constrainting inference variables based on an arbitrarily + // chosen trait impl. + // + // Imagine we have the following code: + // + // ```rust + // #[marker] trait MyTrait {} + // impl MyTrait for u8 {} + // impl MyTrait for bool {} + // ``` + // + // And we are evaluating the predicate `<_#0t as MyTrait>`. + // + // During selection, we will end up with one candidate for each + // impl of `MyTrait`. If we were to discard one impl in favor + // of the other, we would be left with one candidate, causing + // us to "successfully" select the predicate, unifying + // _#0t with (for example) `u8`. + // + // However, we have no reason to believe that this unification + // is correct - we've essentially just picked an arbitrary + // *possibility* for _#0t, and required that this be the *only* + // possibility. + // + // Eventually, we will either: + // 1) Unify all inference variables in the predicate through + // some other means (e.g. type-checking of a function). We will + // then be in a position to drop marker trait candidates + // without constraining inference variables (since there are + // none left to constrin) + // 2) Be left with some unconstrained inference variables. We + // will then correctly report an inference error, since the + // existence of multiple marker trait impls tells us nothing + // about which one should actually apply. + !needs_infer + } + Some(_) => true, + None => false, + }; + } + ParamCandidate(ref cand) => { + // Prefer the impl to a global where clause candidate. + return is_global(cand); + } + _ => (), + } + } + + false + } + ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { has_nested: true } => { + match victim.candidate { + ParamCandidate(ref cand) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + is_global(cand) && other.evaluation.must_apply_modulo_regions() + } + _ => false, + } + } + _ => false, + } + } + + fn sized_conditions( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + use self::BuiltinImplConditions::{Ambiguous, None, Where}; + + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + + match self_ty.kind { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::Never + | ty::Error(_) => { + // safe for everything + Where(ty::Binder::dummy(Vec::new())) + } + + ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None, + + ty::Tuple(tys) => { + Where(ty::Binder::bind(tys.last().into_iter().map(|k| k.expect_ty()).collect())) + } + + ty::Adt(def, substs) => { + let sized_crit = def.sized_constraint(self.tcx()); + // (*) binder moved here + Where(ty::Binder::bind( + sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect(), + )) + } + + ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None, + ty::Infer(ty::TyVar(_)) => Ambiguous, + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + + fn copy_clone_conditions( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + + use self::BuiltinImplConditions::{Ambiguous, None, Where}; + + match self_ty.kind { + ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), + + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) => { + // Implementations provided in libcore + None + } + + ty::Dynamic(..) + | ty::Str + | ty::Slice(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Foreign(..) + | ty::Ref(_, _, hir::Mutability::Mut) => None, + + ty::Array(element_ty, _) => { + // (*) binder moved here + Where(ty::Binder::bind(vec![element_ty])) + } + + ty::Tuple(tys) => { + // (*) binder moved here + Where(ty::Binder::bind(tys.iter().map(|k| k.expect_ty()).collect())) + } + + ty::Closure(_, substs) => { + // (*) binder moved here + Where(ty::Binder::bind(substs.as_closure().upvar_tys().collect())) + } + + ty::Adt(..) | ty::Projection(..) | ty::Param(..) | ty::Opaque(..) => { + // Fallback to whatever user-defined impls exist in this case. + None + } + + ty::Infer(ty::TyVar(_)) => { + // Unbound type variable. Might or might not have + // applicable impls and so forth, depending on what + // those type variables wind up being bound to. + Ambiguous + } + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + + /// For default impls, we need to break apart a type into its + /// "constituent types" -- meaning, the types that it contains. + /// + /// Here are some (simple) examples: + /// + /// ``` + /// (i32, u32) -> [i32, u32] + /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32] + /// Bar where struct Bar { x: T, y: u32 } -> [i32, u32] + /// Zed where enum Zed { A(T), B(u32) } -> [i32, u32] + /// ``` + fn constituent_types_for_ty(&self, t: Ty<'tcx>) -> Vec> { + match t.kind { + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Str + | ty::Error(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Never + | ty::Char => Vec::new(), + + ty::Placeholder(..) + | ty::Dynamic(..) + | ty::Param(..) + | ty::Foreign(..) + | ty::Projection(..) + | ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble constituent types of unexpected type: {:?}", t); + } + + ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { + vec![element_ty] + } + + ty::Array(element_ty, _) | ty::Slice(element_ty) => vec![element_ty], + + ty::Tuple(ref tys) => { + // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet + tys.iter().map(|k| k.expect_ty()).collect() + } + + ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().collect(), + + ty::Generator(_, ref substs, _) => { + let witness = substs.as_generator().witness(); + substs.as_generator().upvar_tys().chain(iter::once(witness)).collect() + } + + ty::GeneratorWitness(types) => { + // This is sound because no regions in the witness can refer to + // the binder outside the witness. So we'll effectivly reuse + // the implicit binder around the witness. + types.skip_binder().to_vec() + } + + // For `PhantomData`, we pass `T`. + ty::Adt(def, substs) if def.is_phantom_data() => substs.types().collect(), + + ty::Adt(def, substs) => def.all_fields().map(|f| f.ty(self.tcx(), substs)).collect(), + + ty::Opaque(def_id, substs) => { + // We can resolve the `impl Trait` to its concrete type, + // which enforces a DAG between the functions requiring + // the auto trait bounds in question. + vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)] + } + } + } + + fn collect_predicates_for_types( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + trait_def_id: DefId, + types: ty::Binder>>, + ) -> Vec> { + // Because the types were potentially derived from + // higher-ranked obligations they may reference late-bound + // regions. For example, `for<'a> Foo<&'a i32> : Copy` would + // yield a type like `for<'a> &'a i32`. In general, we + // maintain the invariant that we never manipulate bound + // regions, so we have to process these bound regions somehow. + // + // The strategy is to: + // + // 1. Instantiate those regions to placeholder regions (e.g., + // `for<'a> &'a i32` becomes `&0 i32`. + // 2. Produce something like `&'0 i32 : Copy` + // 3. Re-bind the regions back to `for<'a> &'a i32 : Copy` + + types + .skip_binder() // binder moved -\ + .iter() + .flat_map(|ty| { + let ty: ty::Binder> = ty::Binder::bind(ty); // <----/ + + self.infcx.commit_unconditionally(|_| { + let (placeholder_ty, _) = self.infcx.replace_bound_vars_with_placeholders(&ty); + let Normalized { value: normalized_ty, mut obligations } = + ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + param_env, + cause.clone(), + recursion_depth, + &placeholder_ty, + ) + }); + let placeholder_obligation = predicate_for_trait_def( + self.tcx(), + param_env, + cause.clone(), + trait_def_id, + recursion_depth, + normalized_ty, + &[], + ); + obligations.push(placeholder_obligation); + obligations + }) + }) + .collect() + } + + /////////////////////////////////////////////////////////////////////////// + // Matching + // + // Matching is a common path used for both evaluation and + // confirmation. It basically unifies types that appear in impls + // and traits. This does affect the surrounding environment; + // therefore, when used during evaluation, match routines must be + // run inside of a `probe()` so that their side-effects are + // contained. + + fn rematch_impl( + &mut self, + impl_def_id: DefId, + obligation: &TraitObligation<'tcx>, + ) -> Normalized<'tcx, SubstsRef<'tcx>> { + match self.match_impl(impl_def_id, obligation) { + Ok(substs) => substs, + Err(()) => { + bug!( + "Impl {:?} was matchable against {:?} but now is not", + impl_def_id, + obligation + ); + } + } + } + + fn match_impl( + &mut self, + impl_def_id: DefId, + obligation: &TraitObligation<'tcx>, + ) -> Result>, ()> { + let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); + + // Before we create the substitutions and everything, first + // consider a "quick reject". This avoids creating more types + // and so forth that we need to. + if self.fast_reject_trait_refs(obligation, &impl_trait_ref) { + return Err(()); + } + + let (placeholder_obligation, _) = + self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); + let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref; + + let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id); + + let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs); + + let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = + ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &impl_trait_ref, + ) + }); + + debug!( + "match_impl(impl_def_id={:?}, obligation={:?}, \ + impl_trait_ref={:?}, placeholder_obligation_trait_ref={:?})", + impl_def_id, obligation, impl_trait_ref, placeholder_obligation_trait_ref + ); + + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(placeholder_obligation_trait_ref, impl_trait_ref) + .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?; + nested_obligations.extend(obligations); + + if !self.intercrate + && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation + { + debug!("match_impl: reservation impls only apply in intercrate mode"); + return Err(()); + } + + debug!("match_impl: success impl_substs={:?}", impl_substs); + Ok(Normalized { value: impl_substs, obligations: nested_obligations }) + } + + fn fast_reject_trait_refs( + &mut self, + obligation: &TraitObligation<'_>, + impl_trait_ref: &ty::TraitRef<'_>, + ) -> bool { + // We can avoid creating type variables and doing the full + // substitution if we find that any of the input types, when + // simplified, do not match. + + obligation.predicate.skip_binder().trait_ref.substs.iter().zip(impl_trait_ref.substs).any( + |(obligation_arg, impl_arg)| { + match (obligation_arg.unpack(), impl_arg.unpack()) { + (GenericArgKind::Type(obligation_ty), GenericArgKind::Type(impl_ty)) => { + let simplified_obligation_ty = + fast_reject::simplify_type(self.tcx(), obligation_ty, true); + let simplified_impl_ty = + fast_reject::simplify_type(self.tcx(), impl_ty, false); + + simplified_obligation_ty.is_some() + && simplified_impl_ty.is_some() + && simplified_obligation_ty != simplified_impl_ty + } + (GenericArgKind::Lifetime(_), GenericArgKind::Lifetime(_)) => { + // Lifetimes can never cause a rejection. + false + } + (GenericArgKind::Const(_), GenericArgKind::Const(_)) => { + // Conservatively ignore consts (i.e. assume they might + // unify later) until we have `fast_reject` support for + // them (if we'll ever need it, even). + false + } + _ => unreachable!(), + } + }, + ) + } + + /// Normalize `where_clause_trait_ref` and try to match it against + /// `obligation`. If successful, return any predicates that + /// result from the normalization. Normalization is necessary + /// because where-clauses are stored in the parameter environment + /// unnormalized. + fn match_where_clause_trait_ref( + &mut self, + obligation: &TraitObligation<'tcx>, + where_clause_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result>, ()> { + self.match_poly_trait_ref(obligation, where_clause_trait_ref) + } + + /// Returns `Ok` if `poly_trait_ref` being true implies that the + /// obligation is satisfied. + fn match_poly_trait_ref( + &mut self, + obligation: &TraitObligation<'tcx>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result>, ()> { + debug!( + "match_poly_trait_ref: obligation={:?} poly_trait_ref={:?}", + obligation, poly_trait_ref + ); + + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(obligation.predicate.to_poly_trait_ref(), poly_trait_ref) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| ()) + } + + /////////////////////////////////////////////////////////////////////////// + // Miscellany + + fn match_fresh_trait_refs( + &self, + previous: &ty::PolyTraitRef<'tcx>, + current: &ty::PolyTraitRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + let mut matcher = ty::_match::Match::new(self.tcx(), param_env); + matcher.relate(previous, current).is_ok() + } + + fn push_stack<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + obligation: &'o TraitObligation<'tcx>, + ) -> TraitObligationStack<'o, 'tcx> { + let fresh_trait_ref = + obligation.predicate.to_poly_trait_ref().fold_with(&mut self.freshener); + + let dfn = previous_stack.cache.next_dfn(); + let depth = previous_stack.depth() + 1; + TraitObligationStack { + obligation, + fresh_trait_ref, + reached_depth: Cell::new(depth), + previous: previous_stack, + dfn, + depth, + } + } + + fn closure_trait_ref_unnormalized( + &mut self, + obligation: &TraitObligation<'tcx>, + substs: SubstsRef<'tcx>, + ) -> ty::PolyTraitRef<'tcx> { + debug!("closure_trait_ref_unnormalized(obligation={:?}, substs={:?})", obligation, substs); + let closure_sig = substs.as_closure().sig(); + + debug!("closure_trait_ref_unnormalized: closure_sig = {:?}", closure_sig); + + // (1) Feels icky to skip the binder here, but OTOH we know + // that the self-type is an unboxed closure type and hence is + // in fact unparameterized (or at least does not reference any + // regions bound in the obligation). Still probably some + // refactoring could make this nicer. + closure_trait_ref_and_return_type( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.skip_binder().self_ty(), // (1) + closure_sig, + util::TupleArgumentsFlag::No, + ) + .map_bound(|(trait_ref, _)| trait_ref) + } + + fn generator_trait_ref_unnormalized( + &mut self, + obligation: &TraitObligation<'tcx>, + substs: SubstsRef<'tcx>, + ) -> ty::PolyTraitRef<'tcx> { + let gen_sig = substs.as_generator().poly_sig(); + + // (1) Feels icky to skip the binder here, but OTOH we know + // that the self-type is an generator type and hence is + // in fact unparameterized (or at least does not reference any + // regions bound in the obligation). Still probably some + // refactoring could make this nicer. + + super::util::generator_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.skip_binder().self_ty(), // (1) + gen_sig, + ) + .map_bound(|(trait_ref, ..)| trait_ref) + } + + /// Returns the obligations that are implied by instantiating an + /// impl or trait. The obligations are substituted and fully + /// normalized. This is used when confirming an impl or default + /// impl. + fn impl_or_trait_obligations( + &mut self, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, // of impl or trait + substs: SubstsRef<'tcx>, // for impl or trait + ) -> Vec> { + debug!("impl_or_trait_obligations(def_id={:?})", def_id); + let tcx = self.tcx(); + + // To allow for one-pass evaluation of the nested obligation, + // each predicate must be preceded by the obligations required + // to normalize it. + // for example, if we have: + // impl, V: Iterator> Foo for V + // the impl will have the following predicates: + // ::Item = U, + // U: Iterator, U: Sized, + // V: Iterator, V: Sized, + // ::Item: Copy + // When we substitute, say, `V => IntoIter, U => $0`, the last + // obligation will normalize to `<$0 as Iterator>::Item = $1` and + // `$1: Copy`, so we must ensure the obligations are emitted in + // that order. + let predicates = tcx.predicates_of(def_id); + assert_eq!(predicates.parent, None); + let mut obligations = Vec::with_capacity(predicates.predicates.len()); + for (predicate, _) in predicates.predicates { + let predicate = normalize_with_depth_to( + self, + param_env, + cause.clone(), + recursion_depth, + &predicate.subst(tcx, substs), + &mut obligations, + ); + obligations.push(Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate, + }); + } + + // We are performing deduplication here to avoid exponential blowups + // (#38528) from happening, but the real cause of the duplication is + // unknown. What we know is that the deduplication avoids exponential + // amount of predicates being propagated when processing deeply nested + // types. + // + // This code is hot enough that it's worth avoiding the allocation + // required for the FxHashSet when possible. Special-casing lengths 0, + // 1 and 2 covers roughly 75-80% of the cases. + if obligations.len() <= 1 { + // No possibility of duplicates. + } else if obligations.len() == 2 { + // Only two elements. Drop the second if they are equal. + if obligations[0] == obligations[1] { + obligations.truncate(1); + } + } else { + // Three or more elements. Use a general deduplication process. + let mut seen = FxHashSet::default(); + obligations.retain(|i| seen.insert(i.clone())); + } + + obligations + } +} + +trait TraitObligationExt<'tcx> { + fn derived_cause( + &self, + variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx>; +} + +impl<'tcx> TraitObligationExt<'tcx> for TraitObligation<'tcx> { + #[allow(unused_comparisons)] + fn derived_cause( + &self, + variant: fn(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + /*! + * Creates a cause for obligations that are derived from + * `obligation` by a recursive search (e.g., for a builtin + * bound, or eventually a `auto trait Foo`). If `obligation` + * is itself a derived obligation, this is just a clone, but + * otherwise we create a "derived obligation" cause so as to + * keep track of the original root obligation for error + * reporting. + */ + + let obligation = self; + + // NOTE(flaper87): As of now, it keeps track of the whole error + // chain. Ideally, we should have a way to configure this either + // by using -Z verbose or just a CLI argument. + let derived_cause = DerivedObligationCause { + parent_trait_ref: obligation.predicate.to_poly_trait_ref(), + parent_code: Rc::new(obligation.cause.code.clone()), + }; + let derived_code = variant(derived_cause); + ObligationCause::new(obligation.cause.span, obligation.cause.body_id, derived_code) + } +} + +impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { + fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList::with(self) + } + + fn cache(&self) -> &'o ProvisionalEvaluationCache<'tcx> { + self.previous.cache + } + + fn iter(&'o self) -> TraitObligationStackList<'o, 'tcx> { + self.list() + } + + /// Indicates that attempting to evaluate this stack entry + /// required accessing something from the stack at depth `reached_depth`. + fn update_reached_depth(&self, reached_depth: usize) { + assert!( + self.depth > reached_depth, + "invoked `update_reached_depth` with something under this stack: \ + self.depth={} reached_depth={}", + self.depth, + reached_depth, + ); + debug!("update_reached_depth(reached_depth={})", reached_depth); + let mut p = self; + while reached_depth < p.depth { + debug!("update_reached_depth: marking {:?} as cycle participant", p.fresh_trait_ref); + p.reached_depth.set(p.reached_depth.get().min(reached_depth)); + p = p.previous.head.unwrap(); + } + } +} + +/// The "provisional evaluation cache" is used to store intermediate cache results +/// when solving auto traits. Auto traits are unusual in that they can support +/// cycles. So, for example, a "proof tree" like this would be ok: +/// +/// - `Foo: Send` :- +/// - `Bar: Send` :- +/// - `Foo: Send` -- cycle, but ok +/// - `Baz: Send` +/// +/// Here, to prove `Foo: Send`, we have to prove `Bar: Send` and +/// `Baz: Send`. Proving `Bar: Send` in turn required `Foo: Send`. +/// For non-auto traits, this cycle would be an error, but for auto traits (because +/// they are coinductive) it is considered ok. +/// +/// However, there is a complication: at the point where we have +/// "proven" `Bar: Send`, we have in fact only proven it +/// *provisionally*. In particular, we proved that `Bar: Send` +/// *under the assumption* that `Foo: Send`. But what if we later +/// find out this assumption is wrong? Specifically, we could +/// encounter some kind of error proving `Baz: Send`. In that case, +/// `Bar: Send` didn't turn out to be true. +/// +/// In Issue #60010, we found a bug in rustc where it would cache +/// these intermediate results. This was fixed in #60444 by disabling +/// *all* caching for things involved in a cycle -- in our example, +/// that would mean we don't cache that `Bar: Send`. But this led +/// to large slowdowns. +/// +/// Specifically, imagine this scenario, where proving `Baz: Send` +/// first requires proving `Bar: Send` (which is true: +/// +/// - `Foo: Send` :- +/// - `Bar: Send` :- +/// - `Foo: Send` -- cycle, but ok +/// - `Baz: Send` +/// - `Bar: Send` -- would be nice for this to be a cache hit! +/// - `*const T: Send` -- but what if we later encounter an error? +/// +/// The *provisional evaluation cache* resolves this issue. It stores +/// cache results that we've proven but which were involved in a cycle +/// in some way. We track the minimal stack depth (i.e., the +/// farthest from the top of the stack) that we are dependent on. +/// The idea is that the cache results within are all valid -- so long as +/// none of the nodes in between the current node and the node at that minimum +/// depth result in an error (in which case the cached results are just thrown away). +/// +/// During evaluation, we consult this provisional cache and rely on +/// it. Accessing a cached value is considered equivalent to accessing +/// a result at `reached_depth`, so it marks the *current* solution as +/// provisional as well. If an error is encountered, we toss out any +/// provisional results added from the subtree that encountered the +/// error. When we pop the node at `reached_depth` from the stack, we +/// can commit all the things that remain in the provisional cache. +struct ProvisionalEvaluationCache<'tcx> { + /// next "depth first number" to issue -- just a counter + dfn: Cell, + + /// Stores the "coldest" depth (bottom of stack) reached by any of + /// the evaluation entries. The idea here is that all things in the provisional + /// cache are always dependent on *something* that is colder in the stack: + /// therefore, if we add a new entry that is dependent on something *colder still*, + /// we have to modify the depth for all entries at once. + /// + /// Example: + /// + /// Imagine we have a stack `A B C D E` (with `E` being the top of + /// the stack). We cache something with depth 2, which means that + /// it was dependent on C. Then we pop E but go on and process a + /// new node F: A B C D F. Now F adds something to the cache with + /// depth 1, meaning it is dependent on B. Our original cache + /// entry is also dependent on B, because there is a path from E + /// to C and then from C to F and from F to B. + reached_depth: Cell, + + /// Map from cache key to the provisionally evaluated thing. + /// The cache entries contain the result but also the DFN in which they + /// were added. The DFN is used to clear out values on failure. + /// + /// Imagine we have a stack like: + /// + /// - `A B C` and we add a cache for the result of C (DFN 2) + /// - Then we have a stack `A B D` where `D` has DFN 3 + /// - We try to solve D by evaluating E: `A B D E` (DFN 4) + /// - `E` generates various cache entries which have cyclic dependices on `B` + /// - `A B D E F` and so forth + /// - the DFN of `F` for example would be 5 + /// - then we determine that `E` is in error -- we will then clear + /// all cache values whose DFN is >= 4 -- in this case, that + /// means the cached value for `F`. + map: RefCell, ProvisionalEvaluation>>, +} + +/// A cache value for the provisional cache: contains the depth-first +/// number (DFN) and result. +#[derive(Copy, Clone, Debug)] +struct ProvisionalEvaluation { + from_dfn: usize, + result: EvaluationResult, +} + +impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { + fn default() -> Self { + Self { dfn: Cell::new(0), reached_depth: Cell::new(usize::MAX), map: Default::default() } + } +} + +impl<'tcx> ProvisionalEvaluationCache<'tcx> { + /// Get the next DFN in sequence (basically a counter). + fn next_dfn(&self) -> usize { + let result = self.dfn.get(); + self.dfn.set(result + 1); + result + } + + /// Check the provisional cache for any result for + /// `fresh_trait_ref`. If there is a hit, then you must consider + /// it an access to the stack slots at depth + /// `self.current_reached_depth()` and above. + fn get_provisional(&self, fresh_trait_ref: ty::PolyTraitRef<'tcx>) -> Option { + debug!( + "get_provisional(fresh_trait_ref={:?}) = {:#?} with reached-depth {}", + fresh_trait_ref, + self.map.borrow().get(&fresh_trait_ref), + self.reached_depth.get(), + ); + Some(self.map.borrow().get(&fresh_trait_ref)?.result) + } + + /// Current value of the `reached_depth` counter -- all the + /// provisional cache entries are dependent on the item at this + /// depth. + fn current_reached_depth(&self) -> usize { + self.reached_depth.get() + } + + /// Insert a provisional result into the cache. The result came + /// from the node with the given DFN. It accessed a minimum depth + /// of `reached_depth` to compute. It evaluated `fresh_trait_ref` + /// and resulted in `result`. + fn insert_provisional( + &self, + from_dfn: usize, + reached_depth: usize, + fresh_trait_ref: ty::PolyTraitRef<'tcx>, + result: EvaluationResult, + ) { + debug!( + "insert_provisional(from_dfn={}, reached_depth={}, fresh_trait_ref={:?}, result={:?})", + from_dfn, reached_depth, fresh_trait_ref, result, + ); + let r_d = self.reached_depth.get(); + self.reached_depth.set(r_d.min(reached_depth)); + + debug!("insert_provisional: reached_depth={:?}", self.reached_depth.get()); + + self.map.borrow_mut().insert(fresh_trait_ref, ProvisionalEvaluation { from_dfn, result }); + } + + /// Invoked when the node with dfn `dfn` does not get a successful + /// result. This will clear out any provisional cache entries + /// that were added since `dfn` was created. This is because the + /// provisional entries are things which must assume that the + /// things on the stack at the time of their creation succeeded -- + /// since the failing node is presently at the top of the stack, + /// these provisional entries must either depend on it or some + /// ancestor of it. + fn on_failure(&self, dfn: usize) { + debug!("on_failure(dfn={:?})", dfn,); + self.map.borrow_mut().retain(|key, eval| { + if !eval.from_dfn >= dfn { + debug!("on_failure: removing {:?}", key); + false + } else { + true + } + }); + } + + /// Invoked when the node at depth `depth` completed without + /// depending on anything higher in the stack (if that completion + /// was a failure, then `on_failure` should have been invoked + /// already). The callback `op` will be invoked for each + /// provisional entry that we can now confirm. + fn on_completion( + &self, + depth: usize, + mut op: impl FnMut(ty::PolyTraitRef<'tcx>, EvaluationResult), + ) { + debug!("on_completion(depth={}, reached_depth={})", depth, self.reached_depth.get(),); + + if self.reached_depth.get() < depth { + debug!("on_completion: did not yet reach depth to complete"); + return; + } + + for (fresh_trait_ref, eval) in self.map.borrow_mut().drain() { + debug!("on_completion: fresh_trait_ref={:?} eval={:?}", fresh_trait_ref, eval,); + + op(fresh_trait_ref, eval.result); + } + + self.reached_depth.set(usize::MAX); + } +} + +#[derive(Copy, Clone)] +struct TraitObligationStackList<'o, 'tcx> { + cache: &'o ProvisionalEvaluationCache<'tcx>, + head: Option<&'o TraitObligationStack<'o, 'tcx>>, +} + +impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> { + fn empty(cache: &'o ProvisionalEvaluationCache<'tcx>) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList { cache, head: None } + } + + fn with(r: &'o TraitObligationStack<'o, 'tcx>) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList { cache: r.cache(), head: Some(r) } + } + + fn head(&self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { + self.head + } + + fn depth(&self) -> usize { + if let Some(head) = self.head { head.depth } else { 0 } + } +} + +impl<'o, 'tcx> Iterator for TraitObligationStackList<'o, 'tcx> { + type Item = &'o TraitObligationStack<'o, 'tcx>; + + fn next(&mut self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { + match self.head { + Some(o) => { + *self = o.previous; + Some(o) + } + None => None, + } + } +} + +impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraitObligationStack({:?})", self.obligation) + } +} diff --git a/src/librustc_trait_selection/traits/specialize/mod.rs b/src/librustc_trait_selection/traits/specialize/mod.rs index d1d4a58fdf297..42901102c1076 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,45 +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); - - let 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), - } -} - /// Is `impl1` a specialization of `impl2`? /// /// Specialization is determined by the sets of types to which the impls apply; @@ -161,13 +122,15 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, // The feature gate should prevent introducing new specializations, but not // taking advantage of upstream ones. - if !tcx.features().specialization && (impl1_def_id.is_local() || impl2_def_id.is_local()) { + let features = tcx.features(); + let specialization_enabled = features.specialization || features.min_specialization; + if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) { return false; } // We determine whether there's a subset relationship by: // - // - skolemizing impl1, + // - replacing bound vars with placeholders in impl1, // - assuming the where clauses for impl1, // - instantiating impl2 with fresh inference variables, // - unifying, @@ -226,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) @@ -263,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) { @@ -295,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 @@ -308,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), @@ -319,85 +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 => { - 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); @@ -405,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 @@ -448,7 +496,7 @@ fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option for (p, _) in predicates { if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() { if Some(poly_trait_ref.def_id()) == sized_trait { - types_without_default_bounds.remove(poly_trait_ref.self_ty()); + types_without_default_bounds.remove(poly_trait_ref.self_ty().skip_binder()); continue; } } 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..201edf27a655c 100644 --- a/src/librustc_trait_selection/traits/structural_match.rs +++ b/src/librustc_trait_selection/traits/structural_match.rs @@ -1,22 +1,30 @@ use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::traits::ObligationCause; -use crate::traits::{self, ConstPatternStructural, TraitEngine}; +use crate::traits::{self, 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::query::Providers; +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, + Foreign, + Opaque, + Generator, + Projection, + Closure, } /// 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 +38,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. /// @@ -39,14 +47,14 @@ pub enum NonStructuralMatchTy<'tcx> { /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. pub fn search_for_structural_match_violation<'tcx>( - id: hir::HirId, + _id: hir::HirId, span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option> { // FIXME: we should instead pass in an `infcx` from the outside. tcx.infer_ctxt().enter(|infcx| { - let mut search = Search { id, span, infcx, found: None, seen: FxHashSet::default() }; + let mut search = Search { infcx, span, found: None, seen: FxHashSet::default() }; ty.visit_with(&mut search); search.found }) @@ -59,27 +67,26 @@ pub fn search_for_structural_match_violation<'tcx>( /// /// Note that this does *not* recursively check if the substructure of `adt_ty` /// implements the traits. -pub fn type_marked_structural( - id: hir::HirId, - span: Span, +fn type_marked_structural( infcx: &InferCtxt<'_, 'tcx>, adt_ty: Ty<'tcx>, + cause: ObligationCause<'tcx>, ) -> bool { 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(cause.span)); fulfillment_cx.register_bound( infcx, ty::ParamEnv::empty(), adt_ty, structural_peq_def_id, - cause, + cause.clone(), ); // 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(cause.span)); fulfillment_cx.register_bound( infcx, ty::ParamEnv::empty(), @@ -104,7 +111,6 @@ pub fn type_marked_structural( /// find instances of ADTs (specifically structs or enums) that do not implement /// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). struct Search<'a, 'tcx> { - id: hir::HirId, span: Span, infcx: InferCtxt<'a, 'tcx>, @@ -123,7 +129,7 @@ impl Search<'a, 'tcx> { } fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { - type_marked_structural(self.id, self.span, &self.infcx, adt_ty) + adt_ty.is_structural_eq_shallow(self.tcx()) } } @@ -137,6 +143,30 @@ 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::Foreign(_) => { + self.found = Some(NonStructuralMatchTy::Foreign); + return true; // Stop visiting. + } + ty::Opaque(..) => { + self.found = Some(NonStructuralMatchTy::Opaque); + return true; // Stop visiting. + } + ty::Projection(..) => { + self.found = Some(NonStructuralMatchTy::Projection); + return true; // Stop visiting. + } + ty::Generator(..) | ty::GeneratorWitness(..) => { + self.found = Some(NonStructuralMatchTy::Generator); + return true; // Stop visiting. + } + ty::Closure(..) => { + self.found = Some(NonStructuralMatchTy::Closure); + return true; // Stop visiting. + } ty::RawPtr(..) => { // structural-match ignores substructure of // `*const _`/`*mut _`, so skip `super_visit_with`. @@ -153,14 +183,14 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { // structural equality on `T` does not recur into the raw // pointer. Therefore, one can still use `C` in a pattern. - // (But still tell caller to continue search.) + // (But still tell the caller to continue search.) return false; } ty::FnDef(..) | ty::FnPtr(..) => { - // types of formals and return in `fn(_) -> _` are also irrelevant; + // Types of formals and return in `fn(_) -> _` are also irrelevant; // so we do not recur into them via `super_visit_with` // - // (But still tell caller to continue search.) + // (But still tell the caller to continue search.) return false; } ty::Array(_, n) @@ -168,17 +198,40 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { { // rust-lang/rust#62336: ignore type of contents // for empty array. + // + // (But still tell the caller to continue search.) return false; } - _ => { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { + // These primitive types are always structural match. + // + // `Never` is kind of special here, but as it is not inhabitable, this should be fine. + // + // (But still tell the caller to continue search.) + return false; + } + + ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { + // First check all contained types and then tell the caller to continue searching. ty.super_visit_with(self); return false; } + ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { + bug!("unexpected type during structural-match checking: {:?}", ty); + } + ty::Error(_) => { + self.tcx().sess.delay_span_bug(self.span, "ty::Error in structural-match check"); + // We still want to check other types after encountering an error, + // as this may still emit relevant errors. + // + // So we continue searching here. + return false; + } }; if !self.seen.insert(adt_def.did) { debug!("Search already seen adt_def: {:?}", adt_def); - // let caller continue its search + // Let caller continue its search. return false; } @@ -202,7 +255,10 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { // fields of ADT. let tcx = self.tcx(); for field_ty in adt_def.all_fields().map(|field| field.ty(tcx, substs)) { - if field_ty.visit_with(self) { + let ty = self.tcx().normalize_erasing_regions(ty::ParamEnv::empty(), field_ty); + debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty); + + if ty.visit_with(self) { // found an ADT without structural-match; halt visiting! assert!(self.found.is_some()); return true; @@ -214,3 +270,12 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { false } } + +pub fn provide(providers: &mut Providers<'_>) { + providers.has_structural_eq_impls = |tcx, ty| { + tcx.infer_ctxt().enter(|infcx| { + let cause = ObligationCause::dummy(); + type_marked_structural(&infcx, ty, cause) + }) + }; +} diff --git a/src/librustc_trait_selection/traits/util.rs b/src/librustc_trait_selection/traits/util.rs index cd4595e76ccec..d3484b8af89fd 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 } } @@ -359,7 +97,7 @@ impl<'tcx> TraitAliasExpander<'tcx> { fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { let tcx = self.tcx; let trait_ref = item.trait_ref(); - let pred = trait_ref.without_const().to_predicate(); + let pred = trait_ref.without_const().to_predicate(tcx); debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); @@ -370,9 +108,9 @@ impl<'tcx> TraitAliasExpander<'tcx> { } // Don't recurse if this trait alias is already on the stack for the DFS search. - let anon_pred = anonymize_predicate(tcx, &pred); + let anon_pred = anonymize_predicate(tcx, pred); if item.path.iter().rev().skip(1).any(|(tr, _)| { - anonymize_predicate(tcx, &tr.without_const().to_predicate()) == anon_pred + anonymize_predicate(tcx, tr.without_const().to_predicate(tcx)) == anon_pred }) { return false; } @@ -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,39 +208,33 @@ 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>( + tcx: TyCtxt<'tcx>, cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, trait_ref: ty::TraitRef<'tcx>, @@ -546,7 +244,7 @@ pub fn predicate_for_trait_ref<'tcx>( cause, param_env, recursion_depth, - predicate: trait_ref.without_const().to_predicate(), + predicate: trait_ref.without_const().to_predicate(tcx), } } @@ -561,7 +259,7 @@ pub fn predicate_for_trait_def( ) -> PredicateObligation<'tcx> { let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) }; - predicate_for_trait_ref(cause, param_env, trait_ref, recursion_depth) + predicate_for_trait_ref(tcx, cause, param_env, trait_ref, recursion_depth) } /// Casts a trait reference into a reference to one of its super @@ -587,7 +285,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; } } @@ -599,20 +297,20 @@ pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<' /// `object.upcast_trait_ref`) within the vtable for `object`. pub fn get_vtable_index_of_object_method( tcx: TyCtxt<'tcx>, - object: &super::VtableObjectData<'tcx, N>, + object: &super::ImplSourceObjectData<'tcx, N>, method_def_id: DefId, ) -> usize { // Count number of methods preceding the one we are selecting and // add them to the total offset. - // Skip over associated types and constants. + // Skip over associated types and constants, as those aren't stored in the vtable. let mut entries = object.vtable_base; for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() { if trait_item.def_id == method_def_id { // The item with the ID we were given really ought to be a method. - assert_eq!(trait_item.kind, ty::AssocKind::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 +349,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 b69c5bdce2abc..1825c159ff3fb 100644 --- a/src/librustc_trait_selection/traits/wf.rs +++ b/src/librustc_trait_selection/traits/wf.rs @@ -1,17 +1,17 @@ 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::{GenericArg, 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 -/// further WF obligations. However, if `ty` IS an unresolved +/// Returns the set of obligations needed to make `arg` well-formed. +/// If `arg` contains unresolved inference variables, this may include +/// further WF obligations. However, if `arg` IS an unresolved /// inference variable, returns `None`, because we are not able to /// make any progress at all. This is to prevent "livelock" where we /// say "$0 is WF if $0 is WF". @@ -19,19 +19,52 @@ pub fn obligations<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, - ty: Ty<'tcx>, + arg: GenericArg<'tcx>, span: Span, ) -> Option>> { + // Handle the "livelock" case (see comment above) by bailing out if necessary. + let arg = match arg.unpack() { + GenericArgKind::Type(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, + } + .into() + } + GenericArgKind::Const(ct) => { + match ct.val { + ty::ConstKind::Infer(infer) => { + let resolved = infcx.shallow_resolve(infer); + if resolved == infer { + // No progress. + return None; + } + + infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Infer(resolved), ty: ct.ty }) + } + _ => ct, + } + .into() + } + // There is nothing we have to do for lifetimes. + GenericArgKind::Lifetime(..) => return Some(Vec::new()), + }; + 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(arg); + debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); + + let result = wf.normalize(); + debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result); + Some(result) } /// Returns the obligations that make this trait reference @@ -55,42 +88,46 @@ pub fn predicate_obligations<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, - predicate: &ty::Predicate<'tcx>, + predicate: ty::Predicate<'tcx>, span: Span, ) -> Vec> { let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; // (*) ok to skip binders, because wf code is prepared for it - match *predicate { - ty::Predicate::Trait(ref t, _) => { + match predicate.kind() { + ty::PredicateKind::Trait(t, _) => { wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*) } - ty::Predicate::RegionOutlives(..) => {} - ty::Predicate::TypeOutlives(ref t) => { - wf.compute(t.skip_binder().0); + ty::PredicateKind::RegionOutlives(..) => {} + ty::PredicateKind::TypeOutlives(t) => { + wf.compute(t.skip_binder().0.into()); } - ty::Predicate::Projection(ref t) => { + ty::PredicateKind::Projection(t) => { let t = t.skip_binder(); // (*) wf.compute_projection(t.projection_ty); - wf.compute(t.ty); + wf.compute(t.ty.into()); } - ty::Predicate::WellFormed(t) => { - wf.compute(t); + &ty::PredicateKind::WellFormed(arg) => { + wf.compute(arg); } - ty::Predicate::ObjectSafe(_) => {} - ty::Predicate::ClosureKind(..) => {} - ty::Predicate::Subtype(ref data) => { - wf.compute(data.skip_binder().a); // (*) - wf.compute(data.skip_binder().b); // (*) + ty::PredicateKind::ObjectSafe(_) => {} + ty::PredicateKind::ClosureKind(..) => {} + ty::PredicateKind::Subtype(data) => { + wf.compute(data.skip_binder().a.into()); // (*) + wf.compute(data.skip_binder().b.into()); // (*) } - ty::Predicate::ConstEvaluatable(def_id, substs) => { + &ty::PredicateKind::ConstEvaluatable(def_id, substs) => { let obligations = wf.nominal_obligations(def_id, substs); wf.out.extend(obligations); - for ty in substs.types() { - wf.compute(ty); + for arg in substs.iter() { + wf.compute(arg); } } + &ty::PredicateKind::ConstEquate(c1, c2) => { + wf.compute(c1.into()); + wf.compute(c2.into()); + } } wf.normalize() @@ -134,8 +171,71 @@ 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.kind() { + ty::PredicateKind::Projection(proj) => { + // The obligation comes not from the current `impl` nor the `trait` being implemented, + // but rather from a "second order" obligation, where an associated type has a + // projection coming from another associated type. See + // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs` and + // `traits-assoc-type-in-supertrait-bad.rs`. + let kind = &proj.ty().skip_binder().kind; + if let ty::Projection(projection_ty) = kind { + 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.make_mut().span = impl_item_span; + } + } + } + ty::PredicateKind::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.make_mut().span = impl_item_span; + } + } + } + _ => {} + } +} + impl<'a, 'tcx> WfPredicates<'a, 'tcx> { - fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { traits::ObligationCause::new(self.span, self.body_id, code) } @@ -160,184 +260,58 @@ 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.make_mut().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)), - )); + let tcx = self.tcx(); + self.out.extend( + trait_ref + .substs + .iter() + .filter(|arg| { + matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) + }) + .filter(|arg| !arg.has_escaping_bound_vars()) + .map(|arg| { + traits::Obligation::new( + cause.clone(), + param_env, + ty::PredicateKind::WellFormed(arg).to_predicate(tcx), + ) + }), + ); } /// Pushes the obligations required for `trait_ref::Item` to be WF @@ -350,27 +324,12 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.compute_trait_ref(&trait_ref, Elaborate::None); if !data.has_escaping_bound_vars() { - let predicate = trait_ref.without_const().to_predicate(); + let predicate = trait_ref.without_const().to_predicate(self.infcx.tcx); let cause = self.cause(traits::ProjectionWf(data)); self.out.push(traits::Obligation::new(cause, self.param_env, predicate)); } } - /// Pushes the obligations required for an array length to be WF - /// into `self.out`. - fn compute_array_len(&mut self, constant: ty::Const<'tcx>) { - if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = constant.val { - assert!(promoted.is_none()); - - let obligations = self.nominal_obligations(def_id, substs); - self.out.extend(obligations); - - let predicate = ty::Predicate::ConstEvaluatable(def_id, substs); - let cause = self.cause(traits::MiscObligation); - self.out.push(traits::Obligation::new(cause, self.param_env, predicate)); - } - } - fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) { if !subty.has_escaping_bound_vars() { let cause = self.cause(cause); @@ -381,26 +340,79 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.push(traits::Obligation::new( cause, self.param_env, - trait_ref.without_const().to_predicate(), + trait_ref.without_const().to_predicate(self.infcx.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, arg: GenericArg<'tcx>) { + let mut walker = arg.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, + + GenericArgKind::Const(constant) => { + match constant.val { + ty::ConstKind::Unevaluated(def_id, substs, promoted) => { + assert!(promoted.is_none()); + + let obligations = self.nominal_obligations(def_id, substs); + self.out.extend(obligations); + + let predicate = ty::PredicateKind::ConstEvaluatable(def_id, substs) + .to_predicate(self.tcx()); + let cause = self.cause(traits::MiscObligation); + self.out.push(traits::Obligation::new( + cause, + self.param_env, + predicate, + )); + } + ty::ConstKind::Infer(infer) => { + let resolved = self.infcx.shallow_resolve(infer); + // the `InferConst` changed, meaning that we made progress. + if resolved != infer { + let cause = self.cause(traits::MiscObligation); + + let resolved_constant = self.infcx.tcx.mk_const(ty::Const { + val: ty::ConstKind::Infer(resolved), + ..*constant + }); + self.out.push(traits::Obligation::new( + cause, + self.param_env, + ty::PredicateKind::WellFormed(resolved_constant.into()) + .to_predicate(self.tcx()), + )); + } + } + ty::ConstKind::Error(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(..) => { + // These variants are trivially WF, so nothing to do here. + } + ty::ConstKind::Value(..) => { + // FIXME: Enforce that values are structurally-matchable. + } + } + continue; + } + }; + match ty.kind { ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) - | ty::Error + | ty::Error(_) | ty::Str | ty::GeneratorWitness(..) | ty::Never @@ -411,13 +423,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) => { + ty::Array(subty, _) => { self.require_sized(subty, traits::SliceOrArrayElem); - self.compute_array_len(*len); + // Note that we handle the len is implicitly checked while walking `arg`. } ty::Tuple(ref tys) => { @@ -429,16 +447,14 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } ty::RawPtr(_) => { - // simple cases that are WF if their type args are WF + // Simple cases that are WF if their type args are WF. } 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); @@ -457,9 +473,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.push(traits::Obligation::new( cause, param_env, - ty::Predicate::TypeOutlives(ty::Binder::dummy(ty::OutlivesPredicate( - rty, r, - ))), + ty::PredicateKind::TypeOutlives(ty::Binder::dummy( + ty::OutlivesPredicate(rty, r), + )) + .to_predicate(self.tcx()), )); } } @@ -474,7 +491,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // generators don't take arguments. } - ty::Closure(def_id, substs) => { + ty::Closure(_, substs) => { // Only check the upvar types for WF, not the rest // of the types within. This is needed because we // capture the signature and it may not be WF @@ -504,9 +521,10 @@ 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 - for upvar_ty in substs.as_closure().upvar_tys(def_id, self.infcx.tcx) { - self.compute(upvar_ty); + walker.skip_current_subtree(); // subtree handled below + for upvar_ty in substs.as_closure().upvar_tys() { + // FIXME(eddyb) add the type to `walker` instead of recursing. + self.compute(upvar_ty.into()); } } @@ -538,16 +556,17 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // obligations that don't refer to Self and // checking those - let defer_to_coercion = self.infcx.tcx.features().object_safe_for_dispatch; + let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; if !defer_to_coercion { let cause = self.cause(traits::MiscObligation); let component_traits = data.auto_traits().chain(data.principal_def_id()); + let tcx = self.tcx(); self.out.extend(component_traits.map(|did| { traits::Obligation::new( cause.clone(), param_env, - ty::Predicate::ObjectSafe(did), + ty::PredicateKind::ObjectSafe(did).to_predicate(tcx), ) })); } @@ -558,44 +577,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::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx()), + )); } 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.into()); } } } } - - // if we made it through that loop above, we made progress! - return true; } fn nominal_obligations( @@ -604,11 +610,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() } @@ -663,7 +672,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.push(traits::Obligation::new( cause, self.param_env, - outlives.to_predicate(), + outlives.to_predicate(self.infcx.tcx), )); } } @@ -685,69 +694,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..8def98a9603d8 100644 --- a/src/librustc_traits/Cargo.toml +++ b/src/librustc_traits/Cargo.toml @@ -10,13 +10,14 @@ 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.11.0" +chalk-solve = "0.11.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..235497d374098 --- /dev/null +++ b/src/librustc_traits/chalk/db.rs @@ -0,0 +1,476 @@ +//! Provides the `RustIrDatabase` implementation for `chalk-solve` +//! +//! The purpose of the `chalk_solve::RustIrDatabase` is to get data about +//! specific types, such as bounds, where clauses, or fields. This file contains +//! the minimal logic to assemble the types for `chalk-solve` by calling out to +//! either the `TyCtxt` (for information about types) or +//! `crate::chalk::lowering` (to lower rustc types into Chalk types). + +use rustc_middle::traits::ChalkRustInterner as RustInterner; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::{self, AssocItemContainer, AssocKind, Binder, TyCtxt}; + +use rustc_hir::def_id::DefId; + +use rustc_span::symbol::sym; + +use std::fmt; +use std::sync::Arc; + +use crate::chalk::lowering::LowerInto; + +pub struct RustIrDatabase<'tcx> { + pub tcx: TyCtxt<'tcx>, + pub interner: RustInterner<'tcx>, +} + +impl fmt::Debug for RustIrDatabase<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RustIrDatabase") + } +} + +impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'tcx> { + fn interner(&self) -> &RustInterner<'tcx> { + &self.interner + } + + fn associated_ty_data( + &self, + assoc_type_id: chalk_ir::AssocTypeId>, + ) -> Arc>> { + let def_id = assoc_type_id.0; + let assoc_item = self.tcx.associated_item(def_id); + let trait_def_id = match assoc_item.container { + AssocItemContainer::TraitContainer(def_id) => def_id, + _ => unimplemented!("Not possible??"), + }; + match assoc_item.kind { + AssocKind::Type => {} + _ => unimplemented!("Not possible??"), + } + let bound_vars = bound_vars_for_item(self.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + // FIXME(chalk): this really isn't right I don't think. The functions + // for GATs are a bit hard to figure out. Are these supposed to be where + // clauses or bounds? + let predicates = self.tcx.predicates_defined_on(def_id).predicates; + let where_clauses: Vec<_> = predicates + .iter() + .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); + + Arc::new(chalk_solve::rust_ir::AssociatedTyDatum { + trait_id: chalk_ir::TraitId(trait_def_id), + id: assoc_type_id, + name: (), + binders: chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::AssociatedTyDatumBound { bounds: vec![], where_clauses }, + ), + }) + } + + fn trait_datum( + &self, + trait_id: chalk_ir::TraitId>, + ) -> Arc>> { + let def_id = trait_id.0; + let trait_def = self.tcx.trait_def(def_id); + + let bound_vars = bound_vars_for_item(self.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + let predicates = self.tcx.predicates_defined_on(def_id).predicates; + let where_clauses: Vec<_> = predicates + .iter() + .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); + + let well_known = + if self.tcx.lang_items().sized_trait().map(|t| def_id == t).unwrap_or(false) { + Some(chalk_solve::rust_ir::WellKnownTrait::SizedTrait) + } else if self.tcx.lang_items().copy_trait().map(|t| def_id == t).unwrap_or(false) { + Some(chalk_solve::rust_ir::WellKnownTrait::CopyTrait) + } else if self.tcx.lang_items().clone_trait().map(|t| def_id == t).unwrap_or(false) { + Some(chalk_solve::rust_ir::WellKnownTrait::CloneTrait) + } else { + None + }; + Arc::new(chalk_solve::rust_ir::TraitDatum { + id: trait_id, + binders: chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::TraitDatumBound { where_clauses }, + ), + flags: chalk_solve::rust_ir::TraitFlags { + auto: trait_def.has_auto_impl, + marker: trait_def.is_marker, + upstream: !def_id.is_local(), + fundamental: self.tcx.has_attr(def_id, sym::fundamental), + non_enumerable: true, + coinductive: false, + }, + associated_ty_ids: vec![], + well_known, + }) + } + + fn adt_datum( + &self, + adt_id: chalk_ir::AdtId>, + ) -> Arc>> { + let adt_def = adt_id.0; + + let bound_vars = bound_vars_for_item(self.tcx, adt_def.did); + let binders = binders_for(&self.interner, bound_vars); + + let predicates = self.tcx.predicates_of(adt_def.did).predicates; + let where_clauses: Vec<_> = predicates + .into_iter() + .map(|(wc, _)| wc.subst(self.tcx, bound_vars)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)) + .collect(); + let fields = match adt_def.adt_kind() { + ty::AdtKind::Struct | ty::AdtKind::Union => { + let variant = adt_def.non_enum_variant(); + variant + .fields + .iter() + .map(|field| { + self.tcx + .type_of(field.did) + .subst(self.tcx, bound_vars) + .lower_into(&self.interner) + }) + .collect() + } + // FIXME(chalk): handle enums; force_impl_for requires this + ty::AdtKind::Enum => vec![], + }; + let struct_datum = Arc::new(chalk_solve::rust_ir::AdtDatum { + id: adt_id, + binders: chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::AdtDatumBound { fields, where_clauses }, + ), + flags: chalk_solve::rust_ir::AdtFlags { + upstream: !adt_def.did.is_local(), + fundamental: adt_def.is_fundamental(), + }, + }); + return struct_datum; + } + + fn fn_def_datum( + &self, + fn_def_id: chalk_ir::FnDefId>, + ) -> Arc>> { + let def_id = fn_def_id.0; + let bound_vars = bound_vars_for_item(self.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + + let predicates = self.tcx.predicates_defined_on(def_id).predicates; + let where_clauses: Vec<_> = predicates + .into_iter() + .map(|(wc, _)| wc.subst(self.tcx, &bound_vars)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); + + let sig = self.tcx.fn_sig(def_id); + // FIXME(chalk): collect into an intermediate SmallVec here since + // we need `TypeFoldable` for `no_bound_vars` + let argument_types: Binder> = + sig.map_bound(|i| i.inputs().iter().copied().collect()); + let argument_types = argument_types + .no_bound_vars() + .expect("FIXME(chalk): late-bound fn parameters not supported in chalk") + .iter() + .map(|t| t.subst(self.tcx, &bound_vars).lower_into(&self.interner)) + .collect(); + + let return_type = sig + .output() + .no_bound_vars() + .expect("FIXME(chalk): late-bound fn parameters not supported in chalk") + .subst(self.tcx, &bound_vars) + .lower_into(&self.interner); + + let bound = + chalk_solve::rust_ir::FnDefDatumBound { argument_types, where_clauses, return_type }; + Arc::new(chalk_solve::rust_ir::FnDefDatum { + id: fn_def_id, + binders: chalk_ir::Binders::new(binders, bound), + }) + } + + fn impl_datum( + &self, + impl_id: chalk_ir::ImplId>, + ) -> Arc>> { + let def_id = impl_id.0; + let bound_vars = bound_vars_for_item(self.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + + let trait_ref = self.tcx.impl_trait_ref(def_id).expect("not an impl"); + let trait_ref = trait_ref.subst(self.tcx, bound_vars); + + let predicates = self.tcx.predicates_of(def_id).predicates; + let where_clauses: Vec<_> = predicates + .iter() + .map(|(wc, _)| wc.subst(self.tcx, bound_vars)) + .filter_map(|wc| LowerInto::>>>::lower_into(wc, &self.interner)).collect(); + + let value = chalk_solve::rust_ir::ImplDatumBound { + trait_ref: trait_ref.lower_into(&self.interner), + where_clauses, + }; + + Arc::new(chalk_solve::rust_ir::ImplDatum { + polarity: chalk_solve::rust_ir::Polarity::Positive, + binders: chalk_ir::Binders::new(binders, value), + impl_type: chalk_solve::rust_ir::ImplType::Local, + associated_ty_value_ids: vec![], + }) + } + + fn impls_for_trait( + &self, + trait_id: chalk_ir::TraitId>, + parameters: &[chalk_ir::GenericArg>], + ) -> Vec>> { + let def_id = trait_id.0; + + // FIXME(chalk): use TraitDef::for_each_relevant_impl, but that will + // require us to be able to interconvert `Ty<'tcx>`, and we're + // not there yet. + + let all_impls = self.tcx.all_impls(def_id); + let matched_impls = all_impls.filter(|impl_def_id| { + use chalk_ir::could_match::CouldMatch; + let trait_ref = self.tcx.impl_trait_ref(*impl_def_id).unwrap(); + let bound_vars = bound_vars_for_item(self.tcx, *impl_def_id); + + let self_ty = trait_ref.self_ty(); + let self_ty = self_ty.subst(self.tcx, bound_vars); + let lowered_ty = self_ty.lower_into(&self.interner); + + parameters[0].assert_ty_ref(&self.interner).could_match(&self.interner, &lowered_ty) + }); + + let impls = matched_impls.map(|matched_impl| chalk_ir::ImplId(matched_impl)).collect(); + impls + } + + fn impl_provided_for( + &self, + auto_trait_id: chalk_ir::TraitId>, + adt_id: chalk_ir::AdtId>, + ) -> bool { + let trait_def_id = auto_trait_id.0; + let adt_def = adt_id.0; + let all_impls = self.tcx.all_impls(trait_def_id); + for impl_def_id in all_impls { + let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); + let self_ty = trait_ref.self_ty(); + match self_ty.kind { + ty::Adt(impl_adt_def, _) => { + if impl_adt_def == adt_def { + return true; + } + } + _ => {} + } + } + false + } + + fn associated_ty_value( + &self, + associated_ty_id: chalk_solve::rust_ir::AssociatedTyValueId>, + ) -> Arc>> { + let def_id = associated_ty_id.0; + let assoc_item = self.tcx.associated_item(def_id); + let impl_id = match assoc_item.container { + AssocItemContainer::TraitContainer(def_id) => def_id, + _ => unimplemented!("Not possible??"), + }; + match assoc_item.kind { + AssocKind::Type => {} + _ => unimplemented!("Not possible??"), + } + let bound_vars = bound_vars_for_item(self.tcx, def_id); + let binders = binders_for(&self.interner, bound_vars); + let ty = self.tcx.type_of(def_id); + + Arc::new(chalk_solve::rust_ir::AssociatedTyValue { + impl_id: chalk_ir::ImplId(impl_id), + associated_ty_id: chalk_ir::AssocTypeId(def_id), + value: chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::AssociatedTyValueBound { ty: ty.lower_into(&self.interner) }, + ), + }) + } + + fn custom_clauses(&self) -> Vec>> { + vec![] + } + + fn local_impls_to_coherence_check( + &self, + _trait_id: chalk_ir::TraitId>, + ) -> Vec>> { + unimplemented!() + } + + fn opaque_ty_data( + &self, + opaque_ty_id: chalk_ir::OpaqueTyId>, + ) -> Arc>> { + // FIXME(chalk): actually lower opaque ty + let value = chalk_solve::rust_ir::OpaqueTyDatumBound { + bounds: chalk_ir::Binders::new(chalk_ir::VariableKinds::new(&self.interner), vec![]), + }; + Arc::new(chalk_solve::rust_ir::OpaqueTyDatum { + opaque_ty_id, + bound: chalk_ir::Binders::new(chalk_ir::VariableKinds::new(&self.interner), value), + }) + } + + /// Since Chalk can't handle all Rust types currently, we have to handle + /// some specially for now. Over time, these `Some` returns will change to + /// `None` and eventually this function will be removed. + fn force_impl_for( + &self, + well_known: chalk_solve::rust_ir::WellKnownTrait, + ty: &chalk_ir::TyData>, + ) -> Option { + use chalk_ir::TyData::*; + match well_known { + chalk_solve::rust_ir::WellKnownTrait::SizedTrait => match ty { + Apply(apply) => match apply.name { + chalk_ir::TypeName::Adt(chalk_ir::AdtId(adt_def)) => match adt_def.adt_kind() { + ty::AdtKind::Struct | ty::AdtKind::Union => None, + ty::AdtKind::Enum => { + let constraint = self.tcx.adt_sized_constraint(adt_def.did); + if constraint.0.len() > 0 { unimplemented!() } else { Some(true) } + } + }, + _ => None, + }, + Dyn(_) + | Alias(_) + | Placeholder(_) + | Function(_) + | InferenceVar(_, _) + | BoundVar(_) => None, + }, + chalk_solve::rust_ir::WellKnownTrait::CopyTrait + | chalk_solve::rust_ir::WellKnownTrait::CloneTrait => match ty { + Apply(apply) => match apply.name { + chalk_ir::TypeName::Adt(chalk_ir::AdtId(adt_def)) => match adt_def.adt_kind() { + ty::AdtKind::Struct | ty::AdtKind::Union => None, + ty::AdtKind::Enum => { + let constraint = self.tcx.adt_sized_constraint(adt_def.did); + if constraint.0.len() > 0 { unimplemented!() } else { Some(true) } + } + }, + _ => None, + }, + Dyn(_) + | Alias(_) + | Placeholder(_) + | Function(_) + | InferenceVar(_, _) + | BoundVar(_) => None, + }, + chalk_solve::rust_ir::WellKnownTrait::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_solve::rust_ir::WellKnownTrait, + ) -> Option>> { + use chalk_solve::rust_ir::WellKnownTrait::*; + let t = match well_known_trait { + SizedTrait => { + self.tcx.lang_items().sized_trait().map(|t| chalk_ir::TraitId(t)).unwrap() + } + CopyTrait => self.tcx.lang_items().copy_trait().map(|t| chalk_ir::TraitId(t)).unwrap(), + CloneTrait => { + self.tcx.lang_items().clone_trait().map(|t| chalk_ir::TraitId(t)).unwrap() + } + DropTrait => self.tcx.lang_items().drop_trait().map(|t| chalk_ir::TraitId(t)).unwrap(), + }; + Some(t) + } + + fn is_object_safe(&self, trait_id: chalk_ir::TraitId>) -> bool { + self.tcx.is_object_safe(trait_id.0) + } + + fn hidden_opaque_type( + &self, + _id: chalk_ir::OpaqueTyId>, + ) -> chalk_ir::Ty> { + // FIXME(chalk): actually get hidden ty + self.tcx.mk_ty(ty::Tuple(self.tcx.intern_substs(&[]))).lower_into(&self.interner) + } +} + +/// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked +/// var bound at index `0`. For types, we use a `BoundVar` index equal to +/// the type parameter index. For regions, we use the `BoundRegion::BrNamed` +/// variant (which has a `DefId`). +fn bound_vars_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> { + InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind { + ty::GenericParamDefKind::Type { .. } => tcx + .mk_ty(ty::Bound( + ty::INNERMOST, + ty::BoundTy { + var: ty::BoundVar::from(param.index), + kind: ty::BoundTyKind::Param(param.name), + }, + )) + .into(), + + ty::GenericParamDefKind::Lifetime => tcx + .mk_region(ty::RegionKind::ReLateBound( + ty::INNERMOST, + ty::BoundRegion::BrAnon(substs.len() as u32), + )) + .into(), + + ty::GenericParamDefKind::Const => tcx + .mk_const(ty::Const { + val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)), + ty: tcx.type_of(param.def_id), + }) + .into(), + }) +} + +fn binders_for<'tcx>( + interner: &RustInterner<'tcx>, + bound_vars: SubstsRef<'tcx>, +) -> chalk_ir::VariableKinds> { + chalk_ir::VariableKinds::from( + interner, + bound_vars.iter().map(|arg| match arg.unpack() { + ty::subst::GenericArgKind::Lifetime(_re) => chalk_ir::VariableKind::Lifetime, + ty::subst::GenericArgKind::Type(_ty) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General) + } + ty::subst::GenericArgKind::Const(c) => { + chalk_ir::VariableKind::Const(c.ty.lower_into(interner)) + } + }), + ) +} diff --git a/src/librustc_traits/chalk/lowering.rs b/src/librustc_traits/chalk/lowering.rs new file mode 100644 index 0000000000000..5546a8db53395 --- /dev/null +++ b/src/librustc_traits/chalk/lowering.rs @@ -0,0 +1,851 @@ +//! Contains the logic to lower rustc types into Chalk types +//! +//! In many cases there is a 1:1 relationship between a rustc type and a Chalk type. +//! For example, a `SubstsRef` maps almost directly to a `Substitution`. In some +//! other cases, such as `Param`s, there is no Chalk type, so we have to handle +//! accordingly. +//! +//! ## `Ty` lowering +//! Much of the `Ty` lowering is 1:1 with Chalk. (Or will be eventually). A +//! helpful table for what types lower to what can be found in the +//! [Chalk book](http://rust-lang.github.io/chalk/book/types/rust_types.html). +//! The most notable difference lies with `Param`s. To convert from rustc to +//! Chalk, we eagerly and deeply convert `Param`s to placeholders (in goals) or +//! bound variables (for clause generation through functions in `db`). +//! +//! ## `Region` lowering +//! Regions are handled in rustc and Chalk is quite differently. In rustc, there +//! is a difference between "early bound" and "late bound" regions, where only +//! the late bound regions have a `DebruijnIndex`. Moreover, in Chalk all +//! regions (Lifetimes) have an associated index. In rustc, only `BrAnon`s have +//! an index, whereas `BrNamed` don't. In order to lower regions to Chalk, we +//! convert all regions into `BrAnon` late-bound regions. +//! +//! ## `Const` lowering +//! Chalk doesn't handle consts currently, so consts are currently lowered to +//! an empty tuple. +//! +//! ## Bound variable collection +//! Another difference between rustc and Chalk lies in the handling of binders. +//! Chalk requires that we store the bound parameter kinds, whereas rustc does +//! not. To lower anything wrapped in a `Binder`, we first deeply find any bound +//! variables from the current `Binder`. + +use rustc_middle::traits::{ + ChalkEnvironmentAndGoal, ChalkEnvironmentClause, ChalkRustInterner as RustInterner, +}; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; +use rustc_middle::ty::{ + self, Binder, BoundRegion, Region, RegionKind, Ty, TyCtxt, TyKind, TypeFoldable, TypeVisitor, +}; +use rustc_span::def_id::DefId; + +use std::collections::btree_map::{BTreeMap, Entry}; + +/// Essentially an `Into` with a `&RustInterner` parameter +crate trait LowerInto<'tcx, T> { + /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk type, consuming `self`. + fn lower_into(self, interner: &RustInterner<'tcx>) -> T; +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution>> for SubstsRef<'tcx> { + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_ir::Substitution> { + chalk_ir::Substitution::from(interner, self.iter().map(|s| s.lower_into(interner))) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::AliasTy>> for ty::ProjectionTy<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::AliasTy> { + chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id: chalk_ir::AssocTypeId(self.item_def_id), + substitution: self.substs.lower_into(interner), + }) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment>>> + for ChalkEnvironmentAndGoal<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_ir::InEnvironment>> { + let clauses = self.environment.into_iter().filter_map(|clause| match clause { + ChalkEnvironmentClause::Predicate(predicate) => { + match predicate.kind() { + ty::PredicateKind::Trait(predicate, _) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate); + + Some( + chalk_ir::ProgramClauseData::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), + ) + } + ty::PredicateKind::RegionOutlives(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::LifetimeOutlives( + chalk_ir::LifetimeOutlives { + a: predicate.0.lower_into(interner), + b: predicate.1.lower_into(interner), + }, + ), + ), + conditions: chalk_ir::Goals::new(interner), + priority: chalk_ir::ClausePriority::High, + }, + )) + .intern(interner), + ) + } + // FIXME(chalk): need to add TypeOutlives + ty::PredicateKind::TypeOutlives(_) => None, + ty::PredicateKind::Projection(predicate) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate); + + Some( + chalk_ir::ProgramClauseData::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::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => { + bug!("unexpected predicate {}", predicate) + } + } + } + ChalkEnvironmentClause::TypeFromEnv(ty) => Some( + chalk_ir::ProgramClauseData::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.kind() { + ty::PredicateKind::Trait(predicate, _) => predicate.lower_into(interner), + ty::PredicateKind::RegionOutlives(predicate) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate); + + chalk_ir::GoalData::Quantified( + chalk_ir::QuantifierKind::ForAll, + chalk_ir::Binders::new( + binders, + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a: predicate.0.lower_into(interner), + b: predicate.1.lower_into(interner), + }), + )) + .intern(interner), + ), + ) + } + // FIXME(chalk): TypeOutlives + ty::PredicateKind::TypeOutlives(_predicate) => { + chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) + } + ty::PredicateKind::Projection(predicate) => predicate.lower_into(interner), + ty::PredicateKind::WellFormed(arg) => match arg.unpack() { + GenericArgKind::Type(ty) => match ty.kind { + // FIXME(chalk): In Chalk, a placeholder is WellFormed if it + // `FromEnv`. However, when we "lower" Params, we don't update + // the environment. + ty::Placeholder(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)), + + _ => chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed( + chalk_ir::WellFormed::Ty(ty.lower_into(interner)), + )), + }, + // FIXME(chalk): handle well formed consts + GenericArgKind::Const(..) => { + chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) + } + GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt), + }, + + ty::PredicateKind::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal( + chalk_ir::DomainGoal::ObjectSafe(chalk_ir::TraitId(*t)), + ), + + // FIXME(chalk): other predicates + // + // We can defer this, but ultimately we'll want to express + // some of these in terms of chalk operations. + ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => { + chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)) + } + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::TraitRef>> + for rustc_middle::ty::TraitRef<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::TraitRef> { + chalk_ir::TraitRef { + trait_id: chalk_ir::TraitId(self.def_id), + substitution: self.substs.lower_into(interner), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> + for ty::PolyTraitPredicate<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { + let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self); + + chalk_ir::GoalData::Quantified( + chalk_ir::QuantifierKind::ForAll, + chalk_ir::Binders::new( + binders, + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::Implemented(ty.trait_ref.lower_into(interner)), + )) + .intern(interner), + ), + ) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq>> + for rustc_middle::ty::ProjectionPredicate<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::AliasEq> { + chalk_ir::AliasEq { + ty: self.ty.lower_into(interner), + alias: self.projection_ty.lower_into(interner), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData>> + for ty::PolyProjectionPredicate<'tcx> +{ + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { + let (ty, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &self); + + chalk_ir::GoalData::Quantified( + chalk_ir::QuantifierKind::ForAll, + chalk_ir::Binders::new( + binders, + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(ty.lower_into(interner)), + )) + .intern(interner), + ), + ) + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Ty> { + use chalk_ir::TyData; + use rustc_ast::ast; + use TyKind::*; + + let empty = || chalk_ir::Substitution::empty(interner); + let struct_ty = + |def_id| chalk_ir::TypeName::Adt(chalk_ir::AdtId(interner.tcx.adt_def(def_id))); + let apply = |name, substitution| { + TyData::Apply(chalk_ir::ApplicationTy { name, substitution }).intern(interner) + }; + let int = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Int(i)), empty()); + let uint = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Uint(i)), empty()); + let float = |f| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Float(f)), empty()); + + match self.kind { + Bool => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Bool), empty()), + Char => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Char), empty()), + Int(ty) => match ty { + ast::IntTy::Isize => int(chalk_ir::IntTy::Isize), + ast::IntTy::I8 => int(chalk_ir::IntTy::I8), + ast::IntTy::I16 => int(chalk_ir::IntTy::I16), + ast::IntTy::I32 => int(chalk_ir::IntTy::I32), + ast::IntTy::I64 => int(chalk_ir::IntTy::I64), + ast::IntTy::I128 => int(chalk_ir::IntTy::I128), + }, + Uint(ty) => match ty { + ast::UintTy::Usize => uint(chalk_ir::UintTy::Usize), + ast::UintTy::U8 => uint(chalk_ir::UintTy::U8), + ast::UintTy::U16 => uint(chalk_ir::UintTy::U16), + ast::UintTy::U32 => uint(chalk_ir::UintTy::U32), + ast::UintTy::U64 => uint(chalk_ir::UintTy::U64), + ast::UintTy::U128 => uint(chalk_ir::UintTy::U128), + }, + Float(ty) => match ty { + ast::FloatTy::F32 => float(chalk_ir::FloatTy::F32), + ast::FloatTy::F64 => float(chalk_ir::FloatTy::F64), + }, + Adt(def, substs) => apply(struct_ty(def.did), substs.lower_into(interner)), + Foreign(_def_id) => unimplemented!(), + Str => apply(chalk_ir::TypeName::Str, empty()), + Array(ty, len) => { + let value = match len.val { + ty::ConstKind::Value(val) => { + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: val }) + } + ty::ConstKind::Bound(db, bound) => { + chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.index(), + )) + } + _ => unimplemented!("Const not implemented. {:?}", len.val), + }; + apply( + chalk_ir::TypeName::Array, + chalk_ir::Substitution::from( + interner, + &[ + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), + chalk_ir::GenericArgData::Const( + chalk_ir::ConstData { ty: len.ty.lower_into(interner), value } + .intern(interner), + ) + .intern(interner), + ], + ), + ) + } + Slice(ty) => apply( + chalk_ir::TypeName::Slice, + chalk_ir::Substitution::from1( + interner, + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), + ), + ), + RawPtr(ptr) => { + let name = match ptr.mutbl { + ast::Mutability::Mut => chalk_ir::TypeName::Raw(chalk_ir::Mutability::Mut), + ast::Mutability::Not => chalk_ir::TypeName::Raw(chalk_ir::Mutability::Not), + }; + apply(name, chalk_ir::Substitution::from1(interner, ptr.ty.lower_into(interner))) + } + Ref(region, ty, mutability) => { + let name = match mutability { + ast::Mutability::Mut => chalk_ir::TypeName::Ref(chalk_ir::Mutability::Mut), + ast::Mutability::Not => chalk_ir::TypeName::Ref(chalk_ir::Mutability::Not), + }; + apply( + name, + chalk_ir::Substitution::from( + interner, + &[ + chalk_ir::GenericArgData::Lifetime(region.lower_into(interner)) + .intern(interner), + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), + ], + ), + ) + } + FnDef(def_id, substs) => apply( + chalk_ir::TypeName::FnDef(chalk_ir::FnDefId(def_id)), + substs.lower_into(interner), + ), + FnPtr(sig) => { + let (inputs_and_outputs, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &sig.inputs_and_output()); + TyData::Function(chalk_ir::Fn { + num_binders: binders.len(interner), + substitution: chalk_ir::Substitution::from( + interner, + inputs_and_outputs.iter().map(|ty| { + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner) + }), + ), + }) + .intern(interner) + } + // FIXME(chalk): add region + Dynamic(predicates, _region) => { + TyData::Dyn(chalk_ir::DynTy { bounds: predicates.lower_into(interner) }) + .intern(interner) + } + Closure(_def_id, _) => unimplemented!(), + Generator(_def_id, _substs, _) => unimplemented!(), + GeneratorWitness(_) => unimplemented!(), + Never => apply(chalk_ir::TypeName::Never, empty()), + Tuple(substs) => { + apply(chalk_ir::TypeName::Tuple(substs.len()), substs.lower_into(interner)) + } + Projection(proj) => TyData::Alias(proj.lower_into(interner)).intern(interner), + Opaque(def_id, substs) => { + TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id: chalk_ir::OpaqueTyId(def_id), + substitution: substs.lower_into(interner), + })) + .intern(interner) + } + // This should have been done eagerly prior to this, and all Params + // should have been substituted to placeholders + Param(_) => panic!("Lowering Param when not expected."), + Bound(db, bound) => TyData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.var.index(), + )) + .intern(interner), + Placeholder(_placeholder) => TyData::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: _placeholder.universe.as_usize() }, + idx: _placeholder.name.as_usize(), + }) + .intern(interner), + Infer(_infer) => unimplemented!(), + Error(_) => apply(chalk_ir::TypeName::Error, empty()), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime>> for Region<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Lifetime> { + use rustc_middle::ty::RegionKind::*; + + match self { + ReEarlyBound(_) => { + panic!("Should have already been substituted."); + } + ReLateBound(db, br) => match br { + ty::BoundRegion::BrAnon(var) => { + chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + *var as usize, + )) + .intern(interner) + } + ty::BoundRegion::BrNamed(_def_id, _name) => unimplemented!(), + ty::BrEnv => unimplemented!(), + }, + ReFree(_) => unimplemented!(), + // FIXME(chalk): need to handle ReStatic + ReStatic => unimplemented!(), + ReVar(_) => unimplemented!(), + RePlaceholder(placeholder_region) => { + chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: placeholder_region.universe.index() }, + idx: 0, + }) + .intern(interner) + } + ReEmpty(_) => unimplemented!(), + // FIXME(chalk): need to handle ReErased + ReErased => unimplemented!(), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::GenericArg>> for GenericArg<'tcx> { + fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GenericArg> { + match self.unpack() { + ty::subst::GenericArgKind::Type(ty) => { + chalk_ir::GenericArgData::Ty(ty.lower_into(interner)) + } + ty::subst::GenericArgKind::Lifetime(lifetime) => { + chalk_ir::GenericArgData::Lifetime(lifetime.lower_into(interner)) + } + ty::subst::GenericArgKind::Const(_) => chalk_ir::GenericArgData::Ty( + chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Tuple(0), + substitution: chalk_ir::Substitution::empty(interner), + }) + .intern(interner), + ), + } + .intern(interner) + } +} + +// We lower into an Option here since there are some predicates which Chalk +// doesn't have a representation for yet (as a `WhereClause`), but are so common +// that we just are accepting the unsoundness for now. The `Option` will +// eventually be removed. +impl<'tcx> LowerInto<'tcx, Option>>> + for ty::Predicate<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> Option>> { + match &self.kind() { + ty::PredicateKind::Trait(predicate, _) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate); + + Some(chalk_ir::Binders::new( + binders, + chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)), + )) + } + ty::PredicateKind::RegionOutlives(predicate) => { + let (predicate, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, predicate); + + Some(chalk_ir::Binders::new( + binders, + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a: predicate.0.lower_into(interner), + b: predicate.1.lower_into(interner), + }), + )) + } + ty::PredicateKind::TypeOutlives(_predicate) => None, + ty::PredicateKind::Projection(_predicate) => None, + ty::PredicateKind::WellFormed(_ty) => None, + + ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => bug!("unexpected predicate {}", &self), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Binders>>> + for Binder<&'tcx ty::List>> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_ir::Binders>> { + let (predicates, binders, _named_regions) = + collect_bound_vars(interner, interner.tcx, &self); + let where_clauses = predicates.into_iter().map(|predicate| match predicate { + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id, substs }) => { + chalk_ir::Binders::new( + chalk_ir::VariableKinds::new(interner), + chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { + trait_id: chalk_ir::TraitId(def_id), + substitution: substs.lower_into(interner), + }), + ) + } + ty::ExistentialPredicate::Projection(_predicate) => unimplemented!(), + ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new( + chalk_ir::VariableKinds::new(interner), + chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { + trait_id: chalk_ir::TraitId(def_id), + substitution: chalk_ir::Substitution::empty(interner), + }), + ), + }); + let value = chalk_ir::QuantifiedWhereClauses::from(interner, where_clauses); + chalk_ir::Binders::new(binders, value) + } +} + +/// To collect bound vars, we have to do two passes. In the first pass, we +/// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then +/// replace `BrNamed` into `BrAnon`. The two separate passes are important, +/// since we can only replace `BrNamed` with `BrAnon`s with indices *after* all +/// "real" `BrAnon`s. +/// +/// It's important to note that because of prior substitution, we may have +/// late-bound regions, even outside of fn contexts, since this is the best way +/// to prep types for chalk lowering. +crate fn collect_bound_vars<'a, 'tcx, T: TypeFoldable<'tcx>>( + interner: &RustInterner<'tcx>, + tcx: TyCtxt<'tcx>, + ty: &'a Binder, +) -> (T, chalk_ir::VariableKinds>, BTreeMap) { + let mut bound_vars_collector = BoundVarsCollector::new(); + ty.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::VariableKind::Lifetime); + } + + (0..parameters.len()).for_each(|i| { + parameters.get(&(i as u32)).expect("Skipped bound var index."); + }); + + let binders = chalk_ir::VariableKinds::from(interner, parameters.into_iter().map(|(_, v)| v)); + + (new_ty, binders, named_parameters) +} + +crate struct BoundVarsCollector<'tcx> { + binder_index: ty::DebruijnIndex, + crate parameters: BTreeMap>>, + crate named_parameters: Vec, +} + +impl<'tcx> BoundVarsCollector<'tcx> { + crate fn new() -> Self { + BoundVarsCollector { + binder_index: ty::INNERMOST, + parameters: BTreeMap::new(), + named_parameters: vec![], + } + } +} + +impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { + fn visit_binder>(&mut self, t: &Binder) -> bool { + self.binder_index.shift_in(1); + let result = t.super_visit_with(self); + self.binder_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match t.kind { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + match self.parameters.entry(bound_ty.var.as_u32()) { + Entry::Vacant(entry) => { + entry.insert(chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General)); + } + Entry::Occupied(entry) => match entry.get() { + chalk_ir::VariableKind::Ty(_) => {} + _ => panic!(), + }, + } + } + + _ => (), + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: Region<'tcx>) -> bool { + match r { + ty::ReLateBound(index, br) if *index == self.binder_index => match br { + ty::BoundRegion::BrNamed(def_id, _name) => { + if self.named_parameters.iter().find(|d| *d == def_id).is_none() { + self.named_parameters.push(*def_id); + } + } + + ty::BoundRegion::BrAnon(var) => match self.parameters.entry(*var) { + Entry::Vacant(entry) => { + entry.insert(chalk_ir::VariableKind::Lifetime); + } + Entry::Occupied(entry) => match entry.get() { + chalk_ir::VariableKind::Lifetime => {} + _ => panic!(), + }, + }, + + ty::BrEnv => unimplemented!(), + }, + + ty::ReEarlyBound(_re) => { + // FIXME(chalk): jackh726 - I think we should always have already + // substituted away `ReEarlyBound`s for `ReLateBound`s, but need to confirm. + unimplemented!(); + } + + _ => (), + }; + + r.super_visit_with(self) + } +} + +/// This is used to replace `BoundRegion::BrNamed` with `BoundRegion::BrAnon`. +/// Note: we assume that we will always have room for more bound vars. (i.e. we +/// won't ever hit the `u32` limit in `BrAnon`s). +struct NamedBoundVarSubstitutor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + binder_index: ty::DebruijnIndex, + named_parameters: &'a BTreeMap, +} + +impl<'a, 'tcx> NamedBoundVarSubstitutor<'a, 'tcx> { + fn new(tcx: TyCtxt<'tcx>, named_parameters: &'a BTreeMap) -> Self { + NamedBoundVarSubstitutor { tcx, binder_index: ty::INNERMOST, named_parameters } + } +} + +impl<'a, 'tcx> TypeFolder<'tcx> for NamedBoundVarSubstitutor<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder>(&mut self, t: &Binder) -> Binder { + self.binder_index.shift_in(1); + let result = t.super_fold_with(self); + self.binder_index.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + t.super_fold_with(self) + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r { + ty::ReLateBound(index, br) if *index == self.binder_index => match br { + ty::BoundRegion::BrNamed(def_id, _name) => { + match self.named_parameters.get(def_id) { + Some(idx) => { + return self.tcx.mk_region(RegionKind::ReLateBound( + *index, + BoundRegion::BrAnon(*idx), + )); + } + None => panic!("Missing `BrNamed`."), + } + } + ty::BrEnv => unimplemented!(), + ty::BoundRegion::BrAnon(_) => {} + }, + _ => (), + }; + + r.super_fold_with(self) + } +} + +/// Used to substitute `Param`s with placeholders. We do this since Chalk +/// have a notion of `Param`s. +crate struct ParamsSubstitutor<'tcx> { + tcx: TyCtxt<'tcx>, + binder_index: ty::DebruijnIndex, + list: Vec, + crate params: rustc_data_structures::fx::FxHashMap, + crate named_regions: BTreeMap, +} + +impl<'tcx> ParamsSubstitutor<'tcx> { + crate fn new(tcx: TyCtxt<'tcx>) -> Self { + ParamsSubstitutor { + tcx, + binder_index: ty::INNERMOST, + list: vec![], + params: rustc_data_structures::fx::FxHashMap::default(), + named_regions: BTreeMap::default(), + } + } +} + +impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder>(&mut self, t: &Binder) -> Binder { + self.binder_index.shift_in(1); + let result = t.super_fold_with(self); + self.binder_index.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind { + // FIXME(chalk): currently we convert params to placeholders starting at + // index `0`. To support placeholders, we'll actually need to do a + // first pass to collect placeholders. Then we can insert params after. + ty::Placeholder(_) => unimplemented!(), + ty::Param(param) => match self.list.iter().position(|r| r == ¶m) { + Some(_idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: ty::UniverseIndex::from_usize(0), + name: ty::BoundVar::from_usize(_idx), + })), + None => { + self.list.push(param); + let idx = self.list.len() - 1; + self.params.insert(idx, param); + self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: ty::UniverseIndex::from_usize(0), + name: ty::BoundVar::from_usize(idx), + })) + } + }, + + _ => t.super_fold_with(self), + } + } + + fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { + match r { + // FIXME(chalk) - jackh726 - this currently isn't hit in any tests. + // This covers any region variables in a goal, right? + ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) { + Some(idx) => self.tcx.mk_region(RegionKind::ReLateBound( + self.binder_index, + BoundRegion::BrAnon(*idx), + )), + None => { + let idx = self.named_regions.len() as u32; + self.named_regions.insert(_re.def_id, idx); + self.tcx.mk_region(RegionKind::ReLateBound( + self.binder_index, + BoundRegion::BrAnon(idx), + )) + } + }, + + _ => r.super_fold_with(self), + } + } +} diff --git a/src/librustc_traits/chalk/mod.rs b/src/librustc_traits/chalk/mod.rs new file mode 100644 index 0000000000000..6f657be0908b4 --- /dev/null +++ b/src/librustc_traits/chalk/mod.rs @@ -0,0 +1,228 @@ +//! Calls `chalk-solve` to solve a `ty::Predicate` +//! +//! In order to call `chalk-solve`, this file must convert a +//! `ChalkCanonicalGoal` into a Chalk ucanonical goal. It then calls Chalk, and +//! converts the answer back into rustc solution. + +crate mod db; +crate mod lowering; + +use rustc_data_structures::fx::FxHashMap; + +use rustc_index::vec::IndexVec; + +use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind}; +use rustc_middle::traits::ChalkRustInterner; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{ + self, Bound, BoundVar, ParamTy, Region, RegionKind, Ty, TyCtxt, TypeFoldable, +}; + +use rustc_infer::infer::canonical::{ + Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse, +}; +use rustc_infer::traits::{self, ChalkCanonicalGoal}; + +use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase; +use crate::chalk::lowering::{LowerInto, ParamsSubstitutor}; + +use chalk_solve::Solution; + +crate fn provide(p: &mut Providers<'_>) { + *p = Providers { evaluate_goal, ..*p }; +} + +crate fn evaluate_goal<'tcx>( + tcx: TyCtxt<'tcx>, + obligation: ChalkCanonicalGoal<'tcx>, +) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> { + let interner = ChalkRustInterner { tcx }; + + // Chalk doesn't have a notion of `Params`, so instead we use placeholders. + let mut params_substitutor = ParamsSubstitutor::new(tcx); + let obligation = obligation.fold_with(&mut params_substitutor); + let _params: FxHashMap = params_substitutor.params; + let max_universe = obligation.max_universe.index(); + + let _lowered_goal: chalk_ir::UCanonical< + chalk_ir::InEnvironment>>, + > = chalk_ir::UCanonical { + canonical: chalk_ir::Canonical { + binders: chalk_ir::CanonicalVarKinds::from( + &interner, + obligation.variables.iter().map(|v| match v.kind { + CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(), + CanonicalVarKind::PlaceholderRegion(_ui) => unimplemented!(), + CanonicalVarKind::Ty(ty) => match ty { + CanonicalTyVarKind::General(ui) => chalk_ir::WithKind::new( + chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General), + chalk_ir::UniverseIndex { counter: ui.index() }, + ), + CanonicalTyVarKind::Int => chalk_ir::WithKind::new( + chalk_ir::VariableKind::Ty(chalk_ir::TyKind::Integer), + chalk_ir::UniverseIndex::root(), + ), + CanonicalTyVarKind::Float => chalk_ir::WithKind::new( + chalk_ir::VariableKind::Ty(chalk_ir::TyKind::Float), + chalk_ir::UniverseIndex::root(), + ), + }, + CanonicalVarKind::Region(ui) => chalk_ir::WithKind::new( + chalk_ir::VariableKind::Lifetime, + chalk_ir::UniverseIndex { counter: ui.index() }, + ), + CanonicalVarKind::Const(_ui) => unimplemented!(), + CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(), + }), + ), + value: obligation.value.lower_into(&interner), + }, + universes: max_universe + 1, + }; + + let solver_choice = chalk_solve::SolverChoice::SLG { max_size: 32, expected_answers: None }; + let mut solver = solver_choice.into_solver::>(); + + let db = ChalkRustIrDatabase { tcx, interner }; + let solution = solver.solve(&db, &_lowered_goal); + + // Ideally, the code to convert *back* to rustc types would live close to + // the code to convert *from* rustc types. Right now though, we don't + // really need this and so it's really minimal. + // Right now, we also treat a `Unique` solution the same as + // `Ambig(Definite)`. This really isn't right. + let make_solution = |_subst: chalk_ir::Substitution<_>| { + let mut var_values: IndexVec> = IndexVec::new(); + _subst.parameters(&interner).iter().for_each(|p| { + // FIXME(chalk): we should move this elsewhere, since this is + // essentially inverse of lowering a `GenericArg`. + let _data = p.data(&interner); + match _data { + chalk_ir::GenericArgData::Ty(_t) => { + use chalk_ir::TyData; + use rustc_ast::ast; + + let _data = _t.data(&interner); + let kind = match _data { + TyData::Apply(_application_ty) => match _application_ty.name { + chalk_ir::TypeName::Adt(_struct_id) => unimplemented!(), + chalk_ir::TypeName::Scalar(scalar) => match scalar { + chalk_ir::Scalar::Bool => ty::Bool, + chalk_ir::Scalar::Char => ty::Char, + chalk_ir::Scalar::Int(int_ty) => match int_ty { + chalk_ir::IntTy::Isize => ty::Int(ast::IntTy::Isize), + chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8), + chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16), + chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32), + chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64), + chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128), + }, + chalk_ir::Scalar::Uint(int_ty) => match int_ty { + chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize), + chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8), + chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16), + chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32), + chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64), + chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128), + }, + chalk_ir::Scalar::Float(float_ty) => match float_ty { + chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32), + chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), + }, + }, + chalk_ir::TypeName::Array => unimplemented!(), + chalk_ir::TypeName::FnDef(_) => unimplemented!(), + chalk_ir::TypeName::Never => unimplemented!(), + chalk_ir::TypeName::Tuple(_size) => unimplemented!(), + chalk_ir::TypeName::Slice => unimplemented!(), + chalk_ir::TypeName::Raw(_) => unimplemented!(), + chalk_ir::TypeName::Ref(_) => unimplemented!(), + chalk_ir::TypeName::Str => unimplemented!(), + chalk_ir::TypeName::OpaqueType(_ty) => unimplemented!(), + chalk_ir::TypeName::AssociatedType(_assoc_ty) => unimplemented!(), + chalk_ir::TypeName::Error => unimplemented!(), + }, + TyData::Placeholder(_placeholder) => { + unimplemented!(); + } + TyData::Alias(_alias_ty) => unimplemented!(), + TyData::Function(_quantified_ty) => unimplemented!(), + TyData::BoundVar(_bound) => Bound( + ty::DebruijnIndex::from_usize(_bound.debruijn.depth() as usize), + ty::BoundTy { + var: ty::BoundVar::from_usize(_bound.index), + kind: ty::BoundTyKind::Anon, + }, + ), + TyData::InferenceVar(_, _) => unimplemented!(), + TyData::Dyn(_) => unimplemented!(), + }; + let _ty: Ty<'_> = tcx.mk_ty(kind); + let _arg: GenericArg<'_> = _ty.into(); + var_values.push(_arg); + } + chalk_ir::GenericArgData::Lifetime(_l) => { + let _data = _l.data(&interner); + let _lifetime: Region<'_> = match _data { + chalk_ir::LifetimeData::BoundVar(_var) => { + tcx.mk_region(RegionKind::ReLateBound( + rustc_middle::ty::DebruijnIndex::from_usize( + _var.debruijn.depth() as usize + ), + rustc_middle::ty::BoundRegion::BrAnon(_var.index as u32), + )) + } + chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(), + chalk_ir::LifetimeData::Placeholder(_index) => unimplemented!(), + chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), + }; + let _arg: GenericArg<'_> = _lifetime.into(); + var_values.push(_arg); + } + chalk_ir::GenericArgData::Const(_) => unimplemented!(), + } + }); + let sol = Canonical { + max_universe: ty::UniverseIndex::from_usize(0), + variables: obligation.variables.clone(), + value: QueryResponse { + var_values: CanonicalVarValues { var_values }, + region_constraints: QueryRegionConstraints::default(), + certainty: Certainty::Proven, + value: (), + }, + }; + &*tcx.arena.alloc(sol) + }; + solution + .map(|s| match s { + Solution::Unique(_subst) => { + // FIXME(chalk): handle constraints + make_solution(_subst.value.subst) + } + Solution::Ambig(_guidance) => { + match _guidance { + chalk_solve::Guidance::Definite(_subst) => make_solution(_subst.value), + chalk_solve::Guidance::Suggested(_) => unimplemented!(), + chalk_solve::Guidance::Unknown => { + // chalk_fulfill doesn't use the var_values here, so + // let's just ignore that + let sol = Canonical { + max_universe: ty::UniverseIndex::from_usize(0), + variables: obligation.variables.clone(), + value: QueryResponse { + var_values: CanonicalVarValues { var_values: IndexVec::new() } + .make_identity(tcx), + region_constraints: QueryRegionConstraints::default(), + certainty: Certainty::Ambiguous, + value: (), + }, + }; + &*tcx.arena.alloc(sol) + } + } + } + }) + .ok_or(traits::query::NoSolution) +} diff --git a/src/librustc_traits/dropck_outlives.rs b/src/librustc_traits/dropck_outlives.rs index b13a7a3acb165..6339f8288d54e 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::{ @@ -163,7 +163,7 @@ fn dtorck_constraint_for_ty<'tcx>( ) -> Result<(), NoSolution> { debug!("dtorck_constraint_for_ty({:?}, {:?}, {:?}, {:?})", span, for_ty, depth, ty); - if depth >= *tcx.sess.recursion_limit.get() { + if !tcx.sess.recursion_limit().value_within_limit(depth) { constraints.overflows.push(ty); return Ok(()); } @@ -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,15 +207,17 @@ fn dtorck_constraint_for_ty<'tcx>( constraints, )?; } - } + Ok::<_, NoSolution>(()) + })?, - ty::Closure(def_id, substs) => { - for ty in substs.as_closure().upvar_tys(def_id, tcx) { + 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(def_id, substs, _movability) => { + ty::Generator(_, substs, _movability) => { // rust-lang/rust#49918: types can be constructed, stored // in the interior, and sit idle when generator yields // (and is subsequently dropped). @@ -240,10 +244,10 @@ fn dtorck_constraint_for_ty<'tcx>( constraints.outlives.extend( substs .as_generator() - .upvar_tys(def_id, tcx) + .upvar_tys() .map(|t| -> ty::subst::GenericArg<'tcx> { t.into() }), ); - constraints.outlives.push(substs.as_generator().resume_ty(def_id, tcx).into()); + constraints.outlives.push(substs.as_generator().resume_ty().into()); } ty::Adt(def, substs) => { @@ -267,9 +271,7 @@ 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 => { + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => { // By the time this code runs, all type variables ought to // be fully resolved. return Err(NoSolution); 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..651596d0379bb 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; @@ -46,14 +47,14 @@ fn compute_implied_outlives_bounds<'tcx>( // process it next. Currently (at least) these resulting // predicates are always guaranteed to be a subset of the original // type, so we need not fear non-termination. - let mut wf_types = vec![ty]; + let mut wf_args = vec![ty.into()]; let mut implied_bounds = vec![]; let mut fulfill_cx = FulfillmentContext::new(); - while let Some(ty) = wf_types.pop() { - // Compute the obligations for `ty` to be well-formed. If `ty` is + while let Some(arg) = wf_args.pop() { + // Compute the obligations for `arg` to be well-formed. If `arg` is // an unresolved inference variable, just substituted an empty set // -- because the return type here is going to be things we *add* // to the environment, it's always ok for this set to be smaller @@ -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, arg, 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 @@ -93,27 +94,28 @@ fn compute_implied_outlives_bounds<'tcx>( // region relationships. implied_bounds.extend(obligations.into_iter().flat_map(|obligation| { assert!(!obligation.has_escaping_bound_vars()); - match obligation.predicate { - ty::Predicate::Trait(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::Projection(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ConstEvaluatable(..) => vec![], + match obligation.predicate.kind() { + ty::PredicateKind::Trait(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => vec![], - ty::Predicate::WellFormed(subty) => { - wf_types.push(subty); + &ty::PredicateKind::WellFormed(arg) => { + wf_args.push(arg); vec![] } - ty::Predicate::RegionOutlives(ref data) => match data.no_bound_vars() { + ty::PredicateKind::RegionOutlives(ref data) => match data.no_bound_vars() { None => vec![], Some(ty::OutlivesPredicate(r_a, r_b)) => { vec![OutlivesBound::RegionSubRegion(r_b, r_a)] } }, - ty::Predicate::TypeOutlives(ref data) => match data.no_bound_vars() { + ty::PredicateKind::TypeOutlives(ref data) => match data.no_bound_vars() { None => vec![], Some(ty::OutlivesPredicate(ty_a, r_b)) => { let ty_a = infcx.resolve_vars_if_possible(&ty_a); 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 db392ede432e1..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); - - return 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::Method(..) => 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 3a0c36a84ae81..0000000000000 --- a/src/librustc_traits/lowering/mod.rs +++ /dev/null @@ -1,629 +0,0 @@ -mod environment; - -use rustc::hir::map::definitions::DefPathData; -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::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..fcb75142269df 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) { @@ -39,14 +40,15 @@ fn normalize_ty_after_erasing_regions<'tcx>( } fn not_outlives_predicate(p: &ty::Predicate<'_>) -> bool { - match p { - ty::Predicate::RegionOutlives(..) | ty::Predicate::TypeOutlives(..) => false, - ty::Predicate::Trait(..) - | ty::Predicate::Projection(..) - | ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::ConstEvaluatable(..) => true, + match p.kind() { + ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::TypeOutlives(..) => false, + ty::PredicateKind::Trait(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => true, } } 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 e174c040e0da1..374ef3fc9c783 100644 --- a/src/librustc_traits/type_op.rs +++ b/src/librustc_traits/type_op.rs @@ -1,14 +1,13 @@ -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::{self, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable, Variance}; +use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Predicate, ToPredicate}; use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::infer::InferCtxtExt; @@ -80,11 +79,11 @@ impl AscribeUserTypeCx<'me, 'tcx> { where T: ToTrace<'tcx>, { - Ok(self - .infcx + self.infcx .at(&ObligationCause::dummy(), self.param_env) .relate(a, variance, b)? - .into_value_registering_obligations(self.infcx, self.fulfill_cx)) + .into_value_registering_obligations(self.infcx, self.fulfill_cx); + Ok(()) } fn prove_predicate(&mut self, predicate: Predicate<'tcx>) { @@ -140,7 +139,9 @@ impl AscribeUserTypeCx<'me, 'tcx> { self.relate(self_ty, Variance::Invariant, impl_self_ty)?; - self.prove_predicate(Predicate::WellFormed(impl_self_ty)); + self.prove_predicate( + ty::PredicateKind::WellFormed(impl_self_ty.into()).to_predicate(self.tcx()), + ); } // In addition to proving the predicates, we have to @@ -154,7 +155,7 @@ impl AscribeUserTypeCx<'me, 'tcx> { // them? This would only be relevant if some input // type were ill-formed but did not appear in `ty`, // which...could happen with normalization... - self.prove_predicate(Predicate::WellFormed(ty)); + self.prove_predicate(ty::PredicateKind::WellFormed(ty.into()).to_predicate(self.tcx())); Ok(()) } } @@ -165,10 +166,11 @@ fn type_op_eq<'tcx>( ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| { let (param_env, Eq { a, b }) = key.into_parts(); - Ok(infcx + infcx .at(&ObligationCause::dummy(), param_env) .eq(a, b)? - .into_value_registering_obligations(infcx, fulfill_cx)) + .into_value_registering_obligations(infcx, fulfill_cx); + Ok(()) }) } @@ -221,10 +223,11 @@ fn type_op_subtype<'tcx>( ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |infcx, fulfill_cx, key| { let (param_env, Subtype { sub, sup }) = key.into_parts(); - Ok(infcx + infcx .at(&ObligationCause::dummy(), param_env) .sup(sup, sub)? - .into_value_registering_obligations(infcx, fulfill_cx)) + .into_value_registering_obligations(infcx, fulfill_cx); + Ok(()) }) } diff --git a/src/librustc_ty/Cargo.toml b/src/librustc_ty/Cargo.toml index 3c790bc4cb13d..b6db75e44f971 100644 --- a/src/librustc_ty/Cargo.toml +++ b/src/librustc_ty/Cargo.toml @@ -10,10 +10,12 @@ 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" } +rustc_session = { path = "../librustc_session" } rustc_target = { path = "../librustc_target" } rustc_trait_selection = { path = "../librustc_trait_selection" } 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 10cc2c0e3033c..0acf769168137 100644 --- a/src/librustc_ty/instance.rs +++ b/src/librustc_ty/instance.rs @@ -1,17 +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); @@ -31,23 +35,28 @@ pub fn resolve_instance<'tcx>( debug!(" => intrinsic"); ty::InstanceDef::Intrinsic(def_id) } - _ => { - if Some(def_id) == tcx.lang_items().drop_in_place_fn() { - let ty = substs.type_at(0); - if ty.needs_drop(tcx, param_env.with_reveal_all()) { - debug!(" => nontrivial drop glue"); - ty::InstanceDef::DropGlue(def_id, Some(ty)) - } else { - debug!(" => trivial drop glue"); - ty::InstanceDef::DropGlue(def_id, None) + ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().drop_in_place_fn() => { + let ty = substs.type_at(0); + + if ty.needs_drop(tcx, param_env) { + // `DropGlue` requires a monomorphic aka concrete type. + if ty.needs_subst() { + return Ok(None); } + + debug!(" => nontrivial drop glue"); + ty::InstanceDef::DropGlue(def_id, Some(ty)) } else { - debug!(" => free item"); - ty::InstanceDef::Item(def_id) + debug!(" => trivial drop glue"); + ty::InstanceDef::DropGlue(def_id, None) } } + _ => { + debug!(" => free item"); + ty::InstanceDef::Item(def_id) + } }; - Some(Instance { def, substs }) + Ok(Some(Instance { def, substs })) }; debug!("resolve(def_id={:?}, substs={:?}) = {:?}", def_id, substs, result); result @@ -59,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={:?}, \ @@ -74,37 +83,109 @@ fn resolve_associated_item<'tcx>( // Now that we know which impl is being used, we can dispatch to // the actual function: - match vtbl { - traits::VtableImpl(impl_data) => { - let (def_id, substs) = - traits::find_associated_item(tcx, param_env, trait_item, rcvr_substs, &impl_data); + Ok(match vtbl { + traits::ImplSourceUserDefined(impl_data) => { + debug!( + "resolving ImplSourceUserDefined: {:?}, {:?}, {:?}, {:?}", + param_env, trait_item, rcvr_substs, impl_data + ); + assert!(!rcvr_substs.needs_infer()); + assert!(!trait_ref.needs_infer()); + + let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap(); + let trait_def = tcx.trait_def(trait_def_id); + let leaf_def = trait_def + .ancestors(tcx, impl_data.impl_def_id)? + .leaf_def(tcx, trait_item.ident, trait_item.kind) + .unwrap_or_else(|| { + bug!("{:?} not found in {:?}", trait_item, impl_data.impl_def_id); + }); - let resolved_item = tcx.associated_item(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 { + traits::ImplSourceGenerator(generator_data) => Some(Instance { def: ty::InstanceDef::Item(generator_data.generator_def_id), substs: generator_data.substs, }), - traits::VtableClosure(closure_data) => { + traits::ImplSourceClosure(closure_data) => { let trait_closure_kind = tcx.fn_trait_kind_from_lang_item(trait_id).unwrap(); Some(Instance::resolve_closure( tcx, @@ -113,24 +194,55 @@ fn resolve_associated_item<'tcx>( trait_closure_kind, )) } - traits::VtableFnPointer(ref data) => Some(Instance { - def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), - substs: rcvr_substs, - }), - traits::VtableObject(ref data) => { + traits::ImplSourceFnPointer(ref data) => { + // `FnPtrShim` requires a monomorphic aka concrete type. + if data.fn_ty.needs_subst() { + return Ok(None); + } + + Some(Instance { + def: ty::InstanceDef::FnPtrShim(trait_item.def_id, data.fn_ty), + substs: rcvr_substs, + }) + } + traits::ImplSourceObject(ref data) => { let index = traits::get_vtable_index_of_object_method(tcx, data, def_id); Some(Instance { def: ty::InstanceDef::Virtual(def_id, index), substs: rcvr_substs }) } - traits::VtableBuiltin(..) => { - if tcx.lang_items().clone_trait().is_some() { - Some(Instance { - def: ty::InstanceDef::CloneShim(def_id, trait_ref.self_ty()), - substs: rcvr_substs, - }) + traits::ImplSourceBuiltin(..) => { + if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() { + // FIXME(eddyb) use lang items for methods instead of names. + let name = tcx.item_name(def_id); + if name == sym::clone { + let self_ty = trait_ref.self_ty(); + + // `CloneShim` requires a monomorphic aka concrete type. + if self_ty.needs_subst() { + return Ok(None); + } + + Some(Instance { + def: ty::InstanceDef::CloneShim(def_id, self_ty), + substs: rcvr_substs, + }) + } else { + assert_eq!(name, sym::clone_from); + + // Use the default `fn clone_from` from `trait Clone`. + let substs = tcx.erase_regions(&rcvr_substs); + Some(ty::Instance::new(def_id, substs)) + } } else { None } } - traits::VtableAutoImpl(..) | traits::VtableParam(..) | traits::VtableTraitAlias(..) => None, - } + traits::ImplSourceAutoImpl(..) + | traits::ImplSourceParam(..) + | traits::ImplSourceTraitAlias(..) + | traits::ImplSourceDiscriminantKind(..) => 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 0f71246c73759..7880c09c2ad81 100644 --- a/src/librustc_ty/needs_drop.rs +++ b/src/librustc_ty/needs_drop.rs @@ -1,17 +1,18 @@ //! 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_session::Limit; use rustc_span::DUMMY_SP; type NeedsDropResult = Result; fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { let adt_fields = - move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter().copied()); + move |adt_def: &ty::AdtDef| tcx.adt_drop_tys(adt_def.did).map(|tys| tys.iter()); // If we don't know a type doesn't need drop, for example if it's a type // parameter without a `Copy` bound, then we conservatively return that it // needs drop. @@ -30,7 +31,7 @@ struct NeedsDropTypes<'tcx, F> { /// if it needs drop. If the result depends on whether some other types /// need drop we push them onto the stack. unchecked_tys: Vec<(Ty<'tcx>, usize)>, - recursion_limit: usize, + recursion_limit: Limit, adt_components: F, } @@ -43,14 +44,13 @@ impl<'tcx, F> NeedsDropTypes<'tcx, F> { ) -> Self { let mut seen_tys = FxHashSet::default(); seen_tys.insert(ty); - let recursion_limit = *tcx.sess.recursion_limit.get(); Self { tcx, param_env, seen_tys, query_ty: ty, unchecked_tys: vec![(ty, 0)], - recursion_limit, + recursion_limit: tcx.sess.recursion_limit(), adt_components, } } @@ -67,7 +67,7 @@ where let tcx = self.tcx; while let Some((ty, level)) = self.unchecked_tys.pop() { - if level > self.recursion_limit { + if !self.recursion_limit.value_within_limit(level) { // Not having a `Span` isn't great. But there's hopefully some other // recursion limit error as well. tcx.sess.span_err( @@ -91,14 +91,37 @@ where for component in components { match component.kind { - _ if component.is_copy_modulo_regions(tcx, self.param_env, DUMMY_SP) => (), + _ if component.is_copy_modulo_regions(tcx.at(DUMMY_SP), self.param_env) => (), - ty::Closure(def_id, substs) => { - for upvar_ty in substs.as_closure().upvar_tys(def_id, tcx) { + ty::Closure(_, substs) => { + for upvar_ty in substs.as_closure().upvar_tys() { queue_type(self, upvar_ty); } } + ty::Generator(def_id, 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), + _ => { + tcx.sess.delay_span_bug( + tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP), + &format!("unexpected generator witness type {:?}", witness), + ); + return Some(Err(AlwaysRequiresDrop)); + } + }; + + 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`. @@ -132,7 +155,7 @@ where } } - return None; + None } } diff --git a/src/librustc_ty/ty.rs b/src/librustc_ty/ty.rs index 387d1d9923da2..595992d01dd2d 100644 --- a/src/librustc_ty/ty.rs +++ b/src/librustc_ty/ty.rs @@ -1,10 +1,12 @@ -use rustc::hir::map as hir_map; -use rustc::session::CrateDisambiguator; -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::DefKind; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use rustc_infer::traits::util; +use rustc_middle::hir::map as hir_map; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_session::CrateDisambiguator; use rustc_span::symbol::Symbol; use rustc_span::Span; use rustc_trait_selection::traits; @@ -20,7 +22,7 @@ fn sized_constraint_for_ty<'tcx>( Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) | FnPtr(_) | Array(..) | Closure(..) | Generator(..) | Never => vec![], - Str | Dynamic(..) | Slice(_) | Foreign(..) | Error | GeneratorWitness(..) => { + Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | GeneratorWitness(..) => { // these are never sized - return the target type vec![ty] } @@ -47,8 +49,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 @@ -63,7 +63,7 @@ fn sized_constraint_for_ty<'tcx>( substs: tcx.mk_substs_trait(ty, &[]), }) .without_const() - .to_predicate(); + .to_predicate(tcx); let predicates = tcx.predicates_of(adtdef.did).predicates; if predicates.iter().any(|(p, _)| *p == sized_predicate) { vec![] } else { vec![ty] } } @@ -78,16 +78,15 @@ 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"), }; ty::AssocItem { @@ -96,23 +95,22 @@ 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), }; ty::AssocItem { @@ -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); @@ -356,6 +367,133 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync { fn_like.asyncness() } +/// For associated types we allow bounds written on the associated type +/// (`type X: Trait`) to be used as candidates. We also allow the same bounds +/// when desugared as bounds on the trait `where Self::X: Trait`. +/// +/// Note that this filtering is done with the items identity substs to +/// simplify checking that these bounds are met in impls. This means that +/// a bound such as `for<'b> >::U: Clone` can't be used, as in +/// `hr-associated-type-bound-1.rs`. +fn associated_type_projection_predicates( + tcx: TyCtxt<'_>, + assoc_item_def_id: DefId, +) -> &'_ ty::List> { + let generic_trait_bounds = tcx.predicates_of(assoc_item_def_id); + // We include predicates from the trait as well to handle + // `where Self::X: Trait`. + let item_bounds = generic_trait_bounds.instantiate_identity(tcx); + let item_predicates = util::elaborate_predicates(tcx, item_bounds.predicates.into_iter()); + + let assoc_item_ty = ty::ProjectionTy { + item_def_id: assoc_item_def_id, + substs: InternalSubsts::identity_for_item(tcx, assoc_item_def_id), + }; + + let predicates = item_predicates.filter_map(|obligation| { + let pred = obligation.predicate; + match pred.kind() { + ty::PredicateKind::Trait(tr, _) => { + if let ty::Projection(p) = tr.skip_binder().self_ty().kind { + if p == assoc_item_ty { + return Some(pred); + } + } + } + ty::PredicateKind::Projection(proj) => { + if let ty::Projection(p) = proj.skip_binder().projection_ty.self_ty().kind { + if p == assoc_item_ty { + return Some(pred); + } + } + } + ty::PredicateKind::TypeOutlives(outlives) => { + if let ty::Projection(p) = outlives.skip_binder().0.kind { + if p == assoc_item_ty { + return Some(pred); + } + } + } + _ => {} + } + None + }); + + let result = tcx.mk_predicates(predicates); + debug!( + "associated_type_projection_predicates({}) = {:?}", + tcx.def_path_str(assoc_item_def_id), + result + ); + result +} + +/// Opaque types don't have the same issues as associated types: the only +/// predicates on an opaque type (excluding those it inherits from its parent +/// item) should be of the form we're expecting. +fn opaque_type_projection_predicates( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> &'_ ty::List> { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + + let bounds = tcx.predicates_of(def_id); + let predicates = + util::elaborate_predicates(tcx, bounds.predicates.into_iter().map(|&(pred, _)| pred)); + + let filtered_predicates = predicates.filter_map(|obligation| { + let pred = obligation.predicate; + match pred.kind() { + ty::PredicateKind::Trait(tr, _) => { + if let ty::Opaque(opaque_def_id, opaque_substs) = tr.skip_binder().self_ty().kind { + if opaque_def_id == def_id && opaque_substs == substs { + return Some(pred); + } + } + } + ty::PredicateKind::Projection(proj) => { + if let ty::Opaque(opaque_def_id, opaque_substs) = + proj.skip_binder().projection_ty.self_ty().kind + { + if opaque_def_id == def_id && opaque_substs == substs { + return Some(pred); + } + } + } + ty::PredicateKind::TypeOutlives(outlives) => { + if let ty::Opaque(opaque_def_id, opaque_substs) = outlives.skip_binder().0.kind { + if opaque_def_id == def_id && opaque_substs == substs { + return Some(pred); + } + } else { + // These can come from elaborating other predicates + return None; + } + } + // These can come from elaborating other predicates + ty::PredicateKind::RegionOutlives(_) => return None, + _ => {} + } + tcx.sess.delay_span_bug( + obligation.cause.span(tcx), + &format!("unexpected predicate {:?} on opaque type", pred), + ); + None + }); + + let result = tcx.mk_predicates(filtered_predicates); + debug!("opaque_type_projection_predicates({}) = {:?}", tcx.def_path_str(def_id), result); + result +} + +fn projection_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List> { + match tcx.def_kind(def_id) { + DefKind::AssocTy => associated_type_projection_predicates(tcx, def_id), + DefKind::OpaqueTy => opaque_type_projection_predicates(tcx, def_id), + k => bug!("projection_predicates called on {}", k.descr(def_id)), + } +} + pub fn provide(providers: &mut ty::query::Providers<'_>) { *providers = ty::query::Providers { asyncness, @@ -371,6 +509,8 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) { crate_hash, instance_def_size_estimate, issue33140_self_ty, + impl_defaultness, + projection_predicates, ..*providers }; } diff --git a/src/librustc_typeck/Cargo.toml b/src/librustc_typeck/Cargo.toml index 83a48ee3995ac..9329069c48dd1 100644 --- a/src/librustc_typeck/Cargo.toml +++ b/src/librustc_typeck/Cargo.toml @@ -11,14 +11,15 @@ test = false doctest = false [dependencies] -arena = { path = "../libarena" } +rustc_arena = { path = "../librustc_arena" } 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_hir = { path = "../librustc_hir" } rustc_target = { path = "../librustc_target" } +rustc_session = { path = "../librustc_session" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_ast = { path = "../librustc_ast" } rustc_span = { path = "../librustc_span" } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index be8090cf21b9c..1b08bf2fc7710 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -6,41 +6,39 @@ // ignore-tidy-filelength use crate::collect::PlaceholderHirTyCollector; -use crate::lint; -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::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; -use rustc::session::{parse::feature_err, Session}; -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_ast::{ast::ParamKindOrd, 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::Visitor; -use rustc_hir::print; -use rustc_hir::{Constness, ExprKind, GenericArg, GenericArgs}; -use rustc_span::symbol::sym; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::{walk_generics, Visitor as _}; +use rustc_hir::lang_items::SizedTraitLangItem; +use rustc_hir::{Constness, GenericArg, GenericArgs}; +use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::{ + self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, +}; +use rustc_middle::ty::{GenericParamDef, GenericParamDefKind}; +use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, LATE_BOUND_LIFETIME_ARGUMENTS}; +use rustc_session::parse::feature_err; +use rustc_session::Session; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::astconv_object_safety_violations; use rustc_trait_selection::traits::error_reporting::report_object_safety_error; use rustc_trait_selection::traits::wf::object_region_bounds; -use smallvec::SmallVec; +use smallvec::SmallVec; use std::collections::BTreeSet; use std::iter; use std::slice; -use rustc::mir::interpret::LitToConstInput; - #[derive(Debug)] pub struct PathSeg(pub DefId, pub usize); @@ -115,7 +113,7 @@ pub enum SizedByDefault { } struct ConvertedBinding<'a, 'tcx> { - item_name: ast::Ident, + item_name: Ident, kind: ConvertedBindingKind<'a, 'tcx>, span: Span, } @@ -125,7 +123,22 @@ enum ConvertedBindingKind<'a, 'tcx> { Constraint(&'a [hir::GenericBound<'a>]), } -#[derive(PartialEq)] +/// New-typed boolean indicating whether explicit late-bound lifetimes +/// are present in a set of generic arguments. +/// +/// For example if we have some method `fn f<'a>(&'a self)` implemented +/// for some type `T`, although `f` is generic in the lifetime `'a`, `'a` +/// is late-bound so should not be provided explicitly. Thus, if `f` is +/// instantiated with some generic arguments providing `'a` explicitly, +/// we taint those arguments with `ExplicitLateBound::Yes` so that we +/// can provide an appropriate diagnostic later. +#[derive(Copy, Clone, PartialEq)] +pub enum ExplicitLateBound { + Yes, + No, +} + +#[derive(Copy, Clone, PartialEq)] enum GenericArgPosition { Type, Value, // e.g., functions @@ -134,6 +147,7 @@ enum GenericArgPosition { /// A marker denoting that the generic arguments that were /// provided did not match the respective generic parameters. +#[derive(Clone, Default)] pub struct GenericArgCountMismatch { /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`). pub reported: Option, @@ -141,6 +155,14 @@ pub struct GenericArgCountMismatch { pub invalid_args: Vec, } +/// Decorates the result of a generic argument count mismatch +/// check with whether explicit late bounds were provided. +#[derive(Clone)] +pub struct GenericArgCountResult { + pub explicit_late_bound: ExplicitLateBound, + pub correct: Result<(), GenericArgCountMismatch>, +} + impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub fn ast_region_to_region( &self, @@ -148,13 +170,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))) } @@ -163,12 +185,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), @@ -214,7 +236,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 } @@ -271,7 +295,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { def: &ty::Generics, seg: &hir::PathSegment<'_>, is_method_call: bool, - ) -> Result<(), GenericArgCountMismatch> { + ) -> GenericArgCountResult { let empty_args = hir::GenericArgs::none(); let suppress_mismatch = Self::check_impl_trait(tcx, seg, &def); Self::check_generic_arg_count( @@ -295,7 +319,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { position: GenericArgPosition, has_self: bool, infer_args: bool, - ) -> Result<(), GenericArgCountMismatch> { + ) -> GenericArgCountResult { // At this stage we are guaranteed that the generic arguments are in the correct order, e.g. // that lifetimes will proceed types. So it suffices to check the number of each generic // arguments in order to validate them with respect to the generic parameters. @@ -320,105 +344,86 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); } - // Prohibit explicit lifetime arguments if late-bound lifetime parameters are present. - let mut explicit_lifetimes = Ok(()); - if !infer_lifetimes { - if let Some(span_late) = def.has_late_bound_regions { - let msg = "cannot specify lifetime arguments explicitly \ - if late bound lifetime parameters are present"; - let note = "the late bound lifetime parameter is introduced here"; - let span = args.args[0].span(); - if position == GenericArgPosition::Value - && arg_counts.lifetimes != param_counts.lifetimes - { - explicit_lifetimes = Err(true); - let mut err = tcx.sess.struct_span_err(span, msg); - err.span_note(span_late, note); - err.emit(); - } else { - explicit_lifetimes = Err(false); - let mut multispan = MultiSpan::from_span(span); - multispan.push_span_label(span_late, note.to_string()); - tcx.struct_span_lint_hir( - lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, - args.args[0].id(), - multispan, - |lint| lint.build(msg).emit(), - ); - } + let explicit_late_bound = + Self::prohibit_explicit_late_bound_lifetimes(tcx, def, args, position); + + let check_kind_count = |kind, + required, + permitted, + provided, + offset, + unexpected_spans: &mut Vec, + silent| { + debug!( + "check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}", + kind, required, permitted, provided, offset + ); + // We enforce the following: `required` <= `provided` <= `permitted`. + // For kinds without defaults (e.g.., lifetimes), `required == permitted`. + // For other kinds (i.e., types), `permitted` may be greater than `required`. + if required <= provided && provided <= permitted { + return Ok(()); } - } - let check_kind_count = - |kind, required, permitted, provided, offset, unexpected_spans: &mut Vec| { - debug!( - "check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}", - kind, required, permitted, provided, offset - ); - // We enforce the following: `required` <= `provided` <= `permitted`. - // For kinds without defaults (e.g.., lifetimes), `required == permitted`. - // For other kinds (i.e., types), `permitted` may be greater than `required`. - if required <= provided && provided <= permitted { - return Ok(()); - } - - // Unfortunately lifetime and type parameter mismatches are typically styled - // differently in diagnostics, which means we have a few cases to consider here. - let (bound, quantifier) = if required != permitted { - if provided < required { - (required, "at least ") - } else { - // provided > permitted - (permitted, "at most ") - } - } else { - (required, "") - }; + if silent { + return Err(true); + } - let (spans, label) = if required == permitted && provided > permitted { - // In the case when the user has provided too many arguments, - // we want to point to the unexpected arguments. - let spans: Vec = args.args[offset + permitted..offset + provided] - .iter() - .map(|arg| arg.span()) - .collect(); - unexpected_spans.extend(spans.clone()); - (spans, format!("unexpected {} argument", kind)) + // Unfortunately lifetime and type parameter mismatches are typically styled + // differently in diagnostics, which means we have a few cases to consider here. + let (bound, quantifier) = if required != permitted { + if provided < required { + (required, "at least ") } else { - ( - vec![span], - format!( - "expected {}{} {} argument{}", - quantifier, - bound, - kind, - pluralize!(bound), - ), - ) - }; - - let mut err = tcx.sess.struct_span_err_with_code( - spans.clone(), - &format!( - "wrong number of {} arguments: expected {}{}, found {}", - kind, quantifier, bound, provided, - ), - DiagnosticId::Error("E0107".into()), - ); - for span in spans { - err.span_label(span, label.as_str()); + // provided > permitted + (permitted, "at most ") } - err.emit(); + } else { + (required, "") + }; - Err(true) + let (spans, label) = if required == permitted && provided > permitted { + // In the case when the user has provided too many arguments, + // we want to point to the unexpected arguments. + let spans: Vec = args.args[offset + permitted..offset + provided] + .iter() + .map(|arg| arg.span()) + .collect(); + unexpected_spans.extend(spans.clone()); + (spans, format!("unexpected {} argument", kind)) + } else { + ( + vec![span], + format!( + "expected {}{} {} argument{}", + quantifier, + bound, + kind, + pluralize!(bound), + ), + ) }; - let mut arg_count_correct = explicit_lifetimes; + let mut err = tcx.sess.struct_span_err_with_code( + spans.clone(), + &format!( + "wrong number of {} arguments: expected {}{}, found {}", + kind, quantifier, bound, provided, + ), + DiagnosticId::Error("E0107".into()), + ); + for span in spans { + err.span_label(span, label.as_str()); + } + err.emit(); + + Err(true) + }; + + let mut arg_count_correct = Ok(()); let mut unexpected_spans = vec![]; - if arg_count_correct.is_ok() - && (!infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes) - { + if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes { arg_count_correct = check_kind_count( "lifetime", param_counts.lifetimes, @@ -426,6 +431,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_counts.lifetimes, 0, &mut unexpected_spans, + explicit_late_bound == ExplicitLateBound::Yes, ) .and(arg_count_correct); } @@ -438,6 +444,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_counts.consts, arg_counts.lifetimes + arg_counts.types, &mut unexpected_spans, + false, ) .and(arg_count_correct); } @@ -451,19 +458,28 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg_counts.types, arg_counts.lifetimes, &mut unexpected_spans, + false, ) .and(arg_count_correct); } - arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { - reported: if reported_err { Some(ErrorReported) } else { None }, - invalid_args: unexpected_spans, - }) + GenericArgCountResult { + explicit_late_bound, + correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { + reported: if reported_err { Some(ErrorReported) } else { None }, + invalid_args: unexpected_spans, + }), + } } /// Report an error that a generic argument did not match the generic parameter that was /// expected. - fn generic_arg_mismatch_err(sess: &Session, arg: &GenericArg<'_>, kind: &'static str) { + fn generic_arg_mismatch_err( + sess: &Session, + arg: &GenericArg<'_>, + kind: &'static str, + help: Option<&str>, + ) { let mut err = struct_span_err!( sess, arg.span(), @@ -472,8 +488,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { arg.descr(), kind, ); + + let kind_ord = match kind { + "lifetime" => ParamKindOrd::Lifetime, + "type" => ParamKindOrd::Type, + "constant" => ParamKindOrd::Const, + // It's more concise to match on the string representation, though it means + // the match is non-exhaustive. + _ => bug!("invalid generic parameter kind {}", kind), + }; + let arg_ord = match arg { + GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, + GenericArg::Type(_) => ParamKindOrd::Type, + GenericArg::Const(_) => ParamKindOrd::Const, + }; + // This note will be true as long as generic parameters are strictly ordered by their kind. - err.note(&format!("{} arguments must be provided before {} arguments", kind, arg.descr())); + let (first, last) = + if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) }; + err.note(&format!("{} arguments must be provided before {} arguments", first, last)); + + if let Some(help) = help { + err.help(help); + } err.emit(); } @@ -512,7 +549,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { parent_substs: &[subst::GenericArg<'tcx>], has_self: bool, self_ty: Option>, - arg_count_correct: bool, + arg_count: GenericArgCountResult, args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool), mut provided_kind: impl FnMut(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>, mut inferred_kind: impl FnMut( @@ -585,29 +622,94 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // input. We try to handle both sensibly. match (args.peek(), params.peek()) { (Some(&arg), Some(¶m)) => { - match (arg, ¶m.kind) { - (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime) - | (GenericArg::Type(_), GenericParamDefKind::Type { .. }) - | (GenericArg::Const(_), GenericParamDefKind::Const) => { + match (arg, ¶m.kind, arg_count.explicit_late_bound) { + (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) + | (GenericArg::Type(_), GenericParamDefKind::Type { .. }, _) + | (GenericArg::Const(_), GenericParamDefKind::Const, _) => { substs.push(provided_kind(param, arg)); args.next(); params.next(); } - (GenericArg::Type(_), 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)); force_infer_lt = Some(arg); params.next(); } - (_, kind) => { + (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { + // We've come across a lifetime when we expected something else in + // the presence of explicit late bounds. This is most likely + // due to the presence of the explicit bound so we're just going to + // ignore it. + args.next(); + } + (_, kind, _) => { // We expected one kind of parameter, but the user provided // another. This is an error. However, if we already know that // the arguments don't match up with the parameters, we won't issue // an additional error, as the user already knows what's wrong. - if arg_count_correct { - Self::generic_arg_mismatch_err(tcx.sess, arg, kind.descr()); + if arg_count.correct.is_ok() + && arg_count.explicit_late_bound == ExplicitLateBound::No + { + // We're going to iterate over the parameters to sort them out, and + // show that order to the user as a possible order for the parameters + let mut param_types_present = defs + .params + .clone() + .into_iter() + .map(|param| { + ( + match param.kind { + GenericParamDefKind::Lifetime => { + ParamKindOrd::Lifetime + } + GenericParamDefKind::Type { .. } => { + ParamKindOrd::Type + } + GenericParamDefKind::Const => { + ParamKindOrd::Const + } + }, + param, + ) + }) + .collect::>(); + param_types_present.sort_by_key(|(ord, _)| *ord); + let (mut param_types_present, ordered_params): ( + Vec, + Vec, + ) = param_types_present.into_iter().unzip(); + param_types_present.dedup(); + + Self::generic_arg_mismatch_err( + tcx.sess, + arg, + kind.descr(), + Some(&format!( + "reorder the arguments: {}: `<{}>`", + param_types_present + .into_iter() + .map(|ord| format!("{}s", ord.to_string())) + .collect::>() + .join(", then "), + ordered_params + .into_iter() + .filter_map(|param| { + if param.name == kw::SelfUpper { + None + } else { + Some(param.name.to_string()) + } + }) + .collect::>() + .join(", ") + )), + ); } // We've reported the error, but we want to make sure that this @@ -622,22 +724,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (Some(&arg), None) => { // We should never be able to reach this point with well-formed input. - // There are two situations in which we can encounter this issue. + // There are three situations in which we can encounter this issue. // // 1. The number of arguments is incorrect. In this case, an error - // will already have been emitted, and we can ignore it. This case - // also occurs when late-bound lifetime parameters are present, yet - // the lifetime arguments have also been explicitly specified by the + // will already have been emitted, and we can ignore it. + // 2. There are late-bound lifetime parameters present, yet the + // lifetime arguments have also been explicitly specified by the // user. - // 2. We've inferred some lifetimes, which have been provided later (i.e. + // 3. We've inferred some lifetimes, which have been provided later (i.e. // after a type or const). We want to throw an error in this case. - if arg_count_correct { + if arg_count.correct.is_ok() + && arg_count.explicit_late_bound == ExplicitLateBound::No + { let kind = arg.descr(); assert_eq!(kind, "lifetime"); let provided = force_infer_lt.expect("lifetimes ought to have been inferred"); - Self::generic_arg_mismatch_err(tcx.sess, provided, kind); + Self::generic_arg_mismatch_err(tcx.sess, provided, kind, None); } break; @@ -697,8 +801,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generic_args: &'a hir::GenericArgs<'_>, infer_args: bool, self_ty: Option>, - ) -> (SubstsRef<'tcx>, Vec>, Result<(), GenericArgCountMismatch>) - { + ) -> (SubstsRef<'tcx>, Vec>, GenericArgCountResult) { // If the type is parameterized by this region, then replace this // region with the current anon region binding (in other words, // whatever & would get replaced with). @@ -724,7 +827,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assert!(self_ty.is_none() && parent_substs.is_empty()); } - let arg_count_correct = Self::check_generic_arg_count( + let arg_count = Self::check_generic_arg_count( tcx, span, &generic_params, @@ -738,8 +841,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; @@ -758,7 +862,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { parent_substs, self_ty.is_some(), self_ty, - arg_count_correct.is_ok(), + arg_count.clone(), // Provide the generic args, and whether types should be inferred. |did| { if did == def_id { @@ -776,13 +880,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { if let (hir::TyKind::Infer, false) = (&ty.kind, self.allow_ty_infer()) { inferred_params.push(ty.span); - tcx.types.err.into() + tcx.ty_error().into() } else { self.ast_ty_to_ty(&ty).into() } } (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - self.ast_const_to_const(&ct.value, tcx.type_of(param.def_id)).into() + let ct_def_id = tcx.hir().local_def_id(ct.value.hir_id); + ty::Const::from_anon_const(tcx, ct_def_id).into() } _ => unreachable!(), }, @@ -801,7 +906,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // careful! if default_needs_object_self(param) { missing_type_params.push(param.name.to_string()); - tcx.types.err.into() + tcx.ty_error().into() } else { // This is a default type parameter. self.normalize_ty( @@ -821,35 +926,23 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.ty_infer(param, span).into() } else { // We've already errored above about the mismatch. - tcx.types.err.into() + tcx.ty_error().into() } } GenericParamDefKind::Const => { + let ty = tcx.at(span).type_of(param.def_id); // FIXME(const_generics:defaults) if infer_args { // No const parameters were provided, we can infer all. - 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.const_error(ty).into() } } } }, ); - if !inferred_params.is_empty() { - // 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. - crate::collect::placeholder_type_error( - tcx, - inferred_params[0], - &[], - inferred_params, - false, - ); - } self.complain_about_missing_type_params( missing_type_params, @@ -888,7 +981,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generic_params, self_ty, substs ); - (substs, assoc_bindings, arg_count_correct) + (substs, assoc_bindings, arg_count) } crate fn create_substs_for_associated_item( @@ -1004,7 +1097,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(), ) @@ -1019,25 +1112,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, speculative: bool, - ) -> Result<(), GenericArgCountMismatch> { - let trait_def_id = trait_ref.trait_def_id(); + ) -> GenericArgCountResult { + let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id); self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1); - let 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, + let (substs, assoc_bindings, arg_count) = self.create_substs_for_ast_trait_ref( + trait_ref.path.span, trait_def_id, self_ty, trait_ref.path.segments.last().unwrap(), @@ -1056,7 +1139,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). } @@ -1066,7 +1149,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_ref, bounds, poly_trait_ref ); - arg_count_correct + arg_count } /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct @@ -1094,7 +1177,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { constness: Constness, self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, - ) -> Result<(), GenericArgCountMismatch> { + ) -> GenericArgCountResult { self.instantiate_poly_trait_ref_inner( &poly_trait_ref.trait_ref, poly_trait_ref.span, @@ -1114,7 +1197,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) } @@ -1131,6 +1216,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 { ( @@ -1144,9 +1230,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .as_ref() .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))) + hir::GenericArg::Type(ty) => match ty.kind { + hir::TyKind::Tup(t) => t + .iter() + .map(|e| sess.source_map().span_to_snippet(e.span)) + .collect::, _>>() + .map(|a| a.join(", ")), + _ => sess.source_map().span_to_snippet(ty.span), } + .map(|s| format!("({})", s)) + .ok(), _ => None, }) .unwrap_or_else(|| "()".to_string()), @@ -1154,20 +1247,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"; @@ -1183,8 +1274,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_def_id: DefId, self_ty: Ty<'tcx>, trait_segment: &'a hir::PathSegment<'a>, - ) -> (SubstsRef<'tcx>, Vec>, Result<(), GenericArgCountMismatch>) - { + ) -> (SubstsRef<'tcx>, Vec>, GenericArgCountResult) { debug!("create_substs_for_ast_trait_ref(trait_segment={:?})", trait_segment); self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment); @@ -1199,11 +1289,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) @@ -1261,7 +1347,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 /// | @@ -1369,13 +1455,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // That is, consider this case: // // ``` - // trait SubTrait: SuperTrait { } + // trait SubTrait: SuperTrait { } // trait SuperTrait { type T; } // // ... B: SubTrait ... // ``` // - // We want to produce `>::T == foo`. + // We want to produce `>::T == foo`. // Find any late-bound regions declared in `ty` that are not // declared in the trait-ref. These are not well-formed. @@ -1441,17 +1527,24 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let (assoc_ident, def_scope) = tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id); - // We have already adjusted the item name above, so compare with `ident.modern()` instead + // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead // of calling `filter_by_name_and_kind`. let assoc_ty = tcx .associated_items(candidate.def_id()) .filter_by_name_unhygienic(assoc_ident.name) - .find(|i| i.kind == ty::AssocKind::Type && i.ident.modern() == assoc_ident) + .find(|i| { + i.kind == ty::AssocKind::Type && i.ident.normalize_to_macros_2_0() == assoc_ident + }) .expect("missing associated type"); if !assoc_ty.vis.is_accessible_from(def_scope, tcx) { - let msg = format!("associated type `{}` is private", binding.item_name); - tcx.sess.span_err(binding.span, &msg); + tcx.sess + .struct_span_err( + binding.span, + &format!("associated type `{}` is private", binding.item_name), + ) + .span_label(binding.span, "private associated type") + .emit(); } tcx.check_stability(assoc_ty.def_id, Some(hir_ref_id), binding.span); @@ -1529,9 +1622,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut potential_assoc_types = Vec::new(); let dummy_self = self.tcx().types.trait_object_dummy_self; for trait_bound in trait_bounds.iter().rev() { - if let Err(GenericArgCountMismatch { - invalid_args: cur_potential_assoc_types, .. - }) = self.instantiate_poly_trait_ref( + if let GenericArgCountResult { + correct: + Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), + .. + } = self.instantiate_poly_trait_ref( trait_bound, Constness::NotConst, dummy_self, @@ -1573,7 +1668,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "at least one trait is required for an object type" ) .emit(); - return tcx.types.err; + return tcx.ty_error(); } // Check that there are no gross object safety violations; @@ -1587,10 +1682,10 @@ 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; + return tcx.ty_error(); } } @@ -1605,13 +1700,13 @@ 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 { - ty::Predicate::Trait(pred, _) => { + match obligation.predicate.kind() { + ty::PredicateKind::Trait(pred, _) => { associated_types.entry(span).or_default().extend( tcx.associated_items(pred.def_id()) .in_definition_order() @@ -1619,10 +1714,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map(|item| item.def_id), ); } - ty::Predicate::Projection(pred) => { + &ty::PredicateKind::Projection(pred) => { // A `Self` within the original bound will be substituted with a // `trait_object_dummy_self`, so check for that. - let references_self = pred.skip_binder().ty.walk().any(|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. @@ -1691,9 +1787,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; // Erase the `dummy_self` (`trait_object_dummy_self`) used above. - let existential_trait_refs = regular_traits - .iter() - .map(|i| i.trait_ref().map_bound(|trait_ref| trait_ref_to_existential(trait_ref))); + let existential_trait_refs = + regular_traits.iter().map(|i| i.trait_ref().map_bound(trait_ref_to_existential)); let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| { bound.map_bound(|b| { let trait_ref = trait_ref_to_existential(b.projection_ty.trait_ref(tcx)); @@ -1731,6 +1826,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, @@ -1761,7 +1857,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(); @@ -1791,9 +1887,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. @@ -1825,8 +1920,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .collect(); } _ => {} - }, - _ => {} + } } names.sort(); trait_bound_spans.sort(); @@ -1948,7 +2042,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)) = ( @@ -1978,8 +2072,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(); @@ -1989,11 +2083,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( || { @@ -2015,7 +2110,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,14 +2221,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Err(ErrorReported); } } - return Ok(bound); + Ok(bound) } fn complain_about_assoc_type_not_found( &self, all_candidates: impl Fn() -> I, ty_param_name: &str, - assoc_name: ast::Ident, + assoc_name: Ident, span: Span, ) where I: Iterator>, @@ -2237,10 +2332,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 @@ -2276,7 +2371,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)); } @@ -2298,12 +2393,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let (assoc_ident, def_scope) = tcx.adjust_ident_and_get_scope(assoc_ident, trait_did, hir_ref_id); - // We have already adjusted the item name above, so compare with `ident.modern()` instead + // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead // of calling `filter_by_name_and_kind`. let item = tcx .associated_items(trait_did) .in_definition_order() - .find(|i| i.kind.namespace() == Namespace::TypeNS && i.ident.modern() == assoc_ident) + .find(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident.normalize_to_macros_2_0() == assoc_ident + }) .expect("missing associated type"); let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound); @@ -2311,8 +2409,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let kind = DefKind::AssocTy; if !item.vis.is_accessible_from(def_scope, tcx) { - let msg = format!("{} `{}` is private", kind.descr(item.def_id), assoc_ident); - tcx.sess.span_err(span, &msg); + let kind = kind.descr(item.def_id); + let msg = format!("{} `{}` is private", kind, assoc_ident); + tcx.sess + .struct_span_err(span, &msg) + .span_label(span, &format!("private {}", kind)) + .emit(); } tcx.check_stability(item.def_id, Some(hir_ref_id), span); @@ -2369,8 +2471,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); @@ -2391,7 +2495,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &path_str, item_segment.ident.name, ); - return tcx.types.err; + return tcx.ty_error(); }; debug!("qpath_to_ty: self_type={:?}", self_ty); @@ -2441,6 +2545,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { continue; } err_for_ct = true; + has_err = true; (ct.span, "const") } }; @@ -2477,6 +2582,47 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.span_label(span, "associated type not allowed here").emit(); } + /// Prohibits explicit lifetime arguments if late-bound lifetime parameters + /// are present. This is used both for datatypes and function calls. + fn prohibit_explicit_late_bound_lifetimes( + tcx: TyCtxt<'_>, + def: &ty::Generics, + args: &hir::GenericArgs<'_>, + position: GenericArgPosition, + ) -> ExplicitLateBound { + let param_counts = def.own_counts(); + let arg_counts = args.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; + + if infer_lifetimes { + ExplicitLateBound::No + } else if let Some(span_late) = def.has_late_bound_regions { + let msg = "cannot specify lifetime arguments explicitly \ + if late bound lifetime parameters are present"; + let note = "the late bound lifetime parameter is introduced here"; + let span = args.args[0].span(); + if position == GenericArgPosition::Value + && arg_counts.lifetimes != param_counts.lifetimes + { + let mut err = tcx.sess.struct_span_err(span, msg); + err.span_note(span_late, note); + err.emit(); + } else { + let mut multispan = MultiSpan::from_span(span); + multispan.push_span_label(span_late, note.to_string()); + tcx.struct_span_lint_hir( + LATE_BOUND_LIFETIME_ARGUMENTS, + args.args[0].id(), + multispan, + |lint| lint.build(msg).emit(), + ); + } + ExplicitLateBound::Yes + } else { + ExplicitLateBound::No + } + } + // FIXME(eddyb, varkor) handle type paths here too, not just value ones. pub fn def_ids_for_value_path_segments( &self, @@ -2628,11 +2774,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 +2808,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); @@ -2699,12 +2848,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { hir::PrimTy::Int(it) => tcx.mk_mach_int(it), hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(uit), hir::PrimTy::Float(ft) => tcx.mk_mach_float(ft), - hir::PrimTy::Str => tcx.mk_str(), + hir::PrimTy::Str => tcx.types.str_, } } Res::Err => { self.set_tainted_by_errors(); - return self.tcx().types.err; + self.tcx().ty_error() } _ => span_bug!(span, "unexpected resolution: {:?}", path.res), } @@ -2734,7 +2883,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } hir::TyKind::BareFn(ref bf) => { require_c_abi_if_c_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); - tcx.mk_fn_ptr(self.ty_of_fn(bf.unsafety, bf.abi, &bf.decl, &[], None)) + tcx.mk_fn_ptr(self.ty_of_fn( + bf.unsafety, + bf.abi, + &bf.decl, + &hir::Generics::empty(), + None, + )) } hir::TyKind::TraitObject(ref bounds, ref lifetime) => { self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime) @@ -2744,9 +2899,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself)); self.res_to_ty(opt_self_ty, path, false) } - hir::TyKind::Def(item_id, ref lifetimes) => { - let did = tcx.hir().local_def_id(item_id.id); - self.impl_trait_ty_to_ty(did, lifetimes) + hir::TyKind::OpaqueDef(item_id, ref lifetimes) => { + let opaque_ty = tcx.hir().expect_item(item_id.id); + let def_id = tcx.hir().local_def_id(item_id.id).to_def_id(); + + match opaque_ty.kind { + hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { + self.impl_trait_ty_to_ty(def_id, lifetimes, impl_trait_fn.is_some()) + } + ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), + } } hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => { debug!("ast_ty_to_ty: qself={:?} segment={:?}", qself, segment); @@ -2759,10 +2921,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, res, segment, false) .map(|(ty, _, _)| ty) - .unwrap_or(tcx.types.err) + .unwrap_or_else(|_| tcx.ty_error()) } hir::TyKind::Array(ref ty, ref length) => { - let length = self.ast_const_to_const(length, tcx.types.usize); + 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) } @@ -2776,7 +2939,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .span_label(ast_ty.span, "reserved keyword") .emit(); - tcx.types.err + tcx.ty_error() } hir::TyKind::Infer => { // Infer also appears as the type of arguments or return @@ -2785,7 +2948,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // handled specially and will not descend into this routine. self.ty_infer(None, ast_ty.span) } - hir::TyKind::Err => tcx.types.err, + hir::TyKind::Err => tcx.ty_error(), }; debug!("ast_ty_to_ty: result_ty={:?}", result_ty); @@ -2794,79 +2957,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { result_ty } - /// Returns the `DefId` of the constant parameter that the provided expression is a path to. - pub fn const_param_def_id(&self, expr: &hir::Expr<'_>) -> Option { - // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments - // currently have to be wrapped in curly brackets, so it's necessary to special-case. - let expr = match &expr.kind { - ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => { - block.expr.as_ref().unwrap() - } - _ => expr, - }; - - match &expr.kind { - ExprKind::Path(hir::QPath::Resolved(_, path)) => match path.res { - Res::Def(DefKind::ConstParam, did) => Some(did), - _ => None, - }, - _ => None, - } - } - - pub fn ast_const_to_const( - &self, - ast_const: &hir::AnonConst, - ty: Ty<'tcx>, - ) -> &'tcx ty::Const<'tcx> { - debug!("ast_const_to_const(id={:?}, ast_const={:?})", ast_const.hir_id, ast_const); - - let tcx = self.tcx(); - let def_id = tcx.hir().local_def_id(ast_const.hir_id); - - let expr = &tcx.hir().body(ast_const.body).value; - - let lit_input = match expr.kind { - hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }), - hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => match expr.kind { - hir::ExprKind::Lit(ref lit) => { - Some(LitToConstInput { lit: &lit.node, ty, neg: true }) - } - _ => None, - }, - _ => None, - }; - - if let Some(lit_input) = lit_input { - // If an error occurred, ignore that it's a literal and leave reporting the error up to - // mir. - if let Ok(c) = tcx.at(expr.span).lit_to_const(lit_input) { - return c; - } else { - tcx.sess.delay_span_bug(expr.span, "ast_const_to_const: couldn't lit_to_const"); - } - } - - let kind = if let Some(def_id) = self.const_param_def_id(expr) { - // 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 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 name = tcx.hir().name(hir_id); - ty::ConstKind::Param(ty::ParamConst::new(index, name)) - } else { - ty::ConstKind::Unevaluated(def_id, InternalSubsts::identity_for_item(tcx, def_id), None) - }; - tcx.mk_const(ty::Const { val: kind, ty }) - } - pub fn impl_trait_ty_to_ty( &self, def_id: DefId, lifetimes: &[hir::GenericArg<'_>], + replace_parent_lifetimes: bool, ) -> Ty<'tcx> { debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes); let tcx = self.tcx(); @@ -2888,9 +2983,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { _ => bug!(), } } else { - // Replace all parent lifetimes with `'static`. match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(), + // For RPIT (return position impl trait), only lifetimes + // mentioned in the impl Trait predicate are captured by + // the opaque type, so the lifetime parameters from the + // parent item need to be replaced with `'static`. + // + // For `impl Trait` in the types of statics, constants, + // locals and type aliases. These capture all parent + // lifetimes, so they can use their identity subst. + GenericParamDefKind::Lifetime if replace_parent_lifetimes => { + tcx.lifetimes.re_static.into() + } _ => tcx.mk_param_from_def(param), } } @@ -2917,7 +3021,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { unsafety: hir::Unsafety, abi: abi::Abi, decl: &hir::FnDecl<'_>, - generic_params: &[hir::GenericParam<'_>], + generics: &hir::Generics<'_>, ident_span: Option, ) -> ty::PolyFnSig<'tcx> { debug!("ty_of_fn"); @@ -2929,6 +3033,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for ty in decl.inputs { visitor.visit_ty(ty); } + walk_generics(&mut visitor, generics); + let input_tys = decl.inputs.iter().map(|a| self.ty_of_arg(a, None)); let output_ty = match decl.output { hir::FnRetTy::Return(ref output) => { @@ -2943,16 +3049,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), - generic_params, + ident_span.shrink_to_hi(), + &generics.params[..], visitor.0, - ident_span.is_some(), + true, ); } @@ -2976,8 +3083,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.sess, decl.output.span(), E0581, - "return type references {} \ - which is not constrained by the fn input types", + "return type references {} which is not constrained by the fn input types", lifetime_name ); if let ty::BrAnon(_) = *br { @@ -2988,8 +3094,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // though we can easily give a hint that ought to be // relevant. err.note( - "lifetimes appearing in an associated type \ - are not considered constrained", + "lifetimes appearing in an associated type are not considered constrained", ); } err.emit(); @@ -3042,7 +3147,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) .emit(); } - return Some(r); + Some(r) } } @@ -3051,7 +3156,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 /// @@ -3104,7 +3209,7 @@ impl<'tcx> Bounds<'tcx> { def_id: sized, substs: tcx.mk_substs_trait(param_ty, &[]), }); - (trait_ref.without_const().to_predicate(), span) + (trait_ref.without_const().to_predicate(tcx), span) }) }); @@ -3119,16 +3224,16 @@ impl<'tcx> Bounds<'tcx> { // or it's a generic associated type that deliberately has escaping bound vars. let region_bound = ty::fold::shift_region(tcx, region_bound, 1); let outlives = ty::OutlivesPredicate(param_ty, region_bound); - (ty::Binder::bind(outlives).to_predicate(), span) + (ty::Binder::bind(outlives).to_predicate(tcx), span) }) .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| { - let predicate = bound_trait_ref.with_constness(constness).to_predicate(); + let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); (predicate, span) })) .chain( self.projection_bounds .iter() - .map(|&(projection, span)| (projection.to_predicate(), span)), + .map(|&(projection, span)| (projection.to_predicate(tcx), span)), ), ) .collect() diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 20737b44e7c17..9e23f5df3c6a8 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}; @@ -105,7 +105,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && i != 0 && self.if_fallback_coercion(expr.span, &arms[0].body, &mut coercion) { - tcx.types.err + tcx.ty_error() } else { // Only call this if this is not an `if` expr with an expected type and no `else` // clause to avoid duplicated type errors. (#60254) @@ -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 991347714e841..97d2b3e5a8e45 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -1,193 +1,46 @@ +//! Some helper functions for `AutoDeref` use super::method::MethodCallee; -use super::{FnCtxt, Needs, PlaceOp}; +use super::{FnCtxt, PlaceOp}; -use rustc::session::DiagnosticMessageId; -use rustc::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; -use rustc::ty::{self, TraitRef, Ty, TyCtxt, WithConstness}; -use rustc::ty::{ToPredicate, TypeFoldable}; -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_infer::infer::{InferCtxt, InferOk}; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{self, TraitEngine}; - -use rustc_ast::ast::Ident; +use rustc_infer::infer::InferOk; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; +use rustc_trait_selection::autoderef::{Autoderef, AutoderefKind}; use std::iter; -#[derive(Copy, Clone, Debug)] -enum AutoderefKind { - Builtin, - Overloaded, -} - -pub struct Autoderef<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - steps: Vec<(Ty<'tcx>, AutoderefKind)>, - cur_ty: Ty<'tcx>, - obligations: Vec>, - at_start: bool, - include_raw_pointers: bool, - span: Span, - silence_errors: bool, - reached_recursion_limit: bool, -} - -impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { - type Item = (Ty<'tcx>, usize); - - fn next(&mut self) -> Option { - let tcx = self.infcx.tcx; - - debug!("autoderef: steps={:?}, cur_ty={:?}", self.steps, self.cur_ty); - if self.at_start { - self.at_start = false; - debug!("autoderef stage #0 is {:?}", self.cur_ty); - return Some((self.cur_ty, 0)); - } - - if self.steps.len() >= *tcx.sess.recursion_limit.get() { - if !self.silence_errors { - report_autoderef_recursion_limit_error(tcx, self.span, self.cur_ty); - } - self.reached_recursion_limit = true; - return None; - } - - if self.cur_ty.is_ty_var() { - return None; - } - - // Otherwise, deref if type is derefable: - let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(self.include_raw_pointers) - { - (AutoderefKind::Builtin, mt.ty) - } else { - let ty = self.overloaded_deref_ty(self.cur_ty)?; - (AutoderefKind::Overloaded, ty) - }; - - if new_ty.references_error() { - return None; - } - - self.steps.push((self.cur_ty, kind)); - debug!( - "autoderef stage #{:?} is {:?} from {:?}", - self.steps.len(), - new_ty, - (self.cur_ty, kind) - ); - self.cur_ty = new_ty; - - Some((self.cur_ty, self.steps.len())) +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { + Autoderef::new(self, self.param_env, self.body_id, span, base_ty) } -} -impl<'a, 'tcx> Autoderef<'a, 'tcx> { - pub fn new( - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, + pub fn try_overloaded_deref( + &self, span: Span, base_ty: Ty<'tcx>, - ) -> Autoderef<'a, 'tcx> { - Autoderef { - infcx, - body_id, - param_env, - steps: vec![], - cur_ty: infcx.resolve_vars_if_possible(&base_ty), - obligations: vec![], - at_start: true, - include_raw_pointers: false, - silence_errors: false, - reached_recursion_limit: false, - span, - } - } - - fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option> { - debug!("overloaded_deref_ty({:?})", ty); - - let tcx = self.infcx.tcx; - - // - let trait_ref = TraitRef { - def_id: tcx.lang_items().deref_trait()?, - substs: tcx.mk_substs_trait(self.cur_ty, &[]), - }; - - let cause = traits::ObligationCause::misc(self.span, self.body_id); - - let obligation = traits::Obligation::new( - cause.clone(), - self.param_env, - trait_ref.without_const().to_predicate(), - ); - if !self.infcx.predicate_may_hold(&obligation) { - debug!("overloaded_deref_ty: cannot match obligation"); - return None; - } - - let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot(); - let normalized_ty = fulfillcx.normalize_projection_type( - &self.infcx, - self.param_env, - ty::ProjectionTy::from_ref_and_name(tcx, trait_ref, Ident::from_str("Target")), - cause, - ); - if let Err(e) = fulfillcx.select_where_possible(&self.infcx) { - // This shouldn't happen, except for evaluate/fulfill mismatches, - // but that's not a reason for an ICE (`predicate_may_hold` is conservative - // by design). - debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", e); - return None; - } - let obligations = fulfillcx.pending_obligations(); - debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); - self.obligations.extend(obligations); - - Some(self.infcx.resolve_vars_if_possible(&normalized_ty)) - } - - /// Returns the final type, generating an error if it is an - /// unresolved inference variable. - pub fn unambiguous_final_ty(&self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> { - fcx.structurally_resolved_type(self.span, self.cur_ty) - } - - /// Returns the final type we ended up with, which may well be an - /// inference variable (we will resolve it first, if possible). - pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> { - self.infcx.resolve_vars_if_possible(&self.cur_ty) - } - - pub fn step_count(&self) -> usize { - self.steps.len() + ) -> Option>> { + self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref) } /// Returns the adjustment steps. - pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>, needs: Needs) -> Vec> { - fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx, needs)) + pub fn adjust_steps(&self, autoderef: &Autoderef<'a, 'tcx>) -> Vec> { + self.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(autoderef)) } pub fn adjust_steps_as_infer_ok( &self, - fcx: &FnCtxt<'a, 'tcx>, - needs: Needs, + autoderef: &Autoderef<'a, 'tcx>, ) -> InferOk<'tcx, Vec>> { let mut obligations = vec![]; - let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(self.cur_ty)); - let steps: Vec<_> = self - .steps + let steps = autoderef.steps(); + let targets = + steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false))); + let steps: Vec<_> = steps .iter() .map(|&(source, kind)| { if let AutoderefKind::Overloaded = kind { - fcx.try_overloaded_deref(self.span, source, needs).and_then( + self.try_overloaded_deref(autoderef.span(), source).and_then( |InferOk { value: method, obligations: o }| { obligations.extend(o); if let ty::Ref(region, _, mutbl) = method.sig.output().kind { @@ -207,68 +60,4 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { InferOk { obligations, value: steps } } - - /// also dereference through raw pointer types - /// e.g., assuming ptr_to_Foo is the type `*const Foo` - /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] - /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] - pub fn include_raw_pointers(mut self) -> Self { - self.include_raw_pointers = true; - self - } - - pub fn silence_errors(mut self) -> Self { - self.silence_errors = true; - self - } - - pub fn reached_recursion_limit(&self) -> bool { - self.reached_recursion_limit - } - - pub fn finalize(self, fcx: &FnCtxt<'a, 'tcx>) { - fcx.register_predicates(self.into_obligations()); - } - - pub fn into_obligations(self) -> Vec> { - self.obligations - } -} - -pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { - // We've reached the recursion limit, error gracefully. - let suggested_limit = *tcx.sess.recursion_limit.get() * 2; - let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty); - let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg); - let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id); - if fresh { - struct_span_err!( - tcx.sess, - span, - E0055, - "reached the recursion limit while auto-dereferencing `{:?}`", - ty - ) - .span_label(span, "deref recursion limit reached") - .help(&format!( - "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)", - suggested_limit, tcx.crate_name, - )) - .emit(); - } -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty) - } - - pub fn try_overloaded_deref( - &self, - span: Span, - base_ty: Ty<'tcx>, - needs: Needs, - ) -> Option>> { - self.try_overloaded_place_op(span, base_ty, &[], needs, PlaceOp::Deref) - } } diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 3f8019e64b2db..308ed5d840202 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -1,29 +1,51 @@ -use super::autoderef::Autoderef; use super::method::MethodCallee; -use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag}; +use super::{Expectation, FnCtxt, 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; +use rustc_trait_selection::autoderef::Autoderef; /// Checks that it is legal to call methods of the trait corresponding /// to `trait_id` (this only cares about the trait, not the specific /// method that is called). -pub fn check_legal_trait_for_method_call(tcx: TyCtxt<'_>, span: Span, trait_id: DefId) { +pub fn check_legal_trait_for_method_call( + tcx: TyCtxt<'_>, + span: Span, + receiver: Option, + trait_id: DefId, +) { if tcx.lang_items().drop_trait() == Some(trait_id) { - struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method") - .span_label(span, "explicit destructor calls not allowed") - .emit(); + let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method"); + err.span_label(span, "explicit destructor calls not allowed"); + + let snippet = receiver + .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok()) + .unwrap_or_default(); + + let suggestion = + if snippet.is_empty() { "drop".to_string() } else { format!("drop({})", snippet) }; + + err.span_suggestion( + span, + &format!("consider using `drop` function: `{}`", suggestion), + String::new(), + Applicability::Unspecified, + ); + + err.emit(); } } @@ -50,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { while result.is_none() && autoderef.next().is_some() { result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef); } - autoderef.finalize(self); + self.register_predicates(autoderef.into_obligations()); let output = match result { None => { @@ -72,7 +94,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // we must check that return type of called functions is WF: - self.register_wf_obligation(output, call_expr.span, traits::MiscObligation); + self.register_wf_obligation(output.into(), call_expr.span, traits::MiscObligation); output } @@ -84,7 +106,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], autoderef: &Autoderef<'a, 'tcx>, ) -> Option> { - let adjusted_ty = autoderef.unambiguous_final_ty(self); + let adjusted_ty = + self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); debug!( "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})", call_expr, adjusted_ty @@ -93,7 +116,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the callee is a bare function or a closure, then we're all set. match adjusted_ty.kind { ty::FnDef(..) | ty::FnPtr(_) => { - let adjustments = autoderef.adjust_steps(self, Needs::None); + let adjustments = self.adjust_steps(autoderef); self.apply_adjustments(callee_expr, adjustments); return Some(CallStep::Builtin(adjusted_ty)); } @@ -104,16 +127,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check whether this is a call to a closure where we // haven't yet decided on whether the closure is fn vs // fnmut vs fnonce. If so, we have to defer further processing. - if self.closure_kind(def_id, substs).is_none() { - let closure_ty = self.closure_sig(def_id, substs); - let fn_sig = self + if self.closure_kind(substs).is_none() { + let closure_sig = substs.as_closure().sig(); + let closure_sig = self .replace_bound_vars_with_fresh_vars( call_expr.span, infer::FnCall, - &closure_ty, + &closure_sig, ) .0; - let adjustments = autoderef.adjust_steps(self, Needs::None); + let adjustments = self.adjust_steps(autoderef); self.record_deferred_call_resolution( def_id, DeferredCallResolution { @@ -121,12 +144,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_expr, adjusted_ty, adjustments, - fn_sig, - closure_def_id: def_id, + fn_sig: closure_sig, closure_substs: substs, }, ); - return Some(CallStep::DeferredClosure(fn_sig)); + return Some(CallStep::DeferredClosure(closure_sig)); } } @@ -155,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None)) .map(|(autoref, method)| { - let mut adjustments = autoderef.adjust_steps(self, Needs::None); + let mut adjustments = self.adjust_steps(autoderef); adjustments.extend(autoref); self.apply_adjustments(callee_expr, adjustments); CallStep::Overloaded(method) @@ -199,21 +221,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; if borrow { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded function call ops. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - autoref = Some(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), - target: method.sig.inputs()[0], - }); - } + // Check for &self vs &mut self in the method signature. Since this is either + // the Fn or FnMut trait, it should be one of those. + let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind + { + (r, mutbl) + } else { + span_bug!(call_expr.span, "input to call/call_mut is not a ref?"); + }; + + let mutbl = match mutbl { + hir::Mutability::Not => AutoBorrowMutability::Not, + hir::Mutability::Mut => AutoBorrowMutability::Mut { + // For initial two-phase borrow + // deployment, conservatively omit + // overloaded function call ops. + allow_two_phase_borrow: AllowTwoPhase::No, + }, + }; + autoref = Some(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), + target: method.sig.inputs()[0], + }); } return Some((autoref, method)); } @@ -266,7 +295,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(); } } } @@ -336,16 +366,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 { @@ -358,7 +391,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ( ty::Binder::bind(self.tcx.mk_fn_sig( self.err_args(arg_exprs.len()).into_iter(), - self.tcx.types.err, + self.tcx.ty_error(), false, hir::Unsafety::Normal, abi::Abi::Rust, @@ -459,7 +492,6 @@ pub struct DeferredCallResolution<'tcx> { adjusted_ty: Ty<'tcx>, adjustments: Vec>, fn_sig: ty::FnSig<'tcx>, - closure_def_id: DefId, closure_substs: SubstsRef<'tcx>, } @@ -469,7 +501,7 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { // we should not be invoked until the closure kind has been // determined by upvar inference - assert!(fcx.closure_kind(self.closure_def_id, self.closure_substs).is_some()); + assert!(fcx.closure_kind(self.closure_substs).is_some()); // We may now know enough to figure out fn vs fnmut etc. match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) { diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index d52b6c39ab58d..1ea7bf25ef2ed 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -31,19 +31,19 @@ use super::FnCtxt; use crate::hir::def_id::DefId; -use crate::lint; use crate::type_error_struct; -use crate::util::common::ErrorReported; -use rustc::middle::lang_items; -use rustc::session::Session; -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::symbol::sym; use rustc_span::Span; use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::report_object_safety_error; @@ -116,7 +116,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. @@ -137,7 +136,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Generator(..) | ty::Adt(..) | ty::Never - | ty::Error => { + | ty::Error(_) => { self.tcx .sess .delay_span_bug(span, &format!("`{:?}` should be sized but is not?", t)); @@ -335,10 +334,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { "only `u8` can be cast as `char`, not `{}`", self.expr_ty ) + .span_label(self.span, "invalid cast") .emit(); } CastError::NonScalar => { - type_error_struct!( + let mut err = type_error_struct!( fcx.tcx.sess, self.span, self.expr_ty, @@ -346,12 +346,75 @@ impl<'a, 'tcx> CastCheck<'tcx> { "non-primitive cast: `{}` as `{}`", self.expr_ty, fcx.ty_to_string(self.cast_ty) - ) - .note( - "an `as` expression can only be used to convert between \ - primitive types. Consider using the `From` trait", - ) - .emit(); + ); + let mut sugg = None; + if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind { + if fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() + { + sugg = Some(format!("&{}", mutbl.prefix_str())); + } + } + if let Some(sugg) = sugg { + err.span_label(self.span, "invalid cast"); + err.span_suggestion_verbose( + self.expr.span.shrink_to_lo(), + "borrow the value for the cast to be valid", + sugg, + Applicability::MachineApplicable, + ); + } else if !matches!( + self.cast_ty.kind, + ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) + ) { + let mut label = true; + // Check `impl From for self.cast_ty {}` for accurate suggestion: + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::from_trait) { + let ty = fcx.resolve_vars_if_possible(&self.cast_ty); + // Erase regions to avoid panic in `prove_value` when calling + // `type_implements_trait`. + let ty = fcx.tcx.erase_regions(&ty); + let expr_ty = fcx.resolve_vars_if_possible(&self.expr_ty); + let expr_ty = fcx.tcx.erase_regions(&expr_ty); + let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); + // Check for infer types because cases like `Option<{integer}>` would + // panic otherwise. + if !expr_ty.has_infer_types() + && fcx.tcx.type_implements_trait(( + from_trait, + ty, + ty_params, + fcx.param_env, + )) + { + label = false; + err.span_suggestion( + self.span, + "consider using the `From` trait instead", + format!("{}::from({})", self.cast_ty, snippet), + Applicability::MaybeIncorrect, + ); + } + } + } + let msg = "an `as` expression can only be used to convert between primitive \ + types or to coerce to a specific trait object"; + if label { + err.span_label(self.span, msg); + } else { + err.note(msg); + } + } else { + err.span_label(self.span, "invalid cast"); + } + err.emit(); } CastError::SizedUnsizedCast => { use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic}; @@ -372,21 +435,22 @@ impl<'a, 'tcx> CastCheck<'tcx> { }; let mut err = struct_span_err!( fcx.tcx.sess, - self.span, + if unknown_cast_to { self.cast_span } else { self.span }, E0641, "cannot cast {} a pointer of an unknown kind", if unknown_cast_to { "to" } else { "from" } ); - err.note( - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); if unknown_cast_to { - err.span_suggestion_short( - self.cast_span, - "consider giving more type information", - String::new(), - Applicability::Unspecified, + err.span_label(self.cast_span, "needs more type information"); + err.note( + "the type information given here is insufficient to check whether \ + the pointer cast is valid", + ); + } else { + err.span_label( + self.span, + "the type information given here is insufficient to check whether \ + the pointer cast is valid", ); } err.emit(); @@ -440,13 +504,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { Ok(s) => { err.span_suggestion( self.cast_span, - "try casting to a `Box` instead", + "you can cast to a `Box` instead", format!("Box<{}>", s), Applicability::MachineApplicable, ); } Err(_) => { - err.span_help(self.cast_span, &format!("did you mean `Box<{}>`?", tstr)); + err.span_help( + self.cast_span, + &format!("you might have meant `Box<{}>`", tstr), + ); } } } @@ -526,8 +593,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 +604,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 +632,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 +652,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 +662,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 @@ -612,12 +678,13 @@ impl<'a, 'tcx> CastCheck<'tcx> { (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), // prim -> prim - (Int(CEnum), Int(_)) => Ok(CastKind::EnumCast), - (Int(Char), Int(_)) | (Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), - - (Int(_), Int(_)) | (Int(_), Float) | (Float, Int(_)) | (Float, Float) => { - Ok(CastKind::NumericCast) + (Int(CEnum), Int(_)) => { + self.cenum_impl_drop_lint(fcx); + Ok(CastKind::EnumCast) } + (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), + + (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), } } @@ -711,11 +778,13 @@ impl<'a, 'tcx> CastCheck<'tcx> { // Coerce to a raw pointer so that we generate AddressOf in MIR. let array_ptr_type = fcx.tcx.mk_ptr(m_expr); fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No) - .unwrap_or_else(|_| bug!( + .unwrap_or_else(|_| { + bug!( "could not cast from reference to array to pointer to array ({:?} to {:?})", self.expr_ty, array_ptr_type, - )); + ) + }); // this will report a type mismatch if needed fcx.demand_eqtype(self.span, ety, m_cast.ty); @@ -745,6 +814,25 @@ impl<'a, 'tcx> CastCheck<'tcx> { Err(err) => Err(err), } } + + fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { + if let ty::Adt(d, _) = self.expr_ty.kind { + if d.has_dtor(fcx.tcx) { + fcx.tcx.struct_span_lint_hir( + lint::builtin::CENUM_IMPL_DROP_CAST, + self.expr.hir_id, + self.span, + |err| { + err.build(&format!( + "cannot cast enum `{}` into integer `{}` because it implements `Drop`", + self.expr_ty, self.cast_ty + )) + .emit(); + }, + ); + } + } + } } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 49b7a99731104..6d09ddc925ffe 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -3,20 +3,19 @@ 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 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; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use rustc_trait_selection::traits::Obligation; use std::cmp; use std::iter; @@ -70,58 +69,68 @@ 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; - // Create type variables (for now) to represent the transformed - // types of upvars. These will be unified during the upvar - // inference phase (`upvar.rs`). - let base_substs = - InternalSubsts::identity_for_item(self.tcx, self.tcx.closure_base_def_id(expr_def_id)); - 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 { .. } => self - .infcx - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr.span, - }) + 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.to_def_id(), |param, _| match param.kind { + GenericParamDefKind::Lifetime => span_bug!(expr.span, "closure has lifetime param"), + GenericParamDefKind::Type { .. } => if param.index as usize == tupled_upvars_idx { + self.tcx.mk_tup(self.tcx.upvars_mentioned(expr_def_id).iter().flat_map( + |upvars| { + upvars.iter().map(|(&var_hir_id, _)| { + // Create type variables (for now) to represent the transformed + // types of upvars. These will be unified during the upvar + // inference phase (`upvar.rs`). + self.infcx.next_ty_var(TypeVariableOrigin { + // FIXME(eddyb) distinguish upvar inference variables from the rest. + kind: TypeVariableOriginKind::ClosureSynthetic, + span: self.tcx.hir().span(var_hir_id), + }) + }) + }, + )) + } else { + // Create type variables (for now) to represent the various + // pieces of information kept in `{Closure,Generic}Substs`. + // They will either be unified below, or later during the upvar + // inference phase (`upvar.rs`) + self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::ClosureSynthetic, + span: expr.span, + }) + } .into(), - GenericParamDefKind::Const => span_bug!(expr.span, "closure has const param"), - }); + 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(); - self.demand_eqtype( - expr.span, - resume_ty, - generator_substs.resume_ty(expr_def_id, self.tcx), - ); - self.demand_eqtype( - expr.span, - yield_ty, - generator_substs.yield_ty(expr_def_id, self.tcx), - ); - self.demand_eqtype( - expr.span, - liberated_sig.output(), - generator_substs.return_ty(expr_def_id, self.tcx), - ); - self.demand_eqtype( - expr.span, - interior, - generator_substs.witness(expr_def_id, self.tcx), - ); - return self.tcx.mk_generator(expr_def_id, substs, movability); - } + self.demand_eqtype(expr.span, resume_ty, generator_substs.resume_ty()); + self.demand_eqtype(expr.span, yield_ty, generator_substs.yield_ty()); + self.demand_eqtype(expr.span, liberated_sig.output(), generator_substs.return_ty()); + self.demand_eqtype(expr.span, interior, generator_substs.witness()); - let closure_type = self.tcx.mk_closure(expr_def_id, substs); + // HACK(eddyb) this forces the types equated above into `substs` but + // it should rely on `GeneratorSubsts` providing a constructor, instead. + let substs = self.resolve_vars_if_possible(&substs); - debug!("check_closure: expr.hir_id={:?} closure_type={:?}", expr.hir_id, closure_type); + return self.tcx.mk_generator(expr_def_id.to_def_id(), substs, movability); + } // Tuple up the arguments and insert the resulting function type into // the `closures` table. @@ -141,20 +150,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let sig_fn_ptr_ty = self.tcx.mk_fn_ptr(sig); - self.demand_eqtype( - expr.span, - sig_fn_ptr_ty, - substs.as_closure().sig_ty(expr_def_id, self.tcx), - ); + self.demand_eqtype(expr.span, sig_fn_ptr_ty, substs.as_closure().sig_as_fn_ptr_ty()); if let Some(kind) = opt_kind { - self.demand_eqtype( - expr.span, - kind.to_ty(self.tcx), - substs.as_closure().kind_ty(expr_def_id, self.tcx), - ); + self.demand_eqtype(expr.span, kind.to_ty(self.tcx), substs.as_closure().kind_ty()); } + // HACK(eddyb) this forces the types equated above into `substs` but + // it should rely on `ClosureSubsts` providing a constructor, instead. + let substs = self.resolve_vars_if_possible(&substs); + + let closure_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); + closure_type } @@ -168,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)); @@ -200,7 +206,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligation.predicate ); - if let ty::Predicate::Projection(ref proj_predicate) = obligation.predicate { + if let &ty::PredicateKind::Projection(proj_predicate) = obligation.predicate.kind() + { // Given a Projection predicate, we can potentially infer // the complete signature. self.deduce_sig_from_projection(Some(obligation.cause.span), proj_predicate) @@ -230,7 +237,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn deduce_sig_from_projection( &self, cause_span: Option, - projection: &ty::PolyProjectionPredicate<'tcx>, + projection: ty::PolyProjectionPredicate<'tcx>, ) -> Option> { let tcx = self.tcx; @@ -239,7 +246,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"); @@ -426,18 +433,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, @@ -505,21 +517,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let InferOk { value: (), obligations } = self.at(&cause, self.param_env).eq(*expected_ty, supplied_ty)?; all_obligations.extend(obligations); - - // Also, require that the supplied type must outlive - // the closure body. - let closure_body_region = self.tcx.mk_region(ty::ReScope(region::Scope { - id: body.value.hir_id.local_id, - data: region::ScopeData::Node, - })); - all_obligations.push(Obligation::new( - cause, - self.param_env, - ty::Predicate::TypeOutlives(ty::Binder::dummy(ty::OutlivesPredicate( - supplied_ty, - closure_body_region, - ))), - )); } let (supplied_output_ty, _) = self.infcx.replace_bound_vars_with_fresh_vars( @@ -630,7 +627,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // where R is the return type we are expecting. This type `T` // will be our output. let output_ty = self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| { - if let ty::Predicate::Projection(ref proj_predicate) = obligation.predicate { + if let &ty::PredicateKind::Projection(proj_predicate) = obligation.predicate.kind() { self.deduce_future_output_from_projection(obligation.cause.span, proj_predicate) } else { None @@ -651,7 +648,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn deduce_future_output_from_projection( &self, cause_span: Span, - predicate: &ty::PolyProjectionPredicate<'tcx>, + predicate: ty::PolyProjectionPredicate<'tcx>, ) -> Option> { debug!("deduce_future_output_from_projection(predicate={:?})", predicate); @@ -667,7 +664,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; @@ -703,7 +700,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let supplied_arguments = decl.inputs.iter().map(|a| { // Convert the types that the user supplied (if any), but ignore them. astconv.ast_ty_to_ty(a); - self.tcx.types.err + self.tcx.ty_error() }); if let hir::FnRetTy::Return(ref output) = decl.output { @@ -712,7 +709,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = ty::Binder::bind(self.tcx.mk_fn_sig( supplied_arguments, - self.tcx.types.err, + self.tcx.ty_error(), decl.c_variadic, hir::Unsafety::Normal, Abi::RustCall, diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index d74623a063f8f..b6cd8da236260 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -10,67 +10,52 @@ //! //! Note that if we are expecting a reference, we will *reborrow* //! even if the argument provided was already a reference. This is -//! useful for freezing mut/const things (that is, when the expected is &T -//! but you have &const T or &mut T) and also for avoiding the linearity +//! useful for freezing mut things (that is, when the expected type is &T +//! but you have &mut T) and also for avoiding the linearity //! of mut things (when the expected is &mut T and you have &mut T). See -//! the various `src/test/ui/coerce-reborrow-*.rs` tests for +//! the various `src/test/ui/coerce/*.rs` tests for //! examples of where this is useful. //! //! ## Subtle note //! -//! When deciding what type coercions to consider, we do not attempt to -//! resolve any type variables we may encounter. This is because `b` -//! represents the expected type "as the user wrote it", meaning that if -//! the user defined a generic function like +//! When infering the generic arguments of functions, the argument +//! order is relevant, which can lead to the following edge case: //! -//! fn foo(a: A, b: A) { ... } +//! ```rust +//! fn foo(a: T, b: T) { +//! // ... +//! } //! -//! and then we wrote `foo(&1, @2)`, we will not auto-borrow -//! either argument. In older code we went to some lengths to -//! resolve the `b` variable, which could mean that we'd -//! auto-borrow later arguments but not earlier ones, which -//! seems very confusing. +//! foo(&7i32, &mut 7i32); +//! // This compiles, as we first infer `T` to be `&i32`, +//! // and then coerce `&mut 7i32` to `&7i32`. //! -//! ## Subtler note -//! -//! However, right now, if the user manually specifies the -//! values for the type variables, as so: -//! -//! foo::<&int>(@1, @2) -//! -//! then we *will* auto-borrow, because we can't distinguish this from a -//! function that declared `&int`. This is inconsistent but it's easiest -//! at the moment. The right thing to do, I think, is to consider the -//! *unsubstituted* type when deciding whether to auto-borrow, but the -//! *substituted* type when considering the bounds and so forth. But most -//! of our methods don't give access to the unsubstituted type, and -//! rightly so because they'd be error-prone. So maybe the thing to do is -//! to actually determine the kind of coercions that should occur -//! separately and pass them in. Or maybe it's ok as is. Anyway, it's -//! sort of a minor point so I've opted to leave it for later -- after all, -//! we may want to adjust precisely when coercions occur. +//! foo(&mut 7i32, &7i32); +//! // This does not compile, as we first infer `T` to be `&mut i32` +//! // and are then unable to coerce `&7i32` to `&mut i32`. +//! ``` use crate::astconv::AstConv; -use crate::check::{FnCtxt, Needs}; -use rustc::session::parse::feature_err; -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 crate::check::FnCtxt; use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; +use rustc_middle::ty::adjustment::{ + 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}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; + use smallvec::{smallvec, SmallVec}; use std::ops::Deref; @@ -96,18 +81,20 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { type CoerceResult<'tcx> = InferResult<'tcx, (Vec>, Ty<'tcx>)>; +/// Coercing a mutable reference to an immutable works, while +/// coercing `&T` to `&mut T` should be forbidden. fn coerce_mutbls<'tcx>( from_mutbl: hir::Mutability, 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 +103,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 +122,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) @@ -160,7 +149,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Just ignore error types. if a.references_error() || b.references_error() { - return success(vec![], self.fcx.tcx.types.err, vec![]); + return success(vec![], self.fcx.tcx.ty_error(), vec![]); } if a.is_never() { @@ -212,12 +201,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); } - _ => {} } @@ -235,11 +221,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // unsafe qualifier. self.coerce_from_fn_pointer(a, a_f, b) } - ty::Closure(def_id_a, substs_a) => { + ty::Closure(_, substs_a) => { // Non-capturing closures are coercible to // function pointers or unsafe function pointers. // It cannot convert closures that require unsafe. - self.coerce_closure_to_fn(a, def_id_a, substs_a, b) + self.coerce_closure_to_fn(a, substs_a, b) } _ => { // Otherwise, just use unification rules. @@ -256,7 +242,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 +255,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 +351,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 +362,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,13 +404,12 @@ 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 InferOk { value: mut adjustments, obligations: o } = - autoderef.adjust_steps_as_infer_ok(self, needs); + self.adjust_steps_as_infer_ok(&autoderef); obligations.extend(o); obligations.extend(autoderef.into_obligations()); @@ -434,7 +419,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 +438,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 +582,32 @@ 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.kind() { + &ty::PredicateKind::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) => @@ -622,7 +643,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // be silent, as it causes a type mismatch later. } - Ok(Some(vtable)) => queue.extend(vtable.nested_obligations()), + Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), } } @@ -692,12 +713,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); @@ -731,7 +762,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fn coerce_closure_to_fn( &self, a: Ty<'tcx>, - def_id_a: DefId, substs_a: SubstsRef<'tcx>, b: Ty<'tcx>, ) -> CoerceResult<'tcx> { @@ -742,16 +772,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); match b.kind { - ty::FnPtr(fn_ty) if self.tcx.upvars(def_id_a).map_or(true, |v| v.is_empty()) => { + ty::FnPtr(fn_ty) if substs_a.as_closure().upvar_tys().next().is_none() => { // We coerce the closure, which has fn type // `extern "rust-call" fn((arg0,arg1,...)) -> _` // to // `fn(arg0,arg1,...) -> _` // or // `unsafe fn(arg0,arg1,...) -> _` - let sig = self.closure_sig(def_id_a, substs_a); + let closure_sig = substs_a.as_closure().sig(); let unsafety = fn_ty.unsafety(); - let pointer_ty = self.tcx.coerce_closure_fn_ty(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, @@ -776,10 +807,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. @@ -819,7 +850,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (adjustments, _) = self.register_infer_ok_obligations(ok); self.apply_adjustments(expr, adjustments); - Ok(if expr_ty.references_error() { self.tcx.types.err } else { target }) + Ok(if expr_ty.references_error() { self.tcx.ty_error() } else { target }) } /// Same as `try_coerce()`, but without side-effects. @@ -833,6 +864,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)| self.probe(|_| 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"). @@ -852,26 +895,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let prev_ty = self.resolve_vars_with_obligations(prev_ty); let new_ty = self.resolve_vars_with_obligations(new_ty); - debug!("coercion::try_find_coercion_lub({:?}, {:?})", prev_ty, new_ty); + debug!( + "coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)", + prev_ty, + new_ty, + exprs.len() + ); // 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) @@ -881,17 +969,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); } @@ -912,6 +1006,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(ok) => { let (adjustments, target) = self.register_infer_ok_obligations(ok); self.apply_adjustments(new, adjustments); + debug!( + "coercion::try_find_coercion_lub: was able to coerce from previous type {:?} to new type {:?}", + prev_ty, new_ty, + ); return Ok(target); } Err(e) => first_error = Some(e), @@ -942,6 +1040,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if !noop { + debug!( + "coercion::try_find_coercion_lub: older expression {:?} had adjustments, requiring LUB", + expr, + ); + return self .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) .map(|ok| self.register_infer_ok_obligations(ok)); @@ -959,6 +1062,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } Ok(ok) => { + debug!( + "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?}", + prev_ty, new_ty, + ); let (adjustments, target) = self.register_infer_ok_obligations(ok); for expr in exprs { let expr = expr.as_coercion_site(); @@ -1136,7 +1243,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // If we see any error types, just propagate that error // upwards. if expression_ty.references_error() || self.merged_ty().references_error() { - self.final_ty = Some(fcx.tcx.types.err); + self.final_ty = Some(fcx.tcx.ty_error()); return; } @@ -1274,7 +1381,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } if let Some(expr) = expression { - fcx.emit_coerce_suggestions(&mut err, expr, found, expected); + fcx.emit_coerce_suggestions(&mut err, expr, found, expected, None); } // Error possibly reported in `check_assign` so avoid emitting error again. @@ -1293,7 +1400,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.emit_unless(assign_to_bool || unsized_return); - self.final_ty = Some(fcx.tcx.types.err); + self.final_ty = Some(fcx.tcx.ty_error()); } } } @@ -1391,11 +1498,11 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let mut is_object_safe = false; if let hir::FnRetTy::Return(ty) = fn_output { // Get the return type. - if let hir::TyKind::Def(..) = ty.kind { + if let hir::TyKind::OpaqueDef(..) = ty.kind { let ty = AstConv::ast_ty_to_ty(fcx, ty); // Get the `impl Trait`'s `DefId`. if let ty::Opaque(def_id, _) = ty.kind { - let hir_id = fcx.tcx.hir().as_local_hir_id(def_id).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, .. }) = @@ -1403,9 +1510,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 1dde57124ee0a..4e97ba41dcbb3 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -1,20 +1,20 @@ -use rustc::hir::map::Map; -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; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::util::ExplicitSelf; +use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt, WithConstness}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; use super::{potentially_plural_count, FnCtxt, Inherited}; +use std::iter; /// Checks that a method from an impl conforms to the signature of /// the same method as declared in the trait. @@ -36,7 +36,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) { @@ -78,28 +78,29 @@ 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, - body_id: impl_m_hir_id, - code: ObligationCauseCode::CompareImplMethodObligation { + // We sometimes modify the span further down. + let mut cause = ObligationCause::new( + impl_m_span, + impl_m_hir_id, + ObligationCauseCode::CompareImplMethodObligation { item_name: impl_m.ident.name, impl_item_def_id: impl_m.def_id, trait_item_def_id: trait_m.def_id, }, - }; + ); // This code is best explained by example. Consider a trait: // - // trait Trait<'t,T> { - // fn method<'a,M>(t: &'t T, m: &'a M) -> Self; + // trait Trait<'t, T> { + // fn method<'a, M>(t: &'t T, m: &'a M) -> Self; // } // // And an impl: // // impl<'i, 'j, U> Trait<'j, &'i U> for Foo { - // fn method<'b,N>(t: &'j &'i U, m: &'b N) -> Foo; + // fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo; // } // // We wish to decide if those two method types are compatible. @@ -117,9 +118,9 @@ fn compare_predicate_entailment<'tcx>( // regions (Note: but only early-bound regions, i.e., those // declared on the impl or used in type parameter bounds). // - // impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 } + // impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 } // - // Now we can apply skol_substs to the type of the impl method + // Now we can apply placeholder_substs to the type of the impl method // to yield a new function type in terms of our fresh, placeholder // types: // @@ -128,11 +129,11 @@ fn compare_predicate_entailment<'tcx>( // We now want to extract and substitute the type of the *trait* // method and compare it. To do so, we must create a compound // substitution by combining trait_to_impl_substs and - // impl_to_skol_substs, and also adding a mapping for the method + // impl_to_placeholder_substs, and also adding a mapping for the method // type parameters. We extend the mapping to also include // the method parameters. // - // trait_to_skol_substs = { T => &'i0 U0, Self => Foo, M => N0 } + // trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 } // // Applying this to the trait method type yields: // @@ -146,20 +147,20 @@ fn compare_predicate_entailment<'tcx>( // satisfied by the implementation's method. // // We do this by creating a parameter environment which contains a - // substitution corresponding to impl_to_skol_substs. We then build - // trait_to_skol_substs and use it to convert the predicates contained + // substitution corresponding to impl_to_placeholder_substs. We then build + // trait_to_placeholder_substs and use it to convert the predicates contained // in the trait_m.generics to the placeholder form. // // Finally we register each of these predicates as an obligation in // a fresh FulfillmentCtxt, and invoke select_all_or_error. // Create mapping from impl to placeholder. - let impl_to_skol_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id); + let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id); // Create mapping from trait to placeholder. - let trait_to_skol_substs = - impl_to_skol_substs.rebase_onto(tcx, impl_m.container.id(), trait_to_impl_substs); - debug!("compare_impl_method: trait_to_skol_substs={:?}", trait_to_skol_substs); + let trait_to_placeholder_substs = + impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container.id(), trait_to_impl_substs); + debug!("compare_impl_method: trait_to_placeholder_substs={:?}", trait_to_placeholder_substs); let impl_m_generics = tcx.generics_of(impl_m.def_id); let trait_m_generics = tcx.generics_of(trait_m.def_id); @@ -195,7 +196,7 @@ fn compare_predicate_entailment<'tcx>( // if all constraints hold. hybrid_preds .predicates - .extend(trait_m_predicates.instantiate_own(tcx, trait_to_skol_substs).predicates); + .extend(trait_m_predicates.instantiate_own(tcx, trait_to_placeholder_substs).predicates); // Construct trait parameter environment and then shift it into the placeholder viewpoint. // The key step here is to update the caller_bounds's predicates to be @@ -214,14 +215,14 @@ fn compare_predicate_entailment<'tcx>( ); tcx.infer_ctxt().enter(|infcx| { - let inh = Inherited::new(infcx, impl_m.def_id); + let inh = Inherited::new(infcx, impl_m.def_id.expect_local()); let infcx = &inh.infcx; debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds); let mut selcx = traits::SelectionContext::new(&infcx); - let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_skol_substs); + let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs); let (impl_m_own_bounds, _) = infcx.replace_bound_vars_with_fresh_vars( impl_m_span, infer::HigherRankedType, @@ -262,7 +263,7 @@ fn compare_predicate_entailment<'tcx>( debug!("compare_impl_method: impl_fty={:?}", impl_fty); let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, &tcx.fn_sig(trait_m.def_id)); - let trait_sig = trait_sig.subst(tcx, trait_to_skol_substs); + let trait_sig = trait_sig.subst(tcx, trait_to_placeholder_substs); let trait_sig = inh.normalize_associated_types_in(impl_m_span, impl_m_hir_id, param_env, &trait_sig); let trait_fty = tcx.mk_fn_ptr(ty::Binder::bind(trait_sig)); @@ -282,7 +283,7 @@ fn compare_predicate_entailment<'tcx>( &infcx, param_env, &terr, &cause, impl_m, impl_sig, trait_m, trait_sig, ); - let cause = ObligationCause { span: impl_err_span, ..cause }; + cause.make_mut().span = impl_err_span; let mut diag = struct_span_err!( tcx.sess, @@ -364,7 +365,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, @@ -376,7 +377,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, @@ -401,9 +402,9 @@ 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::Method(ref impl_m_sig, _) => { + ImplItemKind::Fn(ref impl_m_sig, _) => { (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter()) } _ => bug!("{:?} is not a method", impl_m), @@ -411,7 +412,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), @@ -438,7 +440,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, _) => { @@ -453,16 +456,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) @@ -518,7 +518,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) => { @@ -589,33 +589,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 @@ -678,7 +678,7 @@ fn compare_number_of_generics<'tcx>( impl_count, kind, pluralize!(impl_count), - suffix.unwrap_or_else(|| String::new()), + suffix.unwrap_or_else(String::new), ), ); } @@ -706,8 +706,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 }; @@ -730,9 +730,9 @@ 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::Method(ref impl_m_sig, _) => { + ImplItemKind::Fn(ref impl_m_sig, _) => { let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 }; if let Some(arg) = impl_m_sig.decl.inputs.get(pos) { if pos == 0 { @@ -812,7 +812,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!( @@ -833,10 +833,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 @@ -870,10 +870,10 @@ 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::Method(ref sig, _) => sig.decl.inputs, + hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs, _ => unreachable!(), }; struct Visitor(Option, hir::def_id::DefId); @@ -890,7 +890,7 @@ fn compare_synthetic_generics<'tcx>( } } } - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; fn nested_visit_map( &mut self, ) -> intravisit::NestedVisitorMap @@ -951,7 +951,7 @@ crate fn compare_const_impl<'tcx>( tcx.infer_ctxt().enter(|infcx| { let param_env = tcx.param_env(impl_c.def_id); - let inh = Inherited::new(infcx, impl_c.def_id); + let inh = Inherited::new(infcx, impl_c.def_id.expect_local()); let infcx = &inh.infcx; // The below is for the most part highly similar to the procedure @@ -963,12 +963,16 @@ 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); + let mut cause = ObligationCause::new( + impl_c_span, + impl_c_hir_id, + ObligationCauseCode::CompareImplConstObligation, + ); // There is no "body" here, so just pass dummy id. let impl_ty = @@ -994,7 +998,7 @@ crate fn compare_const_impl<'tcx>( // Locate the Span containing just the type of the offending impl match tcx.hir().expect_impl_item(impl_c_hir_id).kind { - ImplItemKind::Const(ref ty, _) => cause.span = ty.span, + ImplItemKind::Const(ref ty, _) => cause.make_mut().span = ty.span, _ => bug!("{:?} is not a impl const", impl_c), } @@ -1007,7 +1011,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 { @@ -1054,13 +1059,15 @@ crate fn compare_ty_impl<'tcx>( let _: Result<(), ErrorReported> = (|| { compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?; - compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref) + compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)?; + + compare_projection_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref) })(); } /// The equivalent of [compare_predicate_entailment], but for associated types /// instead of associated functions. -fn compare_type_predicate_entailment( +fn compare_type_predicate_entailment<'tcx>( tcx: TyCtxt<'tcx>, impl_ty: &ty::AssocItem, impl_ty_span: Span, @@ -1095,16 +1102,16 @@ 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 cause = ObligationCause { - span: impl_ty_span, - body_id: impl_ty_hir_id, - code: ObligationCauseCode::CompareImplTypeObligation { + let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id.expect_local()); + let cause = ObligationCause::new( + impl_ty_span, + impl_ty_hir_id, + ObligationCauseCode::CompareImplTypeObligation { item_name: impl_ty.ident.name, impl_item_def_id: impl_ty.def_id, trait_item_def_id: trait_ty.def_id, }, - }; + ); debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs); @@ -1131,7 +1138,7 @@ fn compare_type_predicate_entailment( normalize_cause.clone(), ); tcx.infer_ctxt().enter(|infcx| { - let inh = Inherited::new(infcx, impl_ty.def_id); + let inh = Inherited::new(infcx, impl_ty.def_id.expect_local()); let infcx = &inh.infcx; debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds); @@ -1162,10 +1169,149 @@ fn compare_type_predicate_entailment( }) } +/// Validate that `ProjectionCandidate`s created for this associated type will +/// be valid. +/// +/// Usually given +/// +/// trait X { type Y: Copy } impl X for T { type Y = S; } +/// +/// We are able to normalize `::U` to `S`, and so when we check the +/// impl is well-formed we have to prove `S: Copy`. +/// +/// For default associated types the normalization is not possible (the value +/// from the impl could be overridden). We also can't normalize generic +/// associated types (yet) because they contain bound parameters. +fn compare_projection_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ty: &ty::AssocItem, + impl_ty: &ty::AssocItem, + impl_ty_span: Span, + impl_trait_ref: ty::TraitRef<'tcx>, +) -> Result<(), ErrorReported> { + let have_gats = tcx.features().generic_associated_types; + if impl_ty.defaultness.is_final() && !have_gats { + // For "final", non-generic associate type implementations, we + // don't need this as described above. + return Ok(()); + } + + let param_env = tcx.param_env(impl_ty.def_id); + + // Given + // + // impl Foo for (A, B) { + // type Bar =... + // } + // + // - `impl_substs` would be `[A, B, C]` + // - `rebased_substs` would be `[(A, B), u32, C]`, combining the substs from + // the *trait* with the generic associated type parameters. + let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id); + let rebased_substs = + impl_ty_substs.rebase_onto(tcx, impl_ty.container.id(), impl_trait_ref.substs); + let impl_ty_value = tcx.type_of(impl_ty.def_id); + + // Map the predicate from the trait to the corresponding one for the impl. + // For example: + // + // trait X { type Y<'a>: PartialEq } impl X for T { type Y<'a> = &'a S; } + // impl<'x> X<&'x u32> for () { type Y<'c> = &'c u32; } + // + // For the `for<'a> <>::Y<'a>: PartialEq` bound, this + // function would translate and partially normalize + // `[>::Y<'a>, A]` to `[&'a u32, &'x u32]`. + let translate_predicate_substs = move |predicate_substs: SubstsRef<'tcx>| { + tcx.mk_substs( + iter::once(impl_ty_value.into()) + .chain(predicate_substs[1..].iter().map(|s| s.subst(tcx, rebased_substs))), + ) + }; + + tcx.infer_ctxt().enter(move |infcx| { + let inh = Inherited::new(infcx, impl_ty.def_id.expect_local()); + let infcx = &inh.infcx; + let mut selcx = traits::SelectionContext::new(&infcx); + + let impl_ty_hir_id = tcx.hir().as_local_hir_id(impl_ty.def_id.expect_local()); + let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id); + let cause = ObligationCause::new( + impl_ty_span, + impl_ty_hir_id, + ObligationCauseCode::ItemObligation(trait_ty.def_id), + ); + + let predicates = tcx.projection_predicates(trait_ty.def_id); + + debug!("compare_projection_bounds: projection_predicates={:?}", predicates); + + for predicate in predicates { + let concrete_ty_predicate = match predicate.kind() { + ty::PredicateKind::Trait(poly_tr, c) => poly_tr + .map_bound(|tr| { + let trait_substs = translate_predicate_substs(tr.trait_ref.substs); + ty::TraitRef { def_id: tr.def_id(), substs: trait_substs } + }) + .with_constness(*c) + .to_predicate(tcx), + ty::PredicateKind::Projection(poly_projection) => poly_projection + .map_bound(|projection| { + let projection_substs = + translate_predicate_substs(projection.projection_ty.substs); + ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { + substs: projection_substs, + item_def_id: projection.projection_ty.item_def_id, + }, + ty: projection.ty.subst(tcx, rebased_substs), + } + }) + .to_predicate(tcx), + ty::PredicateKind::TypeOutlives(poly_outlives) => poly_outlives + .map_bound(|outlives| { + ty::OutlivesPredicate(impl_ty_value, outlives.1.subst(tcx, rebased_substs)) + }) + .to_predicate(tcx), + _ => bug!("unexepected projection predicate kind: `{:?}`", predicate), + }; + + let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( + &mut selcx, + param_env, + normalize_cause.clone(), + &concrete_ty_predicate, + ); + + debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); + + inh.register_predicates(obligations); + inh.register_predicate(traits::Obligation::new( + cause.clone(), + param_env, + normalized_predicate, + )); + } + + // Check that all obligations are satisfied by the implementation's + // version. + if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) { + infcx.report_fulfillment_errors(errors, None, false); + return Err(ErrorReported); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id); + fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]); + + Ok(()) + }) +} + 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::Type | ty::AssocKind::OpaqueTy => "type", + ty::AssocKind::Fn => "method", + ty::AssocKind::Type => "type", } } diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 0556c80e4f707..85c073ca30034 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -1,20 +1,22 @@ use crate::check::FnCtxt; use rustc_infer::infer::InferOk; 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_trait_selection::traits::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; +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; use super::method::probe; +use std::fmt; + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn emit_coerce_suggestions( &self, @@ -22,21 +24,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'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, expected_ty_expr); if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { return; } self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); self.suggest_missing_await(err, expr, expected, expr_ty); + self.note_need_for_fn_pointer(err, expected, expr_ty); } // 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( @@ -98,9 +104,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, allow_two_phase: AllowTwoPhase, ) -> Ty<'tcx> { - let (ty, err) = self.demand_coerce_diag(expr, checked_ty, expected, allow_two_phase); + let (ty, err) = + self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase); if let Some(mut err) = err { err.emit(); } @@ -117,6 +125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, allow_two_phase: AllowTwoPhase, ) -> (Ty<'tcx>, Option>) { let expected = self.resolve_vars_with_obligations(expected); @@ -137,7 +146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return (expected, None); } - self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected); + self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr); (expected, Some(err)) } @@ -198,13 +207,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 +226,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 +252,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, } @@ -303,7 +312,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (method_path, method_span, method_expr) = match (hir, closure_params_len) { ( Some(Node::Expr(hir::Expr { - kind: hir::ExprKind::MethodCall(path, span, expr), + kind: hir::ExprKind::MethodCall(path, span, expr, _), .. })), 1, @@ -351,6 +360,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 +392,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 +413,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 +462,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 +488,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 +518,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 +529,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,63 +552,116 @@ 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 item_def_id = self - .tcx - .associated_items(deref_trait) - .in_definition_order() - .find(|item| item.kind == ty::AssocKind::Type) - .unwrap() - .def_id; - let predicate = - ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate { - // `::Output` - projection_ty: ty::ProjectionTy { - // `T` - substs: self.tcx.intern_substs(&[checked_ty.into()]), - // `Deref::Output` - item_def_id, - }, - // `U` - ty: expected, - })); - let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); - let impls_deref = self.infcx.predicate_may_hold(&obligation); - - // For a suggestion to make sense, the type would need to be `Copy`. - let is_copy = self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp); - - if is_copy && impls_deref { - if let Ok(code) = sm.span_to_snippet(sp) { - let message = if checked_ty.is_region_ptr() { - "consider dereferencing the borrow" - } else { - "consider dereferencing the type" - }; - let suggestion = if is_struct_pat_shorthand_field { - format!("{}: *{}", code, code) - } else { - format!("*{}", code) - }; - return Some((sp, message, suggestion)); + if let Some(steps) = self.deref_steps(checked_ty, expected) { + if steps == 1 { + // For a suggestion to make sense, the type would need to be `Copy`. + if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) { + if let Ok(code) = sm.span_to_snippet(sp) { + let message = if checked_ty.is_region_ptr() { + "consider dereferencing the borrow" + } else { + "consider dereferencing the type" + }; + let suggestion = if is_struct_pat_shorthand_field { + format!("{}: *{}", code, code) + } else { + format!("*{}", code) + }; + return Some(( + sp, + message, + suggestion, + Applicability::MachineApplicable, + )); + } + } } } } @@ -597,17 +676,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, checked_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool { - if self.tcx.hir().is_const_context(expr.hir_id) { - // Shouldn't suggest `.into()` on `const`s. - // FIXME(estebank): modify once we decide to suggest `as` casts - return false; - } if self.tcx.sess.source_map().is_imported(expr.span) { // Ignore if span is from within a macro. return false; } + let src = if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) { + src + } else { + return false; + }; + // If casting this expression to a given numeric type would be appropriate in case of a type // mismatch. // @@ -618,24 +699,25 @@ 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()) @@ -671,225 +753,262 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = format!("you can convert an `{}` to `{}`", checked_ty, expected_ty); let cast_msg = format!("you can cast an `{} to `{}`", checked_ty, expected_ty); - let try_msg = format!("{} and panic if the converted value wouldn't fit", msg); let lit_msg = format!( "change the type of the numeric literal from `{}` to `{}`", checked_ty, expected_ty, ); - let needs_paren = expr.precedence().order() < (PREC_POSTFIX as i8); - - if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) { - let cast_suggestion = format!( - "{}{}{}{} as {}", - prefix, - if needs_paren { "(" } else { "" }, - src, - if needs_paren { ")" } else { "" }, - expected_ty, - ); - let try_into_suggestion = format!( - "{}{}{}{}.try_into().unwrap()", - prefix, - if needs_paren { "(" } else { "" }, - src, - if needs_paren { ")" } else { "" }, - ); - let into_suggestion = format!( - "{}{}{}{}.into()", - prefix, - if needs_paren { "(" } else { "" }, - src, - if needs_paren { ")" } else { "" }, - ); - let suffix_suggestion = format!( - "{}{}{}{}", - if needs_paren { "(" } else { "" }, - if let (ty::Int(_), ty::Float(_)) | (ty::Uint(_), ty::Float(_)) = - (&expected_ty.kind, &checked_ty.kind,) + let with_opt_paren: fn(&dyn fmt::Display) -> String = + if expr.precedence().order() < PREC_POSTFIX { + |s| format!("({})", s) + } else { + |s| s.to_string() + }; + + let cast_suggestion = format!("{}{} as {}", prefix, with_opt_paren(&src), expected_ty); + let into_suggestion = format!("{}{}.into()", prefix, with_opt_paren(&src)); + let suffix_suggestion = with_opt_paren(&format_args!( + "{}{}", + if matches!( + (&expected_ty.kind, &checked_ty.kind), + (ty::Int(_) | ty::Uint(_), ty::Float(_)) + ) { + // Remove fractional part from literal, for example `42.0f32` into `42` + let src = src.trim_end_matches(&checked_ty.to_string()); + src.split('.').next().unwrap() + } else { + src.trim_end_matches(&checked_ty.to_string()) + }, + expected_ty, + )); + let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| { + if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false } + }; + let is_negative_int = + |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnNeg, ..)); + let is_uint = |ty: Ty<'_>| matches!(ty.kind, ty::Uint(..)); + + let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id); + + let suggest_fallible_into_or_lhs_from = + |err: &mut DiagnosticBuilder<'_>, exp_to_found_is_fallible: bool| { + // If we know the expression the expected type is derived from, we might be able + // to suggest a widening conversion rather than a narrowing one (which may + // panic). For example, given x: u8 and y: u32, if we know the span of "x", + // x > y + // can be given the suggestion "u32::from(x) > y" rather than + // "x > y.try_into().unwrap()". + let lhs_expr_and_src = expected_ty_expr.and_then(|expr| { + match self.tcx.sess.source_map().span_to_snippet(expr.span).ok() { + Some(src) => Some((expr, src)), + None => None, + } + }); + let (span, msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) = + (lhs_expr_and_src, exp_to_found_is_fallible) { - // Remove fractional part from literal, for example `42.0f32` into `42` - let src = src.trim_end_matches(&checked_ty.to_string()); - src.split('.').next().unwrap() + let msg = format!( + "you can convert `{}` from `{}` to `{}`, matching the type of `{}`", + lhs_src, expected_ty, checked_ty, src + ); + let suggestion = format!("{}::from({})", checked_ty, lhs_src); + (lhs_expr.span, msg, suggestion) + } else { + let msg = format!("{} and panic if the converted value wouldn't fit", msg); + let suggestion = + format!("{}{}.try_into().unwrap()", prefix, with_opt_paren(&src)); + (expr.span, msg, suggestion) + }; + err.span_suggestion(span, &msg, suggestion, Applicability::MachineApplicable); + }; + + let suggest_to_change_suffix_or_into = + |err: &mut DiagnosticBuilder<'_>, + found_to_exp_is_fallible: bool, + exp_to_found_is_fallible: bool| { + let always_fallible = found_to_exp_is_fallible + && (exp_to_found_is_fallible || expected_ty_expr.is_none()); + let msg = if literal_is_ty_suffixed(expr) { + &lit_msg + } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) { + // We now know that converting either the lhs or rhs is fallible. Before we + // suggest a fallible conversion, check if the value can never fit in the + // expected type. + let msg = format!("`{}` cannot fit into type `{}`", src, expected_ty); + err.note(&msg); + return; + } else if in_const_context { + // Do not recommend `into` or `try_into` in const contexts. + return; + } else if found_to_exp_is_fallible { + return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible); } else { - src.trim_end_matches(&checked_ty.to_string()) - }, - expected_ty, - if needs_paren { ")" } else { "" }, - ); - let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| { - if let hir::ExprKind::Lit(lit) = &expr.kind { - lit.node.is_suffixed() + &msg + }; + let suggestion = if literal_is_ty_suffixed(expr) { + suffix_suggestion.clone() } else { - false - } + into_suggestion.clone() + }; + err.span_suggestion(expr.span, msg, suggestion, Applicability::MachineApplicable); }; - let suggest_to_change_suffix_or_into = - |err: &mut DiagnosticBuilder<'_>, is_fallible: bool| { - let into_sugg = into_suggestion.clone(); + match (&expected_ty.kind, &checked_ty.kind) { + (&ty::Int(ref exp), &ty::Int(ref found)) => { + let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) + { + (Some(exp), Some(found)) if exp < found => (true, false), + (Some(exp), Some(found)) if exp > found => (false, true), + (None, Some(8 | 16)) => (false, true), + (Some(8 | 16), None) => (true, false), + (None, _) | (_, None) => (true, true), + _ => (false, false), + }; + suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible); + true + } + (&ty::Uint(ref exp), &ty::Uint(ref found)) => { + let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) + { + (Some(exp), Some(found)) if exp < found => (true, false), + (Some(exp), Some(found)) if exp > found => (false, true), + (None, Some(8 | 16)) => (false, true), + (Some(8 | 16), None) => (true, false), + (None, _) | (_, None) => (true, true), + _ => (false, false), + }; + suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible); + true + } + (&ty::Int(exp), &ty::Uint(found)) => { + let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) + { + (Some(exp), Some(found)) if found < exp => (false, true), + (None, Some(8)) => (false, true), + _ => (true, true), + }; + suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible); + true + } + (&ty::Uint(exp), &ty::Int(found)) => { + let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) + { + (Some(exp), Some(found)) if found > exp => (true, false), + (Some(8), None) => (true, false), + _ => (true, true), + }; + suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible); + true + } + (&ty::Float(ref exp), &ty::Float(ref found)) => { + if found.bit_width() < exp.bit_width() { + suggest_to_change_suffix_or_into(err, false, true); + } else if literal_is_ty_suffixed(expr) { err.span_suggestion( expr.span, - if literal_is_ty_suffixed(expr) { - &lit_msg - } else if is_fallible { - &try_msg - } else { - &msg - }, - if literal_is_ty_suffixed(expr) { - suffix_suggestion.clone() - } else if is_fallible { - try_into_suggestion - } else { - into_sugg - }, + &lit_msg, + suffix_suggestion, Applicability::MachineApplicable, ); - }; - - 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, - (None, _) | (_, None) => true, - _ => false, - }; - suggest_to_change_suffix_or_into(err, is_fallible); - 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, - (None, _) | (_, None) => true, - _ => false, - }; - suggest_to_change_suffix_or_into(err, is_fallible); - true - } - (&ty::Int(_), &ty::Uint(_)) | (&ty::Uint(_), &ty::Int(_)) => { - suggest_to_change_suffix_or_into(err, true); - true - } - (&ty::Float(ref exp), &ty::Float(ref found)) => { - if found.bit_width() < exp.bit_width() { - suggest_to_change_suffix_or_into(err, false); - } else if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else if can_cast { - // Missing try_into implementation for `f64` to `f32` - err.span_suggestion( - expr.span, - &format!("{}, producing the closest possible value", cast_msg), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true + } else if can_cast { + // Missing try_into implementation for `f64` to `f32` + err.span_suggestion( + expr.span, + &format!("{}, producing the closest possible value", cast_msg), + cast_suggestion, + Applicability::MaybeIncorrect, // lossy conversion + ); } - (&ty::Uint(_), &ty::Float(_)) | (&ty::Int(_), &ty::Float(_)) => { - if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else if can_cast { - // Missing try_into implementation for `{float}` to `{integer}` - err.span_suggestion( - expr.span, - &format!("{}, rounding the float towards zero", msg), - 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 + true + } + (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => { + if literal_is_ty_suffixed(expr) { + err.span_suggestion( + expr.span, + &lit_msg, + suffix_suggestion, + Applicability::MachineApplicable, + ); + } else if can_cast { + // Missing try_into implementation for `{float}` to `{integer}` + err.span_suggestion( + expr.span, + &format!("{}, rounding the float towards zero", msg), + cast_suggestion, + Applicability::MaybeIncorrect, // lossy conversion + ); } - (&ty::Float(ref exp), &ty::Uint(ref found)) => { - // if `found` is `None` (meaning found is `usize`), don't suggest `.into()` - if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.span_suggestion( - expr.span, - &format!( - "{}, producing the floating point representation of the integer", - msg, - ), - into_suggestion, - Applicability::MachineApplicable, - ); - } else if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else { - // Missing try_into implementation for `{integer}` to `{float}` - err.span_suggestion( - expr.span, - &format!( - "{}, producing the floating point representation of the integer, + true + } + (&ty::Float(ref exp), &ty::Uint(ref found)) => { + // if `found` is `None` (meaning found is `usize`), don't suggest `.into()` + if exp.bit_width() > found.bit_width().unwrap_or(256) { + err.span_suggestion( + expr.span, + &format!( + "{}, producing the floating point representation of the integer", + msg, + ), + into_suggestion, + Applicability::MachineApplicable, + ); + } else if literal_is_ty_suffixed(expr) { + err.span_suggestion( + expr.span, + &lit_msg, + suffix_suggestion, + Applicability::MachineApplicable, + ); + } else { + // Missing try_into implementation for `{integer}` to `{float}` + err.span_suggestion( + expr.span, + &format!( + "{}, producing the floating point representation of the integer, rounded if necessary", - cast_msg, - ), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true + cast_msg, + ), + cast_suggestion, + Applicability::MaybeIncorrect, // lossy conversion + ); } - (&ty::Float(ref exp), &ty::Int(ref found)) => { - // if `found` is `None` (meaning found is `isize`), don't suggest `.into()` - if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.span_suggestion( - expr.span, - &format!( - "{}, producing the floating point representation of the integer", - &msg, - ), - into_suggestion, - Applicability::MachineApplicable, - ); - } else if literal_is_ty_suffixed(expr) { - err.span_suggestion( - expr.span, - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else { - // Missing try_into implementation for `{integer}` to `{float}` - err.span_suggestion( - expr.span, - &format!( - "{}, producing the floating point representation of the integer, \ - rounded if necessary", - &msg, - ), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true + true + } + (&ty::Float(ref exp), &ty::Int(ref found)) => { + // if `found` is `None` (meaning found is `isize`), don't suggest `.into()` + if exp.bit_width() > found.bit_width().unwrap_or(256) { + err.span_suggestion( + expr.span, + &format!( + "{}, producing the floating point representation of the integer", + &msg, + ), + into_suggestion, + Applicability::MachineApplicable, + ); + } else if literal_is_ty_suffixed(expr) { + err.span_suggestion( + expr.span, + &lit_msg, + suffix_suggestion, + Applicability::MachineApplicable, + ); + } else { + // Missing try_into implementation for `{integer}` to `{float}` + err.span_suggestion( + expr.span, + &format!( + "{}, producing the floating point representation of the integer, \ + rounded if necessary", + &msg, + ), + cast_suggestion, + Applicability::MaybeIncorrect, // lossy conversion + ); } - _ => false, + true } - } else { - false + _ => false, } } } diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index dca4f9e7cbe08..e27c2e4503910 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -1,16 +1,14 @@ 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, SuppressRegionErrors, TyCtxtInferExt}; +use rustc_infer::infer::{InferOk, RegionckMode, TyCtxtInferExt}; use rustc_infer::traits::TraitEngineExt as _; +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 +38,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 +46,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 +66,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 +82,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 +93,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, @@ -123,8 +119,6 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>( return Err(ErrorReported); } - let region_scope_tree = region::ScopeTree::default(); - // NB. It seems a bit... suspicious to use an empty param-env // here. The correct thing, I imagine, would be // `OutlivesEnvironment::new(impl_param_env)`, which would @@ -136,10 +130,9 @@ 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, - ®ion_scope_tree, + drop_impl_did.to_def_id(), &outlives_env, - SuppressRegionErrors::default(), + RegionckMode::default(), ); Ok(()) }) @@ -150,7 +143,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 +183,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // absent. So we report an error that the Drop impl injected a // predicate that is not present on the struct definition. - let self_type_hir_id = tcx.hir().as_local_hir_id(self_type_did).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. @@ -208,7 +201,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // just to look for all the predicates directly. assert_eq!(dtor_predicates.parent, None); - for (predicate, predicate_sp) in dtor_predicates.predicates { + for &(predicate, predicate_sp) in dtor_predicates.predicates { // (We do not need to worry about deep analysis of type // expressions etc because the Drop impls are already forced // to take on a structure that is roughly an alpha-renaming of @@ -231,24 +224,25 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // This implementation solves (Issue #59497) and (Issue #58311). // It is unclear to me at the moment whether the approach based on `relate` // could be extended easily also to the other `Predicate`. - let predicate_matches_closure = |p: &'_ Predicate<'tcx>| { + let predicate_matches_closure = |p: Predicate<'tcx>| { let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env); - match (predicate, p) { - (Predicate::Trait(a, _), Predicate::Trait(b, _)) => relator.relate(a, b).is_ok(), - (Predicate::Projection(a), Predicate::Projection(b)) => { + match (predicate.kind(), p.kind()) { + (ty::PredicateKind::Trait(a, _), ty::PredicateKind::Trait(b, _)) => { + relator.relate(a, b).is_ok() + } + (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { relator.relate(a, b).is_ok() } _ => predicate == p, } }; - if !assumptions_in_impl_context.iter().any(predicate_matches_closure) { + if !assumptions_in_impl_context.iter().copied().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, + predicate_sp, E0367, "`Drop` impl requires `{}` but the {} it is implemented for does not", predicate, diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 93f9050b26eb9..1eaa5a6c31e20 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -15,28 +15,27 @@ 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::mir::interpret::ErrorHandled; -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}; +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; @@ -85,7 +84,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, None); extend_err(&mut err); // Error possibly reported in `check_assign` so avoid emitting error again. err.emit_unless(self.is_assign_to_bool(expr, expected_ty)); @@ -97,10 +96,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> Ty<'tcx> { let ty = self.check_expr_with_hint(expr, expected); // checks don't need two phase - self.demand_coerce(expr, ty, expected, AllowTwoPhase::No) + self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) } pub(super) fn check_expr_with_hint( @@ -111,12 +111,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_with_expectation(expr, ExpectHasType(expected)) } - pub(super) fn check_expr_with_expectation( + fn check_expr_with_expectation_and_needs( &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, + needs: Needs, ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_needs(expr, expected, Needs::None) + let ty = self.check_expr_with_expectation(expr, expected); + + // If the expression is used in a place whether mutable place is required + // e.g. LHS of assignment, perform the conversion. + if let Needs::MutPlace = needs { + self.convert_place_derefs_to_mutable(expr); + } + + ty } pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { @@ -141,11 +150,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Note that inspecting a type's structure *directly* may expose the fact /// that there are actually multiple representations for `Error`, so avoid /// that when err needs to be handled differently. - fn check_expr_with_expectation_and_needs( + pub(super) fn check_expr_with_expectation( &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, - needs: Needs, ) -> Ty<'tcx> { debug!(">> type-checking: expr={:?} expected={:?}", expr, expected); @@ -169,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let old_diverges = self.diverges.replace(Diverges::Maybe); let old_has_errors = self.has_errors.replace(false); - let ty = self.check_expr_kind(expr, expected, needs); + let ty = self.check_expr_kind(expr, expected); // Warn for non-block expressions with diverging children. match expr.kind { @@ -181,7 +189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Call(ref callee, _) => { self.warn_if_unreachable(expr.hir_id, callee.span, "call") } - ExprKind::MethodCall(_, ref span, _) => { + ExprKind::MethodCall(_, ref span, _, _) => { self.warn_if_unreachable(expr.hir_id, *span, "call") } _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), @@ -211,9 +219,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, - needs: Needs, ) -> Ty<'tcx> { - debug!("check_expr_kind(expr={:?}, expected={:?}, needs={:?})", expr, expected, needs,); + debug!("check_expr_kind(expr={:?}, expected={:?})", expr, expected); let tcx = self.tcx; match expr.kind { @@ -224,14 +231,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_assign(expr, expected, lhs, rhs, span) } ExprKind::AssignOp(op, ref lhs, ref rhs) => self.check_binop_assign(expr, op, lhs, rhs), - ExprKind::Unary(unop, ref oprnd) => { - self.check_expr_unary(unop, oprnd, expected, needs, expr) - } + ExprKind::Unary(unop, ref oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), ExprKind::AddrOf(kind, mutbl, ref oprnd) => { self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) } ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr), - ExprKind::InlineAsm(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); } @@ -245,7 +251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.types.never } else { // There was an error; make type-check fail. - tcx.types.err + tcx.ty_error() } } ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), @@ -260,8 +266,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Block(ref body, _) => self.check_block_with_expected(&body, expected), ExprKind::Call(ref callee, ref args) => self.check_call(expr, &callee, args, expected), - ExprKind::MethodCall(ref segment, span, ref args) => { - self.check_method_call(expr, segment, span, args, expected, needs) + ExprKind::MethodCall(ref segment, span, ref args, _) => { + self.check_method_call(expr, segment, span, args, expected) } ExprKind::Cast(ref e, ref t) => self.check_expr_cast(e, t, expr), ExprKind::Type(ref e, ref t) => { @@ -278,10 +284,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Struct(ref qpath, fields, ref base_expr) => { self.check_expr_struct(expr, expected, qpath, fields, base_expr) } - ExprKind::Field(ref base, field) => self.check_field(expr, needs, &base, field), - ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, needs, expr), + ExprKind::Field(ref base, field) => self.check_field(expr, &base, field), + ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, expr), ExprKind::Yield(ref value, ref src) => self.check_expr_yield(value, expr, src), - hir::ExprKind::Err => tcx.types.err, + hir::ExprKind::Err => tcx.ty_error(), } } @@ -299,7 +305,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unop: hir::UnOp, oprnd: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, - needs: Needs, expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; @@ -307,40 +312,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::UnOp::UnNot | hir::UnOp::UnNeg => expected, hir::UnOp::UnDeref => NoExpectation, }; - let needs = match unop { - hir::UnOp::UnDeref => needs, - _ => Needs::None, - }; - let mut oprnd_t = self.check_expr_with_expectation_and_needs(&oprnd, expected_inner, needs); + let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner); if !oprnd_t.references_error() { oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t); match unop { hir::UnOp::UnDeref => { - if let Some(mt) = oprnd_t.builtin_deref(true) { - oprnd_t = mt.ty; - } else if let Some(ok) = self.try_overloaded_deref(expr.span, oprnd_t, needs) { - let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // (It shouldn't actually matter for unary ops whether - // we enable two-phase borrows or not, since a unary - // op has no additional operands.) - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - self.apply_adjustments( - oprnd, - vec![Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), - target: method.sig.inputs()[0], - }], - ); - } - oprnd_t = self.make_overloaded_place_return_type(method).ty; - self.write_method_call(expr.hir_id, method); + if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { + oprnd_t = ty; } else { let mut err = type_error_struct!( tcx.sess, @@ -357,7 +336,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp, None); } err.emit(); - oprnd_t = tcx.types.err; + oprnd_t = tcx.ty_error(); } } hir::UnOp::UnNot => { @@ -402,12 +381,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => NoExpectation, } }); - let needs = Needs::maybe_mut_place(mutbl); - let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs); + let ty = + self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl)); let tm = ty::TypeAndMut { ty, mutbl }; match kind { - _ if tm.ty.references_error() => self.tcx.types.err, + _ if tm.ty.references_error() => self.tcx.ty_error(), hir::BorrowKind::Raw => { self.check_named_place_expr(oprnd); self.tcx.mk_ptr(tm) @@ -473,11 +452,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = match res { Res::Err => { self.set_tainted_by_errors(); - tcx.types.err + tcx.ty_error() } Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { - report_unexpected_variant_res(tcx, res, expr.span, qpath); - tcx.types.err + report_unexpected_variant_res(tcx, res, expr.span); + tcx.ty_error() } _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, }; @@ -557,11 +536,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()), None => { // Avoid ICE when `break` is inside a closure (#65383). - self.tcx.sess.delay_span_bug( + return tcx.ty_error_with_message( expr.span, "break was outside loop, but no error was emitted", ); - return tcx.types.err; } } }; @@ -569,7 +547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the loop context is not a `loop { }`, then break with // a value is illegal, and `opt_coerce_to` will be `None`. // Just set expectation to error in that case. - let coerce_to = opt_coerce_to.unwrap_or(tcx.types.err); + let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error()); // Recurse without `enclosing_breakables` borrowed. e_ty = self.check_expr_with_hint(e, coerce_to); @@ -589,11 +567,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ctxt) => ctxt, None => { // Avoid ICE when `break` is inside a closure (#65383). - self.tcx.sess.delay_span_bug( + return tcx.ty_error_with_message( expr.span, "break was outside loop, but no error was emitted", ); - return tcx.types.err; } }; @@ -646,14 +623,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // this can only happen if the `break` was not // inside a loop at all, which is caught by the // loop-checking pass. - self.tcx - .sess - .delay_span_bug(expr.span, "break was outside loop, but no error was emitted"); + let err = self.tcx.ty_error_with_message( + expr.span, + "break was outside loop, but no error was emitted", + ); // We still need to assign a type to the inner expression to // prevent the ICE in #43162. if let Some(ref e) = expr_opt { - self.check_expr_with_hint(e, tcx.types.err); + self.check_expr_with_hint(e, err); // ... except when we try to 'break rust;'. // ICE this expression in particular (see #43162). @@ -663,8 +641,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + // There was an error; make type-check fail. - tcx.types.err + err } } @@ -697,10 +676,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, ); @@ -771,7 +753,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: &Span, ) -> Ty<'tcx> { let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); - let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty); + let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty, Some(lhs)); let expected_ty = expected.coercion_target_type(self, expr.span); if expected_ty == self.tcx.types.bool { @@ -797,7 +779,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); if lhs_ty.references_error() || rhs_ty.references_error() { - self.tcx.types.err + self.tcx.ty_error() } else { self.tcx.mk_unit() } @@ -855,10 +837,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, args: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, - needs: Needs, ) -> Ty<'tcx> { let rcvr = &args[0]; - let rcvr_t = self.check_expr_with_needs(&rcvr, needs); + let rcvr_t = self.check_expr(&rcvr); // no need to check for bot/err -- callee does that let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); @@ -898,8 +879,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, @@ -907,10 +888,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr, probe::ProbeScope::AllTraits, ) { - err.span_label( - pick.item.ident.span, - &format!("the method is available for `{}` here", new_rcvr_t), - ); + debug!("try_alt_rcvr: pick candidate {:?}", pick); + // Make sure the method is defined for the *actual* receiver: + // we don't want to treat `Box` as a receiver if + // it only works because of an autoderef to `&self` + if pick.autoderefs == 0 { + err.span_label( + pick.item.ident.span, + &format!("the method is available for `{}` here", new_rcvr_t), + ); + } } } }; @@ -927,10 +914,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(); } @@ -951,7 +938,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Eagerly check for some obvious errors. if t_expr.references_error() || t_cast.references_error() { - self.tcx.types.err + self.tcx.ty_error() } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); @@ -960,7 +947,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deferred_cast_checks.push(cast_check); t_cast } - Err(ErrorReported) => self.tcx.types.err, + Err(ErrorReported) => self.tcx.ty_error(), } } } @@ -971,18 +958,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 { @@ -1008,13 +996,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; - let count_def_id = tcx.hir().local_def_id(count.hir_id); - let count = if self.const_param_def_id(count).is_some() { - Ok(self.to_const(count, tcx.type_of(count_def_id))) - } else { - tcx.const_eval_poly(count_def_id) - .map(|val| ty::Const::from_value(tcx, val, tcx.type_of(count_def_id))) - }; + let count = self.to_const(count); let uty = match expected { ExpectHasType(uty) => match uty.kind { @@ -1026,7 +1008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (element_ty, t) = match uty { Some(uty) => { - self.check_expr_coercable_to_type(&element, uty); + self.check_expr_coercable_to_type(&element, uty, None); (uty, uty) } None => { @@ -1040,19 +1022,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if element_ty.references_error() { - return tcx.types.err; - } - match count { - Ok(count) => tcx.mk_ty(ty::Array(t, count)), - Err(ErrorHandled::TooGeneric) => { - self.tcx.sess.span_err( - tcx.def_span(count_def_id), - "array lengths can't depend on generic parameters", - ); - tcx.types.err - } - Err(ErrorHandled::Reported) => tcx.types.err, + return tcx.ty_error(); } + + tcx.mk_ty(ty::Array(t, count)) } fn check_expr_tuple( @@ -1069,20 +1042,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }); - let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| { - let t = match flds { - Some(ref fs) if i < fs.len() => { - let ety = fs[i].expect_ty(); - self.check_expr_coercable_to_type(&e, ety); - ety - } - _ => self.check_expr_with_expectation(&e, NoExpectation), - }; - t + let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds { + Some(ref fs) if i < fs.len() => { + let ety = fs[i].expect_ty(); + self.check_expr_coercable_to_type(&e, ety, None); + ety + } + _ => self.check_expr_with_expectation(&e, NoExpectation), }); let tuple = self.tcx.mk_tup(elt_ts_iter); if tuple.references_error() { - self.tcx.types.err + self.tcx.ty_error() } else { self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized); tuple @@ -1103,7 +1073,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant_ty } else { self.check_struct_fields_on_error(fields, base_expr); - return self.tcx.types.err; + return self.tcx.ty_error(); }; let path_span = match *qpath { @@ -1203,7 +1173,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .fields .iter() .enumerate() - .map(|(i, field)| (field.ident.modern(), (i, field))) + .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) .collect::>(); let mut seen_fields = FxHashMap::default(); @@ -1244,12 +1214,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name, span); } - tcx.types.err + tcx.ty_error() }; // Make sure to give a type to the field even if there's // an error, so we can continue type-checking. - self.check_expr_coercable_to_type(&field.expr, field_type); + self.check_expr_coercable_to_type(&field.expr, field_type, None); } // Make sure the programmer specified correct number of fields. @@ -1424,7 +1394,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() @@ -1439,7 +1409,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 = @@ -1454,11 +1424,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_field( &self, 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.check_expr(base); let expr_t = self.structurally_resolved_type(base.span, expr_t); let mut private_candidate = None; let mut autoderef = self.autoderef(expr.span, expr_t); @@ -1469,16 +1438,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (ident, def_scope) = self.tcx.adjust_ident_and_get_scope(field, base_def.did, self.body_id); let fields = &base_def.non_enum_variant().fields; - if let Some(index) = fields.iter().position(|f| f.ident.modern() == ident) { + if let Some(index) = + fields.iter().position(|f| f.ident.normalize_to_macros_2_0() == ident) + { let field = &fields[index]; let field_ty = self.field_ty(expr.span, field, substs); // Save the index of all fields regardless of their visibility in case // of error recovery. self.write_field_index(expr.hir_id, index); if field.vis.is_accessible_from(def_scope, self.tcx) { - let adjustments = autoderef.adjust_steps(self, needs); + let adjustments = self.adjust_steps(&autoderef); self.apply_adjustments(base, adjustments); - autoderef.finalize(self); + self.register_predicates(autoderef.into_obligations()); self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span); return field_ty; @@ -1491,9 +1462,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Ok(index) = fstr.parse::() { if fstr == index.to_string() { if let Some(field_ty) = tys.get(index) { - let adjustments = autoderef.adjust_steps(self, needs); + let adjustments = self.adjust_steps(&autoderef); self.apply_adjustments(base, adjustments); - autoderef.finalize(self); + self.register_predicates(autoderef.into_obligations()); self.write_field_index(expr.hir_id, index); return field_ty.expect_ty(); @@ -1504,7 +1475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } } - autoderef.unambiguous_final_ty(self); + self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); if let Some((did, field_ty)) = private_candidate { self.ban_private_field_access(expr, expr_t, field, did); @@ -1528,12 +1499,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); } - self.tcx().types.err + self.tcx().ty_error() } fn ban_nonexisting_field( &self, - field: ast::Ident, + field: Ident, base: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>, expr_t: Ty<'tcx>, @@ -1571,23 +1542,21 @@ 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, - expr.span, + field.span, E0616, "field `{}` of {} `{}` is private", field, kind_name, struct_path ); + err.span_label(field.span, "private field"); // Also check if an accessible method exists, which is often what is meant. if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id) { @@ -1602,7 +1571,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, @@ -1612,7 +1581,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { field, expr_t ); - + err.span_label(field.span, "method, not a field"); if !self.expr_in_place(expr.hir_id) { self.suggest_method_call( &mut err, @@ -1629,14 +1598,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn point_at_param_definition(&self, err: &mut DiagnosticBuilder<'_>, param: ty::ParamTy) { - let generics = self.tcx.generics_of(self.body_id.owner_def_id()); + let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); let generic_param = generics.type_param(¶m, self.tcx); if let ty::GenericParamDefKind::Type { synthetic: Some(..), .. } = generic_param.kind { return; } let param_def_id = generic_param.def_id; - let param_hir_id = match 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); @@ -1649,7 +1618,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![]) @@ -1678,26 +1647,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); + } } } @@ -1706,17 +1671,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( @@ -1740,10 +1701,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, base: &'tcx hir::Expr<'tcx>, idx: &'tcx hir::Expr<'tcx>, - needs: Needs, expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let base_t = self.check_expr_with_needs(&base, needs); + let base_t = self.check_expr(&base); let idx_t = self.check_expr(&idx); if base_t.references_error() { @@ -1752,10 +1712,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { idx_t } else { let base_t = self.structurally_resolved_type(base.span, base_t); - match self.lookup_indexing(expr, base, base_t, idx_t, needs) { + match self.lookup_indexing(expr, base, base_t, idx_t) { Some((index_ty, element_ty)) => { // two-phase not needed because index_ty is never mutable - self.demand_coerce(idx, idx_t, index_ty, AllowTwoPhase::No); + self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); element_ty } None => { @@ -1794,7 +1754,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } err.emit(); - self.tcx.types.err + self.tcx.ty_error() } } } @@ -1808,7 +1768,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { match self.resume_yield_tys { Some((resume_ty, yield_ty)) => { - self.check_expr_coercable_to_type(&value, yield_ty); + self.check_expr_coercable_to_type(&value, yield_ty, None); resume_ty } @@ -1816,8 +1776,8 @@ 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 => { - self.check_expr_coercable_to_type(&value, self.tcx.mk_unit()); + None if src.is_await() => { + self.check_expr_coercable_to_type(&value, self.tcx.mk_unit(), None); self.tcx.mk_unit() } _ => { @@ -1832,6 +1792,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, None, AllowTwoPhase::No); + } + ty::Ref(_, base_ty, mutbl) => { + let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl }); + self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No); + } + _ => {} + } + } + } + + fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { + for op in asm.operands { + match op { + hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::Const { expr } => { + self.check_expr_asm_operand(expr, true); + } + hir::InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + self.check_expr_asm_operand(expr, false); + } + } + hir::InlineAsmOperand::InOut { expr, .. } => { + self.check_expr_asm_operand(expr, false); + } + hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.check_expr_asm_operand(in_expr, true); + if let Some(out_expr) = out_expr { + self.check_expr_asm_operand(out_expr, false); + } + } + hir::InlineAsmOperand::Sym { expr } => { + self.check_expr(expr); + } + } + } + if asm.options.contains(ast::InlineAsmOptions::NORETURN) { + self.tcx.types.never + } else { + self.tcx.mk_unit() + } + } } pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { @@ -1840,7 +1866,7 @@ pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { ty::Char => "'a'", ty::Int(_) | ty::Uint(_) => "42", ty::Float(_) => "3.14159", - ty::Error | ty::Never => return None, + ty::Error(_) | ty::Never => return None, _ => "value", }) } diff --git a/src/librustc_typeck/check/generator_interior.rs b/src/librustc_typeck/check/generator_interior.rs index 5208e2f56a5c2..ce376a08ea604 100644 --- a/src/librustc_typeck/check/generator_interior.rs +++ b/src/librustc_typeck/check/generator_interior.rs @@ -4,15 +4,14 @@ //! types computed here. use super::FnCtxt; -use rustc::hir::map::Map; -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> { @@ -97,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); @@ -206,10 +206,10 @@ 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 = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -236,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 07f392fa23b37..ef6c7c14404a7 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,14 +69,13 @@ 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" | "bswap" | "bitreverse" | "discriminant_value" | "type_id" | "likely" | "unlikely" - | "minnumf32" | "minnumf64" | "maxnumf32" | "maxnumf64" | "type_name" => { - hir::Unsafety::Normal - } + | "ptr_guaranteed_eq" | "ptr_guaranteed_ne" | "minnumf32" | "minnumf64" | "maxnumf32" + | "maxnumf64" | "type_name" => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, } } @@ -130,28 +129,23 @@ 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[..]); let (n_tps, inputs, output) = match &name[..] { "breakpoint" => (0, Vec::new(), tcx.mk_unit()), "size_of" | "pref_align_of" | "min_align_of" => (1, Vec::new(), tcx.types.usize), - "size_of_val" | "min_align_of_val" => ( - 1, - vec![tcx.mk_imm_ref( - tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0))), - param(0), - )], - tcx.types.usize, - ), + "size_of_val" | "min_align_of_val" => { + (1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize) + } "rustc_peek" => (1, vec![param(0)], param(0)), "caller_location" => (0, vec![], tcx.caller_location_ty()), "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => { (1, Vec::new(), tcx.mk_unit()) } - "init" => (1, Vec::new(), param(0)), - "uninit" => (1, Vec::new(), param(0)), "forget" => (1, vec![param(0)], tcx.mk_unit()), "transmute" => (2, vec![param(0)], param(1)), "move_val_init" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), @@ -263,6 +257,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { (1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool])) } + "ptr_guaranteed_eq" | "ptr_guaranteed_ne" => { + (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool) + } + "ptr_offset_from" => { (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize) } @@ -282,20 +280,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); @@ -346,6 +350,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { return; } + "count_code_region" => (0, vec![tcx.types.u32], tcx.mk_unit()), + ref other => { struct_span_err!( tcx.sess, diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 48c72567b5c31..1c3d23a3a241f 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -1,16 +1,16 @@ use super::{probe, MethodCallee}; use crate::astconv::AstConv; -use crate::check::{callee, FnCtxt, Needs, PlaceOp}; +use crate::check::{callee, FnCtxt}; 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, 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,16 +114,11 @@ 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`. let callee = MethodCallee { def_id: pick.item.def_id, substs: all_substs, sig: method_sig }; - - if let Some(hir::Mutability::Mut) = pick.autoref { - self.convert_place_derefs_to_mutable(); - } - ConfirmResult { callee, illegal_sized_bound } } @@ -141,18 +136,18 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let (_, n) = match autoderef.nth(pick.autoderefs) { Some(n) => n, None => { - self.tcx.sess.delay_span_bug( + return self.tcx.ty_error_with_message( rustc_span::DUMMY_SP, &format!("failed autoderef {}", pick.autoderefs), ); - return self.tcx.types.err; } }; assert_eq!(n, pick.autoderefs); - let mut adjustments = autoderef.adjust_steps(self, Needs::None); + let mut adjustments = self.adjust_steps(&autoderef); - let mut target = autoderef.unambiguous_final_ty(self); + let mut target = + self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); if let Some(mutbl) = pick.autoref { let region = self.next_region_var(infer::Autoref(self.span)); @@ -182,7 +177,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { assert!(pick.unsize.is_none()); } - autoderef.finalize(self); + self.register_predicates(autoderef.into_obligations()); // Write out the final adjustments. self.apply_adjustments(self.self_expr, adjustments); @@ -209,7 +204,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 +264,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 +274,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { )), _ => None, }) - .next() .unwrap_or_else(|| { span_bug!( self.span, @@ -313,7 +307,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { parent_substs, false, None, - arg_count_correct.is_ok(), + arg_count_correct, // Provide the generic args, and whether types should be inferred. |def_id| { // The last component of the returned tuple here is unimportant. @@ -331,7 +325,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => self.to_ty(ty).into(), (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - self.to_const(&ct.value, self.tcx.type_of(param.def_id)).into() + self.to_const(&ct.value).into() } _ => unreachable!(), }, @@ -395,7 +389,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={:?}", @@ -414,151 +408,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // the function type must also be well-formed (this is not // implied by the substs being well-formed because of inherent // impls and late-bound regions - see issue #28609). - self.register_wf_obligation(fty, self.span, traits::MiscObligation); - } - - /////////////////////////////////////////////////////////////////////////// - // RECONCILIATION - - /// When we select a method with a mutable autoref, we have to go convert any - /// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut` - /// respectively. - fn convert_place_derefs_to_mutable(&self) { - // Gather up expressions we want to munge. - let mut exprs = vec![self.self_expr]; - - loop { - match exprs.last().unwrap().kind { - hir::ExprKind::Field(ref expr, _) - | hir::ExprKind::Index(ref expr, _) - | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr), - _ => break, - } - } - - debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); - - // Fix up autoderefs and derefs. - for (i, &expr) in exprs.iter().rev().enumerate() { - debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); - - // Fix up the autoderefs. Autorefs can only occur immediately preceding - // overloaded place ops, and will be fixed by them in order to get - // the correct region. - let mut source = self.node_ty(expr.hir_id); - // Do not mutate adjustments in place, but rather take them, - // and replace them after mutating them, to avoid having the - // tables borrowed during (`deref_mut`) method resolution. - let previous_adjustments = - self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id); - if let Some(mut adjustments) = previous_adjustments { - let needs = Needs::MutPlace; - for adjustment in &mut adjustments { - if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind { - if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) { - let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, mutbl) = method.sig.output().kind { - *deref = OverloadedDeref { region, mutbl }; - } - } - } - source = adjustment.target; - } - self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); - } - - match expr.kind { - hir::ExprKind::Index(ref base_expr, ref index_expr) => { - let index_expr_ty = self.node_ty(index_expr.hir_id); - self.convert_place_op_to_mutable( - PlaceOp::Index, - expr, - base_expr, - &[index_expr_ty], - ); - } - hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { - self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); - } - _ => {} - } - } - } - - fn convert_place_op_to_mutable( - &self, - op: PlaceOp, - expr: &hir::Expr<'_>, - base_expr: &hir::Expr<'_>, - arg_tys: &[Ty<'tcx>], - ) { - debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys); - if !self.tables.borrow().is_method_call(expr) { - debug!("convert_place_op_to_mutable - builtin, nothing to do"); - return; - } - - let base_ty = self - .tables - .borrow() - .expr_adjustments(base_expr) - .last() - .map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target); - let base_ty = self.resolve_vars_if_possible(&base_ty); - - // Need to deref because overloaded place ops take self by-reference. - let base_ty = - base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty; - - let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op); - let method = match method { - Some(ok) => self.register_infer_ok_obligations(ok), - None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"), - }; - debug!("convert_place_op_to_mutable: method={:?}", method); - self.write_method_call(expr.hir_id, method); - - let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind { - (r, mutbl) - } else { - span_bug!(expr.span, "input to place op is not a ref?"); - }; - - // Convert the autoref in the base expr to mutable with the correct - // region and mutability. - let base_expr_ty = self.node_ty(base_expr.hir_id); - if let Some(adjustments) = - self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id) - { - let mut source = base_expr_ty; - for adjustment in &mut adjustments[..] { - if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { - debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded operators. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); - adjustment.target = - self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); - } - source = adjustment.target; - } - - // 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]; - } - _ => {} - } - } + self.register_wf_obligation(fty.into(), self.span, traits::MiscObligation); } /////////////////////////////////////////////////////////////////////////// @@ -573,33 +423,36 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { None => return None, }; - traits::elaborate_predicates(self.tcx, predicates.predicates.clone()) - .filter_map(|predicate| match predicate { - ty::Predicate::Trait(trait_pred, _) if trait_pred.def_id() == sized_def_id => { + traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) + .filter_map(|obligation| match obligation.predicate.kind() { + ty::PredicateKind::Trait(trait_pred, _) if trait_pred.def_id() == sized_def_id => { 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<'_>) { // Disallow calls to the method `drop` defined in the `Drop` trait. match pick.item.container { - ty::TraitContainer(trait_def_id) => { - callee::check_legal_trait_for_method_call(self.tcx, self.span, trait_def_id) - } + ty::TraitContainer(trait_def_id) => callee::check_legal_trait_for_method_call( + self.tcx, + self.span, + Some(self.self_expr.span), + trait_def_id, + ), ty::ImplContainer(..) => {} } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 3cf7b65e30f2f..259c4a8664f1b 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,11 +133,11 @@ 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<'_>, ) { - let has_params = self + let params = self .probe_for_name( method_name.span, probe::Mode::MethodCall, @@ -147,26 +147,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr.hir_id, ProbeScope::TraitsInScope, ) - .and_then(|pick| { + .map(|pick| { let sig = self.tcx.fn_sig(pick.item.def_id); - Ok(sig.inputs().skip_binder().len() > 1) - }); + sig.inputs().skip_binder().len().saturating_sub(1) + }) + .unwrap_or(0); // Account for `foo.bar`; - let sugg_span = method_name.span.with_hi(call_expr.span.hi()); - let snippet = self - .tcx - .sess - .source_map() - .span_to_snippet(sugg_span) - .unwrap_or_else(|_| method_name.to_string()); - let (suggestion, applicability) = if has_params.unwrap_or_default() { - (format!("{}(...)", snippet), Applicability::HasPlaceholders) - } else { - (format!("{}()", snippet), Applicability::MaybeIncorrect) - }; + let sugg_span = call_expr.span.shrink_to_hi(); + let (suggestion, applicability) = ( + format!("({})", (0..params).map(|_| "_").collect::>().join(", ")), + if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect }, + ); - err.span_suggestion(sugg_span, msg, suggestion, applicability); + err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability); } /// Performs method lookup. If lookup is successful, it will return the callee @@ -177,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, @@ -200,11 +194,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; for import_id in &pick.import_ids { - let import_def_id = self.tcx.hir().local_def_id(*import_id); - debug!("used_trait_import: {:?}", import_def_id); + debug!("used_trait_import: {:?}", import_id); Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports) .unwrap() - .insert(import_def_id); + .insert(*import_id); } self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span); @@ -266,7 +259,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, @@ -296,7 +289,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>]>, @@ -330,7 +323,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, self.body_id, self.param_env, - poly_trait_ref.without_const().to_predicate(), + poly_trait_ref.without_const().to_predicate(self.tcx), ); // Now we want to know if this can be matched @@ -368,11 +361,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = tcx.fn_sig(def_id); let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, &fn_sig).0; let fn_sig = fn_sig.subst(self.tcx, substs); - let fn_sig = match self.normalize_associated_types_in_as_infer_ok(span, &fn_sig) { - InferOk { value, obligations: o } => { - obligations.extend(o); - value - } + + let InferOk { value, obligations: o } = + self.normalize_associated_types_in_as_infer_ok(span, &fn_sig); + let fn_sig = { + obligations.extend(o); + value }; // Register obligations for the parameters. This will include the @@ -384,16 +378,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Note that as the method comes from a trait, it should not have // any late-bound regions appearing in its bounds. let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); - let bounds = match self.normalize_associated_types_in_as_infer_ok(span, &bounds) { - InferOk { value, obligations: o } => { - obligations.extend(o); - value - } + + let InferOk { value, obligations: o } = + self.normalize_associated_types_in_as_infer_ok(span, &bounds); + let bounds = { + obligations.extend(o); + value }; + assert!(!bounds.has_escaping_bound_vars()); let cause = traits::ObligationCause::misc(span, self.body_id); - obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, &bounds)); + 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)); @@ -404,7 +400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligations.push(traits::Obligation::new( cause, self.param_env, - ty::Predicate::WellFormed(method_ty), + ty::PredicateKind::WellFormed(method_ty.into()).to_predicate(tcx), )); let callee = MethodCallee { def_id, substs: trait_ref.substs, sig: fn_sig }; @@ -417,7 +413,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>> { @@ -464,13 +460,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut tables = self.tables.borrow_mut(); let used_trait_imports = Lrc::get_mut(&mut tables.used_trait_imports).unwrap(); 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); + debug!("resolve_ufcs: used_trait_import: {:?}", import_id); + used_trait_imports.insert(import_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)) @@ -481,7 +476,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 16bab09feeef0..a3e34815d3182 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -3,19 +3,10 @@ use super::MethodError; use super::NoMatchData; use super::{CandidateSource, ImplSource, TraitSource}; -use crate::check::autoderef::{self, Autoderef}; use crate::check::FnCtxt; use crate::hir::def::DefKind; use crate::hir::def_id::DefId; -use rustc::lint; -use rustc::middle::stability; -use rustc::session::config::nightly_options; -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; @@ -28,7 +19,17 @@ 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_span::{symbol::Symbol, Span, DUMMY_SP}; +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::def_id::LocalDefId; +use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; +use rustc_trait_selection::autoderef::{self, Autoderef}; 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 +56,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 @@ -129,7 +130,7 @@ struct Candidate<'tcx> { xform_ret_ty: Option>, item: ty::AssocItem, kind: CandidateKind<'tcx>, - import_ids: SmallVec<[hir::HirId; 1]>, + import_ids: SmallVec<[LocalDefId; 1]>, } #[derive(Debug)] @@ -158,7 +159,7 @@ enum ProbeResult { pub struct Pick<'tcx> { pub item: ty::AssocItem, pub kind: PickKind<'tcx>, - pub import_ids: SmallVec<[hir::HirId; 1]>, + pub import_ids: SmallVec<[LocalDefId; 1]>, // Indicates that the source expression should be autoderef'd N times // @@ -268,7 +269,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 +296,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>, @@ -400,7 +401,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .probe_instantiate_query_response(span, &orig_values, ty) .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); let ty = self.structurally_resolved_type(span, ty.value); - assert_eq!(ty, self.tcx.types.err); + assert!(matches!(ty.kind, ty::Error(_))); return Err(MethodError::NoMatch(NoMatchData::new( Vec::new(), Vec::new(), @@ -452,7 +453,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; @@ -476,9 +477,9 @@ fn method_autoderef_steps<'tcx>( }) .collect(); - let final_ty = autoderef.maybe_ambiguous_final_ty(); + let final_ty = autoderef.final_ty(true); let opt_bad_ty = match final_ty.kind { - ty::Infer(ty::TyVar(_)) | ty::Error => Some(MethodAutoderefBadTy { + ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx .make_query_response_ignoring_pending_obligations(inference_vars, final_ty), @@ -518,7 +519,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 +571,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 +650,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 { @@ -789,23 +796,28 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) { // FIXME: do we want to commit to this behavior for param bounds? - - let bounds = self.param_env.caller_bounds.iter().filter_map(|predicate| match *predicate { - ty::Predicate::Trait(ref trait_predicate, _) => { - match trait_predicate.skip_binder().trait_ref.self_ty().kind { - ty::Param(ref p) if *p == param_ty => Some(trait_predicate.to_poly_trait_ref()), - _ => None, + debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty); + + let bounds = + self.param_env.caller_bounds.iter().filter_map(|predicate| match predicate.kind() { + ty::PredicateKind::Trait(ref trait_predicate, _) => { + match trait_predicate.skip_binder().trait_ref.self_ty().kind { + ty::Param(ref p) if *p == param_ty => { + Some(trait_predicate.to_poly_trait_ref()) + } + _ => None, + } } - } - ty::Predicate::Subtype(..) - | ty::Predicate::Projection(..) - | ty::Predicate::RegionOutlives(..) - | ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::TypeOutlives(..) - | ty::Predicate::ConstEvaluatable(..) => None, - }); + ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => None, + }); self.elaborate_bounds(bounds, |this, poly_trait_ref, item| { let trait_ref = this.erase_late_bound_regions(&poly_trait_ref); @@ -859,9 +871,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 +905,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); @@ -922,7 +931,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn assemble_extension_candidates_for_trait( &mut self, - import_ids: &SmallVec<[hir::HirId; 1]>, + import_ids: &SmallVec<[LocalDefId; 1]>, trait_def_id: DefId, ) -> Result<(), MethodError<'tcx>> { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); @@ -945,7 +954,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { import_ids: import_ids.clone(), kind: TraitCandidate(new_trait_ref), }, - true, + false, ); }); } else { @@ -975,7 +984,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 +1137,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 }); @@ -1297,7 +1305,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .at(&ObligationCause::dummy(), self.param_env) .sup(candidate.xform_self_ty, self_ty); match self.select_trait_candidate(trait_ref) { - Ok(Some(traits::Vtable::VtableImpl(ref impl_data))) => { + Ok(Some(traits::ImplSource::ImplSourceUserDefined(ref impl_data))) => { // If only a single impl matches, make the error message point // to that impl. ImplSource(impl_data.impl_def_id) @@ -1340,7 +1348,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,33 +1359,37 @@ 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) => { - let predicate = trait_ref.without_const().to_predicate(); + let predicate = trait_ref.without_const().to_predicate(self.tcx); let obligation = traits::Obligation::new(cause, self.param_env, predicate); if !self.predicate_may_hold(&obligation) { result = ProbeResult::NoMatch; if self.probe(|_| { match self.select_trait_candidate(trait_ref) { Err(_) => return true, - Ok(Some(vtable)) - if !vtable.borrow_nested_obligations().is_empty() => + Ok(Some(impl_source)) + if !impl_source.borrow_nested_obligations().is_empty() => { - for obligation in vtable.borrow_nested_obligations() { + for obligation in impl_source.borrow_nested_obligations() { // Determine exactly which obligation wasn't met, so // that we can give more context in the error. if !self.predicate_may_hold(&obligation) { @@ -1410,17 +1422,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; @@ -1462,7 +1468,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// /// ``` /// trait Foo { ... } - /// impl Foo for Vec { ... } + /// impl Foo for Vec { ... } /// impl Foo for Vec { ... } /// ``` /// @@ -1513,7 +1519,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 +1528,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 +1556,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::Type => false, + ty::AssocKind::Fn | ty::AssocKind::Const => true, }, } // FIXME -- check for types that deref to `Self`, @@ -1578,7 +1580,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 +1616,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 ef779cfa83431..67bdd04d3715c 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -2,12 +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::hir::map::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}; @@ -15,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; @@ -58,7 +58,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, self.body_id, self.param_env, - poly_trait_ref.without_const().to_predicate(), + poly_trait_ref.without_const().to_predicate(tcx), ); self.predicate_may_hold(&obligation) }) @@ -71,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>]>, @@ -116,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(), @@ -148,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 { @@ -158,10 +158,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let path = self.tcx.def_path_str(trait_ref.def_id); let ty = match item.kind { - ty::AssocKind::Const - | ty::AssocKind::Type - | ty::AssocKind::OpaqueTy => rcvr_ty, - ty::AssocKind::Method => self + ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty, + ty::AssocKind::Fn => self .tcx .fn_sig(item.def_id) .inputs() @@ -178,6 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path, ty, item.kind, + item.def_id, sugg_span, idx, self.tcx.sess.source_map(), @@ -190,8 +189,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 `{}`", @@ -216,6 +218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path, rcvr_ty, item.kind, + item.def_id, sugg_span, idx, self.tcx.sess.source_map(), @@ -267,11 +270,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, @@ -398,7 +425,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!( @@ -428,7 +455,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); if let Some((field, field_ty)) = field_receiver { - let scope = self.tcx.parent_module(self.body_id); + let scope = self.tcx.parent_module(self.body_id).to_def_id(); let is_accessible = field.vis.is_accessible_from(scope, self.tcx); if is_accessible { @@ -509,7 +536,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. @@ -538,41 +565,35 @@ 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 = |self_ty: Ty<'_>, parent_pred: &ty::Predicate<'_>, obligation: &str| { - if let (ty::Param(_), ty::Predicate::Trait(p, _)) = - (&self_ty.kind, parent_pred) + if let (ty::Param(_), ty::PredicateKind::Trait(p, _)) = + (&self_ty.kind, parent_pred.kind()) { 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()); } - _ => {} } } } @@ -603,9 +624,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } }; - let mut format_pred = |pred| { - match pred { - ty::Predicate::Projection(pred) => { + let mut format_pred = |pred: ty::Predicate<'tcx>| { + match pred.kind() { + ty::PredicateKind::Projection(pred) => { // `::Item = String`. let trait_ref = pred.skip_binder().projection_ty.trait_ref(self.tcx); @@ -623,7 +644,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_span_label(trait_ref.self_ty(), &obligation, &quiet); Some((obligation, trait_ref.self_ty())) } - ty::Predicate::Trait(poly_trait_ref, _) => { + ty::PredicateKind::Trait(poly_trait_ref, _) => { let p = poly_trait_ref.skip_binder().trait_ref; let self_ty = p.self_ty(); let path = p.print_only_trait_path(); @@ -740,7 +761,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!( @@ -759,25 +780,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { MethodError::Ambiguity(sources) => { let mut err = struct_span_err!( self.sess(), - span, + item_name.span, E0034, "multiple applicable items in scope" ); - err.span_label(span, format!("multiple `{}` found", item_name)); + err.span_label(item_name.span, format!("multiple `{}` found", item_name)); report_candidates(span, &mut err, sources, sugg_span); err.emit(); } MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { + let kind = kind.descr(def_id); let mut err = struct_span_err!( self.tcx.sess, - span, + item_name.span, E0624, "{} `{}` is private", - kind.descr(def_id), + kind, item_name ); + err.span_label(item_name.span, &format!("private {}", kind)); self.suggest_valid_traits(&mut err, out_of_scope_traits); err.emit(); } @@ -829,7 +852,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).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 { @@ -897,7 +920,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>)], @@ -921,45 +944,41 @@ 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.kind() { // 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::PredicateKind::Trait(t, _) => t.def_id() == info.def_id, + ty::PredicateKind::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), .. })) = id.map(|id| self.tcx.hir().get(id)) { let self_first_arg = match method { - hir::TraitMethod::Required([ident, ..]) => { + hir::TraitFn::Required([ident, ..]) => { ident.name == kw::SelfLower } - hir::TraitMethod::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, - } + hir::TraitFn::Provided(body_id) => { + 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, }; @@ -1019,12 +1038,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Obtain the span for `param` and use it for a structured suggestion. let mut suggested = false; if let (Some(ref param), Some(ref table)) = (param_type, self.in_progress_tables) { - let table = table.borrow(); - if let Some(did) = table.local_id_root { - let generics = self.tcx.generics_of(did); + let table_owner = table.borrow().hir_owner; + if let Some(table_owner) = table_owner { + 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. @@ -1056,7 +1076,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( @@ -1116,7 +1136,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), @@ -1230,7 +1250,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()); } _ => (), } @@ -1253,7 +1273,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) => { @@ -1347,7 +1367,7 @@ impl intravisit::Visitor<'tcx> for UsePlacementFinder<'tcx> { } } - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { intravisit::NestedVisitorMap::None @@ -1355,18 +1375,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() { @@ -1390,7 +1411,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 d0d421746ae8f..0325782e69d51 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -31,9 +31,6 @@ can be broken down into several distinct phases: final assignments of the various region variables if there is some flexibility. -- vtable: find and records the impls to use for each trait bound that - appears on a type parameter. - - writeback: writes the final types within a function body, replacing type variables with their final inferred types. These final types are written into the `tcx.node_types` table, which should *never* contain @@ -82,56 +79,67 @@ pub mod intrinsic; pub mod method; mod op; mod pat; +mod place_op; mod regionck; mod upvar; mod wfcheck; pub mod writeback; -use crate::astconv::{AstConv, GenericArgCountMismatch, PathSeg}; -use crate::middle::lang_items; -use rustc::hir::map::blocks::FnLikeNode; -use rustc::hir::map::Map; -use rustc::middle::region; -use rustc::mir::interpret::ConstValue; -use rustc::session::parse::feature_err; -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 crate::astconv::{ + AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg, }; use rustc_ast::ast; use rustc_ast::util::parser::ExprPrecedence; use rustc_attr as attr; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::ErrorReported; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::lang_items::{ + FutureTraitLangItem, PinTypeLangItem, SizedTraitLangItem, VaListTypeLangItem, +}; use rustc_hir::{ExprKind, GenericArg, HirIdMap, Item, ItemKind, Node, PatKind, QPath}; +use rustc_index::bit_set::BitSet; use rustc_index::vec::Idx; +use rustc_infer::infer; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_infer::infer::{self, InferCtxt, InferOk, InferResult, TyCtxtInferExt}; +use rustc_infer::infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TyCtxtInferExt}; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, +}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::subst::{GenericArgKind, UserSelfTy, UserSubsts}; +use rustc_middle::ty::util::{Discr, IntTypeExt, Representability}; +use rustc_middle::ty::{ + self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef, + ToPredicate, Ty, TyCtxt, UserType, WithConstness, +}; +use rustc_session::config::{self, EntryFnType}; +use rustc_session::lint; +use rustc_session::parse::feature_err; +use rustc_session::Session; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{original_sp, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{self, BytePos, MultiSpan, Span}; +use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::opaque_types::{InferCtxtExt as _, OpaqueTypeDecl}; use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; +use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ @@ -146,14 +154,9 @@ use std::mem::replace; use std::ops::{self, Deref}; use std::slice; -use crate::lint; use crate::require_c_abi_if_c_variadic; -use crate::session::config::EntryFnType; -use crate::session::Session; -use crate::util::common::{indenter, ErrorReported}; -use crate::TypeAndSubsts; +use crate::util::common::indenter; -use self::autoderef::Autoderef; use self::callee::DeferredCallResolution; use self::coercion::{CoerceMany, DynamicCoerceMany}; use self::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; @@ -634,20 +637,15 @@ impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { /// `F: for<'b, 'tcx> where 'tcx FnOnce(Inherited<'b, 'tcx>)`. pub struct InheritedBuilder<'tcx> { infcx: infer::InferCtxtBuilder<'tcx>, - def_id: DefId, + def_id: LocalDefId, } impl Inherited<'_, 'tcx> { - pub fn build(tcx: TyCtxt<'tcx>, def_id: DefId) -> InheritedBuilder<'tcx> { - let hir_id_root = if def_id.is_local() { - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - DefId::local(hir_id.owner) - } else { - def_id - }; + pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { + let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; InheritedBuilder { - infcx: tcx.infer_ctxt().with_fresh_in_progress_tables(hir_id_root), + infcx: tcx.infer_ctxt().with_fresh_in_progress_tables(hir_owner), def_id, } } @@ -664,17 +662,10 @@ impl<'tcx> InheritedBuilder<'tcx> { } impl Inherited<'a, 'tcx> { - fn new(infcx: InferCtxt<'a, 'tcx>, def_id: DefId) -> Self { + fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { let tcx = infcx.tcx; - let item_id = tcx.hir().as_local_hir_id(def_id); - let body_id = item_id.and_then(|id| tcx.hir().maybe_body_owned_by(id)); - let implicit_region_bound = body_id.map(|body_id| { - let body = tcx.hir().body(body_id); - tcx.mk_region(ty::ReScope(region::Scope { - id: body.value.hir_id.local_id, - data: region::ScopeData::CallSite, - })) - }); + let item_id = tcx.hir().local_def_id_to_hir_id(def_id); + let body_id = tcx.hir().maybe_body_owned_by(item_id); Inherited { tables: MaybeInProgressTables { maybe_tables: infcx.in_progress_tables }, @@ -687,7 +678,7 @@ impl Inherited<'a, 'tcx> { deferred_generator_interiors: RefCell::new(Vec::new()), opaque_types: RefCell::new(Default::default()), opaque_types_vars: RefCell::new(Default::default()), - implicit_region_bound, + implicit_region_bound: None, body_id, } } @@ -757,15 +748,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); } @@ -816,14 +807,14 @@ fn primary_body_of( }, Node::TraitItem(item) => match item.kind { hir::TraitItemKind::Const(ref ty, Some(body)) => Some((body, Some(ty), None, None)), - hir::TraitItemKind::Fn(ref sig, hir::TraitMethod::Provided(body)) => { + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { Some((body, None, Some(&sig.header), Some(&sig.decl))) } _ => None, }, Node::ImplItem(item) => match item.kind { hir::ImplItemKind::Const(ref ty, body) => Some((body, Some(ty), None, None)), - hir::ImplItemKind::Method(ref sig, body) => { + hir::ImplItemKind::Fn(ref sig, body) => { Some((body, None, Some(&sig.header), Some(&sig.decl))) } _ => None, @@ -841,14 +832,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) -> &FxHashSet { &*tcx.typeck_tables_of(def_id).used_trait_imports } @@ -963,8 +955,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) } @@ -972,30 +964,28 @@ 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()); - tcx.sess.delay_span_bug(span, "diagnostic only typeck table used"); - tcx.types.err + let span = tcx.hir().span(tcx.hir().as_local_hir_id(def_id)); + tcx.ty_error_with_message(span, "diagnostic only typeck table used") }; typeck_tables_of_with_fallback(tcx, def_id, fallback) } 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. @@ -1009,7 +999,14 @@ fn typeck_tables_of_with_fallback<'tcx>( let fcx = if let (Some(header), Some(decl)) = (fn_header, fn_decl) { let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - AstConv::ty_of_fn(&fcx, header.unsafety, header.abi, decl, &[], None) + AstConv::ty_of_fn( + &fcx, + header.unsafety, + header.abi, + decl, + &hir::Generics::empty(), + None, + ) } else { tcx.fn_sig(def_id) }; @@ -1017,7 +1014,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, @@ -1049,7 +1046,7 @@ fn typeck_tables_of_with_fallback<'tcx>( // Gather locals in statics (because of block expressions). GatherLocalsVisitor { fcx: &fcx, parent_id: id }.visit_body(body); - fcx.check_expr_coercable_to_type(&body.value, revealed_ty); + fcx.check_expr_coercable_to_type(&body.value, revealed_ty, None); fcx.write_ty(id, revealed_ty); @@ -1109,7 +1106,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); @@ -1129,7 +1126,7 @@ fn typeck_tables_of_with_fallback<'tcx>( // Consistency check our TypeckTables instance can hold all ItemLocalIds // it will need to hold. - assert_eq!(tables.local_id_root, Some(DefId::local(id.owner))); + assert_eq!(tables.hir_owner, Some(id.owner)); tables } @@ -1156,7 +1153,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, @@ -1168,7 +1165,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 } @@ -1177,7 +1174,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -1239,7 +1236,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>, @@ -1287,7 +1284,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); @@ -1297,7 +1294,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); @@ -1325,21 +1321,16 @@ 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 region = tcx.mk_region(ty::ReScope(region::Scope { - id: body.value.hir_id.local_id, - data: region::ScopeData::CallSite, - })); + let span = body.params.last().unwrap().span; + let va_list_did = tcx.require_lang_item(VaListTypeLangItem, Some(span)); + let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); Some(tcx.type_of(va_list_did).subst(tcx, &[region.into()])) } else { @@ -1366,7 +1357,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. @@ -1423,7 +1432,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); @@ -1437,7 +1446,7 @@ fn check_fn<'a, 'tcx>( inherited.register_predicate(traits::Obligation::new( cause, param_env, - trait_ref.without_const().to_predicate(), + trait_ref.without_const().to_predicate(tcx), )); } } @@ -1445,7 +1454,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 `!`"); @@ -1478,7 +1487,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 { @@ -1489,7 +1498,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 `!`"); @@ -1519,7 +1528,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 { @@ -1541,8 +1550,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) { @@ -1550,14 +1559,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()); @@ -1582,14 +1591,14 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: DefId) -> bool { } else { span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind); } - return true; + true } /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` /// projections that would result in "inheriting lifetimes". fn check_opaque<'tcx>( tcx: TyCtxt<'tcx>, - def_id: DefId, + def_id: LocalDefId, substs: SubstsRef<'tcx>, span: Span, origin: &hir::OpaqueTyOrigin, @@ -1600,9 +1609,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 @@ -1612,12 +1620,17 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: DefId, span: struct ProhibitOpaqueVisitor<'tcx> { opaque_identity_ty: Ty<'tcx>, generics: &'tcx ty::Generics, + ty: Option>, }; impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t); - if t == self.opaque_identity_ty { false } else { t.super_visit_with(self) } + if t != self.opaque_identity_ty && t.super_visit_with(self) { + self.ty = Some(t); + return true; + } + false } fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { @@ -1628,68 +1641,259 @@ 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, .. }) => { - let mut visitor = ProhibitOpaqueVisitor { - opaque_identity_ty: tcx - .mk_opaque(def_id, InternalSubsts::identity_for_item(tcx, def_id)), - generics: tcx.generics_of(def_id), + if let ItemKind::OpaqueTy(hir::OpaqueTy { + origin: hir::OpaqueTyOrigin::AsyncFn | hir::OpaqueTyOrigin::FnReturn, + .. + }) = item.kind + { + let mut visitor = ProhibitOpaqueVisitor { + opaque_identity_ty: tcx.mk_opaque( + def_id.to_def_id(), + InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), + ), + generics: tcx.generics_of(def_id), + ty: None, + }; + let prohibit_opaque = tcx + .predicates_of(def_id) + .predicates + .iter() + .any(|(predicate, _)| predicate.visit_with(&mut visitor)); + debug!( + "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor={:?}", + prohibit_opaque, visitor + ); + + if prohibit_opaque { + let is_async = match item.kind { + ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin { + hir::OpaqueTyOrigin::AsyncFn => true, + _ => false, + }, + _ => unreachable!(), }; - debug!("check_opaque_for_inheriting_lifetimes: visitor={:?}", visitor); - tcx.predicates_of(def_id) - .predicates - .iter() - .any(|(predicate, _)| predicate.visit_with(&mut visitor)) + let mut err = struct_span_err!( + tcx.sess, + span, + E0760, + "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ + a parent scope", + if is_async { "async fn" } else { "impl Trait" }, + ); + + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) { + if snippet == "Self" { + if let Some(ty) = visitor.ty { + err.span_suggestion( + span, + "consider spelling out the type instead", + format!("{:?}", ty), + Applicability::MaybeIncorrect, + ); + } + } + } + err.emit(); } - _ => false, - }; + } +} - debug!("check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}", prohibit_opaque); - if prohibit_opaque { - let is_async = match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => match origin { - hir::OpaqueTyOrigin::AsyncFn => true, - _ => false, - }, - _ => unreachable!(), - }; +/// Given a `DefId` for an opaque type in return position, find its parent item's return +/// expressions. +fn get_owner_return_paths( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Option<(hir::HirId, ReturnsVisitor<'tcx>)> { + let hir_id = tcx.hir().as_local_hir_id(def_id); + let id = tcx.hir().get_parent_item(hir_id); + tcx.hir() + .find(id) + .map(|n| (id, n)) + .and_then(|(hir_id, node)| node.body_id().map(|b| (hir_id, b))) + .map(|(hir_id, body_id)| { + let body = tcx.hir().body(body_id); + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(body); + (hir_id, visitor) + }) +} - 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" }, - )); +/// Emit an error for recursive opaque types. +/// +/// If this is a return `impl Trait`, find the item's return expressions and point at them. For +/// direct recursion this is enough, but for indirect recursion also point at the last intermediary +/// `impl Trait`. +/// +/// If all the return expressions evaluate to `!`, then we explain that the error will go away +/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. +fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { + let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); + + let mut label = false; + if let Some((hir_id, visitor)) = get_owner_return_paths(tcx, def_id) { + let tables = tcx.typeck_tables_of(tcx.hir().local_def_id(hir_id)); + if visitor + .returns + .iter() + .filter_map(|expr| tables.node_type_opt(expr.hir_id)) + .all(|ty| matches!(ty.kind, ty::Never)) + { + let spans = visitor + .returns + .iter() + .filter(|expr| tables.node_type_opt(expr.hir_id).is_some()) + .map(|expr| expr.span) + .collect::>(); + let span_len = spans.len(); + if span_len == 1 { + err.span_label(spans[0], "this returned value is of `!` type"); + } else { + let mut multispan: MultiSpan = spans.clone().into(); + for span in spans { + multispan + .push_span_label(span, "this returned value is of `!` type".to_string()); + } + err.span_note(multispan, "these returned values have a concrete \"never\" type"); + } + err.help("this error will resolve once the item's body returns a concrete type"); + } else { + let mut seen = FxHashSet::default(); + seen.insert(span); + err.span_label(span, "recursive opaque type"); + label = true; + for (sp, ty) in visitor + .returns + .iter() + .filter_map(|e| tables.node_type_opt(e.hir_id).map(|t| (e.span, t))) + .filter(|(_, ty)| !matches!(ty.kind, ty::Never)) + { + struct VisitTypes(Vec); + impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes { + fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + match t.kind { + ty::Opaque(def, _) => { + self.0.push(def); + false + } + _ => t.super_visit_with(self), + } + } + } + let mut visitor = VisitTypes(vec![]); + ty.visit_with(&mut visitor); + for def_id in visitor.0 { + let ty_span = tcx.def_span(def_id); + if !seen.contains(&ty_span) { + err.span_label(ty_span, &format!("returning this opaque type `{}`", ty)); + seen.insert(ty_span); + } + err.span_label(sp, &format!("returning here with type `{}`", ty)); + } + } + } } + if !label { + err.span_label(span, "cannot resolve opaque type"); + } + err.emit(); +} + +/// Emit an error for recursive opaque types in a `let` binding. +fn binding_opaque_type_cycle_error( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + span: Span, + partially_expanded_type: Ty<'tcx>, +) { + let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); + err.span_label(span, "cannot resolve opaque type"); + // Find the the owner that declared this `impl Trait` type. + let hir_id = tcx.hir().as_local_hir_id(def_id); + let mut prev_hir_id = hir_id; + let mut hir_id = tcx.hir().get_parent_node(hir_id); + while let Some(node) = tcx.hir().find(hir_id) { + match node { + hir::Node::Local(hir::Local { + pat, + init: None, + ty: Some(ty), + source: hir::LocalSource::Normal, + .. + }) => { + err.span_label(pat.span, "this binding might not have a concrete type"); + err.span_suggestion_verbose( + ty.span.shrink_to_hi(), + "set the binding to a value for a concrete type to be resolved", + " = /* value */".to_string(), + Applicability::HasPlaceholders, + ); + } + hir::Node::Local(hir::Local { + init: Some(expr), + source: hir::LocalSource::Normal, + .. + }) => { + let hir_id = tcx.hir().as_local_hir_id(def_id); + let tables = + tcx.typeck_tables_of(tcx.hir().local_def_id(tcx.hir().get_parent_item(hir_id))); + if let Some(ty) = tables.node_type_opt(expr.hir_id) { + err.span_label( + expr.span, + &format!( + "this is of type `{}`, which doesn't constrain \ + `{}` enough to arrive to a concrete type", + ty, partially_expanded_type + ), + ); + } + } + _ => {} + } + if prev_hir_id == hir_id { + break; + } + prev_hir_id = hir_id; + hir_id = tcx.hir().get_parent_node(hir_id); + } + err.emit(); +} + +fn async_opaque_type_cycle_error(tcx: TyCtxt<'tcx>, span: Span) { + struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing") + .span_label(span, "recursive `async fn`") + .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") + .emit(); } /// Checks that an opaque type does not contain cycles. fn check_opaque_for_cycles<'tcx>( tcx: TyCtxt<'tcx>, - def_id: 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 hir::OpaqueTyOrigin::AsyncFn = origin { - struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing",) - .span_label(span, "recursive `async fn`") - .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") - .emit(); - } else { - let mut err = - struct_span_err!(tcx.sess, span, E0720, "opaque type expands to a recursive type",); - err.span_label(span, "expands to a recursive type"); - if let ty::Opaque(..) = partially_expanded_type.kind { - err.note("type resolves to itself"); - } else { - err.note(&format!("expanded type is `{}`", partially_expanded_type)); + if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs) + { + match origin { + hir::OpaqueTyOrigin::AsyncFn => async_opaque_type_cycle_error(tcx, span), + hir::OpaqueTyOrigin::Binding => { + binding_opaque_type_cycle_error(tcx, def_id, span, partially_expanded_type) } - err.emit(); + _ => opaque_type_cycle_error(tcx, def_id, span), } } } @@ -1706,18 +1910,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); @@ -1734,7 +1938,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); @@ -1753,14 +1957,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); @@ -1816,7 +2020,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; @@ -1836,12 +2040,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); } } @@ -1853,7 +2057,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( @@ -1896,13 +2100,15 @@ fn check_specialization_validity<'tcx>( ) { let kind = match impl_item.kind { hir::ImplItemKind::Const(..) => ty::AssocKind::Const, - hir::ImplItemKind::Method(..) => ty::AssocKind::Method, - hir::ImplItemKind::OpaqueTy(..) => ty::AssocKind::OpaqueTy, + hir::ImplItemKind::Fn(..) => ty::AssocKind::Fn, hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type, }; - let mut ancestor_impls = trait_def - .ancestors(tcx, impl_id) + let ancestors = match trait_def.ancestors(tcx, impl_id) { + Ok(ancestors) => ancestors, + Err(_) => return, + }; + let mut ancestor_impls = ancestors .skip(1) .filter_map(|parent| { if parent.is_from_trait() { @@ -1933,7 +2139,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())) @@ -1954,11 +2160,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` @@ -1967,6 +2173,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); @@ -2006,7 +2230,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() ); @@ -2019,9 +2243,9 @@ fn check_impl_items_against_trait<'tcx>( err.emit() } } - hir::ImplItemKind::Method(..) => { + 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, @@ -2047,7 +2271,7 @@ fn check_impl_items_against_trait<'tcx>( err.emit() } } - hir::ImplItemKind::OpaqueTy(..) | hir::ImplItemKind::TyAlias(_) => { + hir::ImplItemKind::TyAlias(_) => { let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); if ty_trait_item.kind == ty::AssocKind::Type { compare_ty_impl( @@ -2057,7 +2281,7 @@ fn check_impl_items_against_trait<'tcx>( &ty_trait_item, impl_trait_ref, opt_trait_span, - ) + ); } else { let mut err = struct_span_err!( tcx.sess, @@ -2077,22 +2301,29 @@ 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(); - for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { - let is_implemented = trait_def - .ancestors(tcx, impl_id) - .leaf_def(tcx, trait_item.ident, trait_item.kind) - .map(|node_item| !node_item.node.is_from_trait()) - .unwrap_or(false); - - if !is_implemented && !traits::impl_is_default(tcx, impl_id) { - if !trait_item.defaultness.has_value() { - missing_items.push(*trait_item); + if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { + for trait_item in tcx.associated_items(impl_trait_ref.def_id).in_definition_order() { + let is_implemented = ancestors + .leaf_def(tcx, trait_item.ident, trait_item.kind) + .map(|node_item| !node_item.defining_node.is_from_trait()) + .unwrap_or(false); + + if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { + if !trait_item.defaultness.has_value() { + missing_items.push(*trait_item); + } } } } @@ -2157,8 +2388,8 @@ fn bounds_from_generic_predicates( let mut projections = vec![]; for (predicate, _) in predicates.predicates { debug!("predicate {:?}", predicate); - match predicate { - ty::Predicate::Trait(trait_predicate, _) => { + match predicate.kind() { + ty::PredicateKind::Trait(trait_predicate, _) => { let entry = types.entry(trait_predicate.skip_binder().self_ty()).or_default(); let def_id = trait_predicate.skip_binder().def_id(); if Some(def_id) != tcx.lang_items().sized_trait() { @@ -2167,7 +2398,7 @@ fn bounds_from_generic_predicates( entry.push(trait_predicate.skip_binder().def_id()); } } - ty::Predicate::Projection(projection_pred) => { + ty::PredicateKind::Projection(projection_pred) => { projections.push(projection_pred); } _ => {} @@ -2218,26 +2449,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 })) @@ -2266,7 +2510,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 @@ -2276,11 +2520,10 @@ 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), - // FIXME(type_alias_impl_trait): we should print bounds here too. - ty::AssocKind::OpaqueTy => format!("type {} = Type;", assoc.ident), ty::AssocKind::Const => { let ty = tcx.type_of(assoc.def_id); let val = expr::ty_kind_suggestion(ty).unwrap_or("value"); @@ -2292,7 +2535,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 @@ -2302,19 +2545,15 @@ 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); - for span in spans { - err.span_label(span, "recursive without indirection"); - } - err.emit(); + recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans); return false; } Representability::Representable | Representability::ContainsRecursive => (), } - return true; + 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() { @@ -2348,10 +2587,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 { @@ -2377,7 +2616,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, @@ -2386,34 +2625,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; } } } @@ -2498,12 +2735,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( @@ -2516,7 +2752,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; @@ -2568,7 +2804,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, @@ -2596,7 +2832,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)); } } @@ -2623,7 +2859,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), @@ -2648,17 +2884,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(); } @@ -2684,7 +2920,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); @@ -2692,8 +2928,8 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { ty::GenericPredicates { parent: None, predicates: tcx.arena.alloc_from_iter(self.param_env.caller_bounds.iter().filter_map( - |&predicate| match predicate { - ty::Predicate::Trait(ref data, _) + |predicate| match predicate.kind() { + ty::PredicateKind::Trait(ref data, _) if data.skip_binder().self_ty().is_param(index) => { // HACK(eddyb) should get the original `Span`. @@ -3097,6 +3333,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } + let autoborrow_mut = adj.iter().any(|adj| { + matches!(adj, &Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), + .. + }) + }); + match self.tables.borrow_mut().adjustments_mut().entry(expr.hir_id) { Entry::Vacant(entry) => { entry.insert(adj); @@ -3126,6 +3369,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { *entry.get_mut() = adj; } } + + // If there is an mutable auto-borrow, it is equivalent to `&mut `. + // In this case implicit use of `Deref` and `Index` within `` should + // instead be `DerefMut` and `IndexMut`, so fix those up. + if autoborrow_mut { + self.convert_place_derefs_to_mutable(expr); + } } /// Basically whenever we are converting from a type scheme into @@ -3177,7 +3427,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, @@ -3229,7 +3479,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); } } @@ -3264,7 +3514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { let t = AstConv::ast_ty_to_ty(self, ast_t); - self.register_wf_obligation(t, ast_t.span, traits::MiscObligation); + self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation); t } @@ -3281,13 +3531,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } - /// Returns the `DefId` of the constant parameter that the provided expression is a path to. - pub fn const_param_def_id(&self, hir_c: &hir::AnonConst) -> Option { - AstConv::const_param_def_id(self, &self.tcx.hir().body(hir_c.body).value) - } - - pub fn to_const(&self, ast_c: &hir::AnonConst, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> { - AstConv::ast_const_to_const(self, ast_c, ty) + pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> { + let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); + let c = ty::Const::from_anon_const(self.tcx, const_def_id); + self.register_wf_obligation( + c.into(), + self.tcx.hir().span(ast_c.hir_id), + ObligationCauseCode::MiscObligation, + ); + c } // If the type given by the user has free regions, save it for later, since @@ -3307,7 +3559,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { match self.tables.borrow().node_types().get(id) { Some(&t) => t, - None if self.is_tainted_by_errors() => self.tcx.types.err, + None if self.is_tainted_by_errors() => self.tcx.ty_error(), None => { bug!( "no type for node {}: {} in fcx {}", @@ -3319,11 +3571,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// Registers an obligation for checking later, during regionck, that the type `ty` must - /// outlive the region `r`. + /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. pub fn register_wf_obligation( &self, - ty: Ty<'tcx>, + arg: subst::GenericArg<'tcx>, span: Span, code: traits::ObligationCauseCode<'tcx>, ) { @@ -3332,16 +3583,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicate(traits::Obligation::new( cause, self.param_env, - ty::Predicate::WellFormed(ty), + ty::PredicateKind::WellFormed(arg).to_predicate(self.tcx), )); } - /// Registers obligations that all types appearing in `substs` are well-formed. + /// Registers obligations that all `substs` are well-formed. pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) { - for ty in substs.types() { - if !ty.references_error() { - self.register_wf_obligation(ty, expr.span, traits::MiscObligation); - } + for arg in substs.iter().filter(|arg| { + matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) + }) { + self.register_wf_obligation(arg, expr.span, traits::MiscObligation); } } @@ -3366,7 +3617,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()); @@ -3417,12 +3668,12 @@ 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) { - _ if self.is_tainted_by_errors() => self.tcx().types.err, + _ if self.is_tainted_by_errors() => self.tcx().ty_error(), UnconstrainedInt => self.tcx.types.i32, UnconstrainedFloat => self.tcx.types.f64, Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(), @@ -3516,156 +3767,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ret_ty.builtin_deref(true).unwrap() } - fn lookup_indexing( - &self, - expr: &hir::Expr<'_>, - base_expr: &'tcx hir::Expr<'tcx>, - base_ty: Ty<'tcx>, - idx_ty: Ty<'tcx>, - needs: Needs, - ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { - // FIXME(#18741) -- this is almost but not quite the same as the - // autoderef that normal method probing does. They could likely be - // consolidated. - - let mut autoderef = self.autoderef(base_expr.span, base_ty); - let mut result = None; - while result.is_none() && autoderef.next().is_some() { - result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty); - } - autoderef.finalize(self); - result - } - - /// To type-check `base_expr[index_expr]`, we progressively autoderef - /// (and otherwise adjust) `base_expr`, looking for a type which either - /// supports builtin indexing or overloaded indexing. - /// This loop implements one step in that search; the autoderef loop - /// is implemented by `lookup_indexing`. - fn try_index_step( - &self, - expr: &hir::Expr<'_>, - base_expr: &hir::Expr<'_>, - autoderef: &Autoderef<'a, 'tcx>, - needs: Needs, - index_ty: Ty<'tcx>, - ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { - let adjusted_ty = autoderef.unambiguous_final_ty(self); - debug!( - "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ - index_ty={:?})", - expr, base_expr, adjusted_ty, index_ty - ); - - for &unsize in &[false, true] { - let mut self_ty = adjusted_ty; - if unsize { - // We only unsize arrays here. - if let ty::Array(element_ty, _) = adjusted_ty.kind { - self_ty = self.tcx.mk_slice(element_ty); - } else { - continue; - } - } - - // If some lookup succeeds, write callee into table and extract index/element - // type from the method signature. - // If some lookup succeeded, install method in table - let input_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::AutoDeref, - span: base_expr.span, - }); - let method = self.try_overloaded_place_op( - expr.span, - self_ty, - &[input_ty], - needs, - PlaceOp::Index, - ); - - let result = method.map(|ok| { - debug!("try_index_step: success, using overloaded indexing"); - let method = self.register_infer_ok_obligations(ok); - - let mut adjustments = autoderef.adjust_steps(self, needs); - if let ty::Ref(region, _, r_mutbl) = method.sig.inputs()[0].kind { - let mutbl = match r_mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Indexing can be desugared to a method call, - // so maybe we could use two-phase here. - // See the documentation of AllowTwoPhase for why that's - // not the case today. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), - target: self - .tcx - .mk_ref(region, ty::TypeAndMut { mutbl: r_mutbl, ty: adjusted_ty }), - }); - } - if unsize { - adjustments.push(Adjustment { - kind: Adjust::Pointer(PointerCast::Unsize), - target: method.sig.inputs()[0], - }); - } - self.apply_adjustments(base_expr, adjustments); - - self.write_method_call(expr.hir_id, method); - (input_ty, self.make_overloaded_place_return_type(method).ty) - }); - if result.is_some() { - return result; - } - } - - None - } - - fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option, ast::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)) - } - - fn try_overloaded_place_op( - &self, - span: Span, - base_ty: Ty<'tcx>, - arg_tys: &[Ty<'tcx>], - needs: Needs, - op: PlaceOp, - ) -> Option>> { - debug!("try_overloaded_place_op({:?},{:?},{:?},{:?})", span, base_ty, needs, op); - - // Try Mut first, if needed. - let (mut_tr, mut_op) = self.resolve_place_op(op, true); - let method = match (needs, mut_tr) { - (Needs::MutPlace, Some(trait_did)) => { - self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys)) - } - _ => None, - }; - - // Otherwise, fall back to the immutable version. - let (imm_tr, imm_op) = self.resolve_place_op(op, false); - let method = match (method, imm_tr) { - (None, Some(trait_did)) => { - self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys)) - } - (method, _) => method, - }; - - method - } - fn check_method_argument_types( &self, sp: Span, @@ -3697,7 +3798,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, None, ); - return self.tcx.types.err; + return self.tcx.ty_error(); } let method = method.unwrap(); @@ -3726,7 +3827,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { trait_ref: ty::PolyTraitRef<'tcx>, expected_vid: ty::TyVid, ) -> bool { - let self_ty = self.shallow_resolve(trait_ref.self_ty()); + let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); debug!( "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})", trait_ref, self_ty, expected_vid @@ -3763,17 +3864,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .borrow() .pending_obligations() .into_iter() - .filter_map(move |obligation| match obligation.predicate { - ty::Predicate::Projection(ref data) => { + .filter_map(move |obligation| match obligation.predicate.kind() { + ty::PredicateKind::Projection(ref data) => { Some((data.to_poly_trait_ref(self.tcx), obligation)) } - ty::Predicate::Trait(ref data, _) => Some((data.to_poly_trait_ref(), obligation)), - ty::Predicate::Subtype(..) => None, - ty::Predicate::RegionOutlives(..) => None, - ty::Predicate::TypeOutlives(..) => None, - ty::Predicate::WellFormed(..) => None, - ty::Predicate::ObjectSafe(..) => None, - ty::Predicate::ConstEvaluatable(..) => None, + ty::PredicateKind::Trait(ref data, _) => { + Some((data.to_poly_trait_ref(), obligation)) + } + ty::PredicateKind::Subtype(..) => None, + ty::PredicateKind::RegionOutlives(..) => None, + ty::PredicateKind::TypeOutlives(..) => None, + ty::PredicateKind::WellFormed(..) => None, + ty::PredicateKind::ObjectSafe(..) => None, + ty::PredicateKind::ConstEvaluatable(..) => None, + ty::PredicateKind::ConstEquate(..) => None, // N.B., this predicate is created by breaking down a // `ClosureType: FnFoo()` predicate, where // `ClosureType` represents some `Closure`. It can't @@ -3782,7 +3886,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // this closure yet; this is exactly why the other // code is looking for a self type of a unresolved // inference variable. - ty::Predicate::ClosureKind(..) => None, + ty::PredicateKind::ClosureKind(..) => None, }) .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) } @@ -3812,8 +3916,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // All the input types from the fn signature must outlive the call // so as to validate implied bounds. - for (fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) { - self.register_wf_obligation(fn_input_ty, arg_expr.span, traits::MiscObligation); + for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) { + self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); } let expected_arg_count = fn_inputs.len(); @@ -3825,7 +3929,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg_unit: bool| { let (span, start_span, args) = match &expr.kind { hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]), - hir::ExprKind::MethodCall(path_segment, span, args) => ( + hir::ExprKind::MethodCall(path_segment, span, args, _) => ( *span, // `sp` doesn't point at the whole `foo.bar()`, only at `bar`. path_segment @@ -3876,7 +3980,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 { @@ -3980,7 +4084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("check_closures={}", check_closures); // More awful hacks: before we check argument types, try to do - // an "opportunistic" vtable resolution of any trait bounds on + // an "opportunistic" trait resolution of any trait bounds on // the call. This helps coercions. if check_closures { self.select_obligations_where_possible(false, |errors| { @@ -4036,7 +4140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty); // We're processing function arguments so we definitely want to use // two-phase borrows. - self.demand_coerce(&arg, checked_ty, coerce_ty, AllowTwoPhase::Yes); + self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes); final_arg_types.push((i, checked_ty, coerce_ty)); // 3. Relate the expected type and the formal one, @@ -4063,10 +4167,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(..) => { @@ -4081,7 +4185,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn err_args(&self, len: usize) -> Vec> { - vec![self.tcx.types.err; len] + vec![self.tcx.ty_error(); len] } /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk @@ -4111,31 +4215,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } - if let ty::Predicate::Trait(predicate, _) = error.obligation.predicate { + if let ty::PredicateKind::Trait(predicate, _) = error.obligation.predicate.kind() { // Collect the argument position for all arguments that could have caused this // `FulfillmentError`. let mut referenced_in = final_arg_types .iter() - .map(|(i, checked_ty, _)| (i, checked_ty)) - .chain(final_arg_types.iter().map(|(i, _, coerced_ty)| (i, coerced_ty))) + .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()) { // We make sure that only *one* argument matches the obligation failure // and we assign the obligation's span to its expression's. - error.obligation.cause.span = args[ref_in].span; + error.obligation.cause.make_mut().span = args[ref_in].span; error.points_at_arg_span = true; } } @@ -4155,7 +4262,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::ExprKind::Path(qpath) = &path.kind { if let hir::QPath::Resolved(_, path) = &qpath { for error in errors { - if let ty::Predicate::Trait(predicate, _) = error.obligation.predicate { + if let ty::PredicateKind::Trait(predicate, _) = + error.obligation.predicate.kind() + { // If any of the type arguments in this path segment caused the // `FullfillmentError`, point at its span (#61860). for arg in path @@ -4176,7 +4285,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = AstConv::ast_ty_to_ty(self, hir_ty); let ty = self.resolve_vars_if_possible(&ty); if ty == predicate.skip_binder().self_ty() { - error.obligation.cause.span = hir_ty.span; + error.obligation.cause.make_mut().span = hir_ty.span; } } } @@ -4220,28 +4329,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_ty.unwrap_or_else(|| self.next_float_var()) } ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::Err(_) => tcx.types.err, + ast::LitKind::Err(_) => tcx.ty_error(), } } - // 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( @@ -4316,10 +4407,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)) @@ -4337,7 +4425,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 { @@ -4378,7 +4466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let result = AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true); - let ty = result.map(|(ty, _, _)| ty).unwrap_or(self.tcx().types.err); + let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); let result = result.map(|(_, kind, def_id)| (kind, def_id)); // Write back the new resolution. @@ -4422,15 +4510,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 }); @@ -4469,7 +4558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(init.span, local_ty, init_ty); init_ty } else { - self.check_expr_coercable_to_type(init, local_ty) + self.check_expr_coercable_to_type(init, local_ty, None) } } @@ -4505,7 +4594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, ) { if ty.references_error() { - // Override the types everywhere with `types.err` to avoid knock on errors. + // Override the types everywhere with `err()` to avoid knock on errors. self.write_ty(local.hir_id, ty); self.write_ty(local.pat.hir_id, ty); let local_ty = LocalTy { decl_ty, revealed_ty: ty }; @@ -4706,7 +4795,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label( fn_span, "implicitly returns `()` as its body has no tail or `return` \ - expression", + expression", ); } }, @@ -4725,7 +4814,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut ty = ctxt.coerce.unwrap().complete(self); if self.has_errors.get() || ty.references_error() { - ty = self.tcx.types.err + ty = self.tcx.ty_error() } self.write_ty(blk.hir_id, ty); @@ -4738,9 +4827,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id)); match node { Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) - | Node::ImplItem(&hir::ImplItem { - kind: hir::ImplItemKind::Method(_, body_id), .. - }) => { + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { let body = self.tcx.hir().body(body_id); if let ExprKind::Block(block, _) = &body.value.kind { return Some(block.span); @@ -4752,19 +4839,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 @@ -4779,7 +4860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) => Some((&sig.decl, ident, true)), Node::ImplItem(&hir::ImplItem { ident, - kind: hir::ImplItemKind::Method(ref sig, ..), + kind: hir::ImplItemKind::Fn(ref sig, ..), .. }) => Some((&sig.decl, ident, false)), _ => None, @@ -4837,18 +4918,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir = self.tcx.hir(); let (def_id, sig) = match found.kind { ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)), - ty::Closure(def_id, substs) => { - // We don't use `closure_sig` to account for malformed closures like - // `|_: [_; continue]| {}` and instead we don't suggest anything. - let closure_sig_ty = substs.as_closure().sig_ty(def_id, self.tcx); - ( - def_id, - match closure_sig_ty.kind { - ty::FnPtr(sig) => sig, - _ => return false, - }, - ) - } + ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()), _ => return false, }; @@ -4862,15 +4932,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::Method(_, body_id), - .. - })) - | Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitMethod::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 @@ -4912,11 +4983,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"; } _ => {} @@ -4939,7 +5010,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .join(", ") } Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitMethod::Required(idents)), + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), .. })) => { sugg_call = idents @@ -4956,39 +5027,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => {} } - if let Ok(code) = self.sess().source_map().span_to_snippet(expr.span) { - err.span_suggestion( - expr.span, - &format!("use parentheses to {}", msg), - format!("{}({})", code, sugg_call), - applicability, - ); - return true; - } + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + &format!("use parentheses to {}", msg), + format!("({})", sugg_call), + applicability, + ); + return true; } 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>, + expected_ty_expr: Option<&'tcx hir::Expr<'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) { + } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { let is_struct_pat_shorthand_field = self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span); - let methods = self.get_conversion_methods(expr.span, expected, found); + 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()) @@ -5039,7 +5109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) { - if self.tcx.hir().is_const_context(expr.hir_id) { + if self.tcx.hir().is_inside_const_context(expr.hir_id) { // Do not suggest `Box::new` in const context. return; } @@ -5076,7 +5146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> bool { // Handle #68197. - if self.tcx.hir().is_const_context(expr.hir_id) { + if self.tcx.hir().is_inside_const_context(expr.hir_id) { // Do not suggest `Box::new` in const context. return false; } @@ -5088,7 +5158,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), @@ -5235,6 +5305,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); @@ -5244,7 +5315,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) @@ -5252,22 +5323,26 @@ 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, - }, + ty::PredicateKind::Projection(ty::Binder::bind(ty::ProjectionPredicate { + projection_ty, ty: expected, - })); + })) + .to_predicate(self.tcx); let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); + debug!("suggest_missing_await: trying obligation {:?}", obligation); + if self.infcx.predicate_may_hold(&obligation) { debug!("suggest_missing_await: obligation held: {:?}", obligation); if let Ok(code) = self.sess().source_map().span_to_snippet(sp) { @@ -5287,6 +5362,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn note_need_for_fn_pointer( + &self, + err: &mut DiagnosticBuilder<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + let (sig, did, substs) = match (&expected.kind, &found.kind) { + (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { + let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); + let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); + if sig1 != sig2 { + return; + } + err.note( + "different `fn` items always have unique types, even if their signatures are \ + the same", + ); + (sig1, *did1, substs1) + } + (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { + let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs); + if sig1 != *sig2 { + return; + } + (sig1, *did, substs) + } + _ => return, + }; + err.help(&format!("change the expected type to be function pointer `{}`", sig)); + err.help(&format!( + "if the expected type is due to type inference, cast the expected `fn` to a function \ + pointer: `{} as {}`", + self.tcx.def_path_str_with_substs(did, substs), + sig + )); + } + /// A common error is to add an extra semicolon: /// /// ``` @@ -5327,7 +5439,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); @@ -5369,12 +5483,12 @@ 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 { ty::TraitContainer(trait_did) => { - callee::check_legal_trait_for_method_call(tcx, span, trait_did) + callee::check_legal_trait_for_method_call(tcx, span, None, trait_did) } ty::ImplContainer(impl_def_id) => { if segments.len() == 1 { @@ -5436,11 +5550,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // parameter internally, but we don't allow users to specify the // parameter's value explicitly, so we have to do some error- // checking here. - if let Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }) = - AstConv::check_generic_arg_count_for_call( - tcx, span, &generics, &seg, false, // `is_method_call` - ) - { + if let GenericArgCountResult { + correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }), + .. + } = AstConv::check_generic_arg_count_for_call( + tcx, span, &generics, &seg, false, // `is_method_call` + ) { infer_args_for_err.insert(index); self.set_tainted_by_errors(); // See issue #53251. } @@ -5484,7 +5599,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } err.emit(); - return (tcx.types.err, res); + return (tcx.ty_error(), res); } } } else { @@ -5496,6 +5611,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // escaping late-bound regions, and nor should the base type scheme. let ty = tcx.type_of(def_id); + let arg_count = GenericArgCountResult { + explicit_late_bound: ExplicitLateBound::No, + correct: if infer_args_for_err.is_empty() { + Ok(()) + } else { + Err(GenericArgCountMismatch::default()) + }, + }; + let substs = self_ctor_substs.unwrap_or_else(|| { AstConv::create_substs_for_generic_args( tcx, @@ -5503,7 +5627,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &[][..], has_self, self_ty, - infer_args_for_err.is_empty(), + arg_count, // Provide the generic args, and whether types should be inferred. |def_id| { if let Some(&PathSeg(_, index)) = @@ -5531,7 +5655,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.to_ty(ty).into() } (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - self.to_const(&ct.value, self.tcx.type_of(param.def_id)).into() + self.to_const(&ct.value).into() } _ => unreachable!(), }, @@ -5593,11 +5717,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, - )); + ), + ); } } } @@ -5617,14 +5744,13 @@ 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 if let Some(span) = spans.get(i) { - obligation.cause.code = traits::BindingObligation(def_id, *span); + obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span); } self.register_predicate(obligation); } @@ -5666,8 +5792,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .note("type must be known at this point") .emit(); } - self.demand_suptype(sp, self.tcx.types.err, ty); - self.tcx.types.err + let err = self.tcx.ty_error(); + self.demand_suptype(sp, err, ty); + err } } @@ -5733,43 +5860,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); - if own_counts.types == 0 { + assert_eq!(generics.parent, None); + + 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(); + } } } } @@ -5783,12 +5914,12 @@ 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 {}", option_env!("CFG_VERSION").unwrap_or("unknown_version"), - crate::session::config::host_triple(), + config::host_triple(), )); } diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index f589805e1e261..56804792b1944 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -1,14 +1,16 @@ //! Code related to processing overloaded binary and unary operators. 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 super::FnCtxt; 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; @@ -55,9 +57,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match BinOpCategory::from(op) { BinOpCategory::Shortcircuit => { // && and || are a simple case. - self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool); + self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None); let lhs_diverges = self.diverges.get(); - self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool); + self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None); // Depending on the LHS' value, the RHS can never execute. self.diverges.set(lhs_diverges); @@ -119,9 +121,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; match BinOpCategory::from(op) { BinOpCategory::Shortcircuit => { - self.demand_suptype(*lhs_span, tcx.mk_bool(), lhs_ty); - self.demand_suptype(*rhs_span, tcx.mk_bool(), rhs_ty); - tcx.mk_bool() + self.demand_suptype(*lhs_span, tcx.types.bool, lhs_ty); + self.demand_suptype(*rhs_span, tcx.types.bool, rhs_ty); + tcx.types.bool } BinOpCategory::Shift => { @@ -138,7 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { BinOpCategory::Comparison => { // both LHS and RHS and result will have the same type self.demand_suptype(*rhs_span, lhs_ty, rhs_ty); - tcx.mk_bool() + tcx.types.bool } } } @@ -163,19 +165,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // trait matching creating lifetime constraints that are too strict. // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. - let lhs_ty = self.check_expr_with_needs(lhs_expr, Needs::None); + let lhs_ty = self.check_expr(lhs_expr); let fresh_var = self.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span: lhs_expr.span, }); - self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No) + self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No) } IsAssign::Yes => { // rust-lang/rust#52126: We have to use strict // equivalence on the LHS of an assign-op like `+=`; // overwritten or mutably-borrowed places cannot be // coerced to a supertype. - self.check_expr_with_needs(lhs_expr, Needs::MutPlace) + self.check_expr(lhs_expr) } }; let lhs_ty = self.resolve_vars_with_obligations(lhs_ty); @@ -194,7 +196,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign)); // see `NB` above - let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var); + let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); let return_ty = match result { @@ -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); } @@ -471,7 +497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - self.tcx.types.err + self.tcx.ty_error() } }; @@ -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; } } @@ -705,7 +709,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } err.emit(); } - self.tcx.types.err + self.tcx.ty_error() } } } @@ -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 fabf3dd1153b7..ea47ae68ce7d3 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) } @@ -211,7 +212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // errors in some cases, such as this one: // // ``` - // fn foo<'x>(x: &'x int) { + // fn foo<'x>(x: &'x i32) { // let a = 1; // let mut z = x; // z = &a; @@ -219,7 +220,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ``` // // The reason we might get an error is that `z` might be - // assigned a type like `&'x int`, and then we would have + // assigned a type like `&'x i32`, and then we would have // a problem when we try to assign `&a` to `z`, because // the lifetime of `&a` (i.e., the enclosing block) is // shorter than `'x`. @@ -228,11 +229,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // expected type here is whatever type the user wrote, not // the initializer's type. In this case the user wrote // nothing, so we are going to create a type variable `Z`. - // Then we will assign the type of the initializer (`&'x - // int`) as a subtype of `Z`: `&'x int <: Z`. And hence we - // will instantiate `Z` as a type `&'0 int` where `'0` is - // a fresh region variable, with the constraint that `'x : - // '0`. So basically we're all set. + // Then we will assign the type of the initializer (`&'x i32`) + // as a subtype of `Z`: `&'x i32 <: Z`. And hence we + // will instantiate `Z` as a type `&'0 i32` where `'0` is + // a fresh region variable, with the constraint that `'x : '0`. + // So basically we're all set. // // Note that there are two tests to check that this remains true // (`regions-reassign-{match,let}-bound-pointer.rs`). @@ -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 @@ -441,7 +442,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // There exists a side that didn't meet our criteria that the end-point // be of a numeric or char type, as checked in `calc_side` above. self.emit_err_pat_range(span, lhs, rhs); - return self.tcx.types.err; + return self.tcx.ty_error(); } // Now that we know the types can be unified we find the unified type @@ -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); @@ -672,11 +673,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { variant_ty } else { + let err = self.tcx.ty_error(); for field in fields { let ti = TopInfo { parent_pat: Some(&pat), ..ti }; - self.check_pat(&field.pat, self.tcx.types.err, def_bm, ti); + self.check_pat(&field.pat, err, def_bm, ti); } - return self.tcx.types.err; + return err; }; // Type-check the path. @@ -686,7 +688,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, etc, def_bm, ti) { pat_ty } else { - self.tcx.types.err + self.tcx.ty_error() } } @@ -694,7 +696,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> { @@ -705,19 +706,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match res { Res::Err => { self.set_tainted_by_errors(); - return tcx.types.err; + return tcx.ty_error(); } - 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); - return tcx.types.err; + Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => { + report_unexpected_variant_res(tcx, res, pat.span); + return tcx.ty_error(); } - 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), } @@ -753,17 +755,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res.descr(), ), ); - let (msg, sugg) = match parent_pat { - Some(Pat { kind: hir::PatKind::Struct(..), .. }) => ( - "bind the struct field to a different name instead", - format!("{}: other_{}", ident, ident.as_str().to_lowercase()), - ), - _ => ( - "introduce a new binding instead", - format!("other_{}", ident.as_str().to_lowercase()), - ), + match parent_pat { + Some(Pat { kind: hir::PatKind::Struct(..), .. }) => { + e.span_suggestion_verbose( + ident.span.shrink_to_hi(), + "bind the struct field to a different name instead", + format!(": other_{}", ident.as_str().to_lowercase()), + Applicability::HasPlaceholders, + ); + } + _ => { + let msg = "introduce a new binding instead"; + let sugg = format!("other_{}", ident.as_str().to_lowercase()); + e.span_suggestion(ident.span, msg, sugg, Applicability::HasPlaceholders); + } }; - e.span_suggestion(ident.span, msg, sugg, Applicability::HasPlaceholders); } } e.emit(); @@ -783,18 +789,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let on_error = || { let parent_pat = Some(pat); for pat in subpats { - self.check_pat(&pat, tcx.types.err, def_bm, TopInfo { parent_pat, ..ti }); + self.check_pat(&pat, tcx.ty_error(), def_bm, TopInfo { parent_pat, ..ti }); } }; let report_unexpected_res = |res: Res| { + let sm = tcx.sess.source_map(); + let path_str = sm + .span_to_snippet(sm.span_until_char(pat.span, '(')) + .map_or(String::new(), |s| format!(" `{}`", s.trim_end())); let msg = format!( - "expected tuple struct or tuple variant, found {} `{}`", + "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 \ @@ -814,7 +825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if res == Res::Err { self.set_tainted_by_errors(); on_error(); - return self.tcx.types.err; + return self.tcx.ty_error(); } // Type-check the path. @@ -822,18 +833,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); if !pat_ty.is_fn() { report_unexpected_res(res); - return tcx.types.err; + return tcx.ty_error(); } let variant = match res { Res::Err => { self.set_tainted_by_errors(); on_error(); - return tcx.types.err; + return tcx.ty_error(); } - Res::Def(DefKind::AssocConst, _) | Res::Def(DefKind::AssocFn, _) => { + Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { report_unexpected_res(res); - return tcx.types.err; + return tcx.ty_error(); } Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), _ => bug!("unexpected pattern resolution: {:?}", res), @@ -845,8 +856,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() @@ -866,7 +881,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Pattern has wrong number of fields. self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); on_error(); - return tcx.types.err; + return tcx.ty_error(); } pat_ty } @@ -987,9 +1002,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); // Walk subpatterns with an expected type of `err` in this case to silence // further errors being emitted when using the bindings. #50333 - let element_tys_iter = (0..max_len).map(|_| tcx.types.err); + let element_tys_iter = (0..max_len).map(|_| tcx.ty_error()); for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, &tcx.types.err, def_bm, ti); + self.check_pat(elem, &tcx.ty_error(), def_bm, ti); } tcx.mk_tup(element_tys_iter) } else { @@ -1016,14 +1031,13 @@ 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 .fields .iter() .enumerate() - .map(|(i, field)| (field.ident.modern(), (i, field))) + .map(|(i, field)| (field.ident.normalize_to_macros_2_0(), (i, field))) .collect::>(); // Keep track of which fields have already appeared in the pattern. @@ -1039,7 +1053,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Occupied(occupied) => { self.error_field_already_bound(span, field.ident, *occupied.get()); no_field_errors = false; - tcx.types.err + tcx.ty_error() } Vacant(vacant) => { vacant.insert(span); @@ -1053,7 +1067,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or_else(|| { inexistent_fields.push(field.ident); no_field_errors = false; - tcx.types.err + tcx.ty_error() }) } }; @@ -1064,13 +1078,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut unmentioned_fields = variant .fields .iter() - .map(|field| field.ident.modern()) + .map(|field| field.ident.normalize_to_macros_2_0()) .filter(|ident| !used_fields.contains_key(&ident)) .collect::>(); if !inexistent_fields.is_empty() && !variant.recovered { self.error_inexistent_fields( - kind_name, + adt.variant_descr(), &inexistent_fields, &mut unmentioned_fields, variant, @@ -1079,18 +1093,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") @@ -1105,7 +1112,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, @@ -1121,8 +1151,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; @@ -1197,7 +1227,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 { @@ -1252,7 +1282,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype_pat(span, expected, box_ty, ti); (box_ty, inner_ty) } else { - (tcx.types.err, tcx.types.err) + let err = tcx.ty_error(); + (err, err) }; self.check_pat(&inner, inner_ty, def_bm, ti); box_ty @@ -1298,7 +1329,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } else { - (tcx.types.err, tcx.types.err) + let err = tcx.ty_error(); + (err, err) }; self.check_pat(&inner, inner_ty, def_bm, TopInfo { parent_pat: Some(&pat), ..ti }); rptr_ty @@ -1331,77 +1363,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.ty_error(); + (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.ty_error()), arr_ty) } fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { diff --git a/src/librustc_typeck/check/place_op.rs b/src/librustc_typeck/check/place_op.rs new file mode 100644 index 0000000000000..b7c8f310a1414 --- /dev/null +++ b/src/librustc_typeck/check/place_op.rs @@ -0,0 +1,337 @@ +use crate::check::method::MethodCallee; +use crate::check::{FnCtxt, PlaceOp}; +use rustc_hir as hir; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::InferOk; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; +use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; +use rustc_trait_selection::autoderef::Autoderef; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already. + pub(super) fn lookup_derefing( + &self, + expr: &hir::Expr<'_>, + oprnd_expr: &'tcx hir::Expr<'tcx>, + oprnd_ty: Ty<'tcx>, + ) -> Option> { + if let Some(mt) = oprnd_ty.builtin_deref(true) { + return Some(mt.ty); + } + + let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?; + let method = self.register_infer_ok_obligations(ok); + if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind { + self.apply_adjustments( + oprnd_expr, + vec![Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)), + target: method.sig.inputs()[0], + }], + ); + } else { + span_bug!(expr.span, "input to deref is not a ref?"); + } + let ty = self.make_overloaded_place_return_type(method).ty; + self.write_method_call(expr.hir_id, method); + Some(ty) + } + + /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already. + pub(super) fn lookup_indexing( + &self, + expr: &hir::Expr<'_>, + base_expr: &'tcx hir::Expr<'tcx>, + base_ty: Ty<'tcx>, + idx_ty: Ty<'tcx>, + ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { + // FIXME(#18741) -- this is almost but not quite the same as the + // autoderef that normal method probing does. They could likely be + // consolidated. + + let mut autoderef = self.autoderef(base_expr.span, base_ty); + let mut result = None; + while result.is_none() && autoderef.next().is_some() { + result = self.try_index_step(expr, base_expr, &autoderef, idx_ty); + } + self.register_predicates(autoderef.into_obligations()); + result + } + + /// To type-check `base_expr[index_expr]`, we progressively autoderef + /// (and otherwise adjust) `base_expr`, looking for a type which either + /// supports builtin indexing or overloaded indexing. + /// This loop implements one step in that search; the autoderef loop + /// is implemented by `lookup_indexing`. + fn try_index_step( + &self, + expr: &hir::Expr<'_>, + base_expr: &hir::Expr<'_>, + autoderef: &Autoderef<'a, 'tcx>, + index_ty: Ty<'tcx>, + ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { + let adjusted_ty = + self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); + debug!( + "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ + index_ty={:?})", + expr, base_expr, adjusted_ty, index_ty + ); + + for &unsize in &[false, true] { + let mut self_ty = adjusted_ty; + if unsize { + // We only unsize arrays here. + if let ty::Array(element_ty, _) = adjusted_ty.kind { + self_ty = self.tcx.mk_slice(element_ty); + } else { + continue; + } + } + + // If some lookup succeeds, write callee into table and extract index/element + // type from the method signature. + // If some lookup succeeded, install method in table + let input_ty = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::AutoDeref, + span: base_expr.span, + }); + let method = + self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index); + + let result = method.map(|ok| { + debug!("try_index_step: success, using overloaded indexing"); + let method = self.register_infer_ok_obligations(ok); + + let mut adjustments = self.adjust_steps(autoderef); + if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind { + adjustments.push(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)), + target: self.tcx.mk_ref( + region, + ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty }, + ), + }); + } else { + span_bug!(expr.span, "input to index is not a ref?"); + } + if unsize { + adjustments.push(Adjustment { + kind: Adjust::Pointer(PointerCast::Unsize), + target: method.sig.inputs()[0], + }); + } + self.apply_adjustments(base_expr, adjustments); + + self.write_method_call(expr.hir_id, method); + (input_ty, self.make_overloaded_place_return_type(method).ty) + }); + if result.is_some() { + return result; + } + } + + None + } + + /// Try to resolve an overloaded place op. We only deal with the immutable + /// variant here (Deref/Index). In some contexts we would need the mutable + /// variant (DerefMut/IndexMut); those would be later converted by + /// `convert_place_derefs_to_mutable`. + pub(super) fn try_overloaded_place_op( + &self, + span: Span, + base_ty: Ty<'tcx>, + arg_tys: &[Ty<'tcx>], + op: PlaceOp, + ) -> Option>> { + debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); + + let (imm_tr, imm_op) = match op { + PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref), + PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index), + }; + imm_tr.and_then(|trait_did| { + self.lookup_method_in_trait( + span, + Ident::with_dummy_span(imm_op), + trait_did, + base_ty, + Some(arg_tys), + ) + }) + } + + fn try_mutable_overloaded_place_op( + &self, + span: Span, + base_ty: Ty<'tcx>, + arg_tys: &[Ty<'tcx>], + op: PlaceOp, + ) -> Option>> { + debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); + + let (mut_tr, mut_op) = match op { + PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut), + PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut), + }; + mut_tr.and_then(|trait_did| { + self.lookup_method_in_trait( + span, + Ident::with_dummy_span(mut_op), + trait_did, + base_ty, + Some(arg_tys), + ) + }) + } + + /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` + /// into `DerefMut` and `IndexMut` respectively. + /// + /// This is a second pass of typechecking derefs/indices. We need this we do not + /// always know whether a place needs to be mutable or not in the first pass. + /// This happens whether there is an implicit mutable reborrow, e.g. when the type + /// is used as the receiver of a method call. + pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) { + // Gather up expressions we want to munge. + let mut exprs = vec![expr]; + + loop { + match exprs.last().unwrap().kind { + hir::ExprKind::Field(ref expr, _) + | hir::ExprKind::Index(ref expr, _) + | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr), + _ => break, + } + } + + debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); + + // Fix up autoderefs and derefs. + for (i, &expr) in exprs.iter().rev().enumerate() { + debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); + + // Fix up the autoderefs. Autorefs can only occur immediately preceding + // overloaded place ops, and will be fixed by them in order to get + // the correct region. + let mut source = self.node_ty(expr.hir_id); + // Do not mutate adjustments in place, but rather take them, + // and replace them after mutating them, to avoid having the + // tables borrowed during (`deref_mut`) method resolution. + let previous_adjustments = + self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id); + if let Some(mut adjustments) = previous_adjustments { + for adjustment in &mut adjustments { + if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind { + if let Some(ok) = self.try_mutable_overloaded_place_op( + expr.span, + source, + &[], + PlaceOp::Deref, + ) { + let method = self.register_infer_ok_obligations(ok); + if let ty::Ref(region, _, mutbl) = method.sig.output().kind { + *deref = OverloadedDeref { region, mutbl }; + } + } + } + source = adjustment.target; + } + self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); + } + + match expr.kind { + hir::ExprKind::Index(ref base_expr, ref index_expr) => { + // We need to get the final type in case dereferences were needed for the trait + // to apply (#72002). + let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr); + self.convert_place_op_to_mutable( + PlaceOp::Index, + expr, + base_expr, + &[index_expr_ty], + ); + } + hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => { + self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]); + } + _ => {} + } + } + } + + fn convert_place_op_to_mutable( + &self, + op: PlaceOp, + expr: &hir::Expr<'_>, + base_expr: &hir::Expr<'_>, + arg_tys: &[Ty<'tcx>], + ) { + debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys); + if !self.tables.borrow().is_method_call(expr) { + debug!("convert_place_op_to_mutable - builtin, nothing to do"); + return; + } + + // Need to deref because overloaded place ops take self by-reference. + let base_ty = self + .tables + .borrow() + .expr_ty_adjusted(base_expr) + .builtin_deref(false) + .expect("place op takes something that is not a ref") + .ty; + + let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op); + let method = match method { + Some(ok) => self.register_infer_ok_obligations(ok), + // Couldn't find the mutable variant of the place op, keep the + // current, immutable version. + None => return, + }; + debug!("convert_place_op_to_mutable: method={:?}", method); + self.write_method_call(expr.hir_id, method); + + let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind { + r + } else { + span_bug!(expr.span, "input to mutable place op is not a mut ref?"); + }; + + // Convert the autoref in the base expr to mutable with the correct + // region and mutability. + let base_expr_ty = self.node_ty(base_expr.hir_id); + if let Some(adjustments) = + self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id) + { + let mut source = base_expr_ty; + for adjustment in &mut adjustments[..] { + if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { + debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); + let mutbl = AutoBorrowMutability::Mut { + // Deref/indexing can be desugared to a method call, + // so maybe we could use two-phase here. + // See the documentation of AllowTwoPhase for why that's + // not the case today. + allow_two_phase_borrow: AllowTwoPhase::No, + }; + adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl)); + adjustment.target = + self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); + } + source = adjustment.target; + } + + // If we have an autoref followed by unsizing at the end, fix the unsize target. + if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] = + adjustments[..] + { + *target = method.sig.inputs()[0]; + } + } + } +} diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 975c6e101a691..d3bccaaa3e4b9 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -76,20 +76,17 @@ use crate::check::dropck; use crate::check::FnCtxt; use crate::mem_categorization as mc; use crate::middle::region; -use rustc::hir::map::Map; -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, SuppressRegionErrors}; +use rustc_infer::infer::{self, RegionObligation, RegionckMode}; +use rustc_middle::ty::adjustment; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use rustc_trait_selection::infer::OutlivesEnvironmentExt; use rustc_trait_selection::opaque_types::InferCtxtExt; -use std::mem; use std::ops::Deref; // a variation on try that just returns unit @@ -112,8 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn regionck_expr(&self, body: &'tcx hir::Body<'tcx>) { let subject = self.tcx.hir().body_owner_def_id(body.id()); let id = body.value.hir_id; - let mut rcx = - RegionCtxt::new(self, RepeatingScope(id), id, Subject(subject), self.param_env); + let mut rcx = RegionCtxt::new(self, id, Subject(subject), self.param_env); // There are no add'l implied bounds when checking a // standalone expr (e.g., the `E` in a type like `[u32; E]`). @@ -124,10 +120,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcx.visit_body(body); rcx.visit_region_obligations(id); } - rcx.resolve_regions_and_report_errors(SuppressRegionErrors::when_nll_is_enabled(self.tcx)); - - assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); + rcx.resolve_regions_and_report_errors(RegionckMode::for_item_body(self.tcx)); } /// Region checking during the WF phase for items. `wf_tys` are the @@ -135,17 +128,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn regionck_item(&self, item_id: hir::HirId, span: Span, wf_tys: &[Ty<'tcx>]) { debug!("regionck_item(item.id={:?}, wf_tys={:?})", item_id, wf_tys); let subject = self.tcx.hir().local_def_id(item_id); - let mut rcx = RegionCtxt::new( - self, - RepeatingScope(item_id), - item_id, - Subject(subject), - self.param_env, - ); + let mut rcx = RegionCtxt::new(self, item_id, Subject(subject), self.param_env); rcx.outlives_environment.add_implied_bounds(self, wf_tys, item_id, span); rcx.outlives_environment.save_implied_bounds(item_id); rcx.visit_region_obligations(item_id); - rcx.resolve_regions_and_report_errors(SuppressRegionErrors::default()); + rcx.resolve_regions_and_report_errors(RegionckMode::default()); } /// Region check a function body. Not invoked on closures, but @@ -160,21 +147,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("regionck_fn(id={})", fn_id); let subject = self.tcx.hir().body_owner_def_id(body.id()); let hir_id = body.value.hir_id; - let mut rcx = - RegionCtxt::new(self, RepeatingScope(hir_id), hir_id, Subject(subject), self.param_env); + let mut rcx = RegionCtxt::new(self, hir_id, Subject(subject), self.param_env); if !self.errors_reported_since_creation() { // regionck assumes typeck succeeded rcx.visit_fn_body(fn_id, body, self.tcx.hir().span(fn_id)); } - rcx.resolve_regions_and_report_errors(SuppressRegionErrors::when_nll_is_enabled(self.tcx)); - - // In this mode, we also copy the free-region-map into the - // tables of the enclosing fcx. In the other regionck modes - // (e.g., `regionck_item`), we don't have an enclosing tables. - assert!(self.tables.borrow().free_region_map.is_empty()); - self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); + rcx.resolve_regions_and_report_errors(RegionckMode::for_item_body(self.tcx)); } } @@ -190,16 +170,10 @@ pub struct RegionCtxt<'a, 'tcx> { // id of innermost fn body id body_id: hir::HirId, - body_owner: DefId, - - // call_site scope of innermost fn - call_site_scope: Option, - - // id of innermost fn or loop - repeating_scope: hir::HirId, + body_owner: LocalDefId, // 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> { @@ -209,13 +183,11 @@ 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( fcx: &'a FnCtxt<'a, 'tcx>, - RepeatingScope(initial_repeating_scope): RepeatingScope, initial_body_id: hir::HirId, Subject(subject): Subject, param_env: ty::ParamEnv<'tcx>, @@ -225,19 +197,13 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { RegionCtxt { fcx, region_scope_tree, - repeating_scope: initial_repeating_scope, body_id: initial_body_id, body_owner: subject, - call_site_scope: None, subject_def_id: subject, outlives_environment, } } - fn set_repeating_scope(&mut self, scope: hir::HirId) -> hir::HirId { - mem::replace(&mut self.repeating_scope, scope) - } - /// Try to resolve the type for the given node, returning `t_err` if an error results. Note that /// we never care about the details of the error, the same error will be detected and reported /// in the writeback phase. @@ -271,16 +237,10 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { self.resolve_type(t) } - /// Try to resolve the type for the given node. - pub fn resolve_expr_type_adjusted(&mut self, expr: &hir::Expr<'_>) -> Ty<'tcx> { - let ty = self.tables.borrow().expr_ty_adjusted(expr); - self.resolve_type(ty) - } - - /// This is the "main" function when region-checking a function item or a closure - /// within a function item. It begins by updating various fields (e.g., `call_site_scope` - /// and `outlives_environment`) to be appropriate to the function and then adds constraints - /// derived from the function body. + /// This is the "main" function when region-checking a function item or a + /// closure within a function item. It begins by updating various fields + /// (e.g., `outlives_environment`) to be appropriate to the function and + /// then adds constraints derived from the function body. /// /// Note that it does **not** restore the state of the fields that /// it updates! This is intentional, since -- for the main @@ -302,10 +262,6 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { self.body_id = body_id.hir_id; self.body_owner = self.tcx.hir().body_owner_def_id(body_id); - let call_site = - region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::CallSite }; - self.call_site_scope = Some(call_site); - let fn_sig = { match self.tables.borrow().liberated_fn_sigs().get(id) { Some(f) => *f, @@ -334,12 +290,6 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { self.visit_body(body); self.visit_region_obligations(body_id.hir_id); - let call_site_scope = self.call_site_scope.unwrap(); - debug!("visit_fn_body body.id {:?} call_site_scope: {:?}", body.id(), call_site_scope); - let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope)); - - self.type_of_node_must_outlive(infer::CallReturn(span), body_id.hir_id, call_site_region); - self.constrain_opaque_types( &self.fcx.opaque_types.borrow(), self.outlives_environment.free_region_map(), @@ -355,7 +305,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { self.select_all_obligations_or_error(); } - fn resolve_regions_and_report_errors(&self, suppress: SuppressRegionErrors) { + fn resolve_regions_and_report_errors(&self, mode: RegionckMode) { self.infcx.process_registered_region_obligations( self.outlives_environment.region_bound_pairs_map(), self.implicit_region_bound, @@ -363,44 +313,15 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { ); self.fcx.resolve_regions_and_report_errors( - self.subject_def_id, - &self.region_scope_tree, + self.subject_def_id.to_def_id(), &self.outlives_environment, - suppress, + mode, ); } fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat<'_>) { debug!("regionck::visit_pat(pat={:?})", pat); pat.each_binding(|_, hir_id, span, _| { - // If we have a variable that contains region'd data, that - // data will be accessible from anywhere that the variable is - // accessed. We must be wary of loops like this: - // - // // from src/test/compile-fail/borrowck-lend-flow.rs - // let mut v = box 3, w = box 4; - // let mut x = &mut w; - // loop { - // **x += 1; // (2) - // borrow(v); //~ ERROR cannot borrow - // x = &mut v; // (1) - // } - // - // Typically, we try to determine the region of a borrow from - // those points where it is dereferenced. In this case, one - // might imagine that the lifetime of `x` need only be the - // body of the loop. But of course this is incorrect because - // the pointer that is created at point (1) is consumed at - // point (2), meaning that it must be live across the loop - // iteration. The easiest way to guarantee this is to require - // that the lifetime of any regions that appear in a - // variable's type enclose at least the variable's scope. - let var_scope = self.region_scope_tree.var_scope(hir_id.local_id); - let var_region = self.tcx.mk_region(ty::ReScope(var_scope)); - - let origin = infer::BindingTypeIsNotValidAtDecl(span); - self.type_of_node_must_outlive(origin, hir_id, var_region); - let typ = self.resolve_node_type(hir_id); let body_id = self.body_id; let _ = dropck::check_drop_obligations(self, typ, span, body_id); @@ -417,7 +338,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { // hierarchy, and in particular the relationships between free // regions, until regionck, as described in #3238. - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -443,7 +364,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { // `visit_fn_body`. We will restore afterwards. let old_body_id = self.body_id; let old_body_owner = self.body_owner; - let old_call_site_scope = self.call_site_scope; let env_snapshot = self.outlives_environment.push_snapshot_pre_closure(); let body = self.tcx.hir().body(body_id); @@ -451,7 +371,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { // Restore state from previous function. self.outlives_environment.pop_snapshot_post_closure(env_snapshot); - self.call_site_scope = old_call_site_scope; self.body_id = old_body_id; self.body_owner = old_body_owner; } @@ -472,42 +391,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - debug!("regionck::visit_expr(e={:?}, repeating_scope={:?})", expr, self.repeating_scope); - - // No matter what, the type of each expression must outlive the - // scope of that expression. This also guarantees basic WF. - let expr_ty = self.resolve_node_type(expr.hir_id); - // the region corresponding to this expression - let expr_region = self.tcx.mk_region(ty::ReScope(region::Scope { - id: expr.hir_id.local_id, - data: region::ScopeData::Node, - })); - self.type_must_outlive( - infer::ExprTypeIsNotInScope(expr_ty, expr.span), - expr_ty, - expr_region, - ); - - let is_method_call = self.tables.borrow().is_method_call(expr); - - // If we are calling a method (either explicitly or via an - // overloaded operator), check that all of the types provided as - // arguments for its type parameters are well-formed, and all the regions - // provided as arguments outlive the call. - if is_method_call { - let origin = match expr.kind { - hir::ExprKind::MethodCall(..) => infer::ParameterOrigin::MethodCall, - hir::ExprKind::Unary(op, _) if op == hir::UnOp::UnDeref => { - infer::ParameterOrigin::OverloadedDeref - } - _ => infer::ParameterOrigin::OverloadedOperator, - }; - - let substs = self.tables.borrow().node_substs(expr.hir_id); - self.substs_wf_in_scope(origin, substs, expr.span, expr_region); - // Arguments (sub-expressions) are checked via `constrain_call`, below. - } - // Check any autoderefs or autorefs that appear. let cmt_result = self.constrain_adjustments(expr); @@ -522,117 +405,10 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { } } - debug!( - "regionck::visit_expr(e={:?}, repeating_scope={:?}) - visiting subexprs", - expr, self.repeating_scope - ); match expr.kind { - hir::ExprKind::Path(_) => { - let substs = self.tables.borrow().node_substs(expr.hir_id); - let origin = infer::ParameterOrigin::Path; - self.substs_wf_in_scope(origin, substs, expr.span, expr_region); - } - - hir::ExprKind::Call(ref callee, ref args) => { - if is_method_call { - self.constrain_call(expr, Some(&callee), args.iter().map(|e| &*e)); - } else { - self.constrain_callee(&callee); - self.constrain_call(expr, None, args.iter().map(|e| &*e)); - } - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::MethodCall(.., ref args) => { - self.constrain_call(expr, Some(&args[0]), args[1..].iter().map(|e| &*e)); - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::AssignOp(_, ref lhs, ref rhs) => { - if is_method_call { - self.constrain_call(expr, Some(&lhs), Some(&**rhs).into_iter()); - } - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::Index(ref lhs, ref rhs) if is_method_call => { - self.constrain_call(expr, Some(&lhs), Some(&**rhs).into_iter()); - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::Binary(_, ref lhs, ref rhs) if is_method_call => { - // As `ExprKind::MethodCall`, but the call is via an overloaded op. - self.constrain_call(expr, Some(&lhs), Some(&**rhs).into_iter()); - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::Binary(_, ref lhs, ref rhs) => { - // If you do `x OP y`, then the types of `x` and `y` must - // outlive the operation you are performing. - let lhs_ty = self.resolve_expr_type_adjusted(&lhs); - let rhs_ty = self.resolve_expr_type_adjusted(&rhs); - for &ty in &[lhs_ty, rhs_ty] { - self.type_must_outlive(infer::Operand(expr.span), ty, expr_region); - } - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base) => { - // For *a, the lifetime of a must enclose the deref - if is_method_call { - self.constrain_call(expr, Some(base), None::>.iter()); - } - // For overloaded derefs, base_ty is the input to `Deref::deref`, - // but it's a reference type uing the same region as the output. - let base_ty = self.resolve_expr_type_adjusted(base); - if let ty::Ref(r_ptr, _, _) = base_ty.kind { - self.mk_subregion_due_to_dereference(expr.span, expr_region, r_ptr); - } - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::Unary(_, ref lhs) if is_method_call => { - // As above. - self.constrain_call(expr, Some(&lhs), None::>.iter()); - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::Index(ref vec_expr, _) => { - // For a[b], the lifetime of a must enclose the deref - let vec_type = self.resolve_expr_type_adjusted(&vec_expr); - self.constrain_index(expr, vec_type); - - intravisit::walk_expr(self, expr); - } - - hir::ExprKind::Cast(ref source, _) => { - // Determine if we are casting `source` to a trait - // instance. If so, we have to be sure that the type of - // the source obeys the trait's region bound. - self.constrain_cast(expr, &source); - intravisit::walk_expr(self, expr); - } - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref base) => { self.link_addr_of(expr, m, &base); - // Require that when you write a `&expr` expression, the - // resulting pointer has a lifetime that encompasses the - // `&expr` expression itself. Note that we constraining - // the type of the node expr.id here *before applying - // adjustments*. - // - // FIXME(https://github.com/rust-lang/rfcs/issues/811) - // nested method calls requires that this rule change - let ty0 = self.resolve_node_type(expr.hir_id); - self.type_must_outlive(infer::AddrOf(expr.span), ty0, expr_region); intravisit::walk_expr(self, expr); } @@ -642,140 +418,12 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionCtxt<'a, 'tcx> { intravisit::walk_expr(self, expr); } - hir::ExprKind::Closure(.., body_id, _, _) => { - self.check_expr_fn_block(expr, body_id); - } - - hir::ExprKind::Loop(ref body, _, _) => { - let repeating_scope = self.set_repeating_scope(body.hir_id); - intravisit::walk_expr(self, expr); - self.set_repeating_scope(repeating_scope); - } - - hir::ExprKind::Ret(Some(ref ret_expr)) => { - let call_site_scope = self.call_site_scope; - debug!( - "visit_expr ExprKind::Ret ret_expr.hir_id {} call_site_scope: {:?}", - ret_expr.hir_id, call_site_scope - ); - let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope.unwrap())); - self.type_of_node_must_outlive( - infer::CallReturn(ret_expr.span), - ret_expr.hir_id, - call_site_region, - ); - intravisit::walk_expr(self, expr); - } - - _ => { - intravisit::walk_expr(self, expr); - } + _ => intravisit::walk_expr(self, expr), } } } impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { - fn constrain_cast(&mut self, cast_expr: &hir::Expr<'_>, source_expr: &hir::Expr<'_>) { - debug!("constrain_cast(cast_expr={:?}, source_expr={:?})", cast_expr, source_expr); - - let source_ty = self.resolve_node_type(source_expr.hir_id); - let target_ty = self.resolve_node_type(cast_expr.hir_id); - - self.walk_cast(cast_expr, source_ty, target_ty); - } - - fn walk_cast(&mut self, cast_expr: &hir::Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) { - debug!("walk_cast(from_ty={:?}, to_ty={:?})", from_ty, to_ty); - match (&from_ty.kind, &to_ty.kind) { - /*From:*/ - (&ty::Ref(from_r, from_ty, _), /*To: */ &ty::Ref(to_r, to_ty, _)) => { - // Target cannot outlive source, naturally. - self.sub_regions(infer::Reborrow(cast_expr.span), to_r, from_r); - self.walk_cast(cast_expr, from_ty, to_ty); - } - - /*From:*/ - (_, /*To: */ &ty::Dynamic(.., r)) => { - // When T is existentially quantified as a trait - // `Foo+'to`, it must outlive the region bound `'to`. - self.type_must_outlive(infer::RelateObjectBound(cast_expr.span), from_ty, r); - } - - /*From:*/ - (&ty::Adt(from_def, _), /*To: */ &ty::Adt(to_def, _)) - if from_def.is_box() && to_def.is_box() => - { - self.walk_cast(cast_expr, from_ty.boxed_ty(), to_ty.boxed_ty()); - } - - _ => {} - } - } - - fn check_expr_fn_block(&mut self, expr: &'tcx hir::Expr<'tcx>, body_id: hir::BodyId) { - let repeating_scope = self.set_repeating_scope(body_id.hir_id); - intravisit::walk_expr(self, expr); - self.set_repeating_scope(repeating_scope); - } - - fn constrain_callee(&mut self, callee_expr: &hir::Expr<'_>) { - let callee_ty = self.resolve_node_type(callee_expr.hir_id); - match callee_ty.kind { - ty::FnDef(..) | ty::FnPtr(_) => {} - _ => { - // this should not happen, but it does if the program is - // erroneous - // - // bug!( - // callee_expr.span, - // "Calling non-function: {}", - // callee_ty); - } - } - } - - fn constrain_call<'b, I: Iterator>>( - &mut self, - call_expr: &hir::Expr<'_>, - receiver: Option<&hir::Expr<'_>>, - arg_exprs: I, - ) { - //! Invoked on every call site (i.e., normal calls, method calls, - //! and overloaded operators). Constrains the regions which appear - //! in the type of the function. Also constrains the regions that - //! appear in the arguments appropriately. - - debug!("constrain_call(call_expr={:?}, receiver={:?})", call_expr, receiver); - - // `callee_region` is the scope representing the time in which the - // call occurs. - // - // FIXME(#6268) to support nested method calls, should be callee_id - let callee_scope = - region::Scope { id: call_expr.hir_id.local_id, data: region::ScopeData::Node }; - let callee_region = self.tcx.mk_region(ty::ReScope(callee_scope)); - - debug!("callee_region={:?}", callee_region); - - for arg_expr in arg_exprs { - debug!("argument: {:?}", arg_expr); - - // ensure that any regions appearing in the argument type are - // valid for at least the lifetime of the function: - self.type_of_node_must_outlive( - infer::CallArg(arg_expr.span), - arg_expr.hir_id, - callee_region, - ); - } - - // as loop above, but for receiver - if let Some(r) = receiver { - debug!("receiver: {:?}", r); - self.type_of_node_must_outlive(infer::CallRcvr(r.span), r.hir_id, callee_region); - } - } - /// Creates a temporary `MemCategorizationContext` and pass it to the closure. fn with_mc(&self, f: F) -> R where @@ -791,150 +439,61 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { /// Invoked on any adjustments that occur. Checks that if this is a region pointer being /// dereferenced, the lifetime of the pointer includes the deref expr. - fn constrain_adjustments(&mut self, expr: &hir::Expr<'_>) -> mc::McResult> { + fn constrain_adjustments( + &mut self, + expr: &hir::Expr<'_>, + ) -> mc::McResult> { debug!("constrain_adjustments(expr={:?})", expr); - let mut cmt = self.with_mc(|mc| mc.cat_expr_unadjusted(expr))?; + let mut place = self.with_mc(|mc| mc.cat_expr_unadjusted(expr))?; let tables = self.tables.borrow(); let adjustments = tables.expr_adjustments(&expr); if adjustments.is_empty() { - return Ok(cmt); + return Ok(place); } debug!("constrain_adjustments: adjustments={:?}", adjustments); // If necessary, constrain destructors in the unadjusted form of this // expression. - self.check_safety_of_rvalue_destructor_if_necessary(&cmt, expr.span); + self.check_safety_of_rvalue_destructor_if_necessary(&place, expr.span); - let expr_region = self.tcx.mk_region(ty::ReScope(region::Scope { - id: expr.hir_id.local_id, - data: region::ScopeData::Node, - })); for adjustment in adjustments { - debug!("constrain_adjustments: adjustment={:?}, cmt={:?}", adjustment, cmt); + debug!("constrain_adjustments: adjustment={:?}, place={:?}", adjustment, place); if let adjustment::Adjust::Deref(Some(deref)) = adjustment.kind { - debug!("constrain_adjustments: overloaded deref: {:?}", deref); - - // Treat overloaded autoderefs as if an AutoBorrow adjustment - // was applied on the base type, as that is always the case. - let input = self - .tcx - .mk_ref(deref.region, ty::TypeAndMut { ty: cmt.ty, mutbl: deref.mutbl }); - let output = self.tcx.mk_ref( - deref.region, - ty::TypeAndMut { ty: adjustment.target, mutbl: deref.mutbl }, - ); - self.link_region( expr.span, deref.region, ty::BorrowKind::from_mutbl(deref.mutbl), - &cmt, + &place, ); - - // Specialized version of constrain_call. - self.type_must_outlive(infer::CallRcvr(expr.span), input, expr_region); - self.type_must_outlive(infer::CallReturn(expr.span), output, expr_region); } if let adjustment::Adjust::Borrow(ref autoref) = adjustment.kind { - self.link_autoref(expr, &cmt, autoref); - - // Require that the resulting region encompasses - // the current node. - // - // FIXME(#6268) remove to support nested method calls - self.type_of_node_must_outlive( - infer::AutoBorrow(expr.span), - expr.hir_id, - expr_region, - ); + self.link_autoref(expr, &place, autoref); } - cmt = self.with_mc(|mc| mc.cat_expr_adjusted(expr, cmt, &adjustment))?; + place = self.with_mc(|mc| mc.cat_expr_adjusted(expr, place, &adjustment))?; } - Ok(cmt) - } - - pub fn mk_subregion_due_to_dereference( - &mut self, - deref_span: Span, - minimum_lifetime: ty::Region<'tcx>, - maximum_lifetime: ty::Region<'tcx>, - ) { - self.sub_regions(infer::DerefPointer(deref_span), minimum_lifetime, maximum_lifetime) + Ok(place) } fn check_safety_of_rvalue_destructor_if_necessary( &mut self, - place: &mc::Place<'tcx>, + place_with_id: &mc::PlaceWithHirId<'tcx>, span: Span, ) { - if let mc::PlaceBase::Rvalue = place.base { - if place.projections.is_empty() { - let typ = self.resolve_type(place.ty); + if let mc::PlaceBase::Rvalue = place_with_id.place.base { + if place_with_id.place.projections.is_empty() { + let typ = self.resolve_type(place_with_id.place.ty); let body_id = self.body_id; let _ = dropck::check_drop_obligations(self, typ, span, body_id); } } } - - /// Invoked on any index expression that occurs. Checks that if this is a slice - /// being indexed, the lifetime of the pointer includes the deref expr. - fn constrain_index(&mut self, index_expr: &hir::Expr<'_>, indexed_ty: Ty<'tcx>) { - debug!("constrain_index(index_expr=?, indexed_ty={}", self.ty_to_string(indexed_ty)); - - let r_index_expr = ty::ReScope(region::Scope { - id: index_expr.hir_id.local_id, - data: region::ScopeData::Node, - }); - if let ty::Ref(r_ptr, r_ty, _) = indexed_ty.kind { - match r_ty.kind { - ty::Slice(_) | ty::Str => { - self.sub_regions( - infer::IndexSlice(index_expr.span), - self.tcx.mk_region(r_index_expr), - r_ptr, - ); - } - _ => {} - } - } - } - - /// Guarantees that any lifetimes that appear in the type of the node `id` (after applying - /// adjustments) are valid for at least `minimum_lifetime`. - fn type_of_node_must_outlive( - &mut self, - origin: infer::SubregionOrigin<'tcx>, - hir_id: hir::HirId, - minimum_lifetime: ty::Region<'tcx>, - ) { - // Try to resolve the type. If we encounter an error, then typeck - // is going to fail anyway, so just stop here and let typeck - // report errors later on in the writeback phase. - let ty0 = self.resolve_node_type(hir_id); - - let ty = self - .tables - .borrow() - .adjustments() - .get(hir_id) - .and_then(|adj| adj.last()) - .map_or(ty0, |adj| adj.target); - let ty = self.resolve_type(ty); - debug!( - "constrain_regions_in_type_of_node(\ - ty={}, ty0={}, id={:?}, minimum_lifetime={:?})", - ty, ty0, hir_id, minimum_lifetime - ); - self.type_must_outlive(origin, ty, minimum_lifetime); - } - /// Adds constraints to inference such that `T: 'a` holds (or /// reports an error if it cannot). /// @@ -1014,7 +573,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { /// Link lifetimes of any ref bindings in `root_pat` to the pointers found /// in the discriminant, if needed. - fn link_pattern(&self, discr_cmt: mc::Place<'tcx>, root_pat: &hir::Pat<'_>) { + fn link_pattern(&self, discr_cmt: mc::PlaceWithHirId<'tcx>, root_pat: &hir::Pat<'_>) { debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat); ignore_err!(self.with_mc(|mc| { mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id }| { @@ -1035,7 +594,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { fn link_autoref( &self, expr: &hir::Expr<'_>, - expr_cmt: &mc::Place<'tcx>, + expr_cmt: &mc::PlaceWithHirId<'tcx>, autoref: &adjustment::AutoBorrow<'tcx>, ) { debug!("link_autoref(autoref={:?}, expr_cmt={:?})", autoref, expr_cmt); @@ -1045,13 +604,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { self.link_region(expr.span, r, ty::BorrowKind::from_mutbl(m.into()), expr_cmt); } - adjustment::AutoBorrow::RawPtr(m) => { - let r = self.tcx.mk_region(ty::ReScope(region::Scope { - id: expr.hir_id.local_id, - data: region::ScopeData::Node, - })); - self.link_region(expr.span, r, ty::BorrowKind::from_mutbl(m), expr_cmt); - } + adjustment::AutoBorrow::RawPtr(_) => {} } } @@ -1062,7 +615,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { span: Span, id: hir::HirId, mutbl: hir::Mutability, - cmt_borrowed: &mc::Place<'tcx>, + cmt_borrowed: &mc::PlaceWithHirId<'tcx>, ) { debug!( "link_region_from_node_type(id={:?}, mutbl={:?}, cmt_borrowed={:?})", @@ -1085,12 +638,12 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { span: Span, borrow_region: ty::Region<'tcx>, borrow_kind: ty::BorrowKind, - borrow_place: &mc::Place<'tcx>, + borrow_place: &mc::PlaceWithHirId<'tcx>, ) { - let origin = infer::DataBorrowed(borrow_place.ty, span); - self.type_must_outlive(origin, borrow_place.ty, borrow_region); + let origin = infer::DataBorrowed(borrow_place.place.ty, span); + self.type_must_outlive(origin, borrow_place.place.ty, borrow_region); - for pointer_ty in borrow_place.deref_tys() { + for pointer_ty in borrow_place.place.deref_tys() { debug!( "link_region(borrow_region={:?}, borrow_kind={:?}, pointer_ty={:?})", borrow_region, borrow_kind, borrow_place @@ -1106,7 +659,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { _ => assert!(pointer_ty.is_box(), "unexpected built-in deref type {}", pointer_ty), } } - if let mc::PlaceBase::Upvar(upvar_id) = borrow_place.base { + if let mc::PlaceBase::Upvar(upvar_id) = borrow_place.place.base { self.link_upvar_region(span, borrow_region, upvar_id); } } @@ -1115,19 +668,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). @@ -1203,7 +758,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 @@ -1238,9 +793,9 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { // A closure capture can't be borrowed for longer than the // reference to the closure. - if let ty::Closure(closure_def_id, substs) = ty.kind { - match self.infcx.closure_kind(closure_def_id, substs) { - Some(ty::ClosureKind::Fn) | Some(ty::ClosureKind::FnMut) => { + if let ty::Closure(_, substs) = ty.kind { + match self.infcx.closure_kind(substs) { + 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(), @@ -1259,39 +814,4 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { } } } - - /// Checks that the values provided for type/region arguments in a given - /// expression are well-formed and in-scope. - fn substs_wf_in_scope( - &mut self, - origin: infer::ParameterOrigin, - substs: SubstsRef<'tcx>, - expr_span: Span, - expr_region: ty::Region<'tcx>, - ) { - debug!( - "substs_wf_in_scope(substs={:?}, \ - expr_region={:?}, \ - origin={:?}, \ - expr_span={:?})", - substs, expr_region, origin, expr_span - ); - - let origin = infer::ParameterInScope(origin, expr_span); - - for kind in substs { - match kind.unpack() { - GenericArgKind::Lifetime(lt) => { - self.sub_regions(origin.clone(), expr_region, lt); - } - GenericArgKind::Type(ty) => { - let ty = self.resolve_type(ty); - self.type_must_outlive(origin.clone(), ty, expr_region); - } - GenericArgKind::Const(_) => { - // Const parameters don't impose constraints. - } - } - } - } } diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index 7bc121733a4e6..0f3133e0695f1 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -35,16 +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::hir::map::Map; -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>) { @@ -60,7 +58,7 @@ struct InferBorrowKindVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -93,7 +91,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (closure_def_id, substs) = match ty.kind { ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), - ty::Error => { + ty::Error(_) => { // #51714: skip analysis when we have already encountered type errors return; } @@ -108,23 +106,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let infer_kind = if let UpvarSubsts::Closure(closure_substs) = substs { - self.closure_kind(closure_def_id, closure_substs).is_none().then_some(closure_substs) + self.closure_kind(closure_substs).is_none().then_some(closure_substs) } else { None }; - if let Some(upvars) = self.tcx.upvars(closure_def_id) { - let mut upvar_list: FxIndexMap = + if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { + let mut closure_captures: FxIndexMap = FxIndexMap::with_capacity_and_hasher(upvars.len(), Default::default()); for (&var_hir_id, _) in upvars.iter() { let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: LocalDefId::from_def_id(closure_def_id), + closure_expr_id: closure_def_id.expect_local(), }; debug!("seed upvar_id {:?}", upvar_id); // Adding the upvar Id to the list of Upvars, which will be added // to the map for the closure at the end of the for loop. - upvar_list.insert(var_hir_id, upvar_id); + closure_captures.insert(var_hir_id, upvar_id); let capture_kind = match capture_clause { hir::CaptureBy::Value => ty::UpvarCapture::ByValue, @@ -142,13 +140,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Add the vector of upvars to the map keyed with the closure id. // This gives us an easier access to them without having to call // tcx.upvars again.. - if !upvar_list.is_empty() { - self.tables.borrow_mut().upvar_list.insert(closure_def_id, upvar_list); + if !closure_captures.is_empty() { + self.tables.borrow_mut().closure_captures.insert(closure_def_id, closure_captures); } } 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, @@ -169,7 +167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unify the (as yet unbound) type variable in the closure // substs with the kind we inferred. let inferred_kind = delegate.current_closure_kind; - let closure_kind_ty = closure_substs.as_closure().kind_ty(closure_def_id, self.tcx); + let closure_kind_ty = closure_substs.as_closure().kind_ty(); self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty); // If we have an origin, store it. @@ -198,9 +196,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}", closure_hir_id, substs, final_upvar_tys ); - for (upvar_ty, final_upvar_ty) in - substs.upvar_tys(closure_def_id, self.tcx).zip(final_upvar_tys) - { + for (upvar_ty, final_upvar_ty) in substs.upvar_tys().zip(final_upvar_tys) { self.demand_suptype(span, upvar_ty, final_upvar_ty); } @@ -222,14 +218,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let closure_def_id = tcx.hir().local_def_id(closure_id); - tcx.upvars(closure_def_id) + tcx.upvars_mentioned(closure_def_id) .iter() .flat_map(|upvars| { upvars.iter().map(|(&var_hir_id, _)| { 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: LocalDefId::from_def_id(closure_def_id), + closure_expr_id: closure_def_id, }; let capture = self.tables.borrow().upvar_capture(upvar_id); @@ -264,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). @@ -274,10 +270,13 @@ struct InferBorrowKind<'a, 'tcx> { impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { fn adjust_upvar_borrow_kind_for_consume( &mut self, - place: &mc::Place<'tcx>, + place_with_id: &mc::PlaceWithHirId<'tcx>, mode: euv::ConsumeMode, ) { - debug!("adjust_upvar_borrow_kind_for_consume(place={:?}, mode={:?})", place, mode); + debug!( + "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, mode={:?})", + place_with_id, mode + ); // we only care about moves match mode { @@ -288,7 +287,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { } let tcx = self.fcx.tcx; - let upvar_id = if let PlaceBase::Upvar(upvar_id) = place.base { + let upvar_id = if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { upvar_id } else { return; @@ -300,22 +299,22 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { self.adjust_closure_kind( upvar_id.closure_expr_id, ty::ClosureKind::FnOnce, - place.span, + tcx.hir().span(place_with_id.hir_id), var_name(tcx, upvar_id.var_path.hir_id), ); self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue); } - /// Indicates that `place` is being directly mutated (e.g., assigned + /// Indicates that `place_with_id` is being directly mutated (e.g., assigned /// to). If the place is based on a by-ref upvar, this implies that /// the upvar must be borrowed using an `&mut` borrow. - fn adjust_upvar_borrow_kind_for_mut(&mut self, place: &mc::Place<'tcx>) { - debug!("adjust_upvar_borrow_kind_for_mut(place={:?})", place); + fn adjust_upvar_borrow_kind_for_mut(&mut self, place_with_id: &mc::PlaceWithHirId<'tcx>) { + debug!("adjust_upvar_borrow_kind_for_mut(place_with_id={:?})", place_with_id); - if let PlaceBase::Upvar(upvar_id) = place.base { + if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { let mut borrow_kind = ty::MutBorrow; - for pointer_ty in place.deref_tys() { + for pointer_ty in place_with_id.place.deref_tys() { match pointer_ty.kind { // Raw pointers don't inherit mutability. ty::RawPtr(_) => return, @@ -327,20 +326,28 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { _ => (), } } - self.adjust_upvar_deref(upvar_id, place.span, borrow_kind); + self.adjust_upvar_deref( + upvar_id, + self.fcx.tcx.hir().span(place_with_id.hir_id), + borrow_kind, + ); } } - fn adjust_upvar_borrow_kind_for_unique(&mut self, place: &mc::Place<'tcx>) { - debug!("adjust_upvar_borrow_kind_for_unique(place={:?})", place); + fn adjust_upvar_borrow_kind_for_unique(&mut self, place_with_id: &mc::PlaceWithHirId<'tcx>) { + debug!("adjust_upvar_borrow_kind_for_unique(place_with_id={:?})", place_with_id); - if let PlaceBase::Upvar(upvar_id) = place.base { - if place.deref_tys().any(ty::TyS::is_unsafe_ptr) { + if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { + if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { // Raw pointers don't inherit mutability. return; } // for a borrowed pointer to be unique, its base must be unique - self.adjust_upvar_deref(upvar_id, place.span, ty::UniqueImmBorrow); + self.adjust_upvar_deref( + upvar_id, + self.fcx.tcx.hir().span(place_with_id.hir_id), + ty::UniqueImmBorrow, + ); } } @@ -398,8 +405,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 @@ -407,8 +413,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, _) => {} } } @@ -420,7 +425,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={})", @@ -443,14 +448,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; @@ -461,32 +464,32 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { } impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { - fn consume(&mut self, place: &mc::Place<'tcx>, mode: euv::ConsumeMode) { - debug!("consume(place={:?},mode={:?})", place, mode); - self.adjust_upvar_borrow_kind_for_consume(place, mode); + fn consume(&mut self, place_with_id: &mc::PlaceWithHirId<'tcx>, mode: euv::ConsumeMode) { + debug!("consume(place_with_id={:?},mode={:?})", place_with_id, mode); + self.adjust_upvar_borrow_kind_for_consume(place_with_id, mode); } - fn borrow(&mut self, place: &mc::Place<'tcx>, bk: ty::BorrowKind) { - debug!("borrow(place={:?}, bk={:?})", place, bk); + fn borrow(&mut self, place_with_id: &mc::PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { + debug!("borrow(place_with_id={:?}, bk={:?})", place_with_id, bk); match bk { ty::ImmBorrow => {} ty::UniqueImmBorrow => { - self.adjust_upvar_borrow_kind_for_unique(place); + self.adjust_upvar_borrow_kind_for_unique(place_with_id); } ty::MutBorrow => { - self.adjust_upvar_borrow_kind_for_mut(place); + self.adjust_upvar_borrow_kind_for_mut(place_with_id); } } } - fn mutate(&mut self, assignee_place: &mc::Place<'tcx>) { + fn mutate(&mut self, assignee_place: &mc::PlaceWithHirId<'tcx>) { debug!("mutate(assignee_place={:?})", assignee_place); self.adjust_upvar_borrow_kind_for_mut(assignee_place); } } -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 026e68e10e04d..d1a86a7ee89a8 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -1,26 +1,26 @@ use crate::check::{FnCtxt, Inherited}; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; -use rustc::middle::lang_items; -use rustc::session::parse::feature_err; -use rustc::ty::subst::{InternalSubsts, Subst}; -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::def_id::DefId; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::itemlikevisit::ParItemLikeVisitor; +use rustc_hir::lang_items; use rustc_hir::ItemKind; -use rustc_span::symbol::sym; +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, Symbol}; use rustc_span::Span; use rustc_trait_selection::opaque_types::may_define_opaque_type; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; -use rustc_hir as hir; -use rustc_hir::itemlikevisit::ParItemLikeVisitor; - /// Helper type of a temporary returned by `.for_item(...)`. /// This is necessary because we can't write the following bound: /// @@ -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 { @@ -98,34 +98,48 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) { // // won't be allowed unless there's an *explicit* implementation of `Send` // for `T` - hir::ItemKind::Impl { defaultness, ref of_trait, ref self_ty, .. } => { + hir::ItemKind::Impl { + defaultness, + defaultness_span, + polarity, + ref of_trait, + ref self_ty, + .. + } => { let is_auto = tcx .impl_trait_ref(tcx.hir().local_def_id(item.hir_id)) .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id)); - let polarity = tcx.impl_polarity(def_id); if let (hir::Defaultness::Default { .. }, true) = (defaultness, is_auto) { - tcx.sess.span_err(item.span, "impls of auto traits cannot be default"); + let sp = of_trait.as_ref().map(|t| t.path.span).unwrap_or(item.span); + let mut err = + tcx.sess.struct_span_err(sp, "impls of auto traits cannot be default"); + err.span_labels(defaultness_span, "default because of this"); + err.span_label(sp, "auto trait"); + err.emit(); } - match polarity { - ty::ImplPolarity::Positive => { + // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span. + match (tcx.impl_polarity(def_id), polarity) { + (ty::ImplPolarity::Positive, _) => { check_impl(tcx, item, self_ty, of_trait); } - ty::ImplPolarity::Negative => { + (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => { // FIXME(#27579): what amount of WF checking do we need for neg impls? - if of_trait.is_some() && !is_auto { + if let hir::Defaultness::Default { .. } = defaultness { + let mut spans = vec![span]; + spans.extend(defaultness_span); struct_span_err!( tcx.sess, - item.span, - E0192, - "negative impls are only allowed for \ - auto traits (e.g., `Send` and `Sync`)" + spans, + E0750, + "negative impls cannot be default impls" ) - .emit() + .emit(); } } - ty::ImplPolarity::Reservation => { + (ty::ImplPolarity::Reservation, _) => { // FIXME: what amount of WF checking do we need for reservation impls? } + _ => unreachable!(), } } hir::ItemKind::Fn(..) => { @@ -169,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 { @@ -181,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, @@ -243,12 +257,12 @@ 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 { - hir::ImplItemKind::Method(ref sig, _) => Some(sig), + hir::ImplItemKind::Fn(ref sig, _) => Some(sig), _ => None, }; @@ -278,9 +292,9 @@ fn check_associated_item( ty::AssocKind::Const => { let ty = fcx.tcx.type_of(item.def_id); let ty = fcx.normalize_associated_types_in(span, &ty); - fcx.register_wf_obligation(ty, span, code.clone()); + fcx.register_wf_obligation(ty.into(), 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"); @@ -299,12 +313,9 @@ fn check_associated_item( if item.defaultness.has_value() { let ty = fcx.tcx.type_of(item.def_id); let ty = fcx.normalize_associated_types_in(span, &ty); - fcx.register_wf_obligation(ty, span, code.clone()); + fcx.register_wf_obligation(ty.into(), span, code.clone()); } } - ty::AssocKind::OpaqueTy => { - // Do nothing: opaque types check themselves. - } } implied_bounds @@ -355,7 +366,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)); @@ -392,14 +403,32 @@ fn check_type_defn<'tcx, F>( // All field types must be well-formed. for field in &variant.fields { fcx.register_wf_obligation( - field.ty, + field.ty.into(), field.span, 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::PredicateKind::ConstEvaluatable(discr_def_id.to_def_id(), discr_substs) + .to_predicate(fcx.tcx), + )); + } } - 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![] @@ -412,7 +441,9 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { let trait_def_id = tcx.hir().local_def_id(item.hir_id); let trait_def = tcx.trait_def(trait_def_id); - if trait_def.is_marker { + if trait_def.is_marker + || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker) + { for associated_def_id in &*tcx.associated_item_def_ids(trait_def_id) { struct_span_err!( tcx.sess, @@ -425,8 +456,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![] }); @@ -539,7 +570,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 }) } @@ -559,7 +598,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: hir::HirId, ty_span: Span, allow_fo } } - fcx.register_wf_obligation(item_ty, ty_span, ObligationCauseCode::MiscObligation); + fcx.register_wf_obligation(item_ty.into(), ty_span, ObligationCauseCode::MiscObligation); if forbid_unsized { fcx.register_bound( item_ty, @@ -608,16 +647,16 @@ fn check_impl<'tcx>( let self_ty = fcx.tcx.type_of(item_def_id); let self_ty = fcx.normalize_associated_types_in(item.span, &self_ty); fcx.register_wf_obligation( - self_ty, + self_ty.into(), ast_self_ty.span, ObligationCauseCode::MiscObligation, ); } } - 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) }); } @@ -656,7 +695,7 @@ fn check_where_clauses<'tcx, 'fcx>( // be sure if it will error or not as user might always specify the other. if !ty.needs_subst() { fcx.register_wf_obligation( - ty, + ty.into(), fcx.tcx.def_span(param.def_id), ObligationCauseCode::MiscObligation, ); @@ -690,13 +729,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) } } }); @@ -734,7 +773,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. @@ -759,14 +801,14 @@ fn check_where_clauses<'tcx, 'fcx>( traits::Obligation::new(cause, fcx.param_env, pred) }); - let mut predicates = predicates.instantiate_identity(fcx.tcx); + let 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); - for _ in 0..opaque_types.len() { - predicates.spans.push(span); + if let Some((mut return_ty, span)) = return_ty { + if return_ty.has_infer_types_or_consts() { + fcx.select_obligations_where_possible(false, |_| {}); + return_ty = fcx.resolve_vars_if_possible(&return_ty); } - predicates.predicates.extend(opaque_types); + check_opaque_types(tcx, fcx, def_id.expect_local(), span, return_ty); } let predicates = fcx.normalize_associated_types_in(span, &predicates); @@ -774,8 +816,8 @@ fn check_where_clauses<'tcx, 'fcx>( debug!("check_where_clauses: predicates={:?}", predicates.predicates); assert_eq!(predicates.predicates.len(), predicates.spans.len()); let wf_obligations = - predicates.predicates.iter().zip(predicates.spans.iter()).flat_map(|(p, sp)| { - traits::wf::predicate_obligations(fcx, fcx.param_env, fcx.body_id, p, *sp) + predicates.predicates.iter().zip(predicates.spans.iter()).flat_map(|(&p, &sp)| { + traits::wf::predicate_obligations(fcx, fcx.param_env, fcx.body_id, p, sp) }); for obligation in wf_obligations.chain(default_obligations) { @@ -796,13 +838,13 @@ fn check_fn_or_method<'fcx, 'tcx>( let sig = fcx.normalize_associated_types_in(span, &sig); let sig = fcx.tcx.liberate_late_bound_regions(def_id, &sig); - for (input_ty, span) in sig.inputs().iter().zip(hir_sig.decl.inputs.iter().map(|t| t.span)) { - fcx.register_wf_obligation(&input_ty, span, ObligationCauseCode::MiscObligation); + for (&input_ty, span) in sig.inputs().iter().zip(hir_sig.decl.inputs.iter().map(|t| t.span)) { + fcx.register_wf_obligation(input_ty.into(), span, ObligationCauseCode::MiscObligation); } implied_bounds.extend(sig.inputs()); fcx.register_wf_obligation( - sig.output(), + sig.output().into(), hir_sig.decl.output.span(), ObligationCauseCode::ReturnType, ); @@ -835,132 +877,120 @@ 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> { +) { trace!("check_opaque_types(ty={:?})", ty); - let mut substituted_predicates = Vec::new(); ty.fold_with(&mut ty::fold::BottomUpFolder { tcx: fcx.tcx, ty_op: |ty| { if let ty::Opaque(def_id, substs) = ty.kind { trace!("check_opaque_types: opaque_ty, {:?}, {:?}", def_id, substs); let generics = tcx.generics_of(def_id); - // Only check named `impl Trait` types defined in this crate. - if generics.parent.is_none() && def_id.is_local() { - let opaque_hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - 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(); - } - }, - - ty::subst::GenericArgKind::Lifetime(region) => { - let param_span = tcx.def_span(param.def_id); - if let ty::ReStatic = region { - tcx.sess - .struct_span_err( - span, - "non-defining opaque type use \ - in defining scope", - ) - .span_label( - param_span, - "cannot use static lifetime; use a bound lifetime \ - instead or remove the lifetime parameter from the \ - opaque type", - ) - .emit(); - } else { - seen.entry(region).or_default().push(param_span); - } - } - - 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 { + + let opaque_hir_id = if let Some(local_id) = def_id.as_local() { + tcx.hir().as_local_hir_id(local_id) + } else { + // Opaque types from other crates won't have defining uses in this crate. + return ty; + }; + if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: Some(_), .. }) = + tcx.hir().expect_item(opaque_hir_id).kind + { + // No need to check return position impl trait (RPIT) + // because for type and const parameters they are correct + // by construction: we convert + // + // fn foo() -> impl Trait + // + // into + // + // type Foo + // fn foo() -> Foo. + // + // For lifetime parameters we convert + // + // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> + // + // into + // + // type foo::<'p0..'pn>::Foo<'q0..'qm> + // fn foo() -> foo::<'static..'static>::Foo<'l0..'lm>. + // + // which would error here on all of the `'static` args. + return ty; + } + if !may_define_opaque_type(tcx, fn_def_id, opaque_hir_id) { + return ty; + } + trace!("check_opaque_types: may define, generics={:#?}", generics); + let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); + for (i, arg) in substs.iter().enumerate() { + let arg_is_param = match arg.unpack() { + GenericArgKind::Type(ty) => matches!(ty.kind, ty::Param(_)), + + GenericArgKind::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( + 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", ) - .span_note(spans, "lifetime used multiple times") .emit(); + continue; } - } - } // if may_define_opaque_type - // Now register the bounds on the parameters of the opaque type - // so the parameters given by the function need to fulfill them. - // - // type Foo = impl Baz + 'static; - // fn foo() -> Foo { .. *} - // - // becomes - // - // type Foo = impl Baz + 'static; - // fn foo() -> Foo { .. *} - let predicates = tcx.predicates_of(def_id); - trace!("check_opaque_types: may define, predicates={:#?}", predicates,); - for &(pred, _) in predicates.predicates { - let substituted_pred = pred.subst(fcx.tcx, substs); - // Avoid duplication of predicates that contain no parameters, for example. - if !predicates.predicates.iter().any(|&(p, _)| p == substituted_pred) { - substituted_predicates.push(substituted_pred); + true } + + 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") + .span_note( + tcx.def_span(opaque_param.def_id), + &format!( + "used non-generic {} `{}` for generic parameter", + opaque_param.kind.descr(), + arg, + ), + ) + .emit(); } - } // if is_named_opaque_type + } // 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(); + } + } } // if let Opaque ty }, lt_op: |lt| lt, ct_op: |ct| ct, }); - substituted_predicates } const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box`, \ @@ -976,7 +1006,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; } @@ -1012,7 +1042,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, ), ) @@ -1088,7 +1118,7 @@ fn receiver_is_valid<'fcx, 'tcx>( ); if can_eq_self(potential_self_ty) { - autoderef.finalize(fcx); + fcx.register_predicates(autoderef.into_obligations()); if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, self_ty, potential_self_ty) @@ -1140,8 +1170,11 @@ fn receiver_is_implemented( substs: fcx.tcx.mk_substs_trait(receiver_ty, &[]), }; - let obligation = - traits::Obligation::new(cause, fcx.param_env, trait_ref.without_const().to_predicate()); + let obligation = traits::Obligation::new( + cause, + fcx.param_env, + trait_ref.without_const().to_predicate(fcx.tcx), + ); if fcx.predicate_must_hold_modulo_regions(&obligation) { true @@ -1192,7 +1225,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(); @@ -1216,11 +1249,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); @@ -1269,8 +1303,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> { @@ -1279,6 +1319,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() @@ -1291,11 +1332,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> { @@ -1317,7 +1367,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 b8f8030e3cdd7..4704d8fc7666f 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -4,16 +4,14 @@ use crate::check::FnCtxt; -use rustc::hir::map::Map; -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::{DefId, DefIdSet, DefIndex}; 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; @@ -41,8 +39,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 { @@ -62,22 +61,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.visit_fru_field_types(); wbcx.visit_opaque_types(body.value.span); wbcx.visit_coercion_casts(); - wbcx.visit_free_region_map(); wbcx.visit_user_provided_tys(); wbcx.visit_user_provided_sigs(); wbcx.visit_generator_interior_types(); - let used_trait_imports = mem::replace( - &mut self.tables.borrow_mut().used_trait_imports, - Lrc::new(DefIdSet::default()), - ); + let used_trait_imports = mem::take(&mut self.tables.borrow_mut().used_trait_imports); debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); wbcx.tables.used_trait_imports = used_trait_imports; - wbcx.tables.upvar_list = - mem::replace(&mut self.tables.borrow_mut().upvar_list, Default::default()); + wbcx.tables.closure_captures = + mem::replace(&mut self.tables.borrow_mut().closure_captures, 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); @@ -109,11 +107,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { body: &'tcx hir::Body<'tcx>, rustc_dump_user_substs: bool, ) -> WritebackCx<'cx, 'tcx> { - let owner = body.id().hir_id; + let owner = body.id().hir_id.owner; WritebackCx { fcx, - tables: ty::TypeckTables::empty(Some(DefId::local(owner.owner))), + tables: ty::TypeckTables::empty(Some(owner)), body, rustc_dump_user_substs, } @@ -125,7 +123,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn write_ty_to_tables(&mut self, hir_id: hir::HirId, ty: Ty<'tcx>) { debug!("write_ty_to_tables({:?}, {:?})", hir_id, ty); - assert!(!ty.needs_infer() && !ty.has_placeholders()); + assert!(!ty.needs_infer() && !ty.has_placeholders() && !ty.has_free_regions()); self.tables.node_types_mut().insert(hir_id, ty); } @@ -135,8 +133,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); @@ -163,12 +160,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(); + } } _ => {} } @@ -200,11 +203,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // to access an unexistend index. We assume that more relevant errors will // already have been emitted, so we only gate on this with an ICE if no // error has been emitted. (#64638) - self.tcx().sess.delay_span_bug( + self.fcx.tcx.ty_error_with_message( e.span, &format!("bad index {:?} for base: `{:?}`", index, base), - ); - self.fcx.tcx.types.err + ) }); let index_ty = self.fcx.resolve_vars_if_possible(&index_ty); @@ -213,22 +215,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(); } - }); + } } } } @@ -244,7 +245,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // traffic in node-ids or update tables in the type context etc. impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -327,9 +328,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let new_upvar_capture = match *upvar_capture { ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue, ty::UpvarCapture::ByRef(ref upvar_borrow) => { - let r = upvar_borrow.region; - let r = self.resolve(&r, &upvar_id.var_path.hir_id); - ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: upvar_borrow.kind, region: r }) + ty::UpvarCapture::ByRef(ty::UpvarBorrow { + kind: upvar_borrow.kind, + region: self.tcx().lifetimes.re_erased, + }) } }; debug!("Upvar capture for {:?} resolved to {:?}", upvar_id, new_upvar_capture); @@ -339,11 +341,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_closures(&mut self) { let fcx_tables = self.fcx.tables.borrow(); - debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); - let common_local_id_root = fcx_tables.local_id_root.unwrap(); + assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); + let common_hir_owner = fcx_tables.hir_owner.unwrap(); for (&id, &origin) in fcx_tables.closure_kind_origins().iter() { - let hir_id = hir::HirId { owner: common_local_id_root.index, local_id: id }; + let hir_id = hir::HirId { owner: common_hir_owner, local_id: id }; self.tables.closure_kind_origins_mut().insert(hir_id, origin); } } @@ -351,32 +353,31 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_coercion_casts(&mut self) { let fcx_tables = self.fcx.tables.borrow(); let fcx_coercion_casts = fcx_tables.coercion_casts(); - debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); + assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); for local_id in fcx_coercion_casts { self.tables.set_coercion_cast(*local_id); } } - fn visit_free_region_map(&mut self) { - self.tables.free_region_map = self.fcx.tables.borrow().free_region_map.clone(); - debug_assert!(!self.tables.free_region_map.elements().any(|r| r.has_local_value())); - } - fn visit_user_provided_tys(&mut self) { let fcx_tables = self.fcx.tables.borrow(); - debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); - let common_local_id_root = fcx_tables.local_id_root.unwrap(); + assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); + let common_hir_owner = fcx_tables.hir_owner.unwrap(); let mut errors_buffer = Vec::new(); for (&local_id, c_ty) in fcx_tables.user_provided_types().iter() { - let hir_id = hir::HirId { owner: common_local_id_root.index, local_id }; + 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 { @@ -403,32 +404,31 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_user_provided_sigs(&mut self) { let fcx_tables = self.fcx.tables.borrow(); - debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); + 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); } } fn visit_generator_interior_types(&mut self) { let fcx_tables = self.fcx.tables.borrow(); - debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); + assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); self.tables.generator_interior_types = fcx_tables.generator_interior_types.clone(); } 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 instantiated_ty = - self.tcx().erase_regions(&self.resolve(&opaque_defn.concrete_ty, &hir_id)); + 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()); @@ -454,7 +454,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let mut skip_add = false; if let ty::Opaque(defin_ty_def_id, _substs) = definition_ty.kind { - if let hir::OpaqueTyOrigin::TypeAlias = opaque_defn.origin { + if let hir::OpaqueTyOrigin::Misc = opaque_defn.origin { if def_id == defin_ty_def_id { debug!( "skipping adding concrete definition for opaque type {:?} {:?}", @@ -465,7 +465,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 @@ -493,7 +493,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"); } } } @@ -560,36 +560,46 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_liberated_fn_sigs(&mut self) { let fcx_tables = self.fcx.tables.borrow(); - debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); - let common_local_id_root = fcx_tables.local_id_root.unwrap(); + assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); + let common_hir_owner = fcx_tables.hir_owner.unwrap(); for (&local_id, fn_sig) in fcx_tables.liberated_fn_sigs().iter() { - let hir_id = hir::HirId { owner: common_local_id_root.index, local_id }; + 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); } } fn visit_fru_field_types(&mut self) { let fcx_tables = self.fcx.tables.borrow(); - debug_assert_eq!(fcx_tables.local_id_root, self.tables.local_id_root); - let common_local_id_root = fcx_tables.local_id_root.unwrap(); + assert_eq!(fcx_tables.hir_owner, self.tables.hir_owner); + let common_hir_owner = fcx_tables.hir_owner.unwrap(); for (&local_id, ftys) in fcx_tables.fru_field_types().iter() { - let hir_id = hir::HirId { owner: common_local_id_root.index, local_id }; + let hir_id = hir::HirId { owner: common_hir_owner, local_id }; let ftys = self.resolve(ftys, &hir_id); self.tables.fru_field_types_mut().insert(hir_id, ftys); } } - 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 } } @@ -604,28 +614,22 @@ impl Locatable for Span { } } -impl Locatable for DefIndex { - fn to_span(&self, tcx: TyCtxt<'_>) -> Span { - let hir_id = tcx.hir().def_index_to_hir_id(*self); - tcx.hir().span(hir_id) - } -} - impl Locatable for hir::HirId { fn to_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.hir().span(*self) } } -/////////////////////////////////////////////////////////////////////////// -// The Resolver. This is the type folding engine that detects -// unresolved types and so forth. - +/// The Resolver. This is the type folding engine that detects +/// unresolved types and so forth. struct Resolver<'cx, 'tcx> { tcx: TyCtxt<'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> { @@ -634,16 +638,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> { @@ -653,29 +670,29 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match self.infcx.fully_resolve(&t) { - Ok(t) => t, + Ok(t) => self.infcx.tcx.erase_regions(&t), Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); - self.report_error(t); - self.tcx().types.err + self.report_type_error(t); + self.replaced_with_error = true; + self.tcx().ty_error() } } } - // FIXME This should be carefully checked - // We could use `self.report_error` but it doesn't accept a ty::Region, right now. fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - self.infcx.fully_resolve(&r).unwrap_or(self.tcx.lifetimes.re_static) + debug_assert!(!r.is_late_bound(), "Should not be resolving bound region."); + self.tcx.lifetimes.re_erased } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { match self.infcx.fully_resolve(&ct) { - Ok(ct) => ct, + 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().const_error(ct.ty) } } } diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs index 3517e09133c28..81daf064bb368 100644 --- a/src/librustc_typeck/check_unused.rs +++ b/src/librustc_typeck/check_unused.rs @@ -1,16 +1,14 @@ -use crate::lint; -use rustc::ty::TyCtxt; -use rustc_ast::ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_hir::print::visibility_qualified; -use rustc_span::Span; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint; +use rustc_span::{Span, Symbol}; pub fn check_crate(tcx: TyCtxt<'_>) { - let mut used_trait_imports = DefIdSet::default(); + let mut used_trait_imports = FxHashSet::default(); for &body_id in tcx.hir().krate().bodies.keys() { let item_def_id = tcx.hir().body_owner_def_id(body_id); let imports = tcx.used_trait_imports(item_def_id); @@ -41,7 +39,7 @@ impl ItemLikeVisitor<'v> for CheckVisitor<'tcx> { struct CheckVisitor<'tcx> { tcx: TyCtxt<'tcx>, - used_trait_imports: DefIdSet, + used_trait_imports: FxHashSet, } impl CheckVisitor<'tcx> { @@ -72,7 +70,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { // Collect first the crates that are completely unused. These we // can always suggest removing (no matter which edition we are // in). - let unused_extern_crates: FxHashMap = tcx + let unused_extern_crates: FxHashMap = tcx .maybe_unused_extern_crates(LOCAL_CRATE) .iter() .filter(|&&(def_id, _)| { @@ -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)).is_none() { + return false; } true }) @@ -116,13 +112,14 @@ 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 def_id = extern_crate.def_id.expect_local(); + let id = tcx.hir().as_local_hir_id(def_id); let item = tcx.hir().expect_item(id); // If the crate is fully unused, we suggest removing it altogether. // We do this in any edition. if extern_crate.warn_if_unused { - if let Some(&span) = unused_extern_crates.get(&extern_crate.def_id) { + if let Some(&span) = unused_extern_crates.get(&def_id) { tcx.struct_span_lint_hir(lint, id, span, |lint| { // Removal suggestion span needs to include attributes (Issue #54400) let span_with_attrs = tcx @@ -176,16 +173,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 +202,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 +214,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 2ea7601ae6538..8c6161a626473 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -1,18 +1,19 @@ //! 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::{SuppressRegionErrors, TyCtxtInferExt}; +use rustc_infer::infer::{RegionckMode, TyCtxtInferExt}; +use rustc_middle::ty::adjustment::CoerceUnsizedInfo; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_trait_selection::traits::error_reporting::InferCtxtExt; use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError}; use rustc_trait_selection::traits::predicate_for_trait_def; @@ -35,7 +36,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 +48,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 { + 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 +70,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,212 +133,203 @@ 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); - - create_err(&format!( - "the trait `DispatchFromDyn` may only be implemented \ + tcx.infer_ctxt().enter(|infcx| { + let cause = ObligationCause::misc(span, impl_hir_id); + + use ty::TyKind::*; + match (&source.kind, &target.kind) { + (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) + if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok() && mutbl_a == *mutbl_b => {} + (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), + (&Adt(def_a, substs_a), &Adt(def_b, substs_b)) + if def_a.is_struct() && def_b.is_struct() => + { + if def_a != def_b { + let source_path = tcx.def_path_str(def_a.did); + let target_path = tcx.def_path_str(def_b.did); + + create_err(&format!( + "the trait `DispatchFromDyn` may only be implemented \ for a coercion between structures with the same \ definition; expected `{}`, found `{}`", - source_path, target_path, - )) - .emit(); + 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, - SuppressRegionErrors::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 outlives_env = OutlivesEnvironment::new(param_env); + infcx.resolve_regions_and_report_errors( + impl_did.to_def_id(), + &outlives_env, + RegionckMode::default(), + ); } - _ => { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ + } + _ => { + 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()); @@ -562,14 +549,8 @@ pub fn coerce_unsized_info(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedI } // 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, - SuppressRegionErrors::default(), - ); + infcx.resolve_regions_and_report_errors(impl_did, &outlives_env, RegionckMode::default()); CoerceUnsizedInfo { custom_kind: kind } }) diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index 60e5df68b5842..93ee87f6c572e 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, @@ -272,9 +296,7 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { item.span, ); } - ty::Error => { - return; - } + ty::Error(_) => {} _ => { struct_span_err!( self.tcx.sess, @@ -288,7 +310,6 @@ impl ItemLikeVisitor<'v> for InherentCollect<'tcx> { to wrap it instead", ) .emit(); - return; } } } @@ -306,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, @@ -323,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, @@ -331,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 1eae9d3b7fa6c..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) { @@ -26,7 +26,8 @@ impl InherentOverlapChecker<'tcx> { let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).any(|item2| { // Symbols and namespace match, compare hygienically. item1.kind.namespace() == item2.kind.namespace() - && item1.ident.modern() == item2.ident.modern() + && item1.ident.normalize_to_macros_2_0() + == item2.ident.normalize_to_macros_2_0() }); if collision { @@ -50,11 +51,12 @@ impl InherentOverlapChecker<'tcx> { let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).find(|item2| { // Symbols and namespace match, compare hygienically. item1.kind.namespace() == item2.kind.namespace() - && item1.ident.modern() == item2.ident.modern() + && item1.ident.normalize_to_macros_2_0() + == item2.ident.normalize_to_macros_2_0() }); if let Some(item2) = collision { - let name = item1.ident.modern(); + let name = item1.ident.normalize_to_macros_2_0(); let mut err = struct_span_err!( self.tcx.sess, self.tcx.span_of_impl(item1.def_id).unwrap(), diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 0d0149f967358..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!( @@ -76,6 +93,22 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt<'_>, impl_def_id: DefId, tra return; } + if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = + tcx.trait_def(trait_def_id).specialization_kind + { + if !tcx.features().specialization && !tcx.features().min_specialization { + let span = impl_header_span(tcx, impl_def_id); + tcx.sess + .struct_span_err( + span, + "implementing `rustc_specialization_trait` traits is unstable", + ) + .help("add `#![feature(min_specialization)]` to the crate attributes to enable") + .emit(); + return; + } + } + let trait_name = if did == li.fn_trait() { "Fn" } else if did == li.fn_mut_trait() { @@ -101,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; } @@ -132,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 { @@ -161,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 779b30c55e12d..054165f2b0977 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -17,34 +17,36 @@ use crate::astconv::{AstConv, Bounds, SizedByDefault}; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::constrained_generic_params as cgp; -use crate::lint; -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::session::parse::feature_err; -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_span::symbol::{kw, sym, Symbol}; +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; +use rustc_middle::ty::util::Discr; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; +use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; +use rustc_session::config::SanitizerSet; +use rustc_session::lint; +use rustc_session::parse::feature_err; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi; +use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; mod type_of; @@ -105,7 +107,7 @@ pub struct ItemCtxt<'tcx> { crate struct PlaceholderHirTyCollector(crate Vec); impl<'v> Visitor<'v> for PlaceholderHirTyCollector { - type Map = Map<'v>; + type Map = intravisit::ErasedMap<'v>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -135,20 +137,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 +151,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 +208,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 +223,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 +270,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 +296,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> { @@ -320,19 +308,17 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { } fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - self.tcx().sess.delay_span_bug(span, "bad placeholder type"); - self.tcx().types.err + self.tcx().ty_error_with_message(span, "bad_placeholder_type") } 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().const_error(ty) } fn projected_ty_from_poly_trait_ref( @@ -408,9 +394,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(_) @@ -430,7 +418,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> { _ => {} } err.emit(); - self.tcx().types.err + self.tcx().ty_error() } } @@ -489,7 +477,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 +485,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, @@ -536,7 +527,7 @@ fn type_param_predicates( if param_id == item_hir_id { let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id); extend = - Some((identity_trait_ref.without_const().to_predicate(), item.span)); + Some((identity_trait_ref.without_const().to_predicate(tcx), item.span)); } generics } @@ -556,8 +547,10 @@ fn type_param_predicates( let extra_predicates = extend.into_iter().chain( icx.type_parameter_bounds_in_generics(ast_generics, param_id, ty, OnlySelfBounds(true)) .into_iter() - .filter(|(predicate, _)| match predicate { - ty::Predicate::Trait(ref data, _) => data.skip_binder().self_ty().is_param(index), + .filter(|(predicate, _)| match predicate.kind() { + ty::PredicateKind::Trait(ref data, _) => { + data.skip_binder().self_ty().is_param(index) + } _ => false, }), ); @@ -621,7 +614,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 +636,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 +692,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 +705,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,20 +728,20 @@ 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::Method(..) => { - tcx.fn_sig(def_id); + hir::ImplItemKind::Fn(..) => { + tcx.ensure().fn_sig(def_id); } - hir::ImplItemKind::TyAlias(_) | hir::ImplItemKind::OpaqueTy(_) => { + hir::ImplItemKind::TyAlias(_) => { // Account for `type T = _;` let mut visitor = PlaceholderHirTyCollector::default(); visitor.visit_impl_item(impl_item); @@ -760,9 +753,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 +770,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 +791,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,22 +806,22 @@ 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() .map(|f| { let fid = tcx.hir().local_def_id(f.hir_id); - let dup_span = seen_fields.get(&f.ident.modern()).cloned(); + let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned(); if let Some(prev_span) = dup_span { struct_span_err!( tcx.sess, @@ -841,11 +834,11 @@ fn convert_variant( .span_label(prev_span, format!("`{}` first declared here", f.ident)) .emit(); } else { - seen_fields.insert(f.ident.modern(), f.span); + seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span); } ty::FieldDef { - did: fid, + did: fid.to_def_id(), ident: f.ident, vis: ty::Visibility::from_hir(&f.vis, hir_id, tcx), } @@ -858,13 +851,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 +865,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 +886,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 +908,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 +945,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 +953,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, @@ -1001,7 +995,7 @@ fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredi // which will, in turn, reach indirect supertraits. for &(pred, span) in superbounds { debug!("superbound: {:?}", pred); - if let ty::Predicate::Trait(bound, _) = pred { + if let ty::PredicateKind::Trait(bound, _) = pred.kind() { tcx.at(span).super_predicates_of(bound.def_id()); } } @@ -1009,8 +1003,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 { @@ -1032,9 +1026,15 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::TraitDef { } let is_marker = tcx.has_attr(def_id, sym::marker); + let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) { + ty::trait_def::TraitSpecializationKind::Marker + } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) { + ty::trait_def::TraitSpecializationKind::AlwaysApplicable + } else { + ty::trait_def::TraitSpecializationKind::None + }; let def_path_hash = tcx.def_path_hash(def_id); - let def = ty::TraitDef::new(def_id, unsafety, paren_sugar, is_auto, is_marker, 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 { @@ -1045,7 +1045,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option for LateBoundRegionsDetector<'tcx> { - type Map = Map<'tcx>; + type Map = intravisit::ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -1084,13 +1084,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); } @@ -1127,7 +1129,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option None, }, Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Method(ref sig, _) => { + hir::ImplItemKind::Fn(ref sig, _) => { has_late_bound_regions(tcx, &item.generics, &sig.decl) } _ => None, @@ -1148,10 +1150,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 { @@ -1161,18 +1163,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(..), .. }) => { @@ -1182,22 +1201,11 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn, .. }) => { impl_trait_fn.or_else(|| { let parent_id = tcx.hir().get_parent_item(hir_id); - if parent_id != hir_id && parent_id != CRATE_HIR_ID { - debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); - // If this 'impl Trait' is nested inside another 'impl Trait' - // (e.g. `impl Foo>`), we need to use the 'parent' - // 'impl Trait' for its generic parameters, since we can reference them - // from the 'child' 'impl Trait' - if let Node::Item(hir::Item { kind: ItemKind::OpaqueTy(..), .. }) = - tcx.hir().get(parent_id) - { - Some(tcx.hir().local_def_id(parent_id)) - } else { - None - } - } else { - None - } + assert!(parent_id != hir_id && parent_id != CRATE_HIR_ID); + debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); + // Opaque types are always nested within another item, and + // inherit the generics of the item. + Some(tcx.hir().local_def_id(parent_id).to_def_id()) }) } _ => None, @@ -1238,7 +1246,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, @@ -1281,7 +1289,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, })); @@ -1291,47 +1299,67 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { // Now create the real type and const parameters. let type_start = own_start - has_self as u32 + params.len() as u32; let mut i = 0; - params.extend(ast_generics.params.iter().filter_map(|param| { - let kind = match param.kind { - GenericParamKind::Type { ref default, synthetic, .. } => { - if !allow_defaults && default.is_some() { - if !tcx.features().default_type_parameter_fallback { - tcx.struct_span_lint_hir( - lint::builtin::INVALID_TYPE_PARAM_DEFAULT, - param.hir_id, - param.span, - |lint| { - lint.build( - "defaults for type parameters are only allowed in \ - `struct`, `enum`, `type`, or `trait` definitions.", - ) - .emit(); - }, - ); - } - } - ty::GenericParamDefKind::Type { - has_default: default.is_some(), - object_lifetime_default: object_lifetime_defaults - .as_ref() - .map_or(rl::Set1::Empty, |o| o[i]), - synthetic, + // FIXME(const_generics): a few places in the compiler expect generic params + // to be in the order lifetimes, then type params, then const params. + // + // To prevent internal errors in case const parameters are supplied before + // type parameters we first add all type params, then all const params. + params.extend(ast_generics.params.iter().filter_map(|param| { + if let GenericParamKind::Type { ref default, synthetic, .. } = param.kind { + if !allow_defaults && default.is_some() { + if !tcx.features().default_type_parameter_fallback { + tcx.struct_span_lint_hir( + lint::builtin::INVALID_TYPE_PARAM_DEFAULT, + param.hir_id, + param.span, + |lint| { + lint.build( + "defaults for type parameters are only allowed in \ + `struct`, `enum`, `type`, or `trait` definitions.", + ) + .emit(); + }, + ); } } - GenericParamKind::Const { .. } => ty::GenericParamDefKind::Const, - _ => return None, - }; - 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), - pure_wrt_drop: param.pure_wrt_drop, - kind, - }; - i += 1; - Some(param_def) + let kind = ty::GenericParamDefKind::Type { + has_default: default.is_some(), + object_lifetime_default: object_lifetime_defaults + .as_ref() + .map_or(rl::Set1::Empty, |o| o[i]), + synthetic, + }; + + let param_def = ty::GenericParamDef { + index: type_start + i as u32, + name: param.name.ident().name, + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + pure_wrt_drop: param.pure_wrt_drop, + kind, + }; + i += 1; + Some(param_def) + } else { + None + } + })); + + params.extend(ast_generics.params.iter().filter_map(|param| { + if let GenericParamKind::Const { .. } = param.kind { + let param_def = ty::GenericParamDef { + index: type_start + i as u32, + name: param.name.ident().name, + def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), + pure_wrt_drop: param.pure_wrt_drop, + kind: ty::GenericParamDefKind::Const, + }; + i += 1; + Some(param_def) + } else { + None + } })); // provide junk type parameter defs - the only place that @@ -1339,9 +1367,9 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { // and we don't do that for closures. if let Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(.., gen), .. }) = node { let dummy_args = if gen.is_some() { - &["", "", "", ""][..] + &["", "", "", "", ""][..] } else { - &["", ""][..] + &["", "", ""][..] }; params.extend(dummy_args.iter().enumerate().map(|(i, &arg)| ty::GenericParamDef { @@ -1355,34 +1383,18 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { synthetic: None, }, })); - - if let Some(upvars) = tcx.upvars(def_id) { - params.extend(upvars.iter().zip((dummy_args.len() as u32)..).map(|(_, i)| { - ty::GenericParamDef { - index: type_start + i, - name: Symbol::intern(""), - def_id, - pure_wrt_drop: false, - kind: ty::GenericParamDefKind::Type { - has_default: false, - object_lifetime_default: rl::Set1::Empty, - synthetic: None, - }, - } - })); - } } let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); - 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 { @@ -1404,7 +1416,7 @@ fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool { Slice(ty) | Array(ty, _) => is_suggestable_infer_ty(ty), Tup(tys) => tys.iter().any(is_suggestable_infer_ty), Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty), - Def(_, generic_args) => are_suggestable_generic_args(generic_args), + OpaqueDef(_, generic_args) => are_suggestable_generic_args(generic_args), Path(hir::QPath::TypeRelative(ty, segment)) => { is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.generic_args().args) } @@ -1431,18 +1443,19 @@ 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 { - kind: TraitItemKind::Fn(sig, TraitMethod::Provided(_)), + kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)), ident, generics, .. }) - | ImplItem(hir::ImplItem { kind: ImplItemKind::Method(sig, _), ident, generics, .. }) + | ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. }) | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => { match get_infer_ret_ty(&sig.decl.output) { Some(ty) => { @@ -1451,7 +1464,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { visitor.visit_ty(ty); let mut diag = bad_placeholder_type(tcx, visitor.0); let ret_ty = fn_sig.output(); - if ret_ty != tcx.types.err { + if ret_ty != tcx.ty_error() { diag.span_suggestion( ty.span, "replace with the correct return type", @@ -1467,7 +1480,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { sig.header.unsafety, sig.header.abi, &sig.decl, - &generics.params[..], + &generics, Some(ident.span), ), } @@ -1478,22 +1491,21 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { ident, generics, .. - }) => AstConv::ty_of_fn( - &icx, - header.unsafety, - header.abi, - decl, - &generics.params[..], - Some(ident.span), - ), - - ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(ref fn_decl, _, _), .. }) => { + }) => { + AstConv::ty_of_fn(&icx, header.unsafety, header.abi, decl, &generics, Some(ident.span)) + } + + ForeignItem(&hir::ForeignItem { + kind: ForeignItemKind::Fn(ref fn_decl, _, _), + ident, + .. + }) => { let abi = tcx.hir().get_foreign_abi(hir_id); - compute_sig_of_foreign_fn_decl(tcx, def_id, 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( @@ -1512,16 +1524,13 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { // argument. In any case they are embedded within the // closure type as part of the `ClosureSubsts`. // - // To get - // the signature of a closure, you should use the - // `closure_sig` method on the `ClosureSubsts`: - // - // closure_substs.sig(def_id, tcx) - // - // or, inside of an inference context, you can use + // To get the signature of a closure, you should use the + // `sig` method on the `ClosureSubsts`: // - // infcx.closure_sig(def_id, closure_substs) - bug!("to get the signature of a closure, use `closure_sig()` not `fn_sig()`"); + // substs.as_closure().sig(def_id, tcx) + bug!( + "to get the signature of a closure, use `substs.as_closure().sig()` not `fn_sig()`", + ); } x => { @@ -1533,7 +1542,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); @@ -1544,13 +1553,14 @@ 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 { - hir::ItemKind::Impl { polarity: hir::ImplPolarity::Negative(_), .. } => { + hir::ItemKind::Impl { polarity: hir::ImplPolarity::Negative(span), of_trait, .. } => { if is_rustc_reservation { - tcx.sess.span_err(item.span, "reservation impls can't be negative"); + let span = span.to(of_trait.as_ref().map(|t| t.path.span).unwrap_or(*span)); + tcx.sess.span_err(span, "reservation impls can't be negative"); } ty::ImplPolarity::Negative } @@ -1632,10 +1642,10 @@ 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(), + ty::TraitRef::identity(tcx, def_id).without_const().to_predicate(tcx), span, )))); } @@ -1677,11 +1687,12 @@ 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; let mut is_default_impl_trait = None; + let mut is_trait_associated_type = None; let icx = ItemCtxt::new(tcx, def_id); let constness = icx.default_constness_for_trait_bounds(); @@ -1691,33 +1702,14 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat let mut predicates = UniquePredicates::new(); let ast_generics = match node { - Node::TraitItem(item) => &item.generics, - - Node::ImplItem(item) => match item.kind { - ImplItemKind::OpaqueTy(ref bounds) => { - ty::print::with_no_queries(|| { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - let opaque_ty = tcx.mk_opaque(def_id, substs); - debug!( - "explicit_predicates_of({:?}): created opaque type {:?}", - def_id, opaque_ty - ); - - // Collect the bounds, i.e., the `A + B + 'c` in `impl A + B + 'c`. - let bounds = AstConv::compute_bounds( - &icx, - opaque_ty, - bounds, - SizedByDefault::Yes, - tcx.def_span(def_id), - ); - - predicates.extend(bounds.predicates(tcx, opaque_ty)); - &item.generics - }) + Node::TraitItem(item) => { + if let hir::TraitItemKind::Type(bounds, _) = item.kind { + is_trait_associated_type = Some((bounds, item.span)); } - _ => &item.generics, - }, + &item.generics + } + + Node::ImplItem(item) => &item.generics, Node::Item(item) => { match item.kind { @@ -1810,7 +1802,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat // set of defaults that can be incorporated into another impl. if let Some(trait_ref) = is_default_impl_trait { predicates.push(( - trait_ref.to_poly_trait_ref().without_const().to_predicate(), + trait_ref.to_poly_trait_ref().without_const().to_predicate(tcx), tcx.def_span(def_id), )); } @@ -1821,7 +1813,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, })); @@ -1833,7 +1825,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat hir::GenericBound::Outlives(lt) => { let bound = AstConv::ast_region_to_region(&icx, <, None); let outlives = ty::Binder::bind(ty::OutlivesPredicate(region, bound)); - predicates.push((outlives.to_predicate(), lt.span)); + predicates.push((outlives.to_predicate(tcx), lt.span)); } _ => bug!(), }); @@ -1879,7 +1871,8 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat let re_root_empty = tcx.lifetimes.re_root_empty; let predicate = ty::OutlivesPredicate(ty, re_root_empty); predicates.push(( - ty::Predicate::TypeOutlives(ty::Binder::dummy(predicate)), + ty::PredicateKind::TypeOutlives(ty::Binder::dummy(predicate)) + .to_predicate(tcx), span, )); } @@ -1908,7 +1901,10 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat &hir::GenericBound::Outlives(ref lifetime) => { let region = AstConv::ast_region_to_region(&icx, lifetime, None); let pred = ty::Binder::bind(ty::OutlivesPredicate(ty, region)); - predicates.push((ty::Predicate::TypeOutlives(pred), lifetime.span)) + predicates.push(( + ty::PredicateKind::TypeOutlives(pred).to_predicate(tcx), + lifetime.span, + )) } } } @@ -1925,7 +1921,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat }; let pred = ty::Binder::bind(ty::OutlivesPredicate(r1, r2)); - (ty::Predicate::RegionOutlives(pred), span) + (ty::PredicateKind::RegionOutlives(pred).to_predicate(icx.tcx), span) })) } @@ -1935,10 +1931,21 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat } } - // Add predicates from associated type bounds. - if let Some((self_trait_ref, trait_items)) = is_trait { + // Add predicates from associated type bounds (`type X: Bound`) + if tcx.features().generic_associated_types { + // New behavior: bounds declared on associate type are predicates of that + // associated type. Not the default because it needs more testing. + if let Some((bounds, span)) = is_trait_associated_type { + let projection_ty = + tcx.mk_projection(def_id, InternalSubsts::identity_for_item(tcx, def_id)); + + predicates.extend(associated_item_bounds(tcx, def_id, bounds, projection_ty, span)) + } + } else if let Some((self_trait_ref, trait_items)) = is_trait { + // Current behavior: bounds declared on associate type are predicates + // of its parent trait. predicates.extend(trait_items.iter().flat_map(|trait_item_ref| { - associated_item_predicates(tcx, def_id, self_trait_ref, trait_item_ref) + trait_associated_item_predicates(tcx, def_id, self_trait_ref, trait_item_ref) })) } @@ -1968,7 +1975,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat result } -fn associated_item_predicates( +fn trait_associated_item_predicates( tcx: TyCtxt<'tcx>, def_id: DefId, self_trait_ref: ty::TraitRef<'tcx>, @@ -1981,91 +1988,40 @@ fn associated_item_predicates( _ => return Vec::new(), }; - let is_gat = !tcx.generics_of(item_def_id).params.is_empty(); - - let mut had_error = false; - - let mut unimplemented_error = |arg_kind: &str| { - if !had_error { - tcx.sess - .struct_span_err( - trait_item.span, - &format!("{}-generic associated types are not yet implemented", arg_kind), - ) - .note( - "for more information, see issue #44265 \ - for more information", - ) - .emit(); - had_error = true; - } - }; - - let mk_bound_param = |param: &ty::GenericParamDef, _: &_| { - match param.kind { - ty::GenericParamDefKind::Lifetime => tcx - .mk_region(ty::RegionKind::ReLateBound( - ty::INNERMOST, - ty::BoundRegion::BrNamed(param.def_id, param.name), - )) - .into(), - // FIXME(generic_associated_types): Use bound types and constants - // once they are handled by the trait system. - ty::GenericParamDefKind::Type { .. } => { - unimplemented_error("type"); - tcx.types.err.into() - } - ty::GenericParamDefKind::Const => { - unimplemented_error("const"); - tcx.consts.err.into() - } - } - }; + if !tcx.generics_of(item_def_id).params.is_empty() { + // For GATs the substs provided to the mk_projection call below are + // wrong. We should emit a feature gate error if we get here so skip + // this type. + tcx.sess.delay_span_bug(trait_item.span, "gats used without feature gate"); + return Vec::new(); + } - let bound_substs = if is_gat { - // Given: - // - // trait X<'a, B, const C: usize> { - // type T<'d, E, const F: usize>: Default; - // } - // - // We need to create predicates on the trait: - // - // for<'d, E, const F: usize> - // >::T<'d, E, const F: usize>: Sized + Default - // - // We substitute escaping bound parameters for the generic - // arguments to the associated type which are then bound by - // 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) - } else { - self_trait_ref.substs - }; + let assoc_ty = tcx.mk_projection( + tcx.hir().local_def_id(trait_item.hir_id).to_def_id(), + self_trait_ref.substs, + ); - let assoc_ty = tcx.mk_projection(tcx.hir().local_def_id(trait_item.hir_id), bound_substs); + associated_item_bounds(tcx, def_id, bounds, assoc_ty, trait_item.span) +} +fn associated_item_bounds( + tcx: TyCtxt<'tcx>, + def_id: DefId, + bounds: &'tcx [hir::GenericBound<'tcx>], + projection_ty: Ty<'tcx>, + span: Span, +) -> Vec<(ty::Predicate<'tcx>, Span)> { let bounds = AstConv::compute_bounds( &ItemCtxt::new(tcx, def_id), - assoc_ty, + projection_ty, bounds, SizedByDefault::Yes, - trait_item.span, + span, ); - let predicates = bounds.predicates(tcx, assoc_ty); + let predicates = bounds.predicates(tcx, projection_ty); - if is_gat { - // We use shifts to get the regions that we're substituting to - // be bound by the binders in the `Predicate`s rather that - // escaping. - let shifted_in = ty::fold::shift_vars(tcx, &predicates, 1); - let substituted = shifted_in.subst(tcx, bound_substs); - ty::fold::shift_out_vars(tcx, &substituted, 1) - } else { - predicates - } + predicates } /// Converts a specific `GenericBound` from the AST into a set of @@ -2094,7 +2050,7 @@ fn predicates_from_bound<'tcx>( hir::GenericBound::Outlives(ref lifetime) => { let region = astconv.ast_region_to_region(lifetime, None); let pred = ty::Binder::bind(ty::OutlivesPredicate(param_ty, region)); - vec![(ty::Predicate::TypeOutlives(pred), lifetime.span)] + vec![(ty::PredicateKind::TypeOutlives(pred).to_predicate(astconv.tcx()), lifetime.span)] } } } @@ -2104,13 +2060,21 @@ 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()) } else { hir::Unsafety::Unsafe }; - let fty = AstConv::ty_of_fn(&ItemCtxt::new(tcx, def_id), unsafety, abi, decl, &[], None); + let fty = AstConv::ty_of_fn( + &ItemCtxt::new(tcx, def_id), + unsafety, + abi, + decl, + &hir::Generics::empty(), + Some(ident.span), + ); // Feature gate SIMD types in FFI, since I am not sure that the // ABIs are handled at all correctly. -huonw @@ -2120,13 +2084,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") @@ -2154,11 +2123,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), } @@ -2242,6 +2213,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, @@ -2270,7 +2242,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 @@ -2307,6 +2279,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { let attrs = tcx.get_attrs(id); let mut codegen_fn_attrs = CodegenFnAttrs::new(); + if should_inherit_track_caller(tcx, id) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } let whitelist = tcx.target_features_whitelist(LOCAL_CRATE); @@ -2333,6 +2308,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) { @@ -2367,13 +2379,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) { @@ -2405,11 +2416,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { if let Some(list) = attr.meta_item_list() { for item in list.iter() { if item.check_name(sym::address) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_SANITIZE_ADDRESS; + codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS; } else if item.check_name(sym::memory) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_SANITIZE_MEMORY; + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY; } else if item.check_name(sym::thread) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_SANITIZE_THREAD; + codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD; } else { tcx.sess .struct_span_err(item.span(), "invalid argument for `no_sanitize`") @@ -2509,10 +2520,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } } - if codegen_fn_attrs.flags.intersects(CodegenFnAttrFlags::NO_SANITIZE_ANY) { + if !codegen_fn_attrs.no_sanitize.is_empty() { if codegen_fn_attrs.inline == InlineAttr::Always { if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { - let hir_id = tcx.hir().as_local_hir_id(id).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, @@ -2535,7 +2546,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); } @@ -2551,6 +2562,32 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs } +/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller +/// applied to the method prototype. +fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let Some(impl_item) = tcx.opt_associated_item(def_id) { + if let ty::AssocItemContainer::ImplContainer(impl_def_id) = impl_item.container { + if let Some(trait_def_id) = tcx.trait_id_of_impl(impl_def_id) { + if let Some(trait_item) = tcx + .associated_items(trait_def_id) + .filter_by_name_unhygienic(impl_item.ident.name) + .find(move |trait_item| { + trait_item.kind == ty::AssocKind::Fn + && tcx.hygienic_eq(impl_item.ident, trait_item.ident, trait_def_id) + }) + { + return tcx + .codegen_fn_attrs(trait_item.def_id) + .flags + .intersects(CodegenFnAttrFlags::TRACK_CALLER); + } + } + } + } + + false +} + fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { use rustc_ast::ast::{Lit, LitIntType, LitKind}; let meta_item_list = attr.meta_item_list(); @@ -2560,13 +2597,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 } @@ -2594,3 +2631,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 c4a8edd86f83f..3dd9c9c5c39db 100644 --- a/src/librustc_typeck/collect/type_of.rs +++ b/src/librustc_typeck/collect/type_of.rs @@ -1,17 +1,16 @@ -use rustc::hir::map::Map; -use rustc::session::parse::feature_err; -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_span::symbol::{sym, Ident}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable}; +use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::traits; @@ -21,7 +20,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 +33,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 } @@ -47,26 +52,19 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { }, Node::ImplItem(item) => match item.kind { - ImplItemKind::Method(..) => { + ImplItemKind::Fn(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id); tcx.mk_fn_def(def_id, substs) } ImplItemKind::Const(ref ty, body_id) => { if is_suggestable_infer_ty(ty) { - infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) + 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() { - report_assoc_ty_on_inherent_impl(tcx, item.span); - } - - find_opaque_ty_constraints(tcx, def_id) - } 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 +76,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) } @@ -95,26 +99,17 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { let substs = InternalSubsts::identity_for_item(tcx, def_id); tcx.mk_adt(def, substs) } + ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::Binding, .. }) => { + let_position_impl_trait_type(tcx, def_id.expect_local()) + } 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 - } - 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 - } - OpaqueTyOrigin::TypeAlias => { - span_bug!(item.span, "Type alias impl trait shouldn't have an owner") - } - }; - let concrete_ty = concrete_types + ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: Some(owner), .. }) => { + let concrete_ty = tcx + .mir_borrowck(owner.expect_local()) + .concrete_opaque_types .get(&def_id) .map(|opaque| opaque.concrete_type) .unwrap_or_else(|| { @@ -125,11 +120,13 @@ 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. - tcx.types.err + tcx.ty_error() } else { // We failed to resolve the opaque type or it // resolves to itself. Return the non-revealed @@ -141,13 +138,6 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } }); debug!("concrete_ty = {:?}", concrete_ty); - if concrete_ty.has_erased_regions() { - // FIXME(impl_trait_in_bindings) Handle this case. - tcx.sess.span_fatal( - item.span, - "lifetimes in impl Trait types in bindings are not currently supported", - ); - } concrete_ty } ItemKind::Trait(..) @@ -177,7 +167,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); @@ -188,12 +178,12 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { Node::Field(field) => icx.to_ty(&field.ty), Node::Expr(&Expr { kind: ExprKind::Closure(.., gen), .. }) => { - if gen.is_some() { - return tcx.typeck_tables_of(def_id).node_type(hir_id); - } - let substs = InternalSubsts::identity_for_item(tcx, def_id); - tcx.mk_closure(def_id, substs) + if let Some(movability) = gen { + tcx.mk_generator(def_id, substs, movability) + } else { + tcx.mk_closure(def_id, substs) + } } Node::AnonConst(_) => { @@ -207,100 +197,104 @@ 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, + _ => { + return tcx.ty_error_with_message( + DUMMY_SP, + &format!("unexpected const parent path {:?}", parent_node), + ); } - Node::TraitRef(&TraitRef { ref path, .. }) => Some(&**path), - _ => None, }; - if let Some(path) = path { - let arg_index = path - .segments - .iter() - .filter_map(|seg| seg.args.as_ref()) - .map(|generic_args| generic_args.args) - .find_map(|args| { - args.iter() - .filter(|arg| arg.is_const()) - .enumerate() - .filter(|(_, arg)| arg.id() == hir_id) - .map(|(index, _)| index) - .next() - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in 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 generics = match path.res { - Res::Def(DefKind::Ctor(..), def_id) => { - tcx.generics_of(tcx.parent(def_id).unwrap()) - } - Res::Def(_, def_id) => tcx.generics_of(def_id), - Res::Err => return tcx.types.err, - res => { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!("unexpected const parent path def {:?}", res,), + // 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 => { + return tcx.ty_error_with_message( + 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(tcx.types.err) + }) + .nth(arg_index) + .map(|param| tcx.type_of(param.def_id)); + + if let Some(ty) = ty { + ty } else { - tcx.sess.delay_span_bug( + // This is no generic parameter associated with the arg. This is + // probably from an extra arg where one is not needed. + tcx.ty_error_with_message( DUMMY_SP, - &format!("unexpected const parent path {:?}", parent_node,), - ); - return tcx.types.err; + &format!( + "missing generic parameter for `AnonConst`, \ + parent: {:?}, res: {:?}", + parent_node, res + ), + ) } } - x => { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!("unexpected const parent in type_of_def_id(): {:?}", x), - ); - tcx.types.err - } + x => tcx.ty_error_with_message( + DUMMY_SP, + &format!("unexpected const parent in type_of_def_id(): {:?}", x), + ), } } @@ -308,39 +302,64 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty), GenericParamKind::Const { ty: ref hir_ty, .. } => { let ty = icx.to_ty(hir_ty); - if !tcx.features().const_compare_raw_pointers { - let err = match ty.peel_refs().kind { - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => None, - }; - if let Some(unsupported_type) = err { - feature_err( - &tcx.sess.parse_sess, - sym::const_compare_raw_pointers, + let err = match ty.peel_refs().kind { + ty::FnPtr(_) => Some("function pointers"), + ty::RawPtr(_) => Some("raw pointers"), + _ => None, + }; + if let Some(unsupported_type) = err { + tcx.sess + .struct_span_err( hir_ty.span, &format!( - "using {} as const generic parameters is unstable", + "using {} as const generic parameters is forbidden", unsupported_type ), ) .emit(); - }; - } + }; if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty) .is_some() { - 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 } @@ -353,7 +372,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); @@ -361,17 +380,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!( @@ -399,83 +413,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( @@ -488,34 +470,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!( @@ -543,7 +500,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); } @@ -552,7 +509,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); } @@ -565,9 +522,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); @@ -598,18 +555,72 @@ 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"); - tcx.types.err + tcx.ty_error() } } } +/// Retrieve the inferred concrete type for let position impl trait. +/// +/// This is different to other kinds of impl trait because: +/// +/// 1. We know which function contains the defining use (the function that +/// contains the let statement) +/// 2. We do not currently allow (free) lifetimes in the return type. `let` +/// statements in some statically unreachable code are removed from the MIR +/// by the time we borrow check, and it's not clear how we should handle +/// those. +fn let_position_impl_trait_type(tcx: TyCtxt<'_>, opaque_ty_id: LocalDefId) -> Ty<'_> { + let scope = tcx.hir().get_defining_scope(tcx.hir().as_local_hir_id(opaque_ty_id)); + let scope_def_id = tcx.hir().local_def_id(scope); + + let opaque_ty_def_id = opaque_ty_id.to_def_id(); + + let owner_tables = tcx.typeck_tables_of(scope_def_id); + let concrete_ty = owner_tables + .concrete_opaque_types + .get(&opaque_ty_def_id) + .map(|opaque| opaque.concrete_type) + .unwrap_or_else(|| { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!( + "owner {:?} has no opaque type for {:?} in its tables", + scope_def_id, opaque_ty_id + ), + ); + if let Some(ErrorReported) = owner_tables.tainted_by_errors { + // Some error in the owner fn prevented us from populating the + // `concrete_opaque_types` table. + tcx.ty_error() + } else { + // We failed to resolve the opaque type or it resolves to + // itself. Return the non-revealed type, which should result in + // E0720. + tcx.mk_opaque( + opaque_ty_def_id, + InternalSubsts::identity_for_item(tcx, opaque_ty_def_id), + ) + } + }); + debug!("concrete_ty = {:?}", concrete_ty); + if concrete_ty.has_erased_regions() { + // FIXME(impl_trait_in_bindings) Handle this case. + tcx.sess.span_fatal( + tcx.hir().span(tcx.hir().as_local_hir_id(opaque_ty_id)), + "lifetimes in impl Trait types in bindings are not currently supported", + ); + } + concrete_ty +} + fn infer_placeholder_type( tcx: TyCtxt<'_>, - def_id: DefId, + def_id: LocalDefId, body_id: hir::BodyId, span: Span, item_ident: Ident, @@ -635,7 +646,7 @@ fn infer_placeholder_type( } None => { let mut diag = bad_placeholder_type(tcx, vec![span]); - if ty != tcx.types.err { + if !matches!(ty.kind, ty::Error(_)) { diag.span_suggestion( span, "replace `_` with the correct type", @@ -647,7 +658,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 aff3768e35c55..34497d12a4ece 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)] @@ -79,10 +79,18 @@ impl<'tcx> TypeVisitor<'tcx> for ParameterCollector { } fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { - if let ty::ConstKind::Param(data) = c.val { - self.parameters.push(Parameter::from(data)); + match c.val { + ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => { + // Constant expressions are not injective + return c.ty.visit_with(self); + } + ty::ConstKind::Param(data) => { + self.parameters.push(Parameter::from(data)); + } + _ => {} } - false + + c.super_visit_with(self) } } @@ -172,7 +180,7 @@ pub fn setup_constraining_predicates<'tcx>( changed = false; for j in i..predicates.len() { - if let ty::Predicate::Projection(ref poly_projection) = predicates[j].0 { + if let ty::PredicateKind::Projection(ref poly_projection) = predicates[j].0.kind() { // Note that we can skip binder here because the impl // trait ref never contains any late-bound regions. let projection = poly_projection.skip_binder(); diff --git a/src/librustc_typeck/expr_use_visitor.rs b/src/librustc_typeck/expr_use_visitor.rs index 6666b1699943e..b72fae96e4ca0 100644 --- a/src/librustc_typeck/expr_use_visitor.rs +++ b/src/librustc_typeck/expr_use_visitor.rs @@ -5,14 +5,14 @@ pub use self::ConsumeMode::*; // Export these here so that Clippy can use them. -pub use mc::{Place, PlaceBase, Projection}; +pub use mc::{PlaceBase, PlaceWithHirId, 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; @@ -25,13 +25,13 @@ use rustc_span::Span; pub trait Delegate<'tcx> { // The value found at `place` is either copied or moved, depending // on mode. - fn consume(&mut self, place: &mc::Place<'tcx>, mode: ConsumeMode); + fn consume(&mut self, place_with_id: &mc::PlaceWithHirId<'tcx>, mode: ConsumeMode); // The value found at `place` is being borrowed with kind `bk`. - fn borrow(&mut self, place: &mc::Place<'tcx>, bk: ty::BorrowKind); + fn borrow(&mut self, place_with_id: &mc::PlaceWithHirId<'tcx>, bk: ty::BorrowKind); - // The path at `place` is being assigned to. - fn mutate(&mut self, assignee_place: &mc::Place<'tcx>); + // The path at `place_with_id` is being assigned to. + fn mutate(&mut self, assignee_place: &mc::PlaceWithHirId<'tcx>); } #[derive(Copy, Clone, PartialEq, Debug)] @@ -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 { @@ -113,11 +113,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.mc.tcx() } - fn delegate_consume(&mut self, place: &Place<'tcx>) { - debug!("delegate_consume(place={:?})", place); + fn delegate_consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>) { + debug!("delegate_consume(place_with_id={:?})", place_with_id); - let mode = copy_or_move(&self.mc, place); - self.delegate.consume(place, mode); + let mode = copy_or_move(&self.mc, place_with_id); + self.delegate.consume(place_with_id, mode); } fn consume_exprs(&mut self, exprs: &[hir::Expr<'_>]) { @@ -129,22 +129,22 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { pub fn consume_expr(&mut self, expr: &hir::Expr<'_>) { debug!("consume_expr(expr={:?})", expr); - let place = return_if_err!(self.mc.cat_expr(expr)); - self.delegate_consume(&place); + let place_with_id = return_if_err!(self.mc.cat_expr(expr)); + self.delegate_consume(&place_with_id); self.walk_expr(expr); } fn mutate_expr(&mut self, expr: &hir::Expr<'_>) { - let place = return_if_err!(self.mc.cat_expr(expr)); - self.delegate.mutate(&place); + let place_with_id = return_if_err!(self.mc.cat_expr(expr)); + self.delegate.mutate(&place_with_id); self.walk_expr(expr); } fn borrow_expr(&mut self, expr: &hir::Expr<'_>, bk: ty::BorrowKind) { debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk); - let place = return_if_err!(self.mc.cat_expr(expr)); - self.delegate.borrow(&place, bk); + let place_with_id = return_if_err!(self.mc.cat_expr(expr)); + self.delegate.borrow(&place_with_id, bk); self.walk_expr(expr) } @@ -185,7 +185,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_exprs(args); } - hir::ExprKind::MethodCall(.., ref args) => { + hir::ExprKind::MethodCall(.., ref args, _) => { // callee.m(args) self.consume_exprs(args); } @@ -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); @@ -360,7 +384,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // Select just those fields of the `with` // expression that will actually be used - match with_place.ty.kind { + match with_place.place.ty.kind { ty::Adt(adt, substs) if adt.is_struct() => { // Consume those fields of the with expression that are needed. for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() { @@ -398,14 +422,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // process. fn walk_adjustment(&mut self, expr: &hir::Expr<'_>) { let adjustments = self.mc.tables.expr_adjustments(expr); - let mut place = return_if_err!(self.mc.cat_expr_unadjusted(expr)); + let mut place_with_id = return_if_err!(self.mc.cat_expr_unadjusted(expr)); for adjustment in adjustments { debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment); match adjustment.kind { adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => { // Creating a closure/fn-pointer or unsizing consumes // the input and stores it into the resulting rvalue. - self.delegate_consume(&place); + self.delegate_consume(&place_with_id); } adjustment::Adjust::Deref(None) => {} @@ -417,14 +441,15 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // this is an autoref of `x`. adjustment::Adjust::Deref(Some(ref deref)) => { let bk = ty::BorrowKind::from_mutbl(deref.mutbl); - self.delegate.borrow(&place, bk); + self.delegate.borrow(&place_with_id, bk); } adjustment::Adjust::Borrow(ref autoref) => { - self.walk_autoref(expr, &place, autoref); + self.walk_autoref(expr, &place_with_id, autoref); } } - place = return_if_err!(self.mc.cat_expr_adjusted(expr, place, &adjustment)); + place_with_id = + return_if_err!(self.mc.cat_expr_adjusted(expr, place_with_id, &adjustment)); } } @@ -434,7 +459,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn walk_autoref( &mut self, expr: &hir::Expr<'_>, - base_place: &mc::Place<'tcx>, + base_place: &mc::PlaceWithHirId<'tcx>, autoref: &adjustment::AutoBorrow<'tcx>, ) { debug!( @@ -455,7 +480,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } - fn walk_arm(&mut self, discr_place: &Place<'tcx>, arm: &hir::Arm<'_>) { + fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) { self.walk_pat(discr_place, &arm.pat); if let Some(hir::Guard::If(ref e)) = arm.guard { @@ -467,12 +492,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// Walks a pat that occurs in isolation (i.e., top-level of fn argument or /// let binding, and *not* a match arm or nested pat.) - fn walk_irrefutable_pat(&mut self, discr_place: &Place<'tcx>, pat: &hir::Pat<'_>) { + fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) { self.walk_pat(discr_place, pat); } /// The core driver for walking a pattern - fn walk_pat(&mut self, discr_place: &Place<'tcx>, pat: &hir::Pat<'_>) { + fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) { debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat); let tcx = self.tcx(); @@ -515,11 +540,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { debug!("walk_captures({:?})", closure_expr); let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id); - if let Some(upvars) = self.tcx().upvars(closure_def_id) { + if let Some(upvars) = self.tcx().upvars_mentioned(closure_def_id) { 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.to_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( @@ -545,7 +570,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { closure_hir_id: hir::HirId, closure_span: Span, var_id: hir::HirId, - ) -> mc::McResult> { + ) -> mc::McResult> { // Create the place for the variable being borrowed, from the // perspective of the creator (parent) of the closure. let var_ty = self.mc.node_ty(var_id)?; @@ -555,7 +580,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn copy_or_move<'a, 'tcx>( mc: &mc::MemCategorizationContext<'a, 'tcx>, - place: &Place<'tcx>, + place_with_id: &PlaceWithHirId<'tcx>, ) -> ConsumeMode { - if !mc.type_is_copy_modulo_regions(place.ty, place.span) { Move } else { Copy } + if !mc.type_is_copy_modulo_regions( + place_with_id.place.ty, + mc.tcx().hir().span(place_with_id.hir_id), + ) { + Move + } else { + Copy + } } diff --git a/src/librustc_typeck/impl_wf_check.rs b/src/librustc_typeck/impl_wf_check.rs index 0a765a1f9c93c..37d383db68ab6 100644 --- a/src/librustc_typeck/impl_wf_check.rs +++ b/src/librustc_typeck/impl_wf_check.rs @@ -9,16 +9,20 @@ //! fixed, but for the moment it's easier to do these checks early. use crate::constrained_generic_params as cgp; -use rustc::ty::query::Providers; -use rustc::ty::{self, TyCtxt, TypeFoldable}; +use min_specialization::check_min_specialization; + 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}; -use rustc_span::Span; +mod min_specialization; /// Checks that all the type/lifetime parameters on an impl also /// appear in the trait ref or self type (or are constrained by a @@ -55,12 +59,14 @@ 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()); } } fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: DefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut ImplWfCheck { tcx }); + let min_specialization = tcx.features().min_specialization; + tcx.hir() + .visit_item_likes_in_module(module_def_id, &mut ImplWfCheck { tcx, min_specialization }); } pub fn provide(providers: &mut Providers<'_>) { @@ -69,6 +75,7 @@ pub fn provide(providers: &mut Providers<'_>) { struct ImplWfCheck<'tcx> { tcx: TyCtxt<'tcx>, + min_specialization: bool, } impl ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { @@ -77,6 +84,9 @@ impl ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { let impl_def_id = self.tcx.hir().local_def_id(item.hir_id); 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.to_def_id(), item.span); + } } } @@ -87,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. @@ -130,14 +140,7 @@ fn enforce_impl_params_are_constrained( Vec::new() } } - ty::AssocKind::OpaqueTy => { - // We don't know which lifetimes appear in the actual - // opaque type, so use all of the lifetimes that appear - // in the type's predicates. - 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(); @@ -227,7 +230,7 @@ fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_item_refs: &[hir::ImplI hir::ImplItemKind::TyAlias(_) => &mut seen_type_items, _ => &mut seen_value_items, }; - match seen_items.entry(impl_item.ident.modern()) { + match seen_items.entry(impl_item.ident.normalize_to_macros_2_0()) { Occupied(entry) => { let mut err = struct_span_err!( tcx.sess, diff --git a/src/librustc_typeck/impl_wf_check/min_specialization.rs b/src/librustc_typeck/impl_wf_check/min_specialization.rs new file mode 100644 index 0000000000000..e4bffedd620b9 --- /dev/null +++ b/src/librustc_typeck/impl_wf_check/min_specialization.rs @@ -0,0 +1,410 @@ +//! # Minimal Specialization +//! +//! This module contains the checks for sound specialization used when the +//! `min_specialization` feature is enabled. This requires that the impl is +//! *always applicable*. +//! +//! If `impl1` specializes `impl2` then `impl1` is always applicable if we know +//! that all the bounds of `impl2` are satisfied, and all of the bounds of +//! `impl1` are satisfied for some choice of lifetimes then we know that +//! `impl1` applies for any choice of lifetimes. +//! +//! ## Basic approach +//! +//! To enforce this requirement on specializations we take the following +//! approach: +//! +//! 1. Match up the substs for `impl2` so that the implemented trait and +//! self-type match those for `impl1`. +//! 2. Check for any direct use of `'static` in the substs of `impl2`. +//! 3. Check that all of the generic parameters of `impl1` occur at most once +//! in the *unconstrained* substs for `impl2`. A parameter is constrained if +//! its value is completely determined by an associated type projection +//! predicate. +//! 4. Check that all predicates on `impl1` either exist on `impl2` (after +//! matching substs), or are well-formed predicates for the trait's type +//! arguments. +//! +//! ## Example +//! +//! Suppose we have the following always applicable impl: +//! +//! ```rust +//! impl SpecExtend for std::vec::IntoIter { /* specialized impl */ } +//! impl> SpecExtend for I { /* default impl */ } +//! ``` +//! +//! We get that the subst for `impl2` are `[T, std::vec::IntoIter]`. `T` is +//! constrained to be `::Item`, so we check only +//! `std::vec::IntoIter` for repeated parameters, which it doesn't have. The +//! predicates of `impl1` are only `T: Sized`, which is also a predicate of +//! `impl2`. So this specialization is sound. +//! +//! ## Extensions +//! +//! Unfortunately not all specializations in the standard library are allowed +//! by this. So there are two extensions to these rules that allow specializing +//! on some traits: that is, using them as bounds on the specializing impl, +//! even when they don't occur in the base impl. +//! +//! ### rustc_specialization_trait +//! +//! If a trait is always applicable, then it's sound to specialize on it. We +//! check trait is always applicable in the same way as impls, except that step +//! 4 is now "all predicates on `impl1` are always applicable". We require that +//! `specialization` or `min_specialization` is enabled to implement these +//! traits. +//! +//! ### rustc_unsafe_specialization_marker +//! +//! There are also some specialization on traits with no methods, including the +//! stable `FusedIterator` trait. We allow marking marker traits with an +//! unstable attribute that means we ignore them in point 3 of the checks +//! above. This is unsound, in the sense that the specialized impl may be used +//! when it doesn't apply, but we allow it in the short term since it can't +//! cause use after frees with purely safe code in the same way as specializing +//! on traits with methods can. + +use crate::constrained_generic_params as cgp; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +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::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}; + +pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: DefId, span: Span) { + if let Some(node) = parent_specialization_node(tcx, impl_def_id) { + tcx.infer_ctxt().enter(|infcx| { + check_always_applicable(&infcx, impl_def_id, node, span); + }); + } +} + +fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: DefId) -> Option { + let trait_ref = tcx.impl_trait_ref(impl1_def_id)?; + let trait_def = tcx.trait_def(trait_ref.def_id); + + let impl2_node = trait_def.ancestors(tcx, impl1_def_id).ok()?.nth(1)?; + + let always_applicable_trait = + matches!(trait_def.specialization_kind, TraitSpecializationKind::AlwaysApplicable); + if impl2_node.is_from_trait() && !always_applicable_trait { + // Implementing a normal trait isn't a specialization. + return None; + } + Some(impl2_node) +} + +/// Check that `impl1` is a sound specialization +fn check_always_applicable( + infcx: &InferCtxt<'_, '_>, + impl1_def_id: DefId, + impl2_node: Node, + span: Span, +) { + if let Some((impl1_substs, impl2_substs)) = + get_impl_substs(infcx, impl1_def_id, impl2_node, span) + { + let impl2_def_id = impl2_node.def_id(); + debug!( + "check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)", + impl1_def_id, impl2_def_id, impl2_substs + ); + + let tcx = infcx.tcx; + + let parent_substs = if impl2_node.is_from_trait() { + impl2_substs.to_vec() + } else { + unconstrained_parent_impl_substs(tcx, impl2_def_id, impl2_substs) + }; + + check_static_lifetimes(tcx, &parent_substs, span); + check_duplicate_params(tcx, impl1_substs, &parent_substs, span); + + check_predicates( + infcx, + impl1_def_id.expect_local(), + impl1_substs, + impl2_node, + impl2_substs, + span, + ); + } +} + +/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two +/// substitutions `(S1, S2)` that equate their trait references. The returned +/// types are expressed in terms of the generics of `impl1`. +/// +/// Example +/// +/// impl Foo for B { /* impl2 */ } +/// impl Foo> for C { /* impl1 */ } +/// +/// Would return `S1 = [C]` and `S2 = [Vec, C]`. +fn get_impl_substs<'tcx>( + infcx: &InferCtxt<'_, 'tcx>, + impl1_def_id: DefId, + impl2_node: Node, + span: Span, +) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> { + let tcx = infcx.tcx; + let param_env = tcx.param_env(impl1_def_id); + + let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id); + let impl2_substs = translate_substs(infcx, param_env, impl1_def_id, impl1_substs, impl2_node); + + // Conservatively use an empty `ParamEnv`. + let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); + infcx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env, RegionckMode::default()); + let impl2_substs = match infcx.fully_resolve(&impl2_substs) { + Ok(s) => s, + Err(_) => { + tcx.sess.struct_span_err(span, "could not resolve substs on overridden impl").emit(); + return None; + } + }; + Some((impl1_substs, impl2_substs)) +} + +/// Returns a list of all of the unconstrained subst of the given impl. +/// +/// For example given the impl: +/// +/// impl<'a, T, I> ... where &'a I: IntoIterator +/// +/// This would return the substs corresponding to `['a, I]`, because knowing +/// `'a` and `I` determines the value of `T`. +fn unconstrained_parent_impl_substs<'tcx>( + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + impl_substs: SubstsRef<'tcx>, +) -> Vec> { + let impl_generic_predicates = tcx.predicates_of(impl_def_id); + let mut unconstrained_parameters = FxHashSet::default(); + let mut constrained_params = FxHashSet::default(); + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); + + // Unfortunately the functions in `constrained_generic_parameters` don't do + // what we want here. We want only a list of constrained parameters while + // the functions in `cgp` add the constrained parameters to a list of + // unconstrained parameters. + for (predicate, _) in impl_generic_predicates.predicates.iter() { + if let ty::PredicateKind::Projection(proj) = predicate.kind() { + let projection_ty = proj.skip_binder().projection_ty; + let projected_ty = proj.skip_binder().ty; + + let unbound_trait_ref = projection_ty.trait_ref(tcx); + if Some(unbound_trait_ref) == impl_trait_ref { + continue; + } + + unconstrained_parameters.extend(cgp::parameters_for(&projection_ty, true)); + + for param in cgp::parameters_for(&projected_ty, false) { + if !unconstrained_parameters.contains(¶m) { + constrained_params.insert(param.0); + } + } + + unconstrained_parameters.extend(cgp::parameters_for(&projected_ty, true)); + } + } + + impl_substs + .iter() + .enumerate() + .filter(|&(idx, _)| !constrained_params.contains(&(idx as u32))) + .map(|(_, arg)| arg) + .collect() +} + +/// Check that parameters of the derived impl don't occur more than once in the +/// equated substs of the base impl. +/// +/// For example forbid the following: +/// +/// impl Tr for A { } +/// impl Tr for (B, B) { } +/// +/// Note that only consider the unconstrained parameters of the base impl: +/// +/// impl> Tr for I { } +/// impl Tr for Vec { } +/// +/// The substs for the parent impl here are `[T, Vec]`, which repeats `T`, +/// but `S` is constrained in the parent impl, so `parent_substs` is only +/// `[Vec]`. This means we allow this impl. +fn check_duplicate_params<'tcx>( + tcx: TyCtxt<'tcx>, + impl1_substs: SubstsRef<'tcx>, + parent_substs: &Vec>, + span: Span, +) { + let mut base_params = cgp::parameters_for(parent_substs, true); + base_params.sort_by_key(|param| param.0); + if let (_, [duplicate, ..]) = base_params.partition_dedup() { + let param = impl1_substs[duplicate.0 as usize]; + tcx.sess + .struct_span_err(span, &format!("specializing impl repeats parameter `{}`", param)) + .emit(); + } +} + +/// Check that `'static` lifetimes are not introduced by the specializing impl. +/// +/// For example forbid the following: +/// +/// impl Tr for A { } +/// impl Tr for &'static i32 { } +fn check_static_lifetimes<'tcx>( + tcx: TyCtxt<'tcx>, + parent_substs: &Vec>, + span: Span, +) { + if tcx.any_free_region_meets(parent_substs, |r| *r == ty::ReStatic) { + tcx.sess.struct_span_err(span, "cannot specialize on `'static` lifetime").emit(); + } +} + +/// Check whether predicates on the specializing impl (`impl1`) are allowed. +/// +/// Each predicate `P` must be: +/// +/// * global (not reference any parameters) +/// * `T: Tr` predicate where `Tr` is an always-applicable trait +/// * on the base `impl impl2` +/// * Currently this check is done using syntactic equality, which is +/// conservative but generally sufficient. +/// * a well-formed predicate of a type argument of the trait being implemented, +/// including the `Self`-type. +fn check_predicates<'tcx>( + infcx: &InferCtxt<'_, 'tcx>, + impl1_def_id: LocalDefId, + impl1_substs: SubstsRef<'tcx>, + impl2_node: Node, + impl2_substs: SubstsRef<'tcx>, + span: Span, +) { + let tcx = infcx.tcx; + let impl1_predicates = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs); + let mut impl2_predicates = if impl2_node.is_from_trait() { + // Always applicable traits have to be always applicable without any + // assumptions. + InstantiatedPredicates::empty() + } else { + tcx.predicates_of(impl2_node.def_id()).instantiate(tcx, impl2_substs) + }; + debug!( + "check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)", + impl1_predicates, impl2_predicates, + ); + + // Since impls of always applicable traits don't get to assume anything, we + // can also assume their supertraits apply. + // + // For example, we allow: + // + // #[rustc_specialization_trait] + // trait AlwaysApplicable: Debug { } + // + // impl Tr for T { } + // impl Tr for T { } + // + // Specializing on `AlwaysApplicable` allows also specializing on `Debug` + // which is sound because we forbid impls like the following + // + // impl AlwaysApplicable for D { } + let always_applicable_traits = + impl1_predicates.predicates.iter().copied().filter(|&predicate| { + matches!( + trait_predicate_kind(tcx, predicate), + Some(TraitSpecializationKind::AlwaysApplicable) + ) + }); + + // Include the well-formed predicates of the type parameters of the impl. + for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs { + if let Some(obligations) = wf::obligations( + infcx, + tcx.param_env(impl1_def_id), + tcx.hir().as_local_hir_id(impl1_def_id), + arg, + span, + ) { + impl2_predicates + .predicates + .extend(obligations.into_iter().map(|obligation| obligation.predicate)) + } + } + 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) { + check_specialization_on(tcx, predicate, span) + } + } +} + +fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) { + debug!("can_specialize_on(predicate = {:?})", predicate); + match predicate.kind() { + // Global predicates are either always true or always false, so we + // are fine to specialize on. + _ if predicate.is_global() => (), + // We allow specializing on explicitly marked traits with no associated + // items. + ty::PredicateKind::Trait(pred, hir::Constness::NotConst) => { + if !matches!( + trait_predicate_kind(tcx, predicate), + Some(TraitSpecializationKind::Marker) + ) { + tcx.sess + .struct_span_err( + span, + &format!( + "cannot specialize on trait `{}`", + tcx.def_path_str(pred.def_id()), + ), + ) + .emit() + } + } + _ => tcx + .sess + .struct_span_err(span, &format!("cannot specialize on `{:?}`", predicate)) + .emit(), + } +} + +fn trait_predicate_kind<'tcx>( + tcx: TyCtxt<'tcx>, + predicate: ty::Predicate<'tcx>, +) -> Option { + match predicate.kind() { + ty::PredicateKind::Trait(pred, hir::Constness::NotConst) => { + Some(tcx.trait_def(pred.def_id()).specialization_kind) + } + ty::PredicateKind::Trait(_, hir::Constness::Const) + | ty::PredicateKind::RegionOutlives(_) + | ty::PredicateKind::TypeOutlives(_) + | ty::PredicateKind::Projection(_) + | ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) => None, + } +} diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 4e7985dd98812..8d8a1b4d96761 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -62,15 +62,17 @@ This API is completely unstable and subject to change. #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] #![feature(try_blocks)] #![feature(never_type)] +#![feature(slice_partition_dedup)] #![recursion_limit = "256"] #[macro_use] extern crate log; #[macro_use] -extern crate rustc; +extern crate rustc_middle; // This is used by Clippy. pub mod expr_use_visitor; @@ -87,21 +89,17 @@ mod outlives; mod structured_errors; mod variance; -use rustc::lint; -use rustc::middle; -use rustc::session; -use rustc::session::config::EntryFnType; -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; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; @@ -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 4350b3dda97ce..d619d37be2d7b 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; @@ -74,22 +74,24 @@ pub enum PlaceBase { } #[derive(Clone, Debug)] -pub enum Projection<'tcx> { +pub enum ProjectionKind<'tcx> { /// A dereference of a pointer, reference or `Box` of the given type Deref(Ty<'tcx>), /// An index or a field Other, } +#[derive(Clone, Debug)] +pub struct Projection<'tcx> { + /// Defines the type of access + kind: ProjectionKind<'tcx>, +} + /// A `Place` represents how a value is located in memory. /// /// This is an HIR version of `mir::Place` #[derive(Clone, Debug)] pub struct Place<'tcx> { - /// `HirId` of the expression or pattern producing this value. - pub hir_id: hir::HirId, - /// The `Span` of the expression or pattern producing this value. - pub span: Span, /// The type of the `Place` pub ty: Ty<'tcx>, /// The "outermost" place that holds this value. @@ -98,6 +100,32 @@ pub struct Place<'tcx> { pub projections: Vec>, } +/// A `PlaceWithHirId` represents how a value is located in memory. +/// +/// This is an HIR version of `mir::Place` +#[derive(Clone, Debug)] +pub struct PlaceWithHirId<'tcx> { + /// `HirId` of the expression or pattern producing this value. + pub hir_id: hir::HirId, + + /// Information about the `Place` + pub place: Place<'tcx>, +} + +impl<'tcx> PlaceWithHirId<'tcx> { + crate fn new( + hir_id: hir::HirId, + ty: Ty<'tcx>, + base: PlaceBase, + projections: Vec>, + ) -> PlaceWithHirId<'tcx> { + PlaceWithHirId { + hir_id: hir_id, + place: Place { ty: ty, base: base, projections: projections }, + } + } +} + impl<'tcx> Place<'tcx> { /// Returns an iterator of the types that have to be dereferenced to access /// the `Place`. @@ -107,7 +135,7 @@ impl<'tcx> Place<'tcx> { ///`*const u32` then `&*const u32`. crate fn deref_tys(&self) -> impl Iterator> + '_ { self.projections.iter().rev().filter_map(|proj| { - if let Projection::Deref(deref_ty) = *proj { Some(deref_ty) } else { None } + if let ProjectionKind::Deref(deref_ty) = proj.kind { Some(deref_ty) } else { None } }) } } @@ -140,7 +168,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 +179,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 { @@ -159,7 +187,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { infcx, param_env, body_owner, - upvars: infcx.tcx.upvars(body_owner), + upvars: infcx.tcx.upvars_mentioned(body_owner), } } @@ -280,14 +308,14 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { Ok(ret_ty) } - crate fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult> { + crate fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult> { // This recursion helper avoids going through *too many* // adjustments, since *only* non-overloaded deref recurses. fn helper<'a, 'tcx>( mc: &MemCategorizationContext<'a, 'tcx>, expr: &hir::Expr<'_>, adjustments: &[adjustment::Adjustment<'tcx>], - ) -> McResult> { + ) -> McResult> { match adjustments.split_last() { None => mc.cat_expr_unadjusted(expr), Some((adjustment, previous)) => { @@ -302,9 +330,9 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { crate fn cat_expr_adjusted( &self, expr: &hir::Expr<'_>, - previous: Place<'tcx>, + previous: PlaceWithHirId<'tcx>, adjustment: &adjustment::Adjustment<'tcx>, - ) -> McResult> { + ) -> McResult> { self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) } @@ -313,9 +341,9 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { expr: &hir::Expr<'_>, previous: F, adjustment: &adjustment::Adjustment<'tcx>, - ) -> McResult> + ) -> McResult> where - F: FnOnce() -> McResult>, + F: FnOnce() -> McResult>, { debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr); let target = self.resolve_vars_if_possible(&adjustment.target); @@ -342,7 +370,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - crate fn cat_expr_unadjusted(&self, expr: &hir::Expr<'_>) -> McResult> { + crate fn cat_expr_unadjusted(&self, expr: &hir::Expr<'_>) -> McResult> { debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); let expr_ty = self.expr_ty(expr)?; @@ -406,6 +434,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)), } @@ -417,37 +446,30 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { span: Span, expr_ty: Ty<'tcx>, res: Res, - ) -> McResult> { + ) -> McResult> { 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 { - hir_id, - span, - ty: expr_ty, - base: PlaceBase::StaticItem, - projections: Vec::new(), - }), + Res::Def(DefKind::Static, _) => { + Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::StaticItem, Vec::new())) + } Res::Local(var_id) => { if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) { - self.cat_upvar(hir_id, span, var_id) + self.cat_upvar(hir_id, var_id) } else { - Ok(Place { - hir_id, - span, - ty: expr_ty, - base: PlaceBase::Local(var_id), - projections: Vec::new(), - }) + Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new())) } } @@ -460,36 +482,29 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Note: the actual upvar access contains invisible derefs of closure /// environment and upvar reference as appropriate. Only regionck cares /// about these dereferences, so we let it compute them as needed. - fn cat_upvar( - &self, - hir_id: hir::HirId, - span: Span, - var_id: hir::HirId, - ) -> McResult> { + fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult> { let closure_expr_def_id = self.body_owner; let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_id }, - closure_expr_id: closure_expr_def_id.to_local(), + closure_expr_id: closure_expr_def_id, }; let var_ty = self.node_ty(var_id)?; - let ret = Place { - hir_id, - span, - ty: var_ty, - base: PlaceBase::Upvar(upvar_id), - projections: Vec::new(), - }; + let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()); debug!("cat_upvar ret={:?}", ret); Ok(ret) } - crate fn cat_rvalue(&self, hir_id: hir::HirId, span: Span, expr_ty: Ty<'tcx>) -> Place<'tcx> { + crate fn cat_rvalue( + &self, + hir_id: hir::HirId, + span: Span, + expr_ty: Ty<'tcx>, + ) -> PlaceWithHirId<'tcx> { debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span); - let ret = - Place { hir_id, span, base: PlaceBase::Rvalue, projections: Vec::new(), ty: expr_ty }; + let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()); debug!("cat_rvalue ret={:?}", ret); ret } @@ -497,18 +512,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { crate fn cat_projection( &self, node: &N, - base_place: Place<'tcx>, + base_place: PlaceWithHirId<'tcx>, ty: Ty<'tcx>, - ) -> Place<'tcx> { - let mut projections = base_place.projections; - projections.push(Projection::Other); - let ret = Place { - hir_id: node.hir_id(), - span: node.span(), - ty, - base: base_place.base, - projections, - }; + ) -> PlaceWithHirId<'tcx> { + let mut projections = base_place.place.projections; + projections.push(Projection { kind: ProjectionKind::Other }); + let ret = PlaceWithHirId::new(node.hir_id(), ty, base_place.place.base, projections); debug!("cat_field ret {:?}", ret); ret } @@ -517,7 +526,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { &self, expr: &hir::Expr<'_>, base: &hir::Expr<'_>, - ) -> McResult> { + ) -> McResult> { debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base); // Reconstruct the output assuming it's a reference with the @@ -536,10 +545,14 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_deref(expr, base) } - fn cat_deref(&self, node: &impl HirNode, base_place: Place<'tcx>) -> McResult> { + fn cat_deref( + &self, + node: &impl HirNode, + base_place: PlaceWithHirId<'tcx>, + ) -> McResult> { debug!("cat_deref: base_place={:?}", base_place); - let base_ty = base_place.ty; + let base_ty = base_place.place.ty; let deref_ty = match base_ty.builtin_deref(true) { Some(mt) => mt.ty, None => { @@ -547,28 +560,22 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { return Err(()); } }; - let mut projections = base_place.projections; - projections.push(Projection::Deref(base_ty)); - - let ret = Place { - hir_id: node.hir_id(), - span: node.span(), - ty: deref_ty, - base: base_place.base, - projections, - }; + let mut projections = base_place.place.projections; + projections.push(Projection { kind: ProjectionKind::Deref(base_ty) }); + + let ret = PlaceWithHirId::new(node.hir_id(), deref_ty, base_place.place.base, projections); debug!("cat_deref ret {:?}", ret); Ok(ret) } crate fn cat_pattern( &self, - place: Place<'tcx>, + place: PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>, mut op: F, ) -> McResult<()> where - F: FnMut(&Place<'tcx>, &hir::Pat<'_>), + F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), { self.cat_pattern_(place, pat, &mut op) } @@ -576,24 +583,24 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // FIXME(#19596) This is a workaround, but there should be a better way to do this fn cat_pattern_( &self, - mut place: Place<'tcx>, + mut place_with_id: PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>, op: &mut F, ) -> McResult<()> where - F: FnMut(&Place<'tcx>, &hir::Pat<'_>), + F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), { - // Here, `place` is the `Place` being matched and pat is the pattern it + // Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it // is being matched against. // // In general, the way that this works is that we walk down the pattern, - // constructing a `Place` that represents the path that will be taken + // constructing a `PlaceWithHirId` that represents the path that will be taken // to reach the value being matched. - debug!("cat_pattern(pat={:?}, place={:?})", pat, place); + debug!("cat_pattern(pat={:?}, place_with_id={:?})", pat, place_with_id); - // If (pattern) adjustments are active for this pattern, adjust the `Place` correspondingly. - // `Place`s are constructed differently from patterns. For example, in + // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithHirId` correspondingly. + // `PlaceWithHirId`s are constructed differently from patterns. For example, in // // ``` // match foo { @@ -603,7 +610,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // ``` // // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the - // corresponding `Place` we start with the `Place` for `foo`, and then, by traversing the + // corresponding `PlaceWithHirId` we start with the `PlaceWithHirId` for `foo`, and then, by traversing the // pattern, try to answer the question: given the address of `foo`, how is `x` reached? // // `&&Some(x,)` `place_foo` @@ -625,29 +632,29 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. for _ in 0..self.tables.pat_adjustments().get(pat.hir_id).map(|v| v.len()).unwrap_or(0) { - debug!("cat_pattern: applying adjustment to place={:?}", place); - place = self.cat_deref(pat, place)?; + debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id); + place_with_id = self.cat_deref(pat, place_with_id)?; } - let place = place; // lose mutability - debug!("cat_pattern: applied adjustment derefs to get place={:?}", place); + let place_with_id = place_with_id; // lose mutability + debug!("cat_pattern: applied adjustment derefs to get place_with_id={:?}", place_with_id); - // Invoke the callback, but only now, after the `place` has adjusted. + // Invoke the callback, but only now, after the `place_with_id` has adjusted. // // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that - // case, the initial `place` will be that for `&Some(3)` and the pattern is `Some(x)`. We + // case, the initial `place_with_id` will be that for `&Some(3)` and the pattern is `Some(x)`. We // don't want to call `op` with these incompatible values. As written, what happens instead // is that `op` is called with the adjusted place (that for `*&Some(3)`) and the pattern // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` // result in the place `Downcast(*&Some(3)).0` associated to `x` and invoke `op` with // that (where the `ref` on `x` is implied). - op(&place, pat); + op(&place_with_id, pat); match pat.kind { PatKind::TupleStruct(_, ref subpats, _) | PatKind::Tuple(ref subpats, _) => { // S(p1, ..., pN) or (p1, ..., pN) for subpat in subpats.iter() { let subpat_ty = self.pat_ty_adjusted(&subpat)?; - let sub_place = self.cat_projection(pat, place.clone(), subpat_ty); + let sub_place = self.cat_projection(pat, place_with_id.clone(), subpat_ty); self.cat_pattern_(sub_place, &subpat, op)?; } } @@ -656,44 +663,44 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // S { f1: p1, ..., fN: pN } for fp in field_pats { let field_ty = self.pat_ty_adjusted(&fp.pat)?; - let field_place = self.cat_projection(pat, place.clone(), field_ty); + let field_place = self.cat_projection(pat, place_with_id.clone(), field_ty); self.cat_pattern_(field_place, &fp.pat, op)?; } } PatKind::Or(pats) => { for pat in pats { - self.cat_pattern_(place.clone(), &pat, op)?; + self.cat_pattern_(place_with_id.clone(), &pat, op)?; } } PatKind::Binding(.., Some(ref subpat)) => { - self.cat_pattern_(place, &subpat, op)?; + self.cat_pattern_(place_with_id, &subpat, op)?; } PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => { // box p1, &p1, &mut p1. we can ignore the mutability of // PatKind::Ref since that information is already contained // in the type. - let subplace = self.cat_deref(pat, place)?; + let subplace = self.cat_deref(pat, place_with_id)?; self.cat_pattern_(subplace, &subpat, op)?; } PatKind::Slice(before, ref slice, after) => { - let element_ty = match place.ty.builtin_index() { + let element_ty = match place_with_id.place.ty.builtin_index() { Some(ty) => ty, None => { - debug!("explicit index of non-indexable type {:?}", place); + debug!("explicit index of non-indexable type {:?}", place_with_id); return Err(()); } }; - let elt_place = self.cat_projection(pat, place.clone(), element_ty); + let elt_place = self.cat_projection(pat, place_with_id.clone(), element_ty); for before_pat in before { self.cat_pattern_(elt_place.clone(), &before_pat, op)?; } if let Some(ref slice_pat) = *slice { let slice_pat_ty = self.pat_ty_adjusted(&slice_pat)?; - let slice_place = self.cat_projection(pat, place, slice_pat_ty); + let slice_place = self.cat_projection(pat, place_with_id, slice_pat_ty); self.cat_pattern_(slice_place, &slice_pat, op)?; } for after_pat in after { diff --git a/src/librustc_typeck/outlives/explicit.rs b/src/librustc_typeck/outlives/explicit.rs index 7500c39cf216b..5740cc224cc57 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::*; @@ -29,8 +29,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { // process predicates and convert to `RequiredPredicates` entry, see below for &(predicate, span) in predicates.predicates { - match predicate { - ty::Predicate::TypeOutlives(predicate) => { + match predicate.kind() { + ty::PredicateKind::TypeOutlives(predicate) => { let OutlivesPredicate(ref ty, ref reg) = predicate.skip_binder(); insert_outlives_predicate( tcx, @@ -41,7 +41,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { ) } - ty::Predicate::RegionOutlives(predicate) => { + ty::PredicateKind::RegionOutlives(predicate) => { let OutlivesPredicate(ref reg1, ref reg2) = predicate.skip_binder(); insert_outlives_predicate( tcx, @@ -52,13 +52,14 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { ) } - ty::Predicate::Trait(..) - | ty::Predicate::Projection(..) - | ty::Predicate::WellFormed(..) - | ty::Predicate::ObjectSafe(..) - | ty::Predicate::ClosureKind(..) - | ty::Predicate::Subtype(..) - | ty::Predicate::ConstEvaluatable(..) => (), + ty::PredicateKind::Trait(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::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..1b2b08a2e62ee 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, ToPredicate, 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 { @@ -30,9 +30,9 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate if tcx.has_attr(item_def_id, sym::rustc_outlives) { let mut pred: Vec = predicates .iter() - .map(|(out_pred, _)| match out_pred { - ty::Predicate::RegionOutlives(p) => p.to_string(), - ty::Predicate::TypeOutlives(p) => p.to_string(), + .map(|(out_pred, _)| match out_pred.kind() { + ty::PredicateKind::RegionOutlives(p) => p.to_string(), + ty::PredicateKind::TypeOutlives(p) => p.to_string(), err => bug!("unexpected predicate {:?}", err), }) .collect(); @@ -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** @@ -82,22 +82,26 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> &CratePredic .iter() .map(|(&def_id, set)| { let predicates = &*tcx.arena.alloc_from_iter(set.iter().filter_map( - |(ty::OutlivesPredicate(kind1, region2), &span)| match kind1.unpack() { - GenericArgKind::Type(ty1) => Some(( - ty::Predicate::TypeOutlives(ty::Binder::bind(ty::OutlivesPredicate( - ty1, region2, - ))), - span, - )), - GenericArgKind::Lifetime(region1) => Some(( - ty::Predicate::RegionOutlives(ty::Binder::bind(ty::OutlivesPredicate( - region1, region2, - ))), - span, - )), - GenericArgKind::Const(_) => { - // Generic consts don't impose any constraints. - None + |(ty::OutlivesPredicate(kind1, region2), &span)| { + match kind1.unpack() { + GenericArgKind::Type(ty1) => Some(( + ty::PredicateKind::TypeOutlives(ty::Binder::bind( + ty::OutlivesPredicate(ty1, region2), + )) + .to_predicate(tcx), + span, + )), + GenericArgKind::Lifetime(region1) => Some(( + ty::PredicateKind::RegionOutlives(ty::Binder::bind( + ty::OutlivesPredicate(region1, region2), + )) + .to_predicate(tcx), + span, + )), + GenericArgKind::Const(_) => { + // Generic consts don't impose any constraints. + None + } } }, )); @@ -105,5 +109,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 0cc322f8c2d3d..8b06967879638 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; @@ -170,8 +170,6 @@ fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool { // These regions don't appear in types from type declarations: RegionKind::ReErased - | RegionKind::ReClosureBound(..) - | RegionKind::ReScope(..) | RegionKind::ReVar(..) | RegionKind::RePlaceholder(..) | RegionKind::ReFree(..) => { diff --git a/src/librustc_typeck/structured_errors.rs b/src/librustc_typeck/structured_errors.rs index 99b7b2001a9e9..83125a3e2feb6 100644 --- a/src/librustc_typeck/structured_errors.rs +++ b/src/librustc_typeck/structured_errors.rs @@ -1,6 +1,6 @@ -use rustc::session::Session; -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; pub trait StructuredDiagnostic<'tcx> { diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index fc3b7201a1e63..cae09267994e3 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::*; @@ -111,7 +111,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> { } fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Method(..) = impl_item.kind { + if let hir::ImplItemKind::Fn(..) = impl_item.kind { self.visit_node_helper(impl_item.hir_id); } } @@ -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 { @@ -291,7 +291,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::Tuple(subtys) => { - for &subty in subtys { + for subty in subtys { self.add_constraints_from_ty(current, subty.expect_ty(), variance); } } @@ -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, ); } @@ -341,16 +339,12 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_sig(current, sig, variance); } - ty::Error => { + ty::Error(_) => { // we encounter this when walking the trait references for object // 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))) @@ -449,8 +444,6 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::ReFree(..) - | ty::ReClosureBound(..) - | ty::ReScope(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReEmpty(_) diff --git a/src/librustc_typeck/variance/mod.rs b/src/librustc_typeck/variance/mod.rs index b1fcc3d3fe8f9..23f4e1f5346e5 100644 --- a/src/librustc_typeck/variance/mod.rs +++ b/src/librustc_typeck/variance/mod.rs @@ -4,10 +4,11 @@ //! [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_arena::TypedArena; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt}; /// Defines the `TermsContext` basically houses an arena where we can /// allocate terms. @@ -29,16 +30,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 mut arena = TypedArena::default(); let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena); let constraints_cx = constraints::add_constraints_from_crate(terms_cx); - 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") @@ -60,7 +61,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] { }, Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Method(..) => {} + hir::ImplItemKind::Fn(..) => {} _ => unsupported(), }, 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 bd44a3eda98ce..6e15485756d98 100644 --- a/src/librustc_typeck/variance/terms.rs +++ b/src/librustc_typeck/variance/terms.rs @@ -9,11 +9,11 @@ // `InferredIndex` is a newtype'd int representing the index of such // a variable. -use arena::TypedArena; -use rustc::ty::{self, TyCtxt}; +use rustc_arena::TypedArena; 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() } @@ -170,7 +170,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for TermsContext<'a, 'tcx> { } fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Method(..) = impl_item.kind { + if let hir::ImplItemKind::Fn(..) = impl_item.kind { self.add_inferreds_for_item(impl_item.hir_id); } } 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..423160f3a9e01 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.kind() { + ty::PredicateKind::Trait(poly_trait_pred, _) => { + tcx.collect_referenced_late_bound_regions(&poly_trait_pred) + } + ty::PredicateKind::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() } @@ -461,8 +465,8 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { .iter() .filter(|p| { !orig_bounds.contains(p) - || match p { - ty::Predicate::Trait(pred, _) => pred.def_id() == sized_trait, + || match p.kind() { + ty::PredicateKind::Trait(pred, _) => pred.def_id() == sized_trait, _ => false, } }) @@ -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..3d2785541beea 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::*; @@ -65,7 +65,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { match infcx.evaluate_obligation(&traits::Obligation::new( cause, param_env, - trait_ref.without_const().to_predicate(), + trait_ref.without_const().to_predicate(infcx.tcx), )) { Ok(eval_result) => eval_result.may_apply(), Err(traits::OverflowError) => true, // overflow doesn't mean yes *or* no 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..08e04f719e9ba 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -2,16 +2,17 @@ use std::iter::once; -use rustc::ty; use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; 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,7 @@ 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: DefId::local(CRATE_DEF_INDEX), visibility: clean::Public, stability: None, deprecation: None, @@ -482,8 +485,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 +498,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 +512,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 e2fca8f39a398..73fe87b05d477 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), @@ -446,11 +447,9 @@ impl Clean> for ty::RegionKind { ty::ReLateBound(..) | ty::ReFree(..) - | ty::ReScope(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReEmpty(_) - | ty::ReClosureBound(_) | ty::ReErased => { debug!("cannot clean region {:?}", self); None @@ -481,33 +480,33 @@ impl Clean for hir::WherePredicate<'_> { impl<'a> Clean> for ty::Predicate<'a> { fn clean(&self, cx: &DocContext<'_>) -> Option { - use rustc::ty::Predicate; - - match *self { - Predicate::Trait(ref pred, _) => Some(pred.clean(cx)), - Predicate::Subtype(ref pred) => Some(pred.clean(cx)), - Predicate::RegionOutlives(ref pred) => pred.clean(cx), - Predicate::TypeOutlives(ref pred) => pred.clean(cx), - Predicate::Projection(ref pred) => Some(pred.clean(cx)), + match self.kind() { + ty::PredicateKind::Trait(ref pred, _) => Some(pred.clean(cx)), + ty::PredicateKind::Subtype(ref pred) => Some(pred.clean(cx)), + ty::PredicateKind::RegionOutlives(ref pred) => pred.clean(cx), + ty::PredicateKind::TypeOutlives(ref pred) => pred.clean(cx), + ty::PredicateKind::Projection(ref pred) => Some(pred.clean(cx)), - Predicate::WellFormed(..) - | Predicate::ObjectSafe(..) - | Predicate::ClosureKind(..) - | Predicate::ConstEvaluatable(..) => panic!("not user writable"), + ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::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.skip_binder().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 \ @@ -517,16 +516,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 { @@ -536,13 +532,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 { @@ -552,9 +547,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) } } } @@ -628,7 +624,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, @@ -637,7 +633,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), }, ), @@ -759,14 +755,14 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx let mut projection = None; let param_idx = (|| { if let Some(trait_ref) = p.to_opt_poly_trait_ref() { - if let ty::Param(param) = trait_ref.self_ty().kind { + if let ty::Param(param) = trait_ref.skip_binder().self_ty().kind { return Some(param.index); } } else if let Some(outlives) = p.to_opt_type_outlives() { if let ty::Param(param) = outlives.skip_binder().0.kind { return Some(param.index); } - } else if let ty::Predicate::Projection(p) = p { + } else if let ty::PredicateKind::Projection(p) = p.kind() { if let ty::Param(param) = p.skip_binder().projection_ty.self_ty().kind { projection = Some(p); return Some(param.index); @@ -898,7 +894,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 @@ -911,7 +907,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, @@ -923,7 +919,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 @@ -978,11 +974,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)), @@ -1019,7 +1011,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), @@ -1042,7 +1034,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), @@ -1078,16 +1070,36 @@ impl Clean for hir::PolyTraitRef<'_> { } } +impl Clean for hir::def::DefKind { + fn clean(&self, _: &DocContext<'_>) -> TypeKind { + match *self { + hir::def::DefKind::Mod => TypeKind::Module, + hir::def::DefKind::Struct => TypeKind::Struct, + hir::def::DefKind::Union => TypeKind::Union, + hir::def::DefKind::Enum => TypeKind::Enum, + hir::def::DefKind::Trait => TypeKind::Trait, + hir::def::DefKind::TyAlias => TypeKind::Typedef, + hir::def::DefKind::ForeignTy => TypeKind::Foreign, + hir::def::DefKind::TraitAlias => TypeKind::TraitAlias, + hir::def::DefKind::Fn => TypeKind::Function, + hir::def::DefKind::Const => TypeKind::Const, + hir::def::DefKind::Static => TypeKind::Static, + hir::def::DefKind::Macro(_) => TypeKind::Macro, + _ => TypeKind::Foreign, + } + } +} + impl Clean for hir::TraitItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let inner = match self.kind { hir::TraitItemKind::Const(ref ty, default) => { AssocConstItem(ty.clean(cx), default.map(|e| print_const_expr(cx, e))) } - hir::TraitItemKind::Fn(ref sig, hir::TraitMethod::Provided(body)) => { + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { MethodItem((sig, &self.generics, body, None).clean(cx)) } - hir::TraitItemKind::Fn(ref sig, hir::TraitMethod::Required(ref names)) => { + hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref names)) => { let (generics, decl) = enter_impl_trait(cx, || { (self.generics.clean(cx), (&*sig.decl, &names[..]).clean(cx)) }); @@ -1103,10 +1115,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, } } @@ -1118,7 +1130,7 @@ impl Clean for hir::ImplItem<'_> { hir::ImplItemKind::Const(ref ty, expr) => { AssocConstItem(ty.clean(cx), Some(print_const_expr(cx, expr))) } - hir::ImplItemKind::Method(ref sig, body) => { + hir::ImplItemKind::Fn(ref sig, body) => { MethodItem((sig, &self.generics, body, Some(self.defaultness)).clean(cx)) } hir::ImplItemKind::TyAlias(ref ty) => { @@ -1126,20 +1138,16 @@ impl Clean for hir::ImplItem<'_> { let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did)); TypedefItem(Typedef { type_, generics: Generics::default(), item_type }, true) } - hir::ImplItemKind::OpaqueTy(ref bounds) => OpaqueTyItem( - OpaqueTy { bounds: bounds.clean(cx), generics: Generics::default() }, - true, - ), }; let local_did = cx.tcx.hir().local_def_id(self.hir_id); Item { name: Some(self.ident.name.clean(cx)), source: self.span.clean(cx), 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, } } @@ -1157,14 +1165,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, @@ -1296,7 +1304,6 @@ impl Clean for ty::AssocItem { ) } } - ty::AssocKind::OpaqueTy => unimplemented!(), }; let visibility = match self.container { @@ -1331,7 +1338,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)) } @@ -1344,7 +1351,7 @@ impl Clean for hir::Ty<'_> { Array(box ty.clean(cx), length) } TyKind::Tup(ref tys) => Tuple(tys.clean(cx)), - TyKind::Def(item_id, _) => { + TyKind::OpaqueDef(item_id, _) => { let item = cx.tcx.hir().expect_item(item_id.id); if let hir::ItemKind::OpaqueTy(ref ty) = item.kind { ImplTrait(ty.bounds.clean(cx)) @@ -1365,8 +1372,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); } } @@ -1398,7 +1406,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; @@ -1418,9 +1426,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; } @@ -1440,7 +1449,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; @@ -1534,11 +1544,11 @@ impl<'tcx> Clean for Ty<'tcx> { ty::FnDef(..) | ty::FnPtr(_) => { let ty = cx.tcx.lift(self).expect("FnPtr lift failed"); let sig = ty.fn_sig(cx.tcx); - let local_def_id = cx.tcx.hir().local_def_id_from_node_id(ast::CRATE_NODE_ID); + let def_id = DefId::local(CRATE_DEF_INDEX); BareFunction(box BareFunctionDecl { unsafety: sig.unsafety(), generic_params: Vec::new(), - decl: (local_def_id, sig).clean(cx), + decl: (def_id, sig).clean(cx), abi: sig.abi(), }) } @@ -1582,7 +1592,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 = @@ -1643,12 +1655,11 @@ impl<'tcx> Clean for Ty<'tcx> { .filter_map(|predicate| { let trait_ref = if let Some(tr) = predicate.to_opt_poly_trait_ref() { tr - } else if let ty::Predicate::TypeOutlives(pred) = *predicate { + } else if let ty::PredicateKind::TypeOutlives(pred) = predicate.kind() { // 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; @@ -1661,11 +1672,11 @@ impl<'tcx> Clean for Ty<'tcx> { } } - let bounds = bounds + let bounds: Vec<_> = bounds .predicates .iter() .filter_map(|pred| { - if let ty::Predicate::Projection(proj) = *pred { + if let ty::PredicateKind::Projection(proj) = pred.kind() { let proj = proj.skip_binder(); if proj.projection_ty.trait_ref(cx.tcx) == *trait_ref.skip_binder() @@ -1690,7 +1701,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); @@ -1704,10 +1715,9 @@ 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"), + ty::Error(_) => panic!("Error"), } } } @@ -1732,9 +1742,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)), } } @@ -1782,7 +1792,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), @@ -1802,7 +1812,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), @@ -1832,7 +1842,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), @@ -1854,7 +1864,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) }), } } @@ -1923,6 +1933,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, @@ -1987,7 +1998,7 @@ impl Clean for Ident { } } -impl Clean for ast::Name { +impl Clean for Symbol { #[inline] fn clean(&self, _: &DocContext<'_>) -> String { self.to_string() @@ -2002,7 +2013,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), @@ -2017,7 +2028,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), @@ -2048,7 +2059,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), @@ -2069,14 +2080,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), }), } @@ -2116,14 +2127,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), @@ -2163,13 +2174,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; } } @@ -2220,26 +2227,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; } } @@ -2250,7 +2250,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: DefId::local(CRATE_DEF_INDEX), visibility: self.vis.clean(cx), stability: None, deprecation: None, @@ -2292,7 +2292,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), @@ -2336,7 +2336,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) }), } } @@ -2407,10 +2407,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..37c613f41224a 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; @@ -141,7 +141,7 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) .predicates .iter() .filter_map(|(pred, _)| { - if let ty::Predicate::Trait(ref pred, _) = *pred { + if let ty::PredicateKind::Trait(ref pred, _) = pred.kind() { if pred.skip_binder().trait_ref.self_ty() == self_ty { Some(pred.def_id()) } else { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index b1aa094204a1e..6dec016cc2ee3 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; @@ -85,9 +85,7 @@ pub struct Item { impl fmt::Debug for Item { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let fake = MAX_DEF_ID.with(|m| { - m.borrow().get(&self.def_id.krate).map(|id| self.def_id >= *id).unwrap_or(false) - }); + let fake = self.is_fake(); let def_id: &dyn fmt::Debug = if fake { &"**FAKE**" } else { &self.def_id }; fmt.debug_struct("Item") @@ -238,6 +236,13 @@ impl Item { _ => false, } } + + /// See comments on next_def_id + pub fn is_fake(&self) -> bool { + MAX_DEF_ID.with(|m| { + m.borrow().get(&self.def_id.krate).map(|id| self.def_id >= *id).unwrap_or(false) + }) + } } #[derive(Clone, Debug)] @@ -481,6 +486,33 @@ impl Attributes { }) } + /// Enforce the format of attributes inside `#[doc(...)]`. + pub fn check_doc_attributes( + diagnostic: &::rustc_errors::Handler, + mi: &ast::MetaItem, + ) -> Option<(String, String)> { + mi.meta_item_list().and_then(|list| { + for meta in list { + if meta.check_name(sym::alias) { + if !meta.is_value_str() + || meta + .value_str() + .map(|s| s.to_string()) + .unwrap_or_else(String::new) + .is_empty() + { + diagnostic.span_err( + meta.span(), + "doc alias attribute expects a string: #[doc(alias = \"0\")]", + ); + } + } + } + + None + }) + } + pub fn has_doc_flag(&self, flag: Symbol) -> bool { for attr in &self.other_attrs { if !attr.check_name(sym::doc) { @@ -524,6 +556,7 @@ impl Attributes { } else { if attr.check_name(sym::doc) { if let Some(mi) = attr.meta() { + Attributes::check_doc_attributes(&diagnostic, &mi); if let Some(cfg_mi) = Attributes::extract_cfg(&mi) { // Extracted #[doc(cfg(...))] match Cfg::parse(cfg_mi) { @@ -643,6 +676,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 { @@ -836,8 +878,8 @@ pub struct Method { pub decl: FnDecl, pub header: hir::FnHeader, pub defaultness: Option, - pub all_types: Vec, - pub ret_types: Vec, + pub all_types: Vec<(Type, TypeKind)>, + pub ret_types: Vec<(Type, TypeKind)>, } #[derive(Clone, Debug)] @@ -845,8 +887,8 @@ pub struct TyMethod { pub header: hir::FnHeader, pub decl: FnDecl, pub generics: Generics, - pub all_types: Vec, - pub ret_types: Vec, + pub all_types: Vec<(Type, TypeKind)>, + pub ret_types: Vec<(Type, TypeKind)>, } #[derive(Clone, Debug)] @@ -854,8 +896,8 @@ pub struct Function { pub decl: FnDecl, pub generics: Generics, pub header: hir::FnHeader, - pub all_types: Vec, - pub ret_types: Vec, + pub all_types: Vec<(Type, TypeKind)>, + pub ret_types: Vec<(Type, TypeKind)>, } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -1042,7 +1084,7 @@ pub enum PrimitiveType { Never, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Copy, Debug)] pub enum TypeKind { Enum, Function, @@ -1357,6 +1399,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 +1411,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 21e3d24cc968b..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 @@ -184,7 +184,7 @@ pub fn get_real_types( arg: &Type, cx: &DocContext<'_>, recurse: i32, -) -> FxHashSet { +) -> FxHashSet<(Type, TypeKind)> { let arg_s = arg.print().to_string(); let mut res = FxHashSet::default(); if recurse >= 10 { @@ -198,23 +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() { - res.insert(ty); + 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)); } } } } - _ => {} } } } @@ -225,13 +226,17 @@ pub fn get_real_types( if !adds.is_empty() { res.extend(adds); } else if !ty.is_full_generic() { - res.insert(ty.clone()); + if let Some(kind) = ty.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { + res.insert((ty.clone(), kind)); + } } } } } } else { - res.insert(arg.clone()); + 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() { if gen.is_full_generic() { @@ -239,8 +244,8 @@ pub fn get_real_types( if !adds.is_empty() { res.extend(adds); } - } else { - res.insert(gen.clone()); + } else if let Some(kind) = gen.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { + res.insert((gen.clone(), kind)); } } } @@ -256,7 +261,7 @@ pub fn get_all_types( generics: &Generics, decl: &FnDecl, cx: &DocContext<'_>, -) -> (Vec, Vec) { +) -> (Vec<(Type, TypeKind)>, Vec<(Type, TypeKind)>) { let mut all_types = FxHashSet::default(); for arg in decl.inputs.values.iter() { if arg.type_.is_self_type() { @@ -266,7 +271,9 @@ pub fn get_all_types( if !args.is_empty() { all_types.extend(args); } else { - all_types.insert(arg.type_.clone()); + if let Some(kind) = arg.type_.def_id().map(|did| cx.tcx.def_kind(did).clean(cx)) { + all_types.insert((arg.type_.clone(), kind)); + } } } @@ -274,7 +281,9 @@ 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() { - ret.insert(return_type.clone()); + 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() } @@ -458,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) @@ -485,7 +495,7 @@ pub fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String { } pub fn print_evaluated_const(cx: &DocContext<'_>, def_id: DefId) -> Option { - let value = cx.tcx.const_eval_poly(def_id).ok().and_then(|val| { + cx.tcx.const_eval_poly(def_id).ok().and_then(|val| { let ty = cx.tcx.type_of(def_id); match (val, &ty.kind) { (_, &ty::Ref(..)) => None, @@ -496,9 +506,7 @@ pub fn print_evaluated_const(cx: &DocContext<'_>, def_id: DefId) -> Option None, } - }); - - value + }) } fn format_integer_with_underscore_sep(num: &str) -> String { @@ -558,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)), @@ -577,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 58c8a7d82bfb5..5dbcc5c9ec8b9 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -4,15 +4,15 @@ use std::ffi::OsStr; use std::fmt; use std::path::PathBuf; -use rustc::lint::Level; -use rustc::session; -use rustc::session::config::{ +use rustc_session::config::{self, parse_crate_types_from_list, parse_externs, CrateType}; +use rustc_session::config::{ build_codegen_options, build_debugging_options, get_cmd_lint_options, host_triple, nightly_options, }; -use rustc::session::config::{parse_crate_types_from_list, parse_externs, CrateType}; -use rustc::session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs}; -use rustc::session::search_paths::SearchPath; +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}; use rustc_target::spec::TargetTriple; @@ -299,9 +299,9 @@ impl Options { return Err(0); } - let color = session::config::parse_color(&matches); - let (json_rendered, _artifacts) = session::config::parse_json(&matches); - let error_format = session::config::parse_error_format(&matches, color, json_rendered); + let color = config::parse_color(&matches); + let (json_rendered, _artifacts) = config::parse_json(&matches); + let error_format = config::parse_error_format(&matches, color, json_rendered); let codegen_options = build_codegen_options(matches, error_format); let debugging_options = build_debugging_options(matches, error_format); @@ -447,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 b9ae3d53afc04..1690b946bb625 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,28 +1,26 @@ -use rustc::middle::cstore::CrateStore; -use rustc::middle::privacy::AccessLevels; -use rustc::session::config::ErrorOutputType; -use rustc::session::DiagnosticOutput; -use rustc::session::{self, config}; -use rustc::ty::{Ty, TyCtxt}; +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; use rustc_feature::UnstableFeatures; use rustc_hir::def::Namespace::TypeNS; -use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, 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::{self, CrateType, ErrorOutputType}; use rustc_session::lint; - -use rustc_ast::ast::CRATE_NODE_ID; -use rustc_attr as attr; -use rustc_errors::emitter::{Emitter, EmitterWriter}; -use rustc_errors::json::JsonEmitter; +use rustc_session::DiagnosticOutput; +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; @@ -31,11 +29,10 @@ 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}; -pub use rustc::session::search_paths::SearchPath; +pub use rustc_session::config::{CodegenOptions, DebuggingOptions, Input, Options}; +pub use rustc_session::search_paths::SearchPath; pub type ExternalPaths = FxHashMap, clean::TypeKind)>; @@ -68,7 +65,7 @@ pub struct DocContext<'tcx> { } impl<'tcx> DocContext<'tcx> { - pub fn sess(&self) -> &session::Session { + pub fn sess(&self) -> &Session { &self.tcx.sess } @@ -104,7 +101,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. // @@ -131,7 +128,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); @@ -145,7 +142,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)) } } @@ -153,12 +150,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())) } } @@ -183,7 +183,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 } => { @@ -192,7 +192,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), ) } }; @@ -203,6 +203,64 @@ 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| { + // Whitelist feature-gated lints to avoid feature errors when trying to + // allow all lints. + if lint.name == warnings_lint_name || lint.feature_gate.is_some() { + 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. @@ -246,57 +304,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, @@ -350,7 +386,12 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt resolver.borrow_mut().access(|resolver| { for extern_name in &extern_names { resolver - .resolve_str_path_error(DUMMY_SP, extern_name, TypeNS, CRATE_NODE_ID) + .resolve_str_path_error( + DUMMY_SP, + extern_name, + TypeNS, + LocalDefId { local_def_index: CRATE_DEF_INDEX }, + ) .unwrap_or_else(|()| { panic!("Unable to resolve external crate {}", extern_name) }); @@ -380,7 +421,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(), }; @@ -412,10 +453,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..9a11e8fce28b7 100644 --- a/src/librustdoc/docfs.rs +++ b/src/librustdoc/docfs.rs @@ -12,20 +12,23 @@ use std::fs; use std::io; use std::path::Path; +use std::string::ToString; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::Arc; 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 { - fn new>(e: io::Error, path: P) -> Self; + fn new>(e: S, path: P) -> Self + where + S: ToString + Sized; } pub struct ErrorStorage { 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..7a6626766d388 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 @@ -187,12 +192,13 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { fn next(&mut self) -> Option { let event = self.inner.next(); let compile_fail; + let should_panic; let ignore; let edition; 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(), }; @@ -200,6 +206,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { return Some(Event::Start(Tag::CodeBlock(kind))); } compile_fail = parse_result.compile_fail; + should_panic = parse_result.should_panic; ignore = parse_result.ignore; edition = parse_result.edition; } else { @@ -275,6 +282,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { Some(("This example is not tested".to_owned(), "ignore")) } else if compile_fail { Some(("This example deliberately fails to compile".to_owned(), "compile_fail")) + } else if should_panic { + Some(("This example panics".to_owned(), "should_panic")) } else if explicit_edition { Some((format!("This code runs with edition {}", edition), "edition")) } else { @@ -290,6 +299,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { " ignore" } else if compile_fail { " compile_fail" + } else if should_panic { + " should_panic" } else if explicit_edition { " edition " } else { @@ -309,6 +320,8 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { " ignore" } else if compile_fail { " compile_fail" + } else if should_panic { + " should_panic" } else if explicit_edition { " edition " } else { @@ -448,7 +461,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 +573,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 +587,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 +634,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 +714,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 +786,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 +941,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 +972,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 +987,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 +1035,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 +1060,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 +1070,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 +1145,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 +1184,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

", + "

\ + Main

", ); t( &mut map, diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index b3d70475bf3c3..1681b73d0c257 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -31,7 +31,6 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, VecDeque}; use std::default::Default; use std::error; - use std::ffi::OsStr; use std::fmt::{self, Formatter, Write}; use std::fs::{self, File}; @@ -40,17 +39,18 @@ use std::io::{self, BufReader}; use std::path::{Component, Path, PathBuf}; use std::rc::Rc; use std::str; +use std::string::ToString; use std::sync::Arc; -use 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; @@ -58,7 +58,7 @@ use rustc_span::symbol::{sym, Symbol}; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; -use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy}; +use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind}; use crate::config::{OutputFormat, RenderOptions}; use crate::docfs::{DocFS, ErrorStorage, PathError}; use crate::doctree; @@ -92,7 +92,7 @@ crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { #[derive(Debug)] pub struct Error { pub file: PathBuf, - pub error: io::Error, + pub error: String, } impl error::Error for Error {} @@ -109,8 +109,11 @@ impl std::fmt::Display for Error { } impl PathError for Error { - fn new>(e: io::Error, path: P) -> Error { - Error { file: path.as_ref().to_path_buf(), error: e } + fn new>(e: S, path: P) -> Error + where + S: ToString + Sized, + { + Error { file: path.as_ref().to_path_buf(), error: e.to_string() } } } @@ -293,7 +296,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) @@ -302,19 +310,25 @@ impl Serialize for IndexItem { /// A type used for the search index. #[derive(Debug)] -struct Type { +struct RenderType { + ty: Option, + idx: Option, name: Option, - generics: Option>, + generics: Option>, } -impl Serialize for Type { +impl Serialize for RenderType { fn serialize(&self, serializer: S) -> Result where S: Serializer, { if let Some(name) = &self.name { let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&name)?; + if let Some(id) = self.idx { + seq.serialize_element(&id)?; + } else { + seq.serialize_element(&name)?; + } if let Some(generics) = &self.generics { seq.serialize_element(&generics)?; } @@ -325,11 +339,32 @@ impl Serialize for Type { } } +/// A type used for the search index. +#[derive(Debug)] +struct Generic { + name: String, + defid: Option, + idx: Option, +} + +impl Serialize for Generic { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if let Some(id) = self.idx { + serializer.serialize_some(&id) + } else { + serializer.serialize_some(&self.name) + } + } +} + /// Full type of functions/methods in the search index. #[derive(Debug)] struct IndexItemFunctionType { - inputs: Vec, - output: Option>, + inputs: Vec, + output: Option>, } impl Serialize for IndexItemFunctionType { @@ -340,8 +375,8 @@ impl Serialize for IndexItemFunctionType { // If we couldn't figure out a type, just write `null`. let mut iter = self.inputs.iter(); if match self.output { - Some(ref output) => iter.chain(output.iter()).any(|ref i| i.name.is_none()), - None => iter.any(|ref i| i.name.is_none()), + Some(ref output) => iter.chain(output.iter()).any(|ref i| i.ty.name.is_none()), + None => iter.any(|ref i| i.ty.name.is_none()), } { serializer.serialize_none() } else { @@ -359,6 +394,31 @@ impl Serialize for IndexItemFunctionType { } } +#[derive(Debug)] +pub struct TypeWithKind { + ty: RenderType, + kind: TypeKind, +} + +impl From<(RenderType, TypeKind)> for TypeWithKind { + fn from(x: (RenderType, TypeKind)) -> TypeWithKind { + TypeWithKind { ty: x.0, kind: x.1 } + } +} + +impl Serialize for TypeWithKind { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(None)?; + seq.serialize_element(&self.ty.name)?; + let x: ItemType = self.kind.into(); + seq.serialize_element(&x)?; + seq.end() + } +} + thread_local!(static CACHE_KEY: RefCell> = Default::default()); thread_local!(pub static CURRENT_DEPTH: Cell = Cell::new(0)); @@ -413,7 +473,7 @@ pub fn run( } = options; let src_root = match krate.src { - FileName::Real(ref p) => match p.parent() { + FileName::Real(ref p) => match p.local_path().parent() { Some(p) => p.to_path_buf(), None => PathBuf::new(), }, @@ -505,7 +565,7 @@ pub fn run( // Write shared runs within a flock; disable thread dispatching of IO temporarily. Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); - write_shared(&cx, &krate, index, &md_opts, diag)?; + write_shared(&cx, &krate, index, &md_opts)?; Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); // And finally render the whole crate's documentation @@ -525,7 +585,6 @@ fn write_shared( krate: &clean::Crate, search_index: String, options: &RenderOptions, - diag: &rustc_errors::Handler, ) -> Result<(), Error> { // Write out the shared files. Note that these are shared among all rustdoc // docs placed in the output directory, so this needs to be a synchronized @@ -730,47 +789,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; @@ -857,18 +910,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 { @@ -877,7 +930,8 @@ themePicker.onblur = handleThemeButtonsBlur; md_opts.output = cx.dst.clone(); md_opts.external_html = (*cx.shared).layout.external_html.clone(); - crate::markdown::render(index_page, md_opts, diag, cx.shared.edition); + crate::markdown::render(&index_page, md_opts, cx.shared.edition) + .map_err(|e| Error::new(e, &index_page))?; } else { let dst = cx.dst.join("index.html"); let page = layout::Page { @@ -1567,18 +1621,19 @@ impl Context { // We can safely ignore synthetic `SourceFile`s. let file = match item.source.filename { - FileName::Real(ref path) => path, + FileName::Real(ref path) => path.local_path().to_path_buf(), _ => return None, }; + let file = &file; - 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(); @@ -2106,7 +2161,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()] @@ -2198,7 +2253,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) { @@ -3151,7 +3209,6 @@ fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) { continue; } - // FIXME: this currently renders too many spaces as in: `#[repr(C, align (8))]`. attrs.push_str(&pprust::attribute_to_string(&attr)); } if !attrs.is_empty() { @@ -3331,8 +3388,8 @@ fn render_assoc_items( write!( w, "\ -

\ - Methods\ +

\ + Implementations\

\ " ); @@ -3393,10 +3450,10 @@ fn render_assoc_items( write!( w, "\ -

\ - Trait Implementations\ +

\ + Trait Implementations\

\ -
{}
", +
{}
", impls ); } @@ -3445,14 +3502,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 }; @@ -4012,12 +4068,12 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { .filter(|i| i.inner_impl().trait_.is_none()) .flat_map(move |i| get_methods(i.inner_impl(), false, used_links_bor, false)) .collect::>(); - // We want links' order to be reproducible so we don't use unstable sort. - ret.sort(); if !ret.is_empty() { + // We want links' order to be reproducible so we don't use unstable sort. + ret.sort(); out.push_str(&format!( - "Methods\ -
{}
", + "Methods\ +
{}
", ret.join("") )); } @@ -4029,18 +4085,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() @@ -4114,8 +4166,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)); } @@ -4123,7 +4175,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)); } @@ -4131,7 +4183,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)); } @@ -4189,7 +4241,7 @@ fn is_negative_impl(i: &clean::Impl) -> bool { fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { let mut sidebar = String::new(); - let types = t + let mut types = t .items .iter() .filter_map(|m| match m.name { @@ -4198,8 +4250,8 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { } _ => None, }) - .collect::(); - let consts = t + .collect::>(); + let mut consts = t .items .iter() .filter_map(|m| match m.name { @@ -4208,7 +4260,7 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { } _ => None, }) - .collect::(); + .collect::>(); let mut required = t .items .iter() @@ -4231,24 +4283,26 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { .collect::>(); if !types.is_empty() { + types.sort(); sidebar.push_str(&format!( "\ - Associated Types
{}
", - types + Associated Types
{}
", + types.join("") )); } if !consts.is_empty() { + consts.sort(); sidebar.push_str(&format!( "\ - Associated Constants
{}
", - consts + Associated Constants
{}
", + consts.join("") )); } if !required.is_empty() { required.sort(); sidebar.push_str(&format!( "\ - Required Methods
{}
", + Required Methods
{}
", required.join("") )); } @@ -4256,7 +4310,7 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { provided.sort(); sidebar.push_str(&format!( "\ - Provided Methods
{}
", + Provided Methods
{}
", provided.join("") )); } @@ -4267,34 +4321,33 @@ 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("") + Implementations on Foreign Types
{}
", + res.into_iter() + .map(|(name, id)| format!("{}", id, Escape(&name))) + .collect::>() + .join("") )); } } + sidebar.push_str(&sidebar_assoc_items(it)); + sidebar.push_str("Implementors"); if t.auto { sidebar.push_str( "Auto Implementors", + href=\"#synthetic-implementors\">Auto Implementors", ); } - sidebar.push_str(&sidebar_assoc_items(it)); - write!(buf, "
{}
", sidebar) } @@ -4315,18 +4368,18 @@ fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) { } fn get_struct_fields_name(fields: &[clean::Item]) -> String { - fields + let mut fields = fields .iter() .filter(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false }) .filter_map(|f| match f.name { - Some(ref name) => Some(format!( - "\ - {name}", - name = name - )), + Some(ref name) => { + Some(format!("{name}", name = name)) + } _ => None, }) - .collect() + .collect::>(); + fields.sort(); + fields.join("") } fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { @@ -4336,7 +4389,7 @@ fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { if !fields.is_empty() { sidebar.push_str(&format!( "Fields\ -
{}
", +
{}
", fields )); } @@ -4351,23 +4404,20 @@ fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { let mut sidebar = String::new(); - let variants = e + let mut variants = e .variants .iter() .filter_map(|v| match v.name { - Some(ref name) => Some(format!( - "{name}\ - ", - name = name - )), + Some(ref name) => Some(format!("{name}", name = name)), _ => None, }) - .collect::(); + .collect::>(); if !variants.is_empty() { + variants.sort_unstable(); sidebar.push_str(&format!( "Variants\ -
{}
", - variants +
{}
", + variants.join(""), )); } @@ -4542,12 +4592,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 4198369eca8f5..225940773413e 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; @@ -12,7 +12,7 @@ use std::path::{Path, PathBuf}; use serde::Serialize; use super::{plain_summary_line, shorten, Impl, IndexItem, IndexItemFunctionType, ItemType}; -use super::{RenderInfo, Type}; +use super::{Generic, RenderInfo, RenderType, TypeWithKind}; /// Indicates where an external crate can be found. pub enum ExternalLocation { @@ -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 { @@ -173,7 +173,7 @@ impl Cache { // Cache where all our extern crates are located for &(n, ref e) in &krate.externs { let src_root = match e.src { - FileName::Real(ref p) => match p.parent() { + FileName::Real(ref p) => match p.local_path().parent() { Some(p) => p.to_path_buf(), None => PathBuf::new(), }, @@ -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); + } } } @@ -588,17 +568,20 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { let mut lastpathid = 0usize; for item in search_index { - item.parent_idx = item.parent.map(|defid| { + item.parent_idx = item.parent.and_then(|defid| { if defid_to_pathid.contains_key(&defid) { - *defid_to_pathid.get(&defid).expect("no pathid") + defid_to_pathid.get(&defid).copied() } else { let pathid = lastpathid; defid_to_pathid.insert(defid, pathid); lastpathid += 1; - let &(ref fqp, short) = paths.get(&defid).unwrap(); - crate_paths.push((short, fqp.last().unwrap().clone())); - pathid + if let Some(&(ref fqp, short)) = paths.get(&defid) { + crate_paths.push((short, fqp.last().unwrap().clone())); + Some(pathid) + } else { + None + } } }); @@ -624,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("\\\"", "\\\\\"") ) } @@ -647,24 +642,28 @@ fn get_index_search_type(item: &clean::Item) -> Option { _ => return None, }; - let inputs = - all_types.iter().map(|arg| get_index_type(&arg)).filter(|a| a.name.is_some()).collect(); + let inputs = all_types + .iter() + .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind))) + .filter(|a| a.ty.name.is_some()) + .collect(); let output = ret_types .iter() - .map(|arg| get_index_type(&arg)) - .filter(|a| a.name.is_some()) + .map(|(ty, kind)| TypeWithKind::from((get_index_type(&ty), *kind))) + .filter(|a| a.ty.name.is_some()) .collect::>(); let output = if output.is_empty() { None } else { Some(output) }; Some(IndexItemFunctionType { inputs, output }) } -fn get_index_type(clean_type: &clean::Type) -> Type { - let t = Type { +fn get_index_type(clean_type: &clean::Type) -> RenderType { + RenderType { + ty: clean_type.def_id(), + idx: None, name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()), generics: get_generics(clean_type), - }; - t + } } fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option { @@ -685,12 +684,17 @@ fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option } } -fn get_generics(clean_type: &clean::Type) -> Option> { +fn get_generics(clean_type: &clean::Type) -> Option> { clean_type.generics().and_then(|types| { let r = types .iter() - .filter_map(|t| get_index_type_name(t, false)) - .map(|s| s.to_ascii_lowercase()) + .filter_map(|t| { + 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..f0900c34a4ba3 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 @@ -66,10 +67,10 @@ impl<'a> SourceCollector<'a> { /// Renders the given filename into its corresponding HTML source file. fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> { let p = match *filename { - FileName::Real(ref file) => file, + FileName::Real(ref file) => file.local_path().to_path_buf(), _ => return Ok(()), }; - if self.scx.local_sources.contains_key(&**p) { + if self.scx.local_sources.contains_key(&*p) { // We've already emitted this source return Ok(()); } @@ -125,7 +126,7 @@ impl<'a> SourceCollector<'a> { &self.scx.themes, ); self.scx.fs.write(&cur, v.as_bytes())?; - self.scx.local_sources.insert(p.clone(), href); + self.scx.local_sources.insert(p, href); Ok(()) } } diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index a799aed698578..59bb206678f51 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -3,7 +3,7 @@ // Local js definitions: /* global addClass, getCurrentValue, hasClass */ -/* global onEach, removeClass, updateLocalStorage */ +/* global onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */ if (!String.prototype.startsWith) { String.prototype.startsWith = function(searchString, position) { @@ -47,6 +47,16 @@ function getSearchElement() { return document.getElementById("search"); } +// Sets the focus on the search bar at the top of the page +function focusSearchBar() { + getSearchInput().focus(); +} + +// Removes the focus from the search bar +function defocusSearchBar() { + getSearchInput().blur(); +} + (function() { "use strict"; @@ -81,6 +91,7 @@ function getSearchElement() { var disableShortcuts = getCurrentValue("rustdoc-disable-shortcuts") === "true"; var search_input = getSearchInput(); + var searchTimeout = null; // On the search screen, so you remain on the last tab you opened. // @@ -89,8 +100,17 @@ function getSearchElement() { // 2 for "In Return Types" var currentTab = 0; + var mouseMovedAfterSearch = true; + var titleBeforeSearch = document.title; + function clearInputTimeout() { + if (searchTimeout !== null) { + clearTimeout(searchTimeout); + searchTimeout = null; + } + } + function getPageId() { var id = document.location.href.split("#")[1]; if (id) { @@ -144,6 +164,7 @@ function getSearchElement() { } addClass(main, "hidden"); removeClass(search, "hidden"); + mouseMovedAfterSearch = false; } function hideSearchResults(search) { @@ -181,6 +202,7 @@ function getSearchElement() { var savedHash = ""; function handleHashes(ev) { + var elem; var search = getSearchElement(); if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) { // This block occurs when clicking on an element in the navbar while @@ -190,7 +212,7 @@ function getSearchElement() { if (browserSupportsHistoryApi()) { history.replaceState(hash, "", "?search=#" + hash); } - var elem = document.getElementById(hash); + elem = document.getElementById(hash); if (elem) { elem.scrollIntoView(); } @@ -201,7 +223,7 @@ function getSearchElement() { if (savedHash.length === 0) { return; } - var elem = document.getElementById(savedHash.slice(1)); // we remove the '#' + elem = document.getElementById(savedHash.slice(1)); // we remove the '#' if (!elem || !isHidden(elem)) { return; } @@ -324,7 +346,7 @@ function getSearchElement() { } function displayHelp(display, ev, help) { - var help = help ? help : getHelpElement(); + help = help ? help : getHelpElement(); if (display === true) { if (hasClass(help, "hidden")) { ev.preventDefault(); @@ -344,6 +366,7 @@ function getSearchElement() { if (hasClass(help, "hidden") === false) { displayHelp(false, ev, help); } else if (hasClass(search, "hidden") === false) { + clearInputTimeout(); ev.preventDefault(); hideSearchResults(search); document.title = titleBeforeSearch; @@ -404,6 +427,12 @@ function getSearchElement() { document.addEventListener("keypress", handleShortcut); document.addEventListener("keydown", handleShortcut); + function resetMouseMoved(ev) { + mouseMovedAfterSearch = true; + } + + document.addEventListener("mousemove", resetMouseMoved); + var handleSourceHighlight = (function() { var prev_line_id = 0; @@ -438,8 +467,8 @@ function getSearchElement() { set_fragment(cur_line_id); } - } - })(); + }; + }()); document.addEventListener("click", function(ev) { if (hasClass(ev.target, "collapse-toggle")) { @@ -465,27 +494,30 @@ function getSearchElement() { } }); - var x = document.getElementsByClassName("version-selector"); - if (x.length > 0) { - x[0].onchange = function() { - var i, match, - url = document.location.href, - stripped = "", - len = rootPath.match(/\.\.\//g).length + 1; + (function() { + var x = document.getElementsByClassName("version-selector"); + if (x.length > 0) { + x[0].onchange = function() { + var i, match, + url = document.location.href, + stripped = "", + len = rootPath.match(/\.\.\//g).length + 1; - for (i = 0; i < len; ++i) { - match = url.match(/\/[^\/]*$/); - if (i < len - 1) { - stripped = match[0] + stripped; + for (i = 0; i < len; ++i) { + match = url.match(/\/[^\/]*$/); + if (i < len - 1) { + stripped = match[0] + stripped; + } + url = url.substring(0, url.length - match[0].length); } - url = url.substring(0, url.length - match[0].length); - } - url += "/" + document.getElementsByClassName("version-selector")[0].value + stripped; + var selectedVersion = document.getElementsByClassName("version-selector")[0].value; + url += "/" + selectedVersion + stripped; - document.location.href = url; - }; - } + document.location.href = url; + }; + } + }()); /** * A function to compute the Levenshtein distance between two strings @@ -522,14 +554,16 @@ function getSearchElement() { return s1_len + s2_len; } - function initSearch(rawSearchIndex) { - var currentResults, index, searchIndex; + window.initSearch = function(rawSearchIndex) { var MAX_LEV_DISTANCE = 3; var MAX_RESULTS = 200; var GENERICS_DATA = 1; var NAME = 0; var INPUTS_DATA = 0; 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, @@ -556,7 +590,7 @@ function getSearchElement() { return i; } } - return -1; + return NO_TYPE_FILTER; } var valLower = query.query.toLowerCase(), @@ -600,7 +634,7 @@ function getSearchElement() { function sortResults(results, isType) { var ar = []; for (var entry in results) { - if (results.hasOwnProperty(entry)) { + if (hasOwnProperty(results, entry)) { ar.push(results[entry]); } } @@ -719,6 +753,13 @@ function getSearchElement() { }; } + function getObjectFromId(id) { + if (typeof id === "number") { + return searchIndex[id]; + } + return {'name': id}; + } + function checkGenerics(obj, val) { // The names match, but we need to be sure that all generics kinda // match as well. @@ -735,8 +776,10 @@ function getSearchElement() { for (var y = 0; y < vlength; ++y) { var lev = { pos: -1, lev: MAX_LEV_DISTANCE + 1}; var elength = elems.length; + var firstGeneric = getObjectFromId(val.generics[y]).name; for (var x = 0; x < elength; ++x) { - var tmp_lev = levenshtein(elems[x], val.generics[y]); + var tmp_lev = levenshtein(getObjectFromId(elems[x]).name, + firstGeneric); if (tmp_lev < lev.lev) { lev.lev = tmp_lev; lev.pos = x; @@ -771,8 +814,9 @@ function getSearchElement() { for (var y = 0; allFound === true && y < val.generics.length; ++y) { allFound = false; + var firstGeneric = getObjectFromId(val.generics[y]).name; for (x = 0; allFound === false && x < elems.length; ++x) { - allFound = elems[x] === val.generics[y]; + allFound = getObjectFromId(elems[x]).name === firstGeneric; } if (allFound === true) { elems.splice(x - 1, 1); @@ -829,16 +873,22 @@ function getSearchElement() { return lev_distance + 1; } - function findArg(obj, val, literalSearch) { + function findArg(obj, val, literalSearch, typeFilter) { var lev_distance = MAX_LEV_DISTANCE + 1; - if (obj && obj.type && obj.type[INPUTS_DATA] && - obj.type[INPUTS_DATA].length > 0) { + if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) { var length = obj.type[INPUTS_DATA].length; for (var i = 0; i < length; i++) { - var tmp = checkType(obj.type[INPUTS_DATA][i], val, literalSearch); - if (literalSearch === true && tmp === true) { - return true; + var tmp = obj.type[INPUTS_DATA][i]; + if (typePassesFilter(typeFilter, tmp[1]) === false) { + continue; + } + tmp = checkType(tmp, val, literalSearch); + if (literalSearch === true) { + if (tmp === true) { + return true; + } + continue; } lev_distance = Math.min(tmp, lev_distance); if (lev_distance === 0) { @@ -849,20 +899,20 @@ function getSearchElement() { return literalSearch === true ? false : lev_distance; } - function checkReturned(obj, val, literalSearch) { + function checkReturned(obj, val, literalSearch, typeFilter) { var lev_distance = MAX_LEV_DISTANCE + 1; if (obj && obj.type && obj.type.length > OUTPUT_DATA) { var ret = obj.type[OUTPUT_DATA]; - if (!obj.type[OUTPUT_DATA].length) { + if (typeof ret[0] === "string") { ret = [ret]; } for (var x = 0; x < ret.length; ++x) { - var r = ret[x]; - if (typeof r === "string") { - r = [r]; + var tmp = ret[x]; + if (typePassesFilter(typeFilter, tmp[1]) === false) { + continue; } - var tmp = checkType(r, val, literalSearch); + tmp = checkType(tmp, val, literalSearch); if (literalSearch === true) { if (tmp === true) { return true; @@ -917,7 +967,7 @@ function getSearchElement() { function typePassesFilter(filter, type) { // No filter - if (filter < 0) return true; + if (filter <= NO_TYPE_FILTER) return true; // Exact match if (filter === type) return true; @@ -926,11 +976,13 @@ function getSearchElement() { var name = itemTypes[type]; switch (itemTypes[filter]) { case "constant": - return (name == "associatedconstant"); + return name === "associatedconstant"; case "fn": - return (name == "method" || name == "tymethod"); + return name === "method" || name === "tymethod"; case "type": - return (name == "primitive" || name == "keyword"); + return name === "primitive" || name === "associatedtype"; + case "trait": + return name === "traitalias"; } // No match @@ -944,6 +996,73 @@ 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) { + if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) { + for (i = 0; i < ALIASES[filterCrates][query.search].length; ++i) { + aliases.push( + createAliasFromItem( + searchIndex[ALIASES[filterCrates][query.search][i]])); + } + } + } 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; @@ -959,42 +1078,33 @@ function getSearchElement() { if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { continue; } - in_args = findArg(searchIndex[i], val, true); - returned = checkReturned(searchIndex[i], val, true); + in_args = findArg(searchIndex[i], val, true, typeFilter); + returned = checkReturned(searchIndex[i], val, true, typeFilter); ty = searchIndex[i]; fullId = generateId(ty); - if (searchWords[i] === val.name) { - // filter type: ... queries - if (typePassesFilter(typeFilter, searchIndex[i].ty) && - results[fullId] === undefined) - { - results[fullId] = {id: i, index: -1}; - } - } else if ((in_args === true || returned === true) && - typePassesFilter(typeFilter, searchIndex[i].ty)) { - if (in_args === true || returned === true) { - if (in_args === true) { - results_in_args[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (returned === true) { - results_returned[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - } else { - results[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } + if (searchWords[i] === val.name + && typePassesFilter(typeFilter, searchIndex[i].ty) + && results[fullId] === undefined) { + results[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (in_args === true && results_in_args[fullId] === undefined) { + results_in_args[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (returned === true && results_returned[fullId] === undefined) { + results_returned[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; } } query.inputs = [val]; @@ -1023,9 +1133,7 @@ function getSearchElement() { } fullId = generateId(ty); - // allow searching for void (no output) functions as well - var typeOutput = type.length > OUTPUT_DATA ? type[OUTPUT_DATA].name : ""; - returned = checkReturned(ty, output, true); + returned = checkReturned(ty, output, true, NO_TYPE_FILTER); if (output.name === "*" || returned === true) { in_args = false; var is_module = false; @@ -1087,7 +1195,6 @@ function getSearchElement() { var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1); var lev; - var lev_distance; for (j = 0; j < nSearchWords; ++j) { ty = searchIndex[j]; if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) { @@ -1126,16 +1233,8 @@ function getSearchElement() { lev += 1; } } - if ((in_args = findArg(ty, valGenerics)) <= MAX_LEV_DISTANCE) { - if (typePassesFilter(typeFilter, ty.ty) === false) { - in_args = MAX_LEV_DISTANCE + 1; - } - } - if ((returned = checkReturned(ty, valGenerics)) <= MAX_LEV_DISTANCE) { - if (typePassesFilter(typeFilter, ty.ty) === false) { - returned = MAX_LEV_DISTANCE + 1; - } - } + in_args = findArg(ty, valGenerics, false, typeFilter); + returned = checkReturned(ty, valGenerics, false, typeFilter); lev += lev_add; if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) { @@ -1188,23 +1287,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; } @@ -1280,20 +1363,22 @@ function getSearchElement() { } }; var mouseover_func = function(e) { - var el = e.target; - // to retrieve the real "owner" of the event. - while (el.tagName !== "TR") { - el = el.parentNode; - } - clearTimeout(hoverTimeout); - hoverTimeout = setTimeout(function() { - onEachLazy(document.getElementsByClassName("search-results"), function(e) { - onEachLazy(e.getElementsByClassName("result"), function(i_e) { - removeClass(i_e, "highlighted"); + if (mouseMovedAfterSearch) { + var el = e.target; + // to retrieve the real "owner" of the event. + while (el.tagName !== "TR") { + el = el.parentNode; + } + clearTimeout(hoverTimeout); + hoverTimeout = setTimeout(function() { + onEachLazy(document.getElementsByClassName("search-results"), function(e) { + onEachLazy(e.getElementsByClassName("result"), function(i_e) { + removeClass(i_e, "highlighted"); + }); }); - }); - addClass(el, "highlighted"); - }, 20); + addClass(el, "highlighted"); + }, 20); + } }; onEachLazy(document.getElementsByClassName("search-results"), function(e) { onEachLazy(e.getElementsByClassName("result"), function(i_e) { @@ -1597,19 +1682,18 @@ 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() { var elem = document.getElementById("crate-search"); - if (elem && elem.value !== "All crates" && rawSearchIndex.hasOwnProperty(elem.value)) { + if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) { return elem.value; } return undefined; @@ -1654,9 +1738,12 @@ function getSearchElement() { searchIndex = []; var searchWords = []; var i; + var currentIndex = 0; for (var crate in rawSearchIndex) { - if (!rawSearchIndex.hasOwnProperty(crate)) { continue; } + if (!hasOwnProperty(rawSearchIndex, crate)) { continue; } + + var crateSize = 0; searchWords.push(crate); searchIndex.push({ @@ -1667,6 +1754,7 @@ function getSearchElement() { desc: rawSearchIndex[crate].doc, type: null, }); + currentIndex += 1; // an array of [(Number) item type, // (String) name, @@ -1678,6 +1766,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; @@ -1696,9 +1787,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(); @@ -1707,15 +1807,32 @@ 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; } function startSearch() { - var searchTimeout; var callback = function() { - clearTimeout(searchTimeout); + clearInputTimeout(); if (search_input.value.length === 0) { if (browserSupportsHistoryApi()) { history.replaceState("", window.currentCrate + " - Rust", "?search="); @@ -1729,7 +1846,7 @@ function getSearchElement() { search_input.oninput = callback; document.getElementsByClassName("search-form")[0].onsubmit = function(e) { e.preventDefault(); - clearTimeout(searchTimeout); + clearInputTimeout(); search(); }; search_input.onchange = function(e) { @@ -1738,7 +1855,7 @@ function getSearchElement() { return; } // Do NOT e.preventDefault() here. It will prevent pasting. - clearTimeout(searchTimeout); + clearInputTimeout(); // zero-timeout necessary here because at the time of event handler execution the // pasted content is not in the input field yet. Shouldn’t make any difference for // change, though. @@ -1807,7 +1924,7 @@ function getSearchElement() { var crates = []; for (var crate in rawSearchIndex) { - if (!rawSearchIndex.hasOwnProperty(crate)) { + if (!hasOwnProperty(rawSearchIndex, crate)) { continue; } crates.push(crate); @@ -1831,12 +1948,11 @@ function getSearchElement() { sidebar.appendChild(div); } } - } + }; - window.initSearch = initSearch; // delayed sidebar rendering. - function initSidebarItems(items) { + window.initSidebarItems = function(items) { var sidebar = document.getElementsByClassName("sidebar-elems")[0]; var current = window.sidebarCurrent; @@ -1898,9 +2014,7 @@ function getSearchElement() { block("foreigntype", "Foreign Types"); block("keyword", "Keywords"); block("traitalias", "Trait Aliases"); - } - - window.initSidebarItems = initSidebarItems; + }; window.register_implementors = function(imp) { var implementors = document.getElementById("implementors-list"); @@ -2077,19 +2191,13 @@ function getSearchElement() { } } var ns = n.nextElementSibling; - while (true) { - if (ns && ( - hasClass(ns, "docblock") || - hasClass(ns, "stability"))) { - if (addOrRemove) { - addClass(ns, "hidden-by-impl-hider"); - } else { - removeClass(ns, "hidden-by-impl-hider"); - } - ns = ns.nextElementSibling; - continue; + while (ns && (hasClass(ns, "docblock") || hasClass(ns, "stability"))) { + if (addOrRemove) { + addClass(ns, "hidden-by-impl-hider"); + } else { + removeClass(ns, "hidden-by-impl-hider"); } - break; + ns = ns.nextElementSibling; } } }; @@ -2162,7 +2270,7 @@ function getSearchElement() { } } - function collapser(e, collapse) { + function collapser(pageId, e, collapse) { // inherent impl ids are like "impl" or impl-'. // they will never be hidden by default. var n = e.parentElement; @@ -2178,11 +2286,11 @@ 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) { - collapser(e, collapse); + collapser(pageId, e, collapse); }); } @@ -2190,7 +2298,7 @@ function getSearchElement() { if (blanket_list !== null) { onEachLazy(blanket_list.getElementsByClassName("collapse-toggle"), function(e) { - collapser(e, collapse); + collapser(pageId, e, collapse); }); } } @@ -2214,103 +2322,7 @@ function getSearchElement() { return toggle; } - var toggle = createSimpleToggle(false); - var hideMethodDocs = getCurrentValue("rustdoc-auto-hide-method-docs") === "true"; - var pageId = getPageId(); - - var func = function(e) { - var next = e.nextElementSibling; - if (!next) { - return; - } - if (hasClass(next, "docblock") === true || - (hasClass(next, "stability") === true && - hasClass(next.nextElementSibling, "docblock") === true)) { - var newToggle = toggle.cloneNode(true); - insertAfter(newToggle, e.childNodes[e.childNodes.length - 1]); - if (hideMethodDocs === true && hasClass(e, "method") === true) { - collapseDocs(newToggle, "hide", pageId); - } - } - }; - - var funcImpl = function(e) { - var next = e.nextElementSibling; - if (next && hasClass(next, "docblock")) { - next = next.nextElementSibling; - } - if (!next) { - return; - } - if (next.getElementsByClassName("method").length > 0 && hasClass(e, "impl")) { - insertAfter(toggle.cloneNode(true), e.childNodes[e.childNodes.length - 1]); - } - }; - - onEachLazy(document.getElementsByClassName("method"), func); - onEachLazy(document.getElementsByClassName("associatedconstant"), func); - onEachLazy(document.getElementsByClassName("impl"), funcImpl); - var impl_call = function() {}; - if (hideMethodDocs === true) { - impl_call = function(e, newToggle, pageId) { - if (e.id.match(/^impl(?:-\d+)?$/) === null) { - // Automatically minimize all non-inherent impls - if (hasClass(e, "impl") === true) { - collapseDocs(newToggle, "hide", pageId); - } - } - }; - } - var newToggle = document.createElement("a"); - newToggle.href = "javascript:void(0)"; - newToggle.className = "collapse-toggle hidden-default collapsed"; - newToggle.innerHTML = "[" + labelForToggleButton(true) + - "] Show hidden undocumented items"; - function toggleClicked() { - if (hasClass(this, "collapsed")) { - removeClass(this, "collapsed"); - onEachLazy(this.parentNode.getElementsByClassName("hidden"), function(x) { - if (hasClass(x, "content") === false) { - removeClass(x, "hidden"); - addClass(x, "x"); - } - }, true); - this.innerHTML = "[" + labelForToggleButton(false) + - "] Hide undocumented items"; - } else { - addClass(this, "collapsed"); - onEachLazy(this.parentNode.getElementsByClassName("x"), function(x) { - if (hasClass(x, "content") === false) { - addClass(x, "hidden"); - removeClass(x, "x"); - } - }, true); - this.innerHTML = "[" + labelForToggleButton(true) + - "] Show hidden undocumented items"; - } - } - onEachLazy(document.getElementsByClassName("impl-items"), function(e) { - onEachLazy(e.getElementsByClassName("associatedconstant"), func); - var hiddenElems = e.getElementsByClassName("hidden"); - var needToggle = false; - - var hlength = hiddenElems.length; - for (var i = 0; i < hlength; ++i) { - if (hasClass(hiddenElems[i], "content") === false && - hasClass(hiddenElems[i], "docblock") === false) { - needToggle = true; - break; - } - } - if (needToggle === true) { - var inner_toggle = newToggle.cloneNode(true); - inner_toggle.onclick = toggleClicked; - e.insertBefore(inner_toggle, e.firstChild); - impl_call(e.previousSibling, inner_toggle, pageId); - } - }); - - function createToggle(otherMessage, fontSize, extraClass, show) { + function createToggle(toggle, otherMessage, fontSize, extraClass, show) { var span = document.createElement("span"); span.className = "toggle-label"; if (show) { @@ -2345,97 +2357,199 @@ function getSearchElement() { return wrapper; } - var currentType = document.getElementsByClassName("type-decl")[0]; - var className = null; - if (currentType) { - currentType = currentType.getElementsByClassName("rust")[0]; - if (currentType) { - currentType.classList.forEach(function(item) { - if (item !== "main") { - className = item; - return true; + (function() { + var toggle = createSimpleToggle(false); + var hideMethodDocs = getCurrentValue("rustdoc-auto-hide-method-docs") === "true"; + var pageId = getPageId(); + + var func = function(e) { + var next = e.nextElementSibling; + if (!next) { + return; + } + if (hasClass(next, "docblock") === true || + (hasClass(next, "stability") === true && + hasClass(next.nextElementSibling, "docblock") === true)) { + var newToggle = toggle.cloneNode(true); + insertAfter(newToggle, e.childNodes[e.childNodes.length - 1]); + if (hideMethodDocs === true && hasClass(e, "method") === true) { + collapseDocs(newToggle, "hide", pageId); } - }); + } + }; + + var funcImpl = function(e) { + var next = e.nextElementSibling; + if (next && hasClass(next, "docblock")) { + next = next.nextElementSibling; + } + if (!next) { + return; + } + if (hasClass(e, "impl") && + (next.getElementsByClassName("method").length > 0 || + next.getElementsByClassName("associatedconstant").length > 0)) { + insertAfter(toggle.cloneNode(true), e.childNodes[e.childNodes.length - 1]); + } + }; + + onEachLazy(document.getElementsByClassName("method"), func); + onEachLazy(document.getElementsByClassName("associatedconstant"), func); + onEachLazy(document.getElementsByClassName("impl"), funcImpl); + var impl_call = function() {}; + if (hideMethodDocs === true) { + impl_call = function(e, newToggle) { + if (e.id.match(/^impl(?:-\d+)?$/) === null) { + // Automatically minimize all non-inherent impls + if (hasClass(e, "impl") === true) { + collapseDocs(newToggle, "hide", pageId); + } + } + }; } - } - var showItemDeclarations = getCurrentValue("rustdoc-auto-hide-" + className); - if (showItemDeclarations === null) { - if (className === "enum" || className === "macro") { - showItemDeclarations = "false"; - } else if (className === "struct" || className === "union" || className === "trait") { - showItemDeclarations = "true"; - } else { - // In case we found an unknown type, we just use the "parent" value. - showItemDeclarations = getCurrentValue("rustdoc-auto-hide-declarations"); + var newToggle = document.createElement("a"); + newToggle.href = "javascript:void(0)"; + newToggle.className = "collapse-toggle hidden-default collapsed"; + newToggle.innerHTML = "[" + labelForToggleButton(true) + + "] Show hidden undocumented items"; + function toggleClicked() { + if (hasClass(this, "collapsed")) { + removeClass(this, "collapsed"); + onEachLazy(this.parentNode.getElementsByClassName("hidden"), function(x) { + if (hasClass(x, "content") === false) { + removeClass(x, "hidden"); + addClass(x, "x"); + } + }, true); + this.innerHTML = "[" + labelForToggleButton(false) + + "] Hide undocumented items"; + } else { + addClass(this, "collapsed"); + onEachLazy(this.parentNode.getElementsByClassName("x"), function(x) { + if (hasClass(x, "content") === false) { + addClass(x, "hidden"); + removeClass(x, "x"); + } + }, true); + this.innerHTML = "[" + labelForToggleButton(true) + + "] Show hidden undocumented items"; + } } - } - showItemDeclarations = showItemDeclarations === "false"; - function buildToggleWrapper(e) { - if (hasClass(e, "autohide")) { - var wrap = e.previousElementSibling; - if (wrap && hasClass(wrap, "toggle-wrapper")) { - var inner_toggle = wrap.childNodes[0]; - var extra = e.childNodes[0].tagName === "H3"; - - e.style.display = "none"; - addClass(wrap, "collapsed"); - onEachLazy(inner_toggle.getElementsByClassName("inner"), function(e) { - e.innerHTML = labelForToggleButton(true); - }); - onEachLazy(inner_toggle.getElementsByClassName("toggle-label"), function(e) { - e.style.display = "inline-block"; - if (extra === true) { - i_e.innerHTML = " Show " + e.childNodes[0].innerHTML; + onEachLazy(document.getElementsByClassName("impl-items"), function(e) { + onEachLazy(e.getElementsByClassName("associatedconstant"), func); + var hiddenElems = e.getElementsByClassName("hidden"); + var needToggle = false; + + var hlength = hiddenElems.length; + for (var i = 0; i < hlength; ++i) { + if (hasClass(hiddenElems[i], "content") === false && + hasClass(hiddenElems[i], "docblock") === false) { + needToggle = true; + break; + } + } + if (needToggle === true) { + var inner_toggle = newToggle.cloneNode(true); + inner_toggle.onclick = toggleClicked; + e.insertBefore(inner_toggle, e.firstChild); + impl_call(e.previousSibling, inner_toggle); + } + }); + + var currentType = document.getElementsByClassName("type-decl")[0]; + var className = null; + if (currentType) { + currentType = currentType.getElementsByClassName("rust")[0]; + if (currentType) { + currentType.classList.forEach(function(item) { + if (item !== "main") { + className = item; + return true; } }); } } - if (e.parentNode.id === "main") { - var otherMessage = ""; - var fontSize; - var extraClass; - - if (hasClass(e, "type-decl")) { - fontSize = "20px"; - otherMessage = " Show declaration"; - if (showItemDeclarations === false) { - extraClass = "collapsed"; - } - } else if (hasClass(e, "sub-variant")) { - otherMessage = " Show fields"; - } else if (hasClass(e, "non-exhaustive")) { - otherMessage = " This "; - if (hasClass(e, "non-exhaustive-struct")) { - otherMessage += "struct"; - } else if (hasClass(e, "non-exhaustive-enum")) { - otherMessage += "enum"; - } else if (hasClass(e, "non-exhaustive-variant")) { - otherMessage += "enum variant"; - } else if (hasClass(e, "non-exhaustive-type")) { - otherMessage += "type"; - } - otherMessage += " is marked as non-exhaustive"; - } else if (hasClass(e.childNodes[0], "impl-items")) { - extraClass = "marg-left"; - } - - e.parentNode.insertBefore( - createToggle(otherMessage, - fontSize, - extraClass, - hasClass(e, "type-decl") === false || showItemDeclarations === true), - e); - if (hasClass(e, "type-decl") === true && showItemDeclarations === true) { - collapseDocs(e.previousSibling.childNodes[0], "toggle"); - } - if (hasClass(e, "non-exhaustive") === true) { - collapseDocs(e.previousSibling.childNodes[0], "toggle"); + var showItemDeclarations = getCurrentValue("rustdoc-auto-hide-" + className); + if (showItemDeclarations === null) { + if (className === "enum" || className === "macro") { + showItemDeclarations = "false"; + } else if (className === "struct" || className === "union" || className === "trait") { + showItemDeclarations = "true"; + } else { + // In case we found an unknown type, we just use the "parent" value. + showItemDeclarations = getCurrentValue("rustdoc-auto-hide-declarations"); + } + } + showItemDeclarations = showItemDeclarations === "false"; + function buildToggleWrapper(e) { + if (hasClass(e, "autohide")) { + var wrap = e.previousElementSibling; + if (wrap && hasClass(wrap, "toggle-wrapper")) { + var inner_toggle = wrap.childNodes[0]; + var extra = e.childNodes[0].tagName === "H3"; + + e.style.display = "none"; + addClass(wrap, "collapsed"); + onEachLazy(inner_toggle.getElementsByClassName("inner"), function(e) { + e.innerHTML = labelForToggleButton(true); + }); + onEachLazy(inner_toggle.getElementsByClassName("toggle-label"), function(e) { + e.style.display = "inline-block"; + if (extra === true) { + e.innerHTML = " Show " + e.childNodes[0].innerHTML; + } + }); + } + } + if (e.parentNode.id === "main") { + var otherMessage = ""; + var fontSize; + var extraClass; + + if (hasClass(e, "type-decl")) { + fontSize = "20px"; + otherMessage = " Show declaration"; + if (showItemDeclarations === false) { + extraClass = "collapsed"; + } + } else if (hasClass(e, "sub-variant")) { + otherMessage = " Show fields"; + } else if (hasClass(e, "non-exhaustive")) { + otherMessage = " This "; + if (hasClass(e, "non-exhaustive-struct")) { + otherMessage += "struct"; + } else if (hasClass(e, "non-exhaustive-enum")) { + otherMessage += "enum"; + } else if (hasClass(e, "non-exhaustive-variant")) { + otherMessage += "enum variant"; + } else if (hasClass(e, "non-exhaustive-type")) { + otherMessage += "type"; + } + otherMessage += " is marked as non-exhaustive"; + } else if (hasClass(e.childNodes[0], "impl-items")) { + extraClass = "marg-left"; + } + + e.parentNode.insertBefore( + createToggle( + toggle, + otherMessage, + fontSize, + extraClass, + hasClass(e, "type-decl") === false || showItemDeclarations === true), + e); + if (hasClass(e, "type-decl") === true && showItemDeclarations === true) { + collapseDocs(e.previousSibling.childNodes[0], "toggle"); + } + if (hasClass(e, "non-exhaustive") === true) { + collapseDocs(e.previousSibling.childNodes[0], "toggle"); + } } } - } - onEachLazy(document.getElementsByClassName("docblock"), buildToggleWrapper); - onEachLazy(document.getElementsByClassName("sub-variant"), buildToggleWrapper); + onEachLazy(document.getElementsByClassName("docblock"), buildToggleWrapper); + onEachLazy(document.getElementsByClassName("sub-variant"), buildToggleWrapper); + }()); function createToggleWrapper(tog) { var span = document.createElement("span"); @@ -2450,56 +2564,60 @@ function getSearchElement() { return wrapper; } - // To avoid checking on "rustdoc-item-attributes" value on every loop... - var itemAttributesFunc = function() {}; - if (getCurrentValue("rustdoc-auto-hide-attributes") !== "false") { - itemAttributesFunc = function(x) { - collapseDocs(x.previousSibling.childNodes[0], "toggle"); - }; - } - var attributesToggle = createToggleWrapper(createSimpleToggle(false)); - onEachLazy(main.getElementsByClassName("attributes"), function(i_e) { - var attr_tog = attributesToggle.cloneNode(true); - if (hasClass(i_e, "top-attr") === true) { - addClass(attr_tog, "top-attr"); - } - i_e.parentNode.insertBefore(attr_tog, i_e); - itemAttributesFunc(i_e); - }); - - // To avoid checking on "rustdoc-line-numbers" value on every loop... - var lineNumbersFunc = function() {}; - if (getCurrentValue("rustdoc-line-numbers") === "true") { - lineNumbersFunc = function(x) { - var count = x.textContent.split("\n").length; - var elems = []; - for (var i = 0; i < count; ++i) { - elems.push(i + 1); - } - var node = document.createElement("pre"); - addClass(node, "line-number"); - node.innerHTML = elems.join("\n"); - x.parentNode.insertBefore(node, x); - }; - } - onEachLazy(document.getElementsByClassName("rust-example-rendered"), function(e) { - if (hasClass(e, "compile_fail")) { - e.addEventListener("mouseover", function(event) { - this.parentElement.previousElementSibling.childNodes[0].style.color = "#f00"; - }); - e.addEventListener("mouseout", function(event) { - this.parentElement.previousElementSibling.childNodes[0].style.color = ""; - }); - } else if (hasClass(e, "ignore")) { - e.addEventListener("mouseover", function(event) { - this.parentElement.previousElementSibling.childNodes[0].style.color = "#ff9200"; - }); - e.addEventListener("mouseout", function(event) { - this.parentElement.previousElementSibling.childNodes[0].style.color = ""; - }); + (function() { + // To avoid checking on "rustdoc-item-attributes" value on every loop... + var itemAttributesFunc = function() {}; + if (getCurrentValue("rustdoc-auto-hide-attributes") !== "false") { + itemAttributesFunc = function(x) { + collapseDocs(x.previousSibling.childNodes[0], "toggle"); + }; } - lineNumbersFunc(e); - }); + var attributesToggle = createToggleWrapper(createSimpleToggle(false)); + onEachLazy(main.getElementsByClassName("attributes"), function(i_e) { + var attr_tog = attributesToggle.cloneNode(true); + if (hasClass(i_e, "top-attr") === true) { + addClass(attr_tog, "top-attr"); + } + i_e.parentNode.insertBefore(attr_tog, i_e); + itemAttributesFunc(i_e); + }); + }()); + + (function() { + // To avoid checking on "rustdoc-line-numbers" value on every loop... + var lineNumbersFunc = function() {}; + if (getCurrentValue("rustdoc-line-numbers") === "true") { + lineNumbersFunc = function(x) { + var count = x.textContent.split("\n").length; + var elems = []; + for (var i = 0; i < count; ++i) { + elems.push(i + 1); + } + var node = document.createElement("pre"); + addClass(node, "line-number"); + node.innerHTML = elems.join("\n"); + x.parentNode.insertBefore(node, x); + }; + } + onEachLazy(document.getElementsByClassName("rust-example-rendered"), function(e) { + if (hasClass(e, "compile_fail")) { + e.addEventListener("mouseover", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = "#f00"; + }); + e.addEventListener("mouseout", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = ""; + }); + } else if (hasClass(e, "ignore")) { + e.addEventListener("mouseover", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = "#ff9200"; + }); + e.addEventListener("mouseout", function() { + this.parentElement.previousElementSibling.childNodes[0].style.color = ""; + }); + } + lineNumbersFunc(e); + }); + }()); // In the search display, allows to switch between tabs. function printTab(nb) { @@ -2592,7 +2710,7 @@ function getSearchElement() { }); } - function addSearchOptions(crates) { + window.addSearchOptions = function(crates) { var elem = document.getElementById("crate-search"); if (!elem) { @@ -2601,7 +2719,7 @@ function getSearchElement() { var crates_text = []; if (Object.keys(crates).length > 1) { for (var crate in crates) { - if (crates.hasOwnProperty(crate)) { + if (hasOwnProperty(crates, crate)) { crates_text.push(crate); } } @@ -2636,10 +2754,8 @@ function getSearchElement() { if (search_input) { search_input.removeAttribute('disabled'); - }; - } - - window.addSearchOptions = addSearchOptions; + } + }; function buildHelperPopup() { var popup = document.createElement("aside"); @@ -2692,12 +2808,8 @@ function getSearchElement() { buildHelperPopup(); }()); -// Sets the focus on the search bar at the top of the page -function focusSearchBar() { - getSearchInput().focus(); -} - -// Removes the focus from the search bar -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 f05db6c218524..9c6dd25394db0 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; } @@ -606,7 +625,7 @@ a { display: initial; } -.in-band:hover > .anchor { +.in-band:hover > .anchor, .impl:hover > .anchor { display: inline-block; position: absolute; } @@ -1032,7 +1051,7 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { .information { position: absolute; - left: -20px; + left: -25px; margin-top: 7px; z-index: 1; } @@ -1047,12 +1066,13 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { width: 120px; display: none; text-align: center; - padding: 5px 3px; + padding: 5px 3px 3px 3px; border-radius: 6px; margin-left: 5px; top: -5px; left: 105%; z-index: 10; + font-size: 16px; } .tooltip:hover .tooltiptext { @@ -1063,20 +1083,26 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { content: " "; position: absolute; top: 50%; - left: 11px; + left: 16px; margin-top: -5px; border-width: 5px; border-style: solid; } +.tooltip.compile_fail, .tooltip.should_panic, .tooltip.ignore { + font-weight: bold; + font-size: 20px; +} + .tooltip .tooltiptext { border: 1px solid; + font-weight: normal; } 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/settings.js b/src/librustdoc/html/static/settings.js index c21db7371f3c3..427a74c0c87fa 100644 --- a/src/librustdoc/html/static/settings.js +++ b/src/librustdoc/html/static/settings.js @@ -1,3 +1,6 @@ +// Local js definitions: +/* global getCurrentValue, updateLocalStorage */ + (function () { function changeSetting(settingName, isEnabled) { updateLocalStorage('rustdoc-' + settingName, isEnabled); diff --git a/src/librustdoc/html/static/source-script.js b/src/librustdoc/html/static/source-script.js index 567022b4139ad..cfbfe6675f52b 100644 --- a/src/librustdoc/html/static/source-script.js +++ b/src/librustdoc/html/static/source-script.js @@ -1,5 +1,5 @@ // From rust: -/* global sourcesIndex */ +/* global search, sourcesIndex */ // Local js definitions: /* global addClass, getCurrentValue, hasClass, removeClass, updateLocalStorage */ diff --git a/src/librustdoc/html/static/storage.js b/src/librustdoc/html/static/storage.js index d142d99ac704d..0a2fae274fa87 100644 --- a/src/librustdoc/html/static/storage.js +++ b/src/librustdoc/html/static/storage.js @@ -27,14 +27,15 @@ function removeClass(elem, className) { function onEach(arr, func, reversed) { if (arr && arr.length > 0 && func) { var length = arr.length; + var i; if (reversed !== true) { - for (var i = 0; i < length; ++i) { + for (i = 0; i < length; ++i) { if (func(arr[i]) === true) { return true; } } } else { - for (var i = length - 1; i >= 0; --i) { + for (i = length - 1; i >= 0; --i) { if (func(arr[i]) === true) { return true; } @@ -51,6 +52,10 @@ function onEachLazy(lazyArray, func, reversed) { reversed); } +function hasOwnProperty(obj, property) { + return Object.prototype.hasOwnProperty.call(obj, property); +} + function usableLocalStorage() { // Check if the browser supports localStorage at all: if (typeof Storage === "undefined") { diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index e7041d54c6bfd..41dcb5c24507c 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; } @@ -254,13 +276,21 @@ a.test-arrow:hover{ } pre.compile_fail { - border-left: 2px solid rgba(255,0,0,.6); + border-left: 2px solid rgba(255,0,0,.8); } pre.compile_fail:hover, .information:hover + pre.compile_fail { border-left: 2px solid #f00; } +pre.should_panic { + border-left: 2px solid rgba(255,0,0,.8); +} + +pre.should_panic:hover, .information:hover + pre.should_panic { + border-left: 2px solid #f00; +} + pre.ignore { border-left: 2px solid rgba(255,142,0,.6); } @@ -270,19 +300,27 @@ pre.ignore:hover, .information:hover + pre.ignore { } .tooltip.compile_fail { - color: rgba(255,0,0,.6); + color: rgba(255,0,0,.8); } .information > .compile_fail:hover { color: #f00; } +.tooltip.should_panic { + color: rgba(255,0,0,.8); +} + +.information > .should_panic:hover { + color: #f00; +} + .tooltip.ignore { color: rgba(255,142,0,.6); } .information > .ignore:hover { - color: rgba(255,142,0,1); + color: #ff9200; } .search-failed a { @@ -290,8 +328,9 @@ pre.ignore:hover, .information:hover + pre.ignore { } .tooltip .tooltiptext { - background-color: black; + background-color: #000; color: #fff; + border-color: #000; } .tooltip .tooltiptext::after { diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index a1efef6701fd1..386fe2398e63a 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; } @@ -248,15 +271,23 @@ a.test-arrow:hover{ } pre.compile_fail { - border-left: 2px solid rgba(255,0,0,.4); + border-left: 2px solid rgba(255,0,0,.5); } pre.compile_fail:hover, .information:hover + pre.compile_fail { border-left: 2px solid #f00; } +pre.should_panic { + border-left: 2px solid rgba(255,0,0,.5); +} + +pre.should_panic:hover, .information:hover + pre.should_panic { + border-left: 2px solid #f00; +} + pre.ignore { - border-left: 2px solid rgba(255,142,0,.4); + border-left: 2px solid rgba(255,142,0,.6); } pre.ignore:hover, .information:hover + pre.ignore { @@ -264,19 +295,27 @@ pre.ignore:hover, .information:hover + pre.ignore { } .tooltip.compile_fail { - color: rgba(255,0,0,.3); + color: rgba(255,0,0,.5); } .information > .compile_fail:hover { color: #f00; } +.tooltip.should_panic { + color: rgba(255,0,0,.5); +} + +.information > .should_panic:hover { + color: #f00; +} + .tooltip.ignore { - color: rgba(255,142,0,.3); + color: rgba(255,142,0,.6); } .information > .ignore:hover { - color: rgba(255,142,0,1); + color: #ff9200; } .search-failed a { @@ -284,7 +323,7 @@ pre.ignore:hover, .information:hover + pre.ignore { } .tooltip .tooltiptext { - background-color: black; + background-color: #000; 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 2e90d6082bac9..de6fa3dbd4a89 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -7,16 +7,14 @@ #![feature(box_syntax)] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] #![feature(test)] -#![feature(vec_remove_item)] #![feature(ptr_offset_from)] #![feature(crate_visibility_modifier)] #![feature(never_type)] #![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 +24,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; @@ -49,8 +49,9 @@ use std::env; use std::panic; use std::process; -use rustc::session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup}; -use rustc::session::{early_error, early_warn}; +use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup}; +use rustc_session::getopts; +use rustc_session::{early_error, early_warn}; #[macro_use] mod externalfiles; @@ -163,9 +164,8 @@ fn opts() -> Vec { o.optmulti( "", "passes", - "list of passes to also run, you might want \ - to pass it multiple times; a value of `list` \ - will print available passes", + "list of passes to also run, you might want to pass it multiple times; a value of \ + `list` will print available passes", "PASSES", ) }), @@ -246,8 +246,8 @@ fn opts() -> Vec { "e", "extend-css", "To add some CSS rules with a given file to generate doc with your \ - own theme. However, your theme might break if the rustdoc's generated HTML \ - changes, so be careful!", + own theme. However, your theme might break if the rustdoc's generated HTML \ + changes, so be careful!", "PATH", ) }), @@ -260,22 +260,21 @@ fn opts() -> Vec { "", "playground-url", "URL to send code snippets to, may be reset by --markdown-playground-url \ - or `#![doc(html_playground_url=...)]`", + or `#![doc(html_playground_url=...)]`", "URL", ) }), unstable("display-warnings", |o| { o.optflag("", "display-warnings", "to print code warnings when testing doc") }), - unstable("crate-version", |o| { + stable("crate-version", |o| { o.optopt("", "crate-version", "crate version to print into documentation", "VERSION") }), unstable("sort-modules-by-appearance", |o| { o.optflag( "", "sort-modules-by-appearance", - "sort modules by where they appear in the \ - program, rather than alphabetically", + "sort modules by where they appear in the program, rather than alphabetically", ) }), stable("theme", |o| { @@ -356,7 +355,7 @@ fn opts() -> Vec { "", "static-root-path", "Path string to force loading static files from in output pages. \ - If not set, uses combinations of '../' to reach the documentation root.", + If not set, uses combinations of '../' to reach the documentation root.", "PATH", ) }), @@ -448,14 +447,29 @@ fn main_args(args: &[String]) -> i32 { rustc_interface::interface::default_thread_pool(options.edition, move || main_options(options)) } +fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> i32 { + match res { + Ok(()) => 0, + Err(err) => { + if !err.is_empty() { + diag.struct_err(&err).emit(); + } + 1 + } + } +} + fn main_options(options: config::Options) -> i32 { let diag = core::new_handler(options.error_format, None, &options.debugging_options); match (options.should_test, options.markdown_input()) { - (true, true) => return markdown::test(options, &diag), - (true, false) => return test::run(options), + (true, true) => return wrap_return(&diag, markdown::test(options)), + (true, false) => return wrap_return(&diag, test::run(options)), (false, true) => { - return markdown::render(options.input, options.render_options, &diag, options.edition); + return wrap_return( + &diag, + markdown::render(&options.input, options.render_options, options.edition), + ); } (false, false) => {} } diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index a41fdd2ff17af..e0753bcd70f29 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -1,13 +1,12 @@ -use std::fs::File; +use std::fs::{create_dir_all, read_to_string, File}; use std::io::prelude::*; -use std::path::PathBuf; +use std::path::Path; use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; use rustc_span::source_map::DUMMY_SP; use crate::config::{Options, RenderOptions}; -use crate::externalfiles::{load_string, LoadStringError}; use crate::html::escape::Escape; use crate::html::markdown; use crate::html::markdown::{find_testable_code, ErrorCodes, IdMap, Markdown, MarkdownWithToc}; @@ -34,12 +33,16 @@ fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) { /// Render `input` (e.g., "foo.md") into an HTML file in `output` /// (e.g., output = "bar" => "bar/foo.html"). -pub fn render( - input: PathBuf, +pub fn render>( + input: P, options: RenderOptions, - diag: &rustc_errors::Handler, edition: Edition, -) -> i32 { +) -> Result<(), String> { + if let Err(e) = create_dir_all(&options.output) { + return Err(format!("{}: {}", options.output.display(), e)); + } + + let input = input.as_ref(); let mut output = options.output; output.push(input.file_name().unwrap()); output.set_extension("html"); @@ -50,26 +53,15 @@ pub fn render( css.push_str(&s) } - let input_str = match load_string(&input, diag) { - Ok(s) => s, - Err(LoadStringError::ReadFail) => return 1, - Err(LoadStringError::BadUtf8) => return 2, - }; + let input_str = read_to_string(input).map_err(|err| format!("{}: {}", input.display(), err))?; let playground_url = options.markdown_playground_url.or(options.playground_url); let playground = playground_url.map(|url| markdown::Playground { crate_name: None, url }); - let mut out = match File::create(&output) { - Err(e) => { - diag.struct_err(&format!("{}: {}", output.display(), e)).emit(); - return 4; - } - Ok(f) => f, - }; + let mut out = File::create(&output).map_err(|e| format!("{}: {}", output.display(), e))?; let (metadata, text) = extract_leading_metadata(&input_str); if metadata.is_empty() { - diag.struct_err("invalid markdown file: no initial lines starting with `# ` or `%`").emit(); - return 5; + return Err("invalid markdown file: no initial lines starting with `# ` or `%`".to_owned()); } let title = metadata[0]; @@ -117,22 +109,15 @@ pub fn render( ); match err { - Err(e) => { - diag.struct_err(&format!("cannot write to `{}`: {}", output.display(), e)).emit(); - 6 - } - Ok(_) => 0, + Err(e) => Err(format!("cannot write to `{}`: {}", output.display(), e)), + Ok(_) => Ok(()), } } /// Runs any tests/code examples in the markdown file `input`. -pub fn test(mut options: Options, diag: &rustc_errors::Handler) -> i32 { - let input_str = match load_string(&options.input, diag) { - Ok(s) => s, - Err(LoadStringError::ReadFail) => return 1, - Err(LoadStringError::BadUtf8) => return 2, - }; - +pub fn test(mut options: Options) -> Result<(), String> { + let input_str = read_to_string(&options.input) + .map_err(|err| format!("{}: {}", options.input.display(), err))?; let mut opts = TestOptions::default(); opts.no_crate_inject = true; opts.display_warnings = options.display_warnings; @@ -148,7 +133,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( @@ -156,5 +141,5 @@ pub fn test(mut options: Options, diag: &rustc_errors::Handler) -> i32 { collector.tests, Some(testing::Options::new().display_output(options.display_warnings)), ); - 0 + Ok(()) } diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index f48224512ba4f..98300385c8fb8 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -8,7 +8,6 @@ use rustc_ast::attr; use rustc_span::symbol::sym; use rustc_span::FileName; use serde::Serialize; -use serde_json; use std::collections::BTreeMap; use std::ops; 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 75355b84fee83..f5b2f1bb5b178 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1,6 +1,4 @@ -use rustc::lint; -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 +8,12 @@ use rustc_hir::def::{ Namespace::{self, *}, PerNS, Res, }; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty; use rustc_resolve::ParentScope; +use rustc_session::lint; +use rustc_span::hygiene::MacroKind; +use rustc_span::symbol::Ident; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; @@ -60,7 +62,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { &self, path_str: &str, current_item: &Option, - module_id: rustc_ast::ast::NodeId, + module_id: LocalDefId, ) -> Result<(Res, Option), ErrorKind> { let cx = self.cx; @@ -121,6 +123,42 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } + /// Resolves a string as a macro. + fn macro_resolve(&self, path_str: &str, parent_id: Option) -> Option { + let cx = self.cx; + let path = ast::Path::from_ident(Ident::from_str(path_str)); + cx.enter_resolver(|resolver| { + if let Ok((Some(ext), res)) = resolver.resolve_macro_path( + &path, + None, + &ParentScope::module(resolver.graph_root()), + false, + false, + ) { + if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind { + return Some(res.map_id(|_| panic!("unexpected id"))); + } + } + if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) { + return Some(res.map_id(|_| panic!("unexpected id"))); + } + if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) { + let module_id = cx.tcx.hir().local_def_id(module_id); + if let Ok((_, res)) = + resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id) + { + // don't resolve builtins like `#[derive]` + if let Res::Def(..) = res { + let res = res.map_id(|_| panic!("unexpected node_id")); + return Some(res); + } + } + } else { + debug!("attempting to resolve item without parent module: {}", path_str); + } + None + }) + } /// Resolves a string as a path within a particular namespace. Also returns an optional /// URL fragment in the case of variants and methods. fn resolve( @@ -130,12 +168,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().local_def_id(module_id); let result = cx.enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id) }); @@ -149,7 +188,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 +248,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 +265,61 @@ 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 + Res::Def( + DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias, + did, + ) => { + // Checks if item_name belongs to `impl SomeItem` + let impl_item = cx .tcx .inherent_impls(did) .iter() .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order()) .find(|item| item.ident.name == item_name); + let trait_item = item_opt + .and_then(|item| self.cx.as_local_hir_id(item.def_id)) + .and_then(|item_hir| { + // Checks if item_name belongs to `impl SomeTrait for SomeItem` + let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir); + let item_parent = self.cx.tcx.hir().find(parent_hir); + match item_parent { + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl { of_trait: Some(_), self_ty, .. }, + .. + })) => cx + .tcx + .associated_item_def_ids(self_ty.hir_id.owner) + .iter() + .map(|child| { + let associated_item = cx.tcx.associated_item(*child); + associated_item + }) + .find(|child| child.ident.name == item_name), + _ => None, + } + }); + let item = match (impl_item, trait_item) { + (Some(from_impl), Some(_)) => { + // Although it's ambiguous, return impl version for compat. sake. + // To handle that properly resolve() would have to support + // something like + // [`ambi_fn`](::ambi_fn) + Some(from_impl) + } + (None, Some(from_trait)) => Some(from_trait), + (Some(from_impl), None) => Some(from_impl), + _ => None, + }; + if let Some(item) = item { let out = match item.kind { - ty::AssocKind::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 +374,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 { @@ -331,11 +408,27 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } +/// Check for resolve collisions between a trait and its derive +/// +/// These are common and we should just resolve to the trait in that case +fn is_derive_trait_collision(ns: &PerNS>) -> bool { + if let PerNS { + type_ns: Some((Res::Def(DefKind::Trait, _), _)), + macro_ns: Some((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)), + .. + } = *ns + { + true + } else { + false + } +} + 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); @@ -348,7 +441,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let parent_node = self.cx.as_local_hir_id(item.def_id).and_then(|hir_id| { // FIXME: this fails hard for impls in non-module scope, but is necessary for the // current `resolve()` implementation. - match self.cx.as_local_hir_id(self.cx.tcx.parent_module(hir_id)).unwrap() { + match self.cx.as_local_hir_id(self.cx.tcx.parent_module(hir_id).to_def_id()).unwrap() { id if id != hir_id => Some(id), _ => None, } @@ -391,6 +484,43 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { look_for_tests(&cx, &dox, &item, true); + // find item's parent to resolve `Self` in item's docs below + let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| { + let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir); + let item_parent = self.cx.tcx.hir().find(parent_hir); + match item_parent { + Some(hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Impl { + self_ty: + hir::Ty { + kind: + hir::TyKind::Path(hir::QPath::Resolved( + _, + hir::Path { segments, .. }, + )), + .. + }, + .. + }, + .. + })) => segments.first().map(|seg| seg.ident.to_string()), + Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Enum(..), .. + })) + | Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Struct(..), .. + })) + | Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Union(..), .. + })) + | Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Trait(..), .. + })) => Some(ident.to_string()), + _ => None, + } + }); + for (ori_link, link_range) in markdown_links(&dox) { // Bail early for real links. if ori_link.contains('/') { @@ -427,7 +557,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { }; let (res, fragment) = { let mut kind = None; - let path_str = if let Some(prefix) = + let mut path_str = if let Some(prefix) = ["struct@", "enum@", "type@", "trait@", "union@"] .iter() .find(|p| link.starts_with(**p)) @@ -455,6 +585,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } else if link.starts_with("macro@") { kind = Some(MacroNS); link.trim_start_matches("macro@") + } else if link.starts_with("derive@") { + kind = Some(MacroNS); + link.trim_start_matches("derive@") } else if link.ends_with('!') { kind = Some(MacroNS); link.trim_end_matches('!') @@ -481,10 +614,25 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let base_node = if item.is_mod() && item.attrs.inner_docs { None } else { parent_node }; + let resolved_self; + // replace `Self` with suitable item's parent name + if path_str.starts_with("Self::") { + if let Some(ref name) = parent_name { + resolved_self = format!("{}::{}", name, &path_str[6..]); + path_str = &resolved_self; + } + } + 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, + Some(&item), + ) { Ok(res) => res, Err(ErrorKind::ResolutionFailure) => { resolution_failure(cx, &item, path_str, &dox, link_range); @@ -500,8 +648,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, + Some(&item), + ) { Ok(res) => res, Err(ErrorKind::ResolutionFailure) => { resolution_failure(cx, &item, path_str, &dox, link_range); @@ -516,8 +670,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } None => { // Try everything! - let candidates = PerNS { - macro_ns: macro_resolve(cx, path_str) + let mut candidates = PerNS { + macro_ns: self + .macro_resolve(path_str, base_node) .map(|res| (res, extra_fragment.clone())), type_ns: match self.resolve( path_str, @@ -525,6 +680,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); @@ -538,6 +694,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); @@ -568,10 +725,16 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { continue; } - let is_unambiguous = candidates.clone().present_items().count() == 1; - if is_unambiguous { + let len = candidates.clone().present_items().count(); + + if len == 1 { candidates.present_items().next().unwrap() + } else if len == 2 && is_derive_trait_collision(&candidates) { + candidates.type_ns.unwrap() } else { + if is_derive_trait_collision(&candidates) { + candidates.macro_ns = None; + } ambiguity_error( cx, &item, @@ -584,7 +747,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } Some(MacroNS) => { - if let Some(res) = macro_resolve(cx, path_str) { + if let Some(res) = self.macro_resolve(path_str, base_node) { (res, extra_fragment) } else { resolution_failure(cx, &item, path_str, &dox, link_range); @@ -627,28 +790,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } -/// Resolves a string as a macro. -fn macro_resolve(cx: &DocContext<'_>, path_str: &str) -> Option { - let path = ast::Path::from_ident(Ident::from_str(path_str)); - cx.enter_resolver(|resolver| { - if let Ok((Some(ext), res)) = resolver.resolve_macro_path( - &path, - None, - &ParentScope::module(resolver.graph_root()), - false, - false, - ) { - if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind { - return Some(res.map_id(|_| panic!("unexpected id"))); - } - } - if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) { - return Some(res.map_id(|_| panic!("unexpected id"))); - } - None - }) -} - fn build_diagnostic( cx: &DocContext<'_>, item: &Item, @@ -813,10 +954,10 @@ 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(..), _) => { + Res::Def(DefKind::Macro(MacroKind::Bang), _) => { ("add an exclamation mark", format!("{}!", path_str)) } _ => { @@ -830,6 +971,9 @@ fn ambiguity_error( (Res::Def(DefKind::Mod, _), _) => "module", (_, TypeNS) => "type", (_, ValueNS) => "value", + (Res::Def(DefKind::Macro(MacroKind::Derive), _), MacroNS) => { + "derive" + } (_, MacroNS) => "macro", }; @@ -880,7 +1024,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 71cff637c1272..70366c90139c2 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -1,9 +1,9 @@ //! Contains information about "passes", used to modify crate information during the documentation //! process. -use rustc::lint; -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; use std::ops::Range; @@ -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 b1a60e2746fc1..21aa0ded5a4b2 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -1,29 +1,36 @@ -use rustc::hir::map::Map; -use rustc::session::{self, config, DiagnosticOutput}; -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_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 { @@ -36,23 +43,35 @@ pub struct TestOptions { pub attrs: Vec, } -pub fn run(options: Options) -> i32 { +pub fn run(options: Options) -> Result<(), String> { 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::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(), @@ -95,7 +114,7 @@ pub fn run(options: Options) -> i32 { options, false, opts, - Some(compiler.source_map().clone()), + Some(compiler.session().parse_sess.clone_source_map()), None, enable_per_target_ignores, ); @@ -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(); @@ -124,7 +151,7 @@ pub fn run(options: Options) -> i32 { }); let tests = match tests { Ok(tests) => tests, - Err(ErrorReported) => return 1, + Err(ErrorReported) => return Err(String::new()), }; test_args.insert(0, "rustdoctest".to_string()); @@ -135,7 +162,7 @@ pub fn run(options: Options) -> i32 { Some(testing::Options::new().display_output(display_warnings)), ); - 0 + Ok(()) } // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade. @@ -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,11 +671,16 @@ impl Collector { position: DUMMY_SP, source_map, filename, + visited_tests: HashMap::new(), } } fn generate_name(&self, line: usize, filename: &FileName) -> String { - format!("{} - {} (line {})", filename, self.names.join("::"), line) + let mut item_path = self.names.join("::"); + if !item_path.is_empty() { + item_path.push(' '); + } + format!("{} - {}(line {})", filename, item_path, line) } pub fn set_position(&mut self, position: Span) { @@ -678,7 +692,7 @@ impl Collector { let filename = source_map.span_to_filename(self.position); if let FileName::Real(ref filename) = filename { if let Ok(cur_dir) = env::current_dir() { - if let Ok(path) = filename.strip_prefix(&cur_dir) { + if let Ok(path) = filename.local_path().strip_prefix(&cur_dir) { return path.to_owned().into(); } } @@ -698,13 +712,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.local_path().to_path_buf(), + _ => PathBuf::from(r"doctest.rs"), + }; + + let outdir = if let Some(mut path) = options.persist_doctests.clone() { + // For example `module/file.rs` would become `module_file_rs` + let folder_name = filename + .to_string() + .chars() + .map(|c| if c == '/' || c == '.' { '_' } else { c }) + .collect::(); + + path.push(format!( + "{name}_{line}_{number}", + name = folder_name, + number = { + // Increases the current test number, if this file already + // exists or it creates a new entry with a test number of 0. + self.visited_tests + .entry((folder_name.clone(), line)) + .and_modify(|v| *v += 1) + .or_insert(0) + }, + line = line, + )); + + std::fs::create_dir_all(&path) + .expect("Couldn't create directory for doctest executables"); + + DirState::Perm(path) + } else { + DirState::Temp( + TempFileBuilder::new() + .prefix("rustdoctest") + .tempdir() + .expect("rustdoc needs a tempdir"), + ) + }; + debug!("creating test {}: {}", name, test); self.tests.push(testing::TestDescAndFn { desc: testing::TestDesc { @@ -723,7 +779,6 @@ impl Tester for Collector { let res = run_test( &test, &cratename, - &filename, line, options, config.should_panic, @@ -736,6 +791,8 @@ impl Tester for Collector { config.error_codes, &opts, edition, + outdir, + path, ); if let Err(err) = res { @@ -853,18 +910,21 @@ impl Tester for Collector { } } -struct HirCollector<'a, 'hir> { - sess: &'a session::Session, +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 +950,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 +966,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 +975,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 +1009,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 a8a571e7c5491..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, @@ -234,9 +222,7 @@ pub fn load_css_paths(v: &[u8]) -> CssPath { } pub fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec) { - if against.name != other.name { - return; - } else { + if against.name == other.name { for child in &against.children { let mut found = false; let mut found_working = false; @@ -267,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 feaf391c95afb..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 { @@ -563,6 +554,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { polarity, defaultness, constness, + defaultness_span: _, ref generics, ref of_trait, self_ty, @@ -596,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. @@ -615,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. @@ -627,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/Cargo.toml b/src/libserialize/Cargo.toml deleted file mode 100644 index 96a0d51bc716d..0000000000000 --- a/src/libserialize/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "serialize" -version = "0.0.0" -edition = "2018" - -[lib] -name = "serialize" -path = "lib.rs" - -[dependencies] -indexmap = "1" -smallvec = { version = "1.0", features = ["union", "may_dangle"] } 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 deleted file mode 100644 index 1f9d43cb93043..0000000000000 --- a/src/libserialize/json.rs +++ /dev/null @@ -1,2794 +0,0 @@ -// Rust JSON serialization library. -// Copyright (c) 2011 Google Inc. - -#![forbid(non_camel_case_types)] -#![allow(missing_docs)] - -//! JSON parsing and serialization -//! -//! # What is JSON? -//! -//! JSON (JavaScript Object Notation) is a way to write data in Javascript. -//! Like XML, it allows to encode structured data in a text format that can be easily read by humans -//! Its simple syntax and native compatibility with JavaScript have made it a widely used format. -//! -//! Data types that can be encoded are JavaScript types (see the `Json` enum for more details): -//! -//! * `Boolean`: equivalent to rust's `bool` -//! * `Number`: equivalent to rust's `f64` -//! * `String`: equivalent to rust's `String` -//! * `Array`: equivalent to rust's `Vec`, but also allowing objects of different types in the -//! same array -//! * `Object`: equivalent to rust's `BTreeMap` -//! * `Null` -//! -//! An object is a series of string keys mapping to values, in `"key": value` format. -//! Arrays are enclosed in square brackets ([ ... ]) and objects in curly brackets ({ ... }). -//! A simple JSON document encoding a person, their age, address and phone numbers could look like -//! -//! ```json -//! { -//! "FirstName": "John", -//! "LastName": "Doe", -//! "Age": 43, -//! "Address": { -//! "Street": "Downing Street 10", -//! "City": "London", -//! "Country": "Great Britain" -//! }, -//! "PhoneNumbers": [ -//! "+44 1234567", -//! "+44 2345678" -//! ] -//! } -//! ``` -//! -//! # Rust Type-based Encoding and Decoding -//! -//! Rust provides a mechanism for low boilerplate encoding & decoding of values to and from JSON via -//! the serialization API. -//! To be able to encode a piece of data, it must implement the `serialize::RustcEncodable` trait. -//! To be able to decode a piece of data, it must implement the `serialize::RustcDecodable` trait. -//! The Rust compiler provides an annotation to automatically generate the code for these traits: -//! `#[derive(RustcDecodable, RustcEncodable)]` -//! -//! The JSON API provides an enum `json::Json` and a trait `ToJson` to encode objects. -//! The `ToJson` trait provides a `to_json` method to convert an object into a `json::Json` value. -//! A `json::Json` value can be encoded as a string or buffer using the functions described above. -//! You can also use the `json::Encoder` object, which implements the `Encoder` trait. -//! -//! When using `ToJson` the `RustcEncodable` trait implementation is not mandatory. -//! -//! # Examples of use -//! -//! ## Using Autoserialization -//! -//! Create a struct called `TestStruct` and serialize and deserialize it to and from JSON using the -//! serialization API, using the derived serialization code. -//! -//! ```rust -//! # #![feature(rustc_private)] -//! extern crate serialize as rustc_serialize; // for the deriving below -//! use rustc_serialize::json; -//! -//! // Automatically generate `Decodable` and `Encodable` trait implementations -//! #[derive(RustcDecodable, RustcEncodable)] -//! pub struct TestStruct { -//! data_int: u8, -//! data_str: String, -//! data_vector: Vec, -//! } -//! -//! fn main() { -//! let object = TestStruct { -//! data_int: 1, -//! data_str: "homura".to_string(), -//! data_vector: vec![2,3,4,5], -//! }; -//! -//! // Serialize using `json::encode` -//! let encoded = json::encode(&object).unwrap(); -//! -//! // Deserialize using `json::decode` -//! let decoded: TestStruct = json::decode(&encoded[..]).unwrap(); -//! } -//! ``` -//! -//! ## Using the `ToJson` trait -//! -//! The examples above use the `ToJson` trait to generate the JSON string, which is required -//! for custom mappings. -//! -//! ### Simple example of `ToJson` usage -//! -//! ```rust -//! # #![feature(rustc_private)] -//! extern crate serialize as rustc_serialize; -//! use rustc_serialize::json::{self, ToJson, Json}; -//! -//! // A custom data structure -//! struct ComplexNum { -//! a: f64, -//! b: f64, -//! } -//! -//! // JSON value representation -//! impl ToJson for ComplexNum { -//! fn to_json(&self) -> Json { -//! Json::String(format!("{}+{}i", self.a, self.b)) -//! } -//! } -//! -//! // Only generate `RustcEncodable` trait implementation -//! #[derive(RustcEncodable)] -//! pub struct ComplexNumRecord { -//! uid: u8, -//! dsc: String, -//! val: Json, -//! } -//! -//! fn main() { -//! let num = ComplexNum { a: 0.0001, b: 12.539 }; -//! let data: String = json::encode(&ComplexNumRecord{ -//! uid: 1, -//! dsc: "test".to_string(), -//! val: num.to_json(), -//! }).unwrap(); -//! println!("data: {}", data); -//! // data: {"uid":1,"dsc":"test","val":"0.0001+12.539i"}; -//! } -//! ``` -//! -//! ### Verbose example of `ToJson` usage -//! -//! ```rust -//! # #![feature(rustc_private)] -//! extern crate serialize as rustc_serialize; -//! use std::collections::BTreeMap; -//! use rustc_serialize::json::{self, Json, ToJson}; -//! -//! // Only generate `RustcDecodable` trait implementation -//! #[derive(RustcDecodable)] -//! pub struct TestStruct { -//! data_int: u8, -//! data_str: String, -//! data_vector: Vec, -//! } -//! -//! // Specify encoding method manually -//! impl ToJson for TestStruct { -//! fn to_json(&self) -> Json { -//! let mut d = BTreeMap::new(); -//! // All standard types implement `to_json()`, so use it -//! d.insert("data_int".to_string(), self.data_int.to_json()); -//! d.insert("data_str".to_string(), self.data_str.to_json()); -//! d.insert("data_vector".to_string(), self.data_vector.to_json()); -//! Json::Object(d) -//! } -//! } -//! -//! fn main() { -//! // Serialize using `ToJson` -//! let input_data = TestStruct { -//! data_int: 1, -//! data_str: "madoka".to_string(), -//! data_vector: vec![2,3,4,5], -//! }; -//! let json_obj: Json = input_data.to_json(); -//! let json_str: String = json_obj.to_string(); -//! -//! // Deserialize like before -//! let decoded: TestStruct = json::decode(&json_str).unwrap(); -//! } -//! ``` - -use self::DecoderError::*; -use self::ErrorCode::*; -use self::InternalStackElement::*; -use self::JsonEvent::*; -use self::ParserError::*; -use self::ParserState::*; - -use std::borrow::Cow; -use std::collections::{BTreeMap, HashMap}; -use std::io; -use std::io::prelude::*; -use std::mem::swap; -use std::num::FpCategory as Fp; -use std::ops::Index; -use std::str::FromStr; -use std::string; -use std::{char, f64, fmt, str}; - -use crate::Encodable; - -/// Represents a json value -#[derive(Clone, PartialEq, PartialOrd, Debug)] -pub enum Json { - I64(i64), - U64(u64), - F64(f64), - String(string::String), - Boolean(bool), - Array(self::Array), - Object(self::Object), - Null, -} - -pub type Array = Vec; -pub type Object = BTreeMap; - -pub struct PrettyJson<'a> { - inner: &'a Json, -} - -pub struct AsJson<'a, T> { - inner: &'a T, -} -pub struct AsPrettyJson<'a, T> { - inner: &'a T, - indent: Option, -} - -/// The errors that can arise while parsing a JSON stream. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum ErrorCode { - InvalidSyntax, - InvalidNumber, - EOFWhileParsingObject, - EOFWhileParsingArray, - EOFWhileParsingValue, - EOFWhileParsingString, - KeyMustBeAString, - ExpectedColon, - TrailingCharacters, - TrailingComma, - InvalidEscape, - InvalidUnicodeCodePoint, - LoneLeadingSurrogateInHexEscape, - UnexpectedEndOfHexEscape, - UnrecognizedHex, - NotFourDigit, - NotUtf8, -} - -#[derive(Clone, PartialEq, Debug)] -pub enum ParserError { - /// msg, line, col - SyntaxError(ErrorCode, usize, usize), - IoError(io::ErrorKind, String), -} - -// Builder and Parser have the same errors. -pub type BuilderError = ParserError; - -#[derive(Clone, PartialEq, Debug)] -pub enum DecoderError { - ParseError(ParserError), - ExpectedError(string::String, string::String), - MissingFieldError(string::String), - UnknownVariantError(string::String), - ApplicationError(string::String), -} - -#[derive(Copy, Clone, Debug)] -pub enum EncoderError { - FmtError(fmt::Error), - BadHashmapKey, -} - -/// Returns a readable error string for a given error code. -pub fn error_str(error: ErrorCode) -> &'static str { - match error { - InvalidSyntax => "invalid syntax", - InvalidNumber => "invalid number", - EOFWhileParsingObject => "EOF While parsing object", - EOFWhileParsingArray => "EOF While parsing array", - EOFWhileParsingValue => "EOF While parsing value", - EOFWhileParsingString => "EOF While parsing string", - KeyMustBeAString => "key must be a string", - ExpectedColon => "expected `:`", - TrailingCharacters => "trailing characters", - TrailingComma => "trailing comma", - InvalidEscape => "invalid escape", - UnrecognizedHex => "invalid \\u{ esc}ape (unrecognized hex)", - NotFourDigit => "invalid \\u{ esc}ape (not four digits)", - NotUtf8 => "contents not utf-8", - InvalidUnicodeCodePoint => "invalid Unicode code point", - LoneLeadingSurrogateInHexEscape => "lone leading surrogate in hex escape", - UnexpectedEndOfHexEscape => "unexpected end of hex escape", - } -} - -/// Shortcut function to decode a JSON `&str` into an object -pub fn decode(s: &str) -> DecodeResult { - let json = match from_str(s) { - Ok(x) => x, - Err(e) => return Err(ParseError(e)), - }; - - let mut decoder = Decoder::new(json); - crate::Decodable::decode(&mut decoder) -} - -/// Shortcut function to encode a `T` into a JSON `String` -pub fn encode(object: &T) -> Result { - let mut s = String::new(); - { - let mut encoder = Encoder::new(&mut s); - object.encode(&mut encoder)?; - } - Ok(s) -} - -impl fmt::Display for ErrorCode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - error_str(*self).fmt(f) - } -} - -fn io_error_to_error(io: io::Error) -> ParserError { - IoError(io.kind(), io.to_string()) -} - -impl fmt::Display for ParserError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME this should be a nicer error - fmt::Debug::fmt(self, f) - } -} - -impl fmt::Display for DecoderError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME this should be a nicer error - fmt::Debug::fmt(self, f) - } -} - -impl std::error::Error for DecoderError {} - -impl fmt::Display for EncoderError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME this should be a nicer error - fmt::Debug::fmt(self, f) - } -} - -impl std::error::Error for EncoderError {} - -impl From for EncoderError { - /// Converts a [`fmt::Error`] into `EncoderError` - /// - /// This conversion does not allocate memory. - fn from(err: fmt::Error) -> EncoderError { - EncoderError::FmtError(err) - } -} - -pub type EncodeResult = Result<(), EncoderError>; -pub type DecodeResult = Result; - -fn escape_str(wr: &mut dyn fmt::Write, v: &str) -> EncodeResult { - wr.write_str("\"")?; - - let mut start = 0; - - for (i, byte) in v.bytes().enumerate() { - let escaped = match byte { - b'"' => "\\\"", - b'\\' => "\\\\", - b'\x00' => "\\u0000", - b'\x01' => "\\u0001", - b'\x02' => "\\u0002", - b'\x03' => "\\u0003", - b'\x04' => "\\u0004", - b'\x05' => "\\u0005", - b'\x06' => "\\u0006", - b'\x07' => "\\u0007", - b'\x08' => "\\b", - b'\t' => "\\t", - b'\n' => "\\n", - b'\x0b' => "\\u000b", - b'\x0c' => "\\f", - b'\r' => "\\r", - b'\x0e' => "\\u000e", - b'\x0f' => "\\u000f", - b'\x10' => "\\u0010", - b'\x11' => "\\u0011", - b'\x12' => "\\u0012", - b'\x13' => "\\u0013", - b'\x14' => "\\u0014", - b'\x15' => "\\u0015", - b'\x16' => "\\u0016", - b'\x17' => "\\u0017", - b'\x18' => "\\u0018", - b'\x19' => "\\u0019", - b'\x1a' => "\\u001a", - b'\x1b' => "\\u001b", - b'\x1c' => "\\u001c", - b'\x1d' => "\\u001d", - b'\x1e' => "\\u001e", - b'\x1f' => "\\u001f", - b'\x7f' => "\\u007f", - _ => { - continue; - } - }; - - if start < i { - wr.write_str(&v[start..i])?; - } - - wr.write_str(escaped)?; - - start = i + 1; - } - - if start != v.len() { - wr.write_str(&v[start..])?; - } - - wr.write_str("\"")?; - Ok(()) -} - -fn escape_char(writer: &mut dyn fmt::Write, v: char) -> EncodeResult { - escape_str(writer, v.encode_utf8(&mut [0; 4])) -} - -fn spaces(wr: &mut dyn fmt::Write, mut n: usize) -> EncodeResult { - const BUF: &str = " "; - - while n >= BUF.len() { - wr.write_str(BUF)?; - n -= BUF.len(); - } - - if n > 0 { - wr.write_str(&BUF[..n])?; - } - Ok(()) -} - -fn fmt_number_or_null(v: f64) -> string::String { - match v.classify() { - Fp::Nan | Fp::Infinite => string::String::from("null"), - _ if v.fract() != 0f64 => v.to_string(), - _ => v.to_string() + ".0", - } -} - -/// A structure for implementing serialization to JSON. -pub struct Encoder<'a> { - writer: &'a mut (dyn fmt::Write + 'a), - is_emitting_map_key: bool, -} - -impl<'a> Encoder<'a> { - /// Creates a new JSON encoder whose output will be written to the writer - /// specified. - pub fn new(writer: &'a mut dyn fmt::Write) -> Encoder<'a> { - Encoder { writer, is_emitting_map_key: false } - } -} - -macro_rules! emit_enquoted_if_mapkey { - ($enc:ident,$e:expr) => {{ - if $enc.is_emitting_map_key { - write!($enc.writer, "\"{}\"", $e)?; - } else { - write!($enc.writer, "{}", $e)?; - } - Ok(()) - }}; -} - -impl<'a> crate::Encoder for Encoder<'a> { - type Error = EncoderError; - - fn emit_unit(&mut self) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "null")?; - Ok(()) - } - - fn emit_usize(&mut self, v: usize) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u128(&mut self, v: u128) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u64(&mut self, v: u64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u32(&mut self, v: u32) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u16(&mut self, v: u16) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u8(&mut self, v: u8) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - - fn emit_isize(&mut self, v: isize) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i128(&mut self, v: i128) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i64(&mut self, v: i64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i32(&mut self, v: i32) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i16(&mut self, v: i16) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i8(&mut self, v: i8) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - - fn emit_bool(&mut self, v: bool) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if v { - write!(self.writer, "true")?; - } else { - write!(self.writer, "false")?; - } - Ok(()) - } - - fn emit_f64(&mut self, v: f64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, fmt_number_or_null(v)) - } - fn emit_f32(&mut self, v: f32) -> EncodeResult { - self.emit_f64(f64::from(v)) - } - - fn emit_char(&mut self, v: char) -> EncodeResult { - escape_char(self.writer, v) - } - fn emit_str(&mut self, v: &str) -> EncodeResult { - escape_str(self.writer, v) - } - - fn emit_enum(&mut self, _name: &str, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - f(self) - } - - fn emit_enum_variant(&mut self, name: &str, _id: usize, cnt: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - // enums are encoded as strings or objects - // Bunny => "Bunny" - // Kangaroo(34,"William") => {"variant": "Kangaroo", "fields": [34,"William"]} - if cnt == 0 { - escape_str(self.writer, name) - } else { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "{{\"variant\":")?; - escape_str(self.writer, name)?; - write!(self.writer, ",\"fields\":[")?; - f(self)?; - write!(self.writer, "]}}")?; - Ok(()) - } - } - - fn emit_enum_variant_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - write!(self.writer, ",")?; - } - f(self) - } - - fn emit_enum_struct_variant( - &mut self, - name: &str, - id: usize, - cnt: usize, - f: F, - ) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_enum_variant(name, id, cnt, f) - } - - fn emit_enum_struct_variant_field(&mut self, _: &str, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_enum_variant_arg(idx, f) - } - - fn emit_struct(&mut self, _: &str, _: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "{{")?; - f(self)?; - write!(self.writer, "}}")?; - Ok(()) - } - - fn emit_struct_field(&mut self, name: &str, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - write!(self.writer, ",")?; - } - escape_str(self.writer, name)?; - write!(self.writer, ":")?; - f(self) - } - - fn emit_tuple(&mut self, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq(len, f) - } - fn emit_tuple_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq_elt(idx, f) - } - - fn emit_tuple_struct(&mut self, _name: &str, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq(len, f) - } - fn emit_tuple_struct_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq_elt(idx, f) - } - - fn emit_option(&mut self, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - f(self) - } - fn emit_option_none(&mut self) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_unit() - } - fn emit_option_some(&mut self, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - f(self) - } - - fn emit_seq(&mut self, _len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "[")?; - f(self)?; - write!(self.writer, "]")?; - Ok(()) - } - - fn emit_seq_elt(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - write!(self.writer, ",")?; - } - f(self) - } - - fn emit_map(&mut self, _len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "{{")?; - f(self)?; - write!(self.writer, "}}")?; - Ok(()) - } - - fn emit_map_elt_key(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - write!(self.writer, ",")? - } - self.is_emitting_map_key = true; - f(self)?; - self.is_emitting_map_key = false; - Ok(()) - } - - fn emit_map_elt_val(&mut self, _idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut Encoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, ":")?; - f(self) - } -} - -/// Another encoder for JSON, but prints out human-readable JSON instead of -/// compact data -pub struct PrettyEncoder<'a> { - writer: &'a mut (dyn fmt::Write + 'a), - curr_indent: usize, - indent: usize, - is_emitting_map_key: bool, -} - -impl<'a> PrettyEncoder<'a> { - /// Creates a new encoder whose output will be written to the specified writer - pub fn new(writer: &'a mut dyn fmt::Write) -> PrettyEncoder<'a> { - PrettyEncoder { writer, curr_indent: 0, indent: 2, is_emitting_map_key: false } - } - - /// Sets the number of spaces to indent for each level. - /// This is safe to set during encoding. - pub fn set_indent(&mut self, indent: usize) { - // self.indent very well could be 0 so we need to use checked division. - let level = self.curr_indent.checked_div(self.indent).unwrap_or(0); - self.indent = indent; - self.curr_indent = level * self.indent; - } -} - -impl<'a> crate::Encoder for PrettyEncoder<'a> { - type Error = EncoderError; - - fn emit_unit(&mut self) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, "null")?; - Ok(()) - } - - fn emit_usize(&mut self, v: usize) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u128(&mut self, v: u128) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u64(&mut self, v: u64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u32(&mut self, v: u32) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u16(&mut self, v: u16) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_u8(&mut self, v: u8) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - - fn emit_isize(&mut self, v: isize) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i128(&mut self, v: i128) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i64(&mut self, v: i64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i32(&mut self, v: i32) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i16(&mut self, v: i16) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - fn emit_i8(&mut self, v: i8) -> EncodeResult { - emit_enquoted_if_mapkey!(self, v) - } - - fn emit_bool(&mut self, v: bool) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if v { - write!(self.writer, "true")?; - } else { - write!(self.writer, "false")?; - } - Ok(()) - } - - fn emit_f64(&mut self, v: f64) -> EncodeResult { - emit_enquoted_if_mapkey!(self, fmt_number_or_null(v)) - } - fn emit_f32(&mut self, v: f32) -> EncodeResult { - self.emit_f64(f64::from(v)) - } - - fn emit_char(&mut self, v: char) -> EncodeResult { - escape_char(self.writer, v) - } - fn emit_str(&mut self, v: &str) -> EncodeResult { - escape_str(self.writer, v) - } - - fn emit_enum(&mut self, _name: &str, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - f(self) - } - - fn emit_enum_variant(&mut self, name: &str, _id: usize, cnt: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if cnt == 0 { - escape_str(self.writer, name) - } else { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - writeln!(self.writer, "{{")?; - self.curr_indent += self.indent; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "\"variant\": ")?; - escape_str(self.writer, name)?; - writeln!(self.writer, ",")?; - spaces(self.writer, self.curr_indent)?; - writeln!(self.writer, "\"fields\": [")?; - self.curr_indent += self.indent; - f(self)?; - self.curr_indent -= self.indent; - writeln!(self.writer)?; - spaces(self.writer, self.curr_indent)?; - self.curr_indent -= self.indent; - writeln!(self.writer, "]")?; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "}}")?; - Ok(()) - } - } - - fn emit_enum_variant_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx != 0 { - writeln!(self.writer, ",")?; - } - spaces(self.writer, self.curr_indent)?; - f(self) - } - - fn emit_enum_struct_variant( - &mut self, - name: &str, - id: usize, - cnt: usize, - f: F, - ) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_enum_variant(name, id, cnt, f) - } - - fn emit_enum_struct_variant_field(&mut self, _: &str, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_enum_variant_arg(idx, f) - } - - fn emit_struct(&mut self, _: &str, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if len == 0 { - write!(self.writer, "{{}}")?; - } else { - write!(self.writer, "{{")?; - self.curr_indent += self.indent; - f(self)?; - self.curr_indent -= self.indent; - writeln!(self.writer)?; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "}}")?; - } - Ok(()) - } - - fn emit_struct_field(&mut self, name: &str, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx == 0 { - writeln!(self.writer)?; - } else { - writeln!(self.writer, ",")?; - } - spaces(self.writer, self.curr_indent)?; - escape_str(self.writer, name)?; - write!(self.writer, ": ")?; - f(self) - } - - fn emit_tuple(&mut self, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq(len, f) - } - fn emit_tuple_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq_elt(idx, f) - } - - fn emit_tuple_struct(&mut self, _: &str, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq(len, f) - } - fn emit_tuple_struct_arg(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_seq_elt(idx, f) - } - - fn emit_option(&mut self, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - f(self) - } - fn emit_option_none(&mut self) -> EncodeResult { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - self.emit_unit() - } - fn emit_option_some(&mut self, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - f(self) - } - - fn emit_seq(&mut self, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if len == 0 { - write!(self.writer, "[]")?; - } else { - write!(self.writer, "[")?; - self.curr_indent += self.indent; - f(self)?; - self.curr_indent -= self.indent; - writeln!(self.writer)?; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "]")?; - } - Ok(()) - } - - fn emit_seq_elt(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx == 0 { - writeln!(self.writer)?; - } else { - writeln!(self.writer, ",")?; - } - spaces(self.writer, self.curr_indent)?; - f(self) - } - - fn emit_map(&mut self, len: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if len == 0 { - write!(self.writer, "{{}}")?; - } else { - write!(self.writer, "{{")?; - self.curr_indent += self.indent; - f(self)?; - self.curr_indent -= self.indent; - writeln!(self.writer)?; - spaces(self.writer, self.curr_indent)?; - write!(self.writer, "}}")?; - } - Ok(()) - } - - fn emit_map_elt_key(&mut self, idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - if idx == 0 { - writeln!(self.writer)?; - } else { - writeln!(self.writer, ",")?; - } - spaces(self.writer, self.curr_indent)?; - self.is_emitting_map_key = true; - f(self)?; - self.is_emitting_map_key = false; - Ok(()) - } - - fn emit_map_elt_val(&mut self, _idx: usize, f: F) -> EncodeResult - where - F: FnOnce(&mut PrettyEncoder<'a>) -> EncodeResult, - { - if self.is_emitting_map_key { - return Err(EncoderError::BadHashmapKey); - } - write!(self.writer, ": ")?; - f(self) - } -} - -impl Encodable for Json { - fn encode(&self, e: &mut E) -> Result<(), E::Error> { - match *self { - Json::I64(v) => v.encode(e), - Json::U64(v) => v.encode(e), - Json::F64(v) => v.encode(e), - Json::String(ref v) => v.encode(e), - Json::Boolean(v) => v.encode(e), - Json::Array(ref v) => v.encode(e), - Json::Object(ref v) => v.encode(e), - Json::Null => e.emit_unit(), - } - } -} - -/// Creates an `AsJson` wrapper which can be used to print a value as JSON -/// on-the-fly via `write!` -pub fn as_json(t: &T) -> AsJson<'_, T> { - AsJson { inner: t } -} - -/// Creates an `AsPrettyJson` wrapper which can be used to print a value as JSON -/// on-the-fly via `write!` -pub fn as_pretty_json(t: &T) -> AsPrettyJson<'_, T> { - AsPrettyJson { inner: t, indent: None } -} - -impl Json { - /// Borrow this json object as a pretty object to generate a pretty - /// representation for it via `Display`. - pub fn pretty(&self) -> PrettyJson<'_> { - PrettyJson { inner: self } - } - - /// If the Json value is an Object, returns the value associated with the provided key. - /// Otherwise, returns None. - pub fn find(&self, key: &str) -> Option<&Json> { - match *self { - Json::Object(ref map) => map.get(key), - _ => None, - } - } - - /// Attempts to get a nested Json Object for each key in `keys`. - /// If any key is found not to exist, `find_path` will return `None`. - /// Otherwise, it will return the Json value associated with the final key. - pub fn find_path<'a>(&'a self, keys: &[&str]) -> Option<&'a Json> { - let mut target = self; - for key in keys { - target = target.find(*key)?; - } - Some(target) - } - - /// If the Json value is an Object, performs a depth-first search until - /// a value associated with the provided key is found. If no value is found - /// or the Json value is not an Object, returns `None`. - pub fn search(&self, key: &str) -> Option<&Json> { - match *self { - Json::Object(ref map) => match map.get(key) { - Some(json_value) => Some(json_value), - None => { - for v in map.values() { - match v.search(key) { - x if x.is_some() => return x, - _ => (), - } - } - None - } - }, - _ => None, - } - } - - /// Returns `true` if the Json value is an `Object`. - pub fn is_object(&self) -> bool { - self.as_object().is_some() - } - - /// If the Json value is an `Object`, returns the associated `BTreeMap`; - /// returns `None` otherwise. - pub fn as_object(&self) -> Option<&Object> { - match *self { - Json::Object(ref map) => Some(map), - _ => None, - } - } - - /// Returns `true` if the Json value is an `Array`. - pub fn is_array(&self) -> bool { - self.as_array().is_some() - } - - /// If the Json value is an `Array`, returns the associated vector; - /// returns `None` otherwise. - pub fn as_array(&self) -> Option<&Array> { - match *self { - Json::Array(ref array) => Some(&*array), - _ => None, - } - } - - /// Returns `true` if the Json value is a `String`. - pub fn is_string(&self) -> bool { - self.as_string().is_some() - } - - /// If the Json value is a `String`, returns the associated `str`; - /// returns `None` otherwise. - pub fn as_string(&self) -> Option<&str> { - match *self { - Json::String(ref s) => Some(&s[..]), - _ => None, - } - } - - /// Returns `true` if the Json value is a `Number`. - pub fn is_number(&self) -> bool { - match *self { - Json::I64(_) | Json::U64(_) | Json::F64(_) => true, - _ => false, - } - } - - /// Returns `true` if the Json value is a `i64`. - pub fn is_i64(&self) -> bool { - match *self { - Json::I64(_) => true, - _ => false, - } - } - - /// Returns `true` if the Json value is a `u64`. - pub fn is_u64(&self) -> bool { - match *self { - Json::U64(_) => true, - _ => false, - } - } - - /// Returns `true` if the Json value is a `f64`. - pub fn is_f64(&self) -> bool { - match *self { - Json::F64(_) => true, - _ => false, - } - } - - /// If the Json value is a number, returns or cast it to a `i64`; - /// returns `None` otherwise. - pub fn as_i64(&self) -> Option { - match *self { - Json::I64(n) => Some(n), - Json::U64(n) => Some(n as i64), - _ => None, - } - } - - /// If the Json value is a number, returns or cast it to a `u64`; - /// returns `None` otherwise. - pub fn as_u64(&self) -> Option { - match *self { - Json::I64(n) => Some(n as u64), - Json::U64(n) => Some(n), - _ => None, - } - } - - /// If the Json value is a number, returns or cast it to a `f64`; - /// returns `None` otherwise. - pub fn as_f64(&self) -> Option { - match *self { - Json::I64(n) => Some(n as f64), - Json::U64(n) => Some(n as f64), - Json::F64(n) => Some(n), - _ => None, - } - } - - /// Returns `true` if the Json value is a `Boolean`. - pub fn is_boolean(&self) -> bool { - self.as_boolean().is_some() - } - - /// If the Json value is a `Boolean`, returns the associated `bool`; - /// returns `None` otherwise. - pub fn as_boolean(&self) -> Option { - match *self { - Json::Boolean(b) => Some(b), - _ => None, - } - } - - /// Returns `true` if the Json value is a `Null`. - pub fn is_null(&self) -> bool { - self.as_null().is_some() - } - - /// If the Json value is a `Null`, returns `()`; - /// returns `None` otherwise. - pub fn as_null(&self) -> Option<()> { - match *self { - Json::Null => Some(()), - _ => None, - } - } -} - -impl<'a> Index<&'a str> for Json { - type Output = Json; - - fn index(&self, idx: &'a str) -> &Json { - self.find(idx).unwrap() - } -} - -impl Index for Json { - type Output = Json; - - fn index(&self, idx: usize) -> &Json { - match *self { - Json::Array(ref v) => &v[idx], - _ => panic!("can only index Json with usize if it is an array"), - } - } -} - -/// The output of the streaming parser. -#[derive(PartialEq, Clone, Debug)] -pub enum JsonEvent { - ObjectStart, - ObjectEnd, - ArrayStart, - ArrayEnd, - BooleanValue(bool), - I64Value(i64), - U64Value(u64), - F64Value(f64), - StringValue(string::String), - NullValue, - Error(ParserError), -} - -#[derive(PartialEq, Debug)] -enum ParserState { - // Parse a value in an array, true means first element. - ParseArray(bool), - // Parse ',' or ']' after an element in an array. - ParseArrayComma, - // Parse a key:value in an object, true means first element. - ParseObject(bool), - // Parse ',' or ']' after an element in an object. - ParseObjectComma, - // Initial state. - ParseStart, - // Expecting the stream to end. - ParseBeforeFinish, - // Parsing can't continue. - ParseFinished, -} - -/// A Stack represents the current position of the parser in the logical -/// structure of the JSON stream. -/// For example 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 -#[derive(PartialEq, Clone, Debug)] -pub enum StackElement<'l> { - Index(u32), - Key(&'l str), -} - -// Internally, Key elements are stored as indices in a buffer to avoid -// allocating a string for every member of an object. -#[derive(PartialEq, Clone, Debug)] -enum InternalStackElement { - InternalIndex(u32), - InternalKey(u16, u16), // start, size -} - -impl Stack { - pub fn new() -> Stack { - Stack { stack: Vec::new(), str_buffer: Vec::new() } - } - - /// Returns The number of elements in the Stack. - pub fn len(&self) -> usize { - self.stack.len() - } - - /// Returns `true` if the stack is empty. - pub fn is_empty(&self) -> bool { - self.stack.is_empty() - } - - /// Provides access to the StackElement at a given index. - /// lower indices are at the bottom of the stack while higher indices are - /// at the top. - pub fn get(&self, idx: usize) -> StackElement<'_> { - match self.stack[idx] { - InternalIndex(i) => StackElement::Index(i), - InternalKey(start, size) => StackElement::Key( - str::from_utf8(&self.str_buffer[start as usize..start as usize + size as usize]) - .unwrap(), - ), - } - } - - /// Compares this stack with an array of StackElement<'_>s. - pub fn is_equal_to(&self, rhs: &[StackElement<'_>]) -> bool { - if self.stack.len() != rhs.len() { - return false; - } - for (i, r) in rhs.iter().enumerate() { - if self.get(i) != *r { - return false; - } - } - true - } - - /// Returns `true` if the bottom-most elements of this stack are the same as - /// the ones passed as parameter. - pub fn starts_with(&self, rhs: &[StackElement<'_>]) -> bool { - if self.stack.len() < rhs.len() { - return false; - } - for (i, r) in rhs.iter().enumerate() { - if self.get(i) != *r { - return false; - } - } - true - } - - /// Returns `true` if the top-most elements of this stack are the same as - /// the ones passed as parameter. - pub fn ends_with(&self, rhs: &[StackElement<'_>]) -> bool { - if self.stack.len() < rhs.len() { - return false; - } - let offset = self.stack.len() - rhs.len(); - for (i, r) in rhs.iter().enumerate() { - if self.get(i + offset) != *r { - return false; - } - } - true - } - - /// Returns the top-most element (if any). - pub fn top(&self) -> Option> { - match self.stack.last() { - None => None, - Some(&InternalIndex(i)) => Some(StackElement::Index(i)), - Some(&InternalKey(start, size)) => Some(StackElement::Key( - str::from_utf8(&self.str_buffer[start as usize..(start + size) as usize]).unwrap(), - )), - } - } - - // Used by Parser to insert StackElement::Key elements at the top of the stack. - fn push_key(&mut self, key: string::String) { - self.stack.push(InternalKey(self.str_buffer.len() as u16, key.len() as u16)); - self.str_buffer.extend(key.as_bytes()); - } - - // Used by Parser to insert StackElement::Index elements at the top of the stack. - fn push_index(&mut self, index: u32) { - self.stack.push(InternalIndex(index)); - } - - // Used by Parser to remove the top-most element of the stack. - fn pop(&mut self) { - assert!(!self.is_empty()); - match *self.stack.last().unwrap() { - InternalKey(_, sz) => { - let new_size = self.str_buffer.len() - sz as usize; - self.str_buffer.truncate(new_size); - } - InternalIndex(_) => {} - } - self.stack.pop(); - } - - // Used by Parser to test whether the top-most element is an index. - fn last_is_index(&self) -> bool { - match self.stack.last() { - Some(InternalIndex(_)) => true, - _ => false, - } - } - - // Used by Parser to increment the index of the top-most element. - fn bump_index(&mut self) { - let len = self.stack.len(); - let idx = match *self.stack.last().unwrap() { - InternalIndex(i) => i + 1, - _ => { - panic!(); - } - }; - self.stack[len - 1] = InternalIndex(idx); - } -} - -/// A streaming JSON parser implemented as an iterator of JsonEvent, consuming -/// an iterator of char. -pub struct Parser { - rdr: T, - ch: Option, - line: usize, - col: usize, - // We maintain a stack representing where we are in the logical structure - // of the JSON stream. - stack: Stack, - // A state machine is kept to make it possible to interrupt and resume parsing. - state: ParserState, -} - -impl> Iterator for Parser { - type Item = JsonEvent; - - fn next(&mut self) -> Option { - if self.state == ParseFinished { - return None; - } - - if self.state == ParseBeforeFinish { - self.parse_whitespace(); - // Make sure there is no trailing characters. - if self.eof() { - self.state = ParseFinished; - return None; - } else { - return Some(self.error_event(TrailingCharacters)); - } - } - - Some(self.parse()) - } -} - -impl> Parser { - /// Creates the JSON parser. - pub fn new(rdr: T) -> Parser { - let mut p = Parser { - rdr, - ch: Some('\x00'), - line: 1, - col: 0, - stack: Stack::new(), - state: ParseStart, - }; - p.bump(); - p - } - - /// Provides access to the current position in the logical structure of the - /// JSON stream. - pub fn stack(&self) -> &Stack { - &self.stack - } - - fn eof(&self) -> bool { - self.ch.is_none() - } - fn ch_or_null(&self) -> char { - self.ch.unwrap_or('\x00') - } - fn bump(&mut self) { - self.ch = self.rdr.next(); - - if self.ch_is('\n') { - self.line += 1; - self.col = 1; - } else { - self.col += 1; - } - } - - fn next_char(&mut self) -> Option { - self.bump(); - self.ch - } - fn ch_is(&self, c: char) -> bool { - self.ch == Some(c) - } - - fn error(&self, reason: ErrorCode) -> Result { - Err(SyntaxError(reason, self.line, self.col)) - } - - fn parse_whitespace(&mut self) { - while self.ch_is(' ') || self.ch_is('\n') || self.ch_is('\t') || self.ch_is('\r') { - self.bump(); - } - } - - fn parse_number(&mut self) -> JsonEvent { - let neg = if self.ch_is('-') { - self.bump(); - true - } else { - false - }; - - let res = match self.parse_u64() { - Ok(res) => res, - Err(e) => { - return Error(e); - } - }; - - if self.ch_is('.') || self.ch_is('e') || self.ch_is('E') { - let mut res = res as f64; - - if self.ch_is('.') { - res = match self.parse_decimal(res) { - Ok(res) => res, - Err(e) => { - return Error(e); - } - }; - } - - if self.ch_is('e') || self.ch_is('E') { - res = match self.parse_exponent(res) { - Ok(res) => res, - Err(e) => { - return Error(e); - } - }; - } - - if neg { - res *= -1.0; - } - - F64Value(res) - } else if neg { - let res = (res as i64).wrapping_neg(); - - // Make sure we didn't underflow. - if res > 0 { - Error(SyntaxError(InvalidNumber, self.line, self.col)) - } else { - I64Value(res) - } - } else { - U64Value(res) - } - } - - fn parse_u64(&mut self) -> Result { - let mut accum = 0u64; - let last_accum = 0; // necessary to detect overflow. - - match self.ch_or_null() { - '0' => { - self.bump(); - - // A leading '0' must be the only digit before the decimal point. - if let '0'..='9' = self.ch_or_null() { - return self.error(InvalidNumber); - } - } - '1'..='9' => { - while !self.eof() { - match self.ch_or_null() { - c @ '0'..='9' => { - accum = accum.wrapping_mul(10); - accum = accum.wrapping_add((c as u64) - ('0' as u64)); - - // Detect overflow by comparing to the last value. - if accum <= last_accum { - return self.error(InvalidNumber); - } - - self.bump(); - } - _ => break, - } - } - } - _ => return self.error(InvalidNumber), - } - - Ok(accum) - } - - fn parse_decimal(&mut self, mut res: f64) -> Result { - self.bump(); - - // Make sure a digit follows the decimal place. - match self.ch_or_null() { - '0'..='9' => (), - _ => return self.error(InvalidNumber), - } - - let mut dec = 1.0; - while !self.eof() { - match self.ch_or_null() { - c @ '0'..='9' => { - dec /= 10.0; - res += (((c as isize) - ('0' as isize)) as f64) * dec; - self.bump(); - } - _ => break, - } - } - - Ok(res) - } - - fn parse_exponent(&mut self, mut res: f64) -> Result { - self.bump(); - - let mut exp = 0; - let mut neg_exp = false; - - if self.ch_is('+') { - self.bump(); - } else if self.ch_is('-') { - self.bump(); - neg_exp = true; - } - - // Make sure a digit follows the exponent place. - match self.ch_or_null() { - '0'..='9' => (), - _ => return self.error(InvalidNumber), - } - while !self.eof() { - match self.ch_or_null() { - c @ '0'..='9' => { - exp *= 10; - exp += (c as usize) - ('0' as usize); - - self.bump(); - } - _ => break, - } - } - - let exp = 10_f64.powi(exp as i32); - if neg_exp { - res /= exp; - } else { - res *= exp; - } - - Ok(res) - } - - fn decode_hex_escape(&mut self) -> Result { - let mut i = 0; - let mut n = 0; - while i < 4 && !self.eof() { - self.bump(); - n = match self.ch_or_null() { - c @ '0'..='9' => n * 16 + ((c as u16) - ('0' as u16)), - 'a' | 'A' => n * 16 + 10, - 'b' | 'B' => n * 16 + 11, - 'c' | 'C' => n * 16 + 12, - 'd' | 'D' => n * 16 + 13, - 'e' | 'E' => n * 16 + 14, - 'f' | 'F' => n * 16 + 15, - _ => return self.error(InvalidEscape), - }; - - i += 1; - } - - // Error out if we didn't parse 4 digits. - if i != 4 { - return self.error(InvalidEscape); - } - - Ok(n) - } - - fn parse_str(&mut self) -> Result { - let mut escape = false; - let mut res = string::String::new(); - - loop { - self.bump(); - if self.eof() { - return self.error(EOFWhileParsingString); - } - - if escape { - match self.ch_or_null() { - '"' => res.push('"'), - '\\' => res.push('\\'), - '/' => res.push('/'), - 'b' => res.push('\x08'), - 'f' => res.push('\x0c'), - 'n' => res.push('\n'), - 'r' => res.push('\r'), - 't' => res.push('\t'), - 'u' => match self.decode_hex_escape()? { - 0xDC00..=0xDFFF => return self.error(LoneLeadingSurrogateInHexEscape), - - // Non-BMP characters are encoded as a sequence of - // two hex escapes, representing UTF-16 surrogates. - n1 @ 0xD800..=0xDBFF => { - match (self.next_char(), self.next_char()) { - (Some('\\'), Some('u')) => (), - _ => return self.error(UnexpectedEndOfHexEscape), - } - - let n2 = self.decode_hex_escape()?; - if n2 < 0xDC00 || n2 > 0xDFFF { - return self.error(LoneLeadingSurrogateInHexEscape); - } - let c = - (u32::from(n1 - 0xD800) << 10 | u32::from(n2 - 0xDC00)) + 0x1_0000; - res.push(char::from_u32(c).unwrap()); - } - - n => match char::from_u32(u32::from(n)) { - Some(c) => res.push(c), - None => return self.error(InvalidUnicodeCodePoint), - }, - }, - _ => return self.error(InvalidEscape), - } - escape = false; - } else if self.ch_is('\\') { - escape = true; - } else { - match self.ch { - Some('"') => { - self.bump(); - return Ok(res); - } - Some(c) => res.push(c), - None => unreachable!(), - } - } - } - } - - // Invoked at each iteration, consumes the stream until it has enough - // information to return a JsonEvent. - // Manages an internal state so that parsing can be interrupted and resumed. - // Also keeps track of the position in the logical structure of the json - // stream isize the form of a stack that can be queried by the user using the - // stack() method. - fn parse(&mut self) -> JsonEvent { - loop { - // The only paths where the loop can spin a new iteration - // are in the cases ParseArrayComma and ParseObjectComma if ',' - // is parsed. In these cases the state is set to (respectively) - // ParseArray(false) and ParseObject(false), which always return, - // so there is no risk of getting stuck in an infinite loop. - // All other paths return before the end of the loop's iteration. - self.parse_whitespace(); - - match self.state { - ParseStart => { - return self.parse_start(); - } - ParseArray(first) => { - return self.parse_array(first); - } - ParseArrayComma => { - if let Some(evt) = self.parse_array_comma_or_end() { - return evt; - } - } - ParseObject(first) => { - return self.parse_object(first); - } - ParseObjectComma => { - self.stack.pop(); - if self.ch_is(',') { - self.state = ParseObject(false); - self.bump(); - } else { - return self.parse_object_end(); - } - } - _ => { - return self.error_event(InvalidSyntax); - } - } - } - } - - fn parse_start(&mut self) -> JsonEvent { - let val = self.parse_value(); - self.state = match val { - Error(_) => ParseFinished, - ArrayStart => ParseArray(true), - ObjectStart => ParseObject(true), - _ => ParseBeforeFinish, - }; - val - } - - fn parse_array(&mut self, first: bool) -> JsonEvent { - if self.ch_is(']') { - if !first { - self.error_event(InvalidSyntax) - } else { - self.state = if self.stack.is_empty() { - ParseBeforeFinish - } else if self.stack.last_is_index() { - ParseArrayComma - } else { - ParseObjectComma - }; - self.bump(); - ArrayEnd - } - } else { - if first { - self.stack.push_index(0); - } - let val = self.parse_value(); - self.state = match val { - Error(_) => ParseFinished, - ArrayStart => ParseArray(true), - ObjectStart => ParseObject(true), - _ => ParseArrayComma, - }; - val - } - } - - fn parse_array_comma_or_end(&mut self) -> Option { - if self.ch_is(',') { - self.stack.bump_index(); - self.state = ParseArray(false); - self.bump(); - None - } else if self.ch_is(']') { - self.stack.pop(); - self.state = if self.stack.is_empty() { - ParseBeforeFinish - } else if self.stack.last_is_index() { - ParseArrayComma - } else { - ParseObjectComma - }; - self.bump(); - Some(ArrayEnd) - } else if self.eof() { - Some(self.error_event(EOFWhileParsingArray)) - } else { - Some(self.error_event(InvalidSyntax)) - } - } - - fn parse_object(&mut self, first: bool) -> JsonEvent { - if self.ch_is('}') { - if !first { - if self.stack.is_empty() { - return self.error_event(TrailingComma); - } else { - self.stack.pop(); - } - } - self.state = if self.stack.is_empty() { - ParseBeforeFinish - } else if self.stack.last_is_index() { - ParseArrayComma - } else { - ParseObjectComma - }; - self.bump(); - return ObjectEnd; - } - if self.eof() { - return self.error_event(EOFWhileParsingObject); - } - if !self.ch_is('"') { - return self.error_event(KeyMustBeAString); - } - let s = match self.parse_str() { - Ok(s) => s, - Err(e) => { - self.state = ParseFinished; - return Error(e); - } - }; - self.parse_whitespace(); - if self.eof() { - return self.error_event(EOFWhileParsingObject); - } else if self.ch_or_null() != ':' { - return self.error_event(ExpectedColon); - } - self.stack.push_key(s); - self.bump(); - self.parse_whitespace(); - - let val = self.parse_value(); - - self.state = match val { - Error(_) => ParseFinished, - ArrayStart => ParseArray(true), - ObjectStart => ParseObject(true), - _ => ParseObjectComma, - }; - val - } - - fn parse_object_end(&mut self) -> JsonEvent { - if self.ch_is('}') { - self.state = if self.stack.is_empty() { - ParseBeforeFinish - } else if self.stack.last_is_index() { - ParseArrayComma - } else { - ParseObjectComma - }; - self.bump(); - ObjectEnd - } else if self.eof() { - self.error_event(EOFWhileParsingObject) - } else { - self.error_event(InvalidSyntax) - } - } - - fn parse_value(&mut self) -> JsonEvent { - if self.eof() { - return self.error_event(EOFWhileParsingValue); - } - match self.ch_or_null() { - 'n' => self.parse_ident("ull", NullValue), - 't' => self.parse_ident("rue", BooleanValue(true)), - 'f' => self.parse_ident("alse", BooleanValue(false)), - '0'..='9' | '-' => self.parse_number(), - '"' => match self.parse_str() { - Ok(s) => StringValue(s), - Err(e) => Error(e), - }, - '[' => { - self.bump(); - ArrayStart - } - '{' => { - self.bump(); - ObjectStart - } - _ => self.error_event(InvalidSyntax), - } - } - - fn parse_ident(&mut self, ident: &str, value: JsonEvent) -> JsonEvent { - if ident.chars().all(|c| Some(c) == self.next_char()) { - self.bump(); - value - } else { - Error(SyntaxError(InvalidSyntax, self.line, self.col)) - } - } - - fn error_event(&mut self, reason: ErrorCode) -> JsonEvent { - self.state = ParseFinished; - Error(SyntaxError(reason, self.line, self.col)) - } -} - -/// A Builder consumes a json::Parser to create a generic Json structure. -pub struct Builder { - parser: Parser, - token: Option, -} - -impl> Builder { - /// Creates a JSON Builder. - pub fn new(src: T) -> Builder { - Builder { parser: Parser::new(src), token: None } - } - - // Decode a Json value from a Parser. - pub fn build(&mut self) -> Result { - self.bump(); - let result = self.build_value(); - self.bump(); - match self.token { - None => {} - Some(Error(ref e)) => { - return Err(e.clone()); - } - ref tok => { - panic!("unexpected token {:?}", tok.clone()); - } - } - result - } - - fn bump(&mut self) { - self.token = self.parser.next(); - } - - fn build_value(&mut self) -> Result { - match self.token { - Some(NullValue) => Ok(Json::Null), - Some(I64Value(n)) => Ok(Json::I64(n)), - Some(U64Value(n)) => Ok(Json::U64(n)), - Some(F64Value(n)) => Ok(Json::F64(n)), - Some(BooleanValue(b)) => Ok(Json::Boolean(b)), - Some(StringValue(ref mut s)) => { - let mut temp = string::String::new(); - swap(s, &mut temp); - Ok(Json::String(temp)) - } - Some(Error(ref e)) => Err(e.clone()), - Some(ArrayStart) => self.build_array(), - Some(ObjectStart) => self.build_object(), - Some(ObjectEnd) => self.parser.error(InvalidSyntax), - Some(ArrayEnd) => self.parser.error(InvalidSyntax), - None => self.parser.error(EOFWhileParsingValue), - } - } - - fn build_array(&mut self) -> Result { - self.bump(); - let mut values = Vec::new(); - - loop { - if self.token == Some(ArrayEnd) { - return Ok(Json::Array(values.into_iter().collect())); - } - match self.build_value() { - Ok(v) => values.push(v), - Err(e) => return Err(e), - } - self.bump(); - } - } - - fn build_object(&mut self) -> Result { - self.bump(); - - let mut values = BTreeMap::new(); - - loop { - match self.token { - Some(ObjectEnd) => { - return Ok(Json::Object(values)); - } - Some(Error(ref e)) => { - return Err(e.clone()); - } - None => { - break; - } - _ => {} - } - let key = match self.parser.stack().top() { - Some(StackElement::Key(k)) => k.to_owned(), - _ => { - panic!("invalid state"); - } - }; - match self.build_value() { - Ok(value) => { - values.insert(key, value); - } - Err(e) => { - return Err(e); - } - } - self.bump(); - } - self.parser.error(EOFWhileParsingObject) - } -} - -/// Decodes a json value from an `&mut io::Read` -pub fn from_reader(rdr: &mut dyn Read) -> Result { - let mut contents = Vec::new(); - match rdr.read_to_end(&mut contents) { - Ok(c) => c, - Err(e) => return Err(io_error_to_error(e)), - }; - let s = match str::from_utf8(&contents).ok() { - Some(s) => s, - _ => return Err(SyntaxError(NotUtf8, 0, 0)), - }; - let mut builder = Builder::new(s.chars()); - builder.build() -} - -/// Decodes a json value from a string -pub fn from_str(s: &str) -> Result { - let mut builder = Builder::new(s.chars()); - builder.build() -} - -/// A structure to decode JSON to values in rust. -pub struct Decoder { - stack: Vec, -} - -impl Decoder { - /// Creates a new decoder instance for decoding the specified JSON value. - pub fn new(json: Json) -> Decoder { - Decoder { stack: vec![json] } - } - - fn pop(&mut self) -> Json { - self.stack.pop().unwrap() - } -} - -macro_rules! expect { - ($e:expr, Null) => {{ - match $e { - Json::Null => Ok(()), - other => Err(ExpectedError("Null".to_owned(), other.to_string())), - } - }}; - ($e:expr, $t:ident) => {{ - match $e { - Json::$t(v) => Ok(v), - other => Err(ExpectedError(stringify!($t).to_owned(), other.to_string())), - } - }}; -} - -macro_rules! read_primitive { - ($name:ident, $ty:ty) => { - fn $name(&mut self) -> DecodeResult<$ty> { - match self.pop() { - Json::I64(f) => Ok(f as $ty), - Json::U64(f) => Ok(f as $ty), - Json::F64(f) => Err(ExpectedError("Integer".to_owned(), f.to_string())), - // re: #12967.. a type w/ numeric keys (ie HashMap etc) - // is going to have a string here, as per JSON spec. - Json::String(s) => match s.parse().ok() { - Some(f) => Ok(f), - None => Err(ExpectedError("Number".to_owned(), s)), - }, - value => Err(ExpectedError("Number".to_owned(), value.to_string())), - } - } - } -} - -impl crate::Decoder for Decoder { - type Error = DecoderError; - - fn read_nil(&mut self) -> DecodeResult<()> { - expect!(self.pop(), Null) - } - - read_primitive! { read_usize, usize } - read_primitive! { read_u8, u8 } - read_primitive! { read_u16, u16 } - read_primitive! { read_u32, u32 } - read_primitive! { read_u64, u64 } - read_primitive! { read_u128, u128 } - read_primitive! { read_isize, isize } - read_primitive! { read_i8, i8 } - read_primitive! { read_i16, i16 } - read_primitive! { read_i32, i32 } - read_primitive! { read_i64, i64 } - read_primitive! { read_i128, i128 } - - fn read_f32(&mut self) -> DecodeResult { - self.read_f64().map(|x| x as f32) - } - - fn read_f64(&mut self) -> DecodeResult { - match self.pop() { - Json::I64(f) => Ok(f as f64), - Json::U64(f) => Ok(f as f64), - Json::F64(f) => Ok(f), - Json::String(s) => { - // re: #12967.. a type w/ numeric keys (ie HashMap etc) - // is going to have a string here, as per JSON spec. - match s.parse().ok() { - Some(f) => Ok(f), - None => Err(ExpectedError("Number".to_owned(), s)), - } - } - Json::Null => Ok(f64::NAN), - value => Err(ExpectedError("Number".to_owned(), value.to_string())), - } - } - - fn read_bool(&mut self) -> DecodeResult { - expect!(self.pop(), Boolean) - } - - fn read_char(&mut self) -> DecodeResult { - let s = self.read_str()?; - { - let mut it = s.chars(); - if let (Some(c), None) = (it.next(), it.next()) { - // exactly one character - return Ok(c); - } - } - Err(ExpectedError("single character string".to_owned(), s.to_string())) - } - - fn read_str(&mut self) -> DecodeResult> { - expect!(self.pop(), String).map(Cow::Owned) - } - - fn read_enum(&mut self, _name: &str, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn read_enum_variant(&mut self, names: &[&str], mut f: F) -> DecodeResult - where - F: FnMut(&mut Decoder, usize) -> DecodeResult, - { - let name = match self.pop() { - Json::String(s) => s, - Json::Object(mut o) => { - let n = match o.remove(&"variant".to_owned()) { - Some(Json::String(s)) => s, - Some(val) => return Err(ExpectedError("String".to_owned(), val.to_string())), - None => return Err(MissingFieldError("variant".to_owned())), - }; - match o.remove(&"fields".to_string()) { - Some(Json::Array(l)) => { - self.stack.extend(l.into_iter().rev()); - } - Some(val) => return Err(ExpectedError("Array".to_owned(), val.to_string())), - None => return Err(MissingFieldError("fields".to_owned())), - } - n - } - json => return Err(ExpectedError("String or Object".to_owned(), json.to_string())), - }; - let idx = match names.iter().position(|n| *n == &name[..]) { - Some(idx) => idx, - None => return Err(UnknownVariantError(name)), - }; - f(self, idx) - } - - fn read_enum_variant_arg(&mut self, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn read_enum_struct_variant(&mut self, names: &[&str], f: F) -> DecodeResult - where - F: FnMut(&mut Decoder, usize) -> DecodeResult, - { - self.read_enum_variant(names, f) - } - - fn read_enum_struct_variant_field( - &mut self, - _name: &str, - idx: usize, - f: F, - ) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_enum_variant_arg(idx, f) - } - - fn read_struct(&mut self, _name: &str, _len: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - let value = f(self)?; - self.pop(); - Ok(value) - } - - fn read_struct_field(&mut self, name: &str, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - let mut obj = expect!(self.pop(), Object)?; - - let value = match obj.remove(&name.to_string()) { - None => { - // Add a Null and try to parse it as an Option<_> - // to get None as a default value. - self.stack.push(Json::Null); - match f(self) { - Ok(x) => x, - Err(_) => return Err(MissingFieldError(name.to_string())), - } - } - Some(json) => { - self.stack.push(json); - f(self)? - } - }; - self.stack.push(Json::Object(obj)); - Ok(value) - } - - fn read_tuple(&mut self, tuple_len: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_seq(move |d, len| { - if len == tuple_len { - f(d) - } else { - Err(ExpectedError(format!("Tuple{}", tuple_len), format!("Tuple{}", len))) - } - }) - } - - fn read_tuple_arg(&mut self, idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_seq_elt(idx, f) - } - - fn read_tuple_struct(&mut self, _name: &str, len: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_tuple(len, f) - } - - fn read_tuple_struct_arg(&mut self, idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - self.read_tuple_arg(idx, f) - } - - fn read_option(&mut self, mut f: F) -> DecodeResult - where - F: FnMut(&mut Decoder, bool) -> DecodeResult, - { - match self.pop() { - Json::Null => f(self, false), - value => { - self.stack.push(value); - f(self, true) - } - } - } - - fn read_seq(&mut self, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder, usize) -> DecodeResult, - { - let array = expect!(self.pop(), Array)?; - let len = array.len(); - self.stack.extend(array.into_iter().rev()); - f(self, len) - } - - fn read_seq_elt(&mut self, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn read_map(&mut self, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder, usize) -> DecodeResult, - { - let obj = expect!(self.pop(), Object)?; - let len = obj.len(); - for (key, value) in obj { - self.stack.push(value); - self.stack.push(Json::String(key)); - } - f(self, len) - } - - fn read_map_elt_key(&mut self, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn read_map_elt_val(&mut self, _idx: usize, f: F) -> DecodeResult - where - F: FnOnce(&mut Decoder) -> DecodeResult, - { - f(self) - } - - fn error(&mut self, err: &str) -> DecoderError { - ApplicationError(err.to_string()) - } -} - -/// A trait for converting values to JSON -pub trait ToJson { - /// Converts the value of `self` to an instance of JSON - fn to_json(&self) -> Json; -} - -macro_rules! to_json_impl_i64 { - ($($t:ty), +) => ( - $(impl ToJson for $t { - fn to_json(&self) -> Json { - Json::I64(*self as i64) - } - })+ - ) -} - -to_json_impl_i64! { isize, i8, i16, i32, i64 } - -macro_rules! to_json_impl_u64 { - ($($t:ty), +) => ( - $(impl ToJson for $t { - fn to_json(&self) -> Json { - Json::U64(*self as u64) - } - })+ - ) -} - -to_json_impl_u64! { usize, u8, u16, u32, u64 } - -impl ToJson for Json { - fn to_json(&self) -> Json { - self.clone() - } -} - -impl ToJson for f32 { - fn to_json(&self) -> Json { - f64::from(*self).to_json() - } -} - -impl ToJson for f64 { - fn to_json(&self) -> Json { - match self.classify() { - Fp::Nan | Fp::Infinite => Json::Null, - _ => Json::F64(*self), - } - } -} - -impl ToJson for () { - fn to_json(&self) -> Json { - Json::Null - } -} - -impl ToJson for bool { - fn to_json(&self) -> Json { - Json::Boolean(*self) - } -} - -impl ToJson for str { - fn to_json(&self) -> Json { - Json::String(self.to_string()) - } -} - -impl ToJson for string::String { - fn to_json(&self) -> Json { - Json::String((*self).clone()) - } -} - -macro_rules! tuple_impl { - // use variables to indicate the arity of the tuple - ($($tyvar:ident),* ) => { - // the trailing commas are for the 1 tuple - impl< - $( $tyvar : ToJson ),* - > ToJson for ( $( $tyvar ),* , ) { - - #[inline] - #[allow(non_snake_case)] - fn to_json(&self) -> Json { - match *self { - ($(ref $tyvar),*,) => Json::Array(vec![$($tyvar.to_json()),*]) - } - } - } - } -} - -tuple_impl! {A} -tuple_impl! {A, B} -tuple_impl! {A, B, C} -tuple_impl! {A, B, C, D} -tuple_impl! {A, B, C, D, E} -tuple_impl! {A, B, C, D, E, F} -tuple_impl! {A, B, C, D, E, F, G} -tuple_impl! {A, B, C, D, E, F, G, H} -tuple_impl! {A, B, C, D, E, F, G, H, I} -tuple_impl! {A, B, C, D, E, F, G, H, I, J} -tuple_impl! {A, B, C, D, E, F, G, H, I, J, K} -tuple_impl! {A, B, C, D, E, F, G, H, I, J, K, L} - -impl ToJson for [A] { - fn to_json(&self) -> Json { - Json::Array(self.iter().map(|elt| elt.to_json()).collect()) - } -} - -impl ToJson for Vec { - fn to_json(&self) -> Json { - Json::Array(self.iter().map(|elt| elt.to_json()).collect()) - } -} - -impl ToJson for BTreeMap { - fn to_json(&self) -> Json { - let mut d = BTreeMap::new(); - for (key, value) in self { - d.insert((*key).clone(), value.to_json()); - } - Json::Object(d) - } -} - -impl ToJson for HashMap { - fn to_json(&self) -> Json { - let mut d = BTreeMap::new(); - for (key, value) in self { - d.insert((*key).clone(), value.to_json()); - } - Json::Object(d) - } -} - -impl ToJson for Option { - fn to_json(&self) -> Json { - match *self { - None => Json::Null, - Some(ref value) => value.to_json(), - } - } -} - -struct FormatShim<'a, 'b> { - inner: &'a mut fmt::Formatter<'b>, -} - -impl<'a, 'b> fmt::Write for FormatShim<'a, 'b> { - fn write_str(&mut self, s: &str) -> fmt::Result { - match self.inner.write_str(s) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl fmt::Display for Json { - /// Encodes a json value into a string - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut shim = FormatShim { inner: f }; - let mut encoder = Encoder::new(&mut shim); - match self.encode(&mut encoder) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl<'a> fmt::Display for PrettyJson<'a> { - /// Encodes a json value into a string - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut shim = FormatShim { inner: f }; - let mut encoder = PrettyEncoder::new(&mut shim); - match self.inner.encode(&mut encoder) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl<'a, T: Encodable> fmt::Display for AsJson<'a, T> { - /// Encodes a json value into a string - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut shim = FormatShim { inner: f }; - let mut encoder = Encoder::new(&mut shim); - match self.inner.encode(&mut encoder) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl<'a, T> AsPrettyJson<'a, T> { - /// Sets the indentation level for the emitted JSON - pub fn indent(mut self, indent: usize) -> AsPrettyJson<'a, T> { - self.indent = Some(indent); - self - } -} - -impl<'a, T: Encodable> fmt::Display for AsPrettyJson<'a, T> { - /// Encodes a json value into a string - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut shim = FormatShim { inner: f }; - let mut encoder = PrettyEncoder::new(&mut shim); - if let Some(n) = self.indent { - encoder.set_indent(n); - } - match self.inner.encode(&mut encoder) { - Ok(_) => Ok(()), - Err(_) => Err(fmt::Error), - } - } -} - -impl FromStr for Json { - type Err = BuilderError; - fn from_str(s: &str) -> Result { - from_str(s) - } -} - -#[cfg(test)] -mod tests; diff --git a/src/libserialize/lib.rs b/src/libserialize/lib.rs deleted file mode 100644 index b990e71bef0dd..0000000000000 --- a/src/libserialize/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Support code for encoding and decoding types. - -/* -Core encoding and decoding interfaces. -*/ - -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/", - test(attr(allow(unused_variables), deny(warnings))) -)] -#![feature(box_syntax)] -#![feature(specialization)] -#![feature(never_type)] -#![feature(nll)] -#![feature(associated_type_bounds)] -#![cfg_attr(test, feature(test))] -#![allow(rustc::internal)] - -pub use self::serialize::{Decodable, Decoder, Encodable, Encoder}; - -pub use self::serialize::{SpecializationError, SpecializedDecoder, SpecializedEncoder}; -pub use self::serialize::{UseSpecializedDecodable, UseSpecializedEncodable}; - -mod collection_impls; -mod serialize; - -pub mod hex; -pub mod json; - -pub mod leb128; -pub mod opaque; diff --git a/src/libserialize/tests/json.rs b/src/libserialize/tests/json.rs deleted file mode 100644 index c16426b5919c9..0000000000000 --- a/src/libserialize/tests/json.rs +++ /dev/null @@ -1,1271 +0,0 @@ -#![allow(rustc::internal)] - -extern crate serialize as rustc_serialize; - -use json::DecoderError::*; -use json::ErrorCode::*; -use json::Json::*; -use json::JsonEvent::*; -use json::ParserError::*; -use json::{ - from_str, DecodeResult, Decoder, DecoderError, Encoder, EncoderError, Json, JsonEvent, Parser, - StackElement, -}; -use rustc_serialize::json; -use rustc_serialize::{Decodable, Encodable}; - -use std::collections::BTreeMap; -use std::io::prelude::*; -use std::string; -use std::{f32, f64, i64, u64}; -use Animal::*; - -#[derive(RustcDecodable, Eq, PartialEq, Debug)] -struct OptionData { - opt: Option, -} - -#[test] -fn test_decode_option_none() { - let s = "{}"; - let obj: OptionData = json::decode(s).unwrap(); - assert_eq!(obj, OptionData { opt: None }); -} - -#[test] -fn test_decode_option_some() { - let s = "{ \"opt\": 10 }"; - let obj: OptionData = json::decode(s).unwrap(); - assert_eq!(obj, OptionData { opt: Some(10) }); -} - -#[test] -fn test_decode_option_malformed() { - check_err::( - "{ \"opt\": [] }", - ExpectedError("Number".to_string(), "[]".to_string()), - ); - check_err::( - "{ \"opt\": false }", - ExpectedError("Number".to_string(), "false".to_string()), - ); -} - -#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] -enum Animal { - Dog, - Frog(string::String, isize), -} - -#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] -struct Inner { - a: (), - b: usize, - c: Vec, -} - -#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)] -struct Outer { - inner: Vec, -} - -fn mk_object(items: &[(string::String, Json)]) -> Json { - let mut d = BTreeMap::new(); - - for item in items { - match *item { - (ref key, ref value) => { - d.insert((*key).clone(), (*value).clone()); - } - } - } - - Object(d) -} - -#[test] -fn test_from_str_trait() { - let s = "null"; - assert!(s.parse::().unwrap() == s.parse().unwrap()); -} - -#[test] -fn test_write_null() { - assert_eq!(Null.to_string(), "null"); - assert_eq!(Null.pretty().to_string(), "null"); -} - -#[test] -fn test_write_i64() { - assert_eq!(U64(0).to_string(), "0"); - assert_eq!(U64(0).pretty().to_string(), "0"); - - assert_eq!(U64(1234).to_string(), "1234"); - assert_eq!(U64(1234).pretty().to_string(), "1234"); - - assert_eq!(I64(-5678).to_string(), "-5678"); - assert_eq!(I64(-5678).pretty().to_string(), "-5678"); - - assert_eq!(U64(7650007200025252000).to_string(), "7650007200025252000"); - assert_eq!(U64(7650007200025252000).pretty().to_string(), "7650007200025252000"); -} - -#[test] -fn test_write_f64() { - assert_eq!(F64(3.0).to_string(), "3.0"); - assert_eq!(F64(3.0).pretty().to_string(), "3.0"); - - assert_eq!(F64(3.1).to_string(), "3.1"); - assert_eq!(F64(3.1).pretty().to_string(), "3.1"); - - assert_eq!(F64(-1.5).to_string(), "-1.5"); - assert_eq!(F64(-1.5).pretty().to_string(), "-1.5"); - - assert_eq!(F64(0.5).to_string(), "0.5"); - assert_eq!(F64(0.5).pretty().to_string(), "0.5"); - - assert_eq!(F64(f64::NAN).to_string(), "null"); - assert_eq!(F64(f64::NAN).pretty().to_string(), "null"); - - assert_eq!(F64(f64::INFINITY).to_string(), "null"); - assert_eq!(F64(f64::INFINITY).pretty().to_string(), "null"); - - assert_eq!(F64(f64::NEG_INFINITY).to_string(), "null"); - assert_eq!(F64(f64::NEG_INFINITY).pretty().to_string(), "null"); -} - -#[test] -fn test_write_str() { - assert_eq!(String("".to_string()).to_string(), "\"\""); - assert_eq!(String("".to_string()).pretty().to_string(), "\"\""); - - assert_eq!(String("homura".to_string()).to_string(), "\"homura\""); - assert_eq!(String("madoka".to_string()).pretty().to_string(), "\"madoka\""); -} - -#[test] -fn test_write_bool() { - assert_eq!(Boolean(true).to_string(), "true"); - assert_eq!(Boolean(true).pretty().to_string(), "true"); - - assert_eq!(Boolean(false).to_string(), "false"); - assert_eq!(Boolean(false).pretty().to_string(), "false"); -} - -#[test] -fn test_write_array() { - assert_eq!(Array(vec![]).to_string(), "[]"); - assert_eq!(Array(vec![]).pretty().to_string(), "[]"); - - assert_eq!(Array(vec![Boolean(true)]).to_string(), "[true]"); - assert_eq!( - Array(vec![Boolean(true)]).pretty().to_string(), - "\ - [\n \ - true\n\ - ]" - ); - - let long_test_array = - Array(vec![Boolean(false), Null, Array(vec![String("foo\nbar".to_string()), F64(3.5)])]); - - assert_eq!(long_test_array.to_string(), "[false,null,[\"foo\\nbar\",3.5]]"); - assert_eq!( - long_test_array.pretty().to_string(), - "\ - [\n \ - false,\n \ - null,\n \ - [\n \ - \"foo\\nbar\",\n \ - 3.5\n \ - ]\n\ - ]" - ); -} - -#[test] -fn test_write_object() { - assert_eq!(mk_object(&[]).to_string(), "{}"); - assert_eq!(mk_object(&[]).pretty().to_string(), "{}"); - - assert_eq!(mk_object(&[("a".to_string(), Boolean(true))]).to_string(), "{\"a\":true}"); - assert_eq!( - mk_object(&[("a".to_string(), Boolean(true))]).pretty().to_string(), - "\ - {\n \ - \"a\": true\n\ - }" - ); - - let complex_obj = mk_object(&[( - "b".to_string(), - Array(vec![ - mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]), - mk_object(&[("d".to_string(), String("".to_string()))]), - ]), - )]); - - assert_eq!( - complex_obj.to_string(), - "{\ - \"b\":[\ - {\"c\":\"\\f\\r\"},\ - {\"d\":\"\"}\ - ]\ - }" - ); - assert_eq!( - complex_obj.pretty().to_string(), - "\ - {\n \ - \"b\": [\n \ - {\n \ - \"c\": \"\\f\\r\"\n \ - },\n \ - {\n \ - \"d\": \"\"\n \ - }\n \ - ]\n\ - }" - ); - - let a = mk_object(&[ - ("a".to_string(), Boolean(true)), - ( - "b".to_string(), - Array(vec![ - mk_object(&[("c".to_string(), String("\x0c\r".to_string()))]), - mk_object(&[("d".to_string(), String("".to_string()))]), - ]), - ), - ]); - - // We can't compare the strings directly because the object fields be - // printed in a different order. - assert_eq!(a.clone(), a.to_string().parse().unwrap()); - assert_eq!(a.clone(), a.pretty().to_string().parse().unwrap()); -} - -#[test] -fn test_write_enum() { - let animal = Dog; - assert_eq!(json::as_json(&animal).to_string(), "\"Dog\""); - assert_eq!(json::as_pretty_json(&animal).to_string(), "\"Dog\""); - - let animal = Frog("Henry".to_string(), 349); - assert_eq!( - json::as_json(&animal).to_string(), - "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}" - ); - assert_eq!( - json::as_pretty_json(&animal).to_string(), - "{\n \ - \"variant\": \"Frog\",\n \ - \"fields\": [\n \ - \"Henry\",\n \ - 349\n \ - ]\n\ - }" - ); -} - -macro_rules! check_encoder_for_simple { - ($value:expr, $expected:expr) => {{ - let s = json::as_json(&$value).to_string(); - assert_eq!(s, $expected); - - let s = json::as_pretty_json(&$value).to_string(); - assert_eq!(s, $expected); - }}; -} - -#[test] -fn test_write_some() { - check_encoder_for_simple!(Some("jodhpurs".to_string()), "\"jodhpurs\""); -} - -#[test] -fn test_write_none() { - check_encoder_for_simple!(None::, "null"); -} - -#[test] -fn test_write_char() { - check_encoder_for_simple!('a', "\"a\""); - check_encoder_for_simple!('\t', "\"\\t\""); - check_encoder_for_simple!('\u{0000}', "\"\\u0000\""); - check_encoder_for_simple!('\u{001b}', "\"\\u001b\""); - check_encoder_for_simple!('\u{007f}', "\"\\u007f\""); - check_encoder_for_simple!('\u{00a0}', "\"\u{00a0}\""); - check_encoder_for_simple!('\u{abcd}', "\"\u{abcd}\""); - check_encoder_for_simple!('\u{10ffff}', "\"\u{10ffff}\""); -} - -#[test] -fn test_trailing_characters() { - assert_eq!(from_str("nulla"), Err(SyntaxError(TrailingCharacters, 1, 5))); - assert_eq!(from_str("truea"), Err(SyntaxError(TrailingCharacters, 1, 5))); - assert_eq!(from_str("falsea"), Err(SyntaxError(TrailingCharacters, 1, 6))); - assert_eq!(from_str("1a"), Err(SyntaxError(TrailingCharacters, 1, 2))); - assert_eq!(from_str("[]a"), Err(SyntaxError(TrailingCharacters, 1, 3))); - assert_eq!(from_str("{}a"), Err(SyntaxError(TrailingCharacters, 1, 3))); -} - -#[test] -fn test_read_identifiers() { - assert_eq!(from_str("n"), Err(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(from_str("nul"), Err(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(from_str("t"), Err(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(from_str("truz"), Err(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(from_str("f"), Err(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(from_str("faz"), Err(SyntaxError(InvalidSyntax, 1, 3))); - - assert_eq!(from_str("null"), Ok(Null)); - assert_eq!(from_str("true"), Ok(Boolean(true))); - assert_eq!(from_str("false"), Ok(Boolean(false))); - assert_eq!(from_str(" null "), Ok(Null)); - assert_eq!(from_str(" true "), Ok(Boolean(true))); - assert_eq!(from_str(" false "), Ok(Boolean(false))); -} - -#[test] -fn test_decode_identifiers() { - let v: () = json::decode("null").unwrap(); - assert_eq!(v, ()); - - let v: bool = json::decode("true").unwrap(); - assert_eq!(v, true); - - let v: bool = json::decode("false").unwrap(); - assert_eq!(v, false); -} - -#[test] -fn test_read_number() { - assert_eq!(from_str("+"), Err(SyntaxError(InvalidSyntax, 1, 1))); - assert_eq!(from_str("."), Err(SyntaxError(InvalidSyntax, 1, 1))); - assert_eq!(from_str("NaN"), Err(SyntaxError(InvalidSyntax, 1, 1))); - assert_eq!(from_str("-"), Err(SyntaxError(InvalidNumber, 1, 2))); - assert_eq!(from_str("00"), Err(SyntaxError(InvalidNumber, 1, 2))); - assert_eq!(from_str("1."), Err(SyntaxError(InvalidNumber, 1, 3))); - assert_eq!(from_str("1e"), Err(SyntaxError(InvalidNumber, 1, 3))); - assert_eq!(from_str("1e+"), Err(SyntaxError(InvalidNumber, 1, 4))); - - assert_eq!(from_str("18446744073709551616"), Err(SyntaxError(InvalidNumber, 1, 20))); - assert_eq!(from_str("-9223372036854775809"), Err(SyntaxError(InvalidNumber, 1, 21))); - - assert_eq!(from_str("3"), Ok(U64(3))); - assert_eq!(from_str("3.1"), Ok(F64(3.1))); - assert_eq!(from_str("-1.2"), Ok(F64(-1.2))); - assert_eq!(from_str("0.4"), Ok(F64(0.4))); - assert_eq!(from_str("0.4e5"), Ok(F64(0.4e5))); - assert_eq!(from_str("0.4e+15"), Ok(F64(0.4e15))); - assert_eq!(from_str("0.4e-01"), Ok(F64(0.4e-01))); - assert_eq!(from_str(" 3 "), Ok(U64(3))); - - assert_eq!(from_str("-9223372036854775808"), Ok(I64(i64::MIN))); - assert_eq!(from_str("9223372036854775807"), Ok(U64(i64::MAX as u64))); - assert_eq!(from_str("18446744073709551615"), Ok(U64(u64::MAX))); -} - -#[test] -fn test_decode_numbers() { - let v: f64 = json::decode("3").unwrap(); - assert_eq!(v, 3.0); - - let v: f64 = json::decode("3.1").unwrap(); - assert_eq!(v, 3.1); - - let v: f64 = json::decode("-1.2").unwrap(); - assert_eq!(v, -1.2); - - let v: f64 = json::decode("0.4").unwrap(); - assert_eq!(v, 0.4); - - let v: f64 = json::decode("0.4e5").unwrap(); - assert_eq!(v, 0.4e5); - - let v: f64 = json::decode("0.4e15").unwrap(); - assert_eq!(v, 0.4e15); - - let v: f64 = json::decode("0.4e-01").unwrap(); - assert_eq!(v, 0.4e-01); - - let v: u64 = json::decode("0").unwrap(); - assert_eq!(v, 0); - - let v: u64 = json::decode("18446744073709551615").unwrap(); - assert_eq!(v, u64::MAX); - - let v: i64 = json::decode("-9223372036854775808").unwrap(); - assert_eq!(v, i64::MIN); - - let v: i64 = json::decode("9223372036854775807").unwrap(); - assert_eq!(v, i64::MAX); - - let res: DecodeResult = json::decode("765.25"); - assert_eq!(res, Err(ExpectedError("Integer".to_string(), "765.25".to_string()))); -} - -#[test] -fn test_read_str() { - assert_eq!(from_str("\""), Err(SyntaxError(EOFWhileParsingString, 1, 2))); - assert_eq!(from_str("\"lol"), Err(SyntaxError(EOFWhileParsingString, 1, 5))); - - assert_eq!(from_str("\"\""), Ok(String("".to_string()))); - assert_eq!(from_str("\"foo\""), Ok(String("foo".to_string()))); - assert_eq!(from_str("\"\\\"\""), Ok(String("\"".to_string()))); - assert_eq!(from_str("\"\\b\""), Ok(String("\x08".to_string()))); - assert_eq!(from_str("\"\\n\""), Ok(String("\n".to_string()))); - assert_eq!(from_str("\"\\r\""), Ok(String("\r".to_string()))); - assert_eq!(from_str("\"\\t\""), Ok(String("\t".to_string()))); - assert_eq!(from_str(" \"foo\" "), Ok(String("foo".to_string()))); - assert_eq!(from_str("\"\\u12ab\""), Ok(String("\u{12ab}".to_string()))); - assert_eq!(from_str("\"\\uAB12\""), Ok(String("\u{AB12}".to_string()))); -} - -#[test] -fn test_decode_str() { - let s = [ - ("\"\"", ""), - ("\"foo\"", "foo"), - ("\"\\\"\"", "\""), - ("\"\\b\"", "\x08"), - ("\"\\n\"", "\n"), - ("\"\\r\"", "\r"), - ("\"\\t\"", "\t"), - ("\"\\u12ab\"", "\u{12ab}"), - ("\"\\uAB12\"", "\u{AB12}"), - ]; - - for &(i, o) in &s { - let v: string::String = json::decode(i).unwrap(); - assert_eq!(v, o); - } -} - -#[test] -fn test_read_array() { - assert_eq!(from_str("["), Err(SyntaxError(EOFWhileParsingValue, 1, 2))); - assert_eq!(from_str("[1"), Err(SyntaxError(EOFWhileParsingArray, 1, 3))); - assert_eq!(from_str("[1,"), Err(SyntaxError(EOFWhileParsingValue, 1, 4))); - assert_eq!(from_str("[1,]"), Err(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(from_str("[6 7]"), Err(SyntaxError(InvalidSyntax, 1, 4))); - - assert_eq!(from_str("[]"), Ok(Array(vec![]))); - assert_eq!(from_str("[ ]"), Ok(Array(vec![]))); - assert_eq!(from_str("[true]"), Ok(Array(vec![Boolean(true)]))); - assert_eq!(from_str("[ false ]"), Ok(Array(vec![Boolean(false)]))); - assert_eq!(from_str("[null]"), Ok(Array(vec![Null]))); - assert_eq!(from_str("[3, 1]"), Ok(Array(vec![U64(3), U64(1)]))); - assert_eq!(from_str("\n[3, 2]\n"), Ok(Array(vec![U64(3), U64(2)]))); - assert_eq!(from_str("[2, [4, 1]]"), Ok(Array(vec![U64(2), Array(vec![U64(4), U64(1)])]))); -} - -#[test] -fn test_decode_array() { - let v: Vec<()> = json::decode("[]").unwrap(); - assert_eq!(v, []); - - let v: Vec<()> = json::decode("[null]").unwrap(); - assert_eq!(v, [()]); - - let v: Vec = json::decode("[true]").unwrap(); - assert_eq!(v, [true]); - - let v: Vec = json::decode("[3, 1]").unwrap(); - assert_eq!(v, [3, 1]); - - let v: Vec> = json::decode("[[3], [1, 2]]").unwrap(); - assert_eq!(v, [vec![3], vec![1, 2]]); -} - -#[test] -fn test_decode_tuple() { - let t: (usize, usize, usize) = json::decode("[1, 2, 3]").unwrap(); - assert_eq!(t, (1, 2, 3)); - - let t: (usize, string::String) = json::decode("[1, \"two\"]").unwrap(); - assert_eq!(t, (1, "two".to_string())); -} - -#[test] -fn test_decode_tuple_malformed_types() { - assert!(json::decode::<(usize, string::String)>("[1, 2]").is_err()); -} - -#[test] -fn test_decode_tuple_malformed_length() { - assert!(json::decode::<(usize, usize)>("[1, 2, 3]").is_err()); -} - -#[test] -fn test_read_object() { - assert_eq!(from_str("{"), Err(SyntaxError(EOFWhileParsingObject, 1, 2))); - assert_eq!(from_str("{ "), Err(SyntaxError(EOFWhileParsingObject, 1, 3))); - assert_eq!(from_str("{1"), Err(SyntaxError(KeyMustBeAString, 1, 2))); - assert_eq!(from_str("{ \"a\""), Err(SyntaxError(EOFWhileParsingObject, 1, 6))); - assert_eq!(from_str("{\"a\""), Err(SyntaxError(EOFWhileParsingObject, 1, 5))); - assert_eq!(from_str("{\"a\" "), Err(SyntaxError(EOFWhileParsingObject, 1, 6))); - - assert_eq!(from_str("{\"a\" 1"), Err(SyntaxError(ExpectedColon, 1, 6))); - assert_eq!(from_str("{\"a\":"), Err(SyntaxError(EOFWhileParsingValue, 1, 6))); - assert_eq!(from_str("{\"a\":1"), Err(SyntaxError(EOFWhileParsingObject, 1, 7))); - assert_eq!(from_str("{\"a\":1 1"), Err(SyntaxError(InvalidSyntax, 1, 8))); - assert_eq!(from_str("{\"a\":1,"), Err(SyntaxError(EOFWhileParsingObject, 1, 8))); - - assert_eq!(from_str("{}").unwrap(), mk_object(&[])); - assert_eq!(from_str("{\"a\": 3}").unwrap(), mk_object(&[("a".to_string(), U64(3))])); - - assert_eq!( - from_str("{ \"a\": null, \"b\" : true }").unwrap(), - mk_object(&[("a".to_string(), Null), ("b".to_string(), Boolean(true))]) - ); - assert_eq!( - from_str("\n{ \"a\": null, \"b\" : true }\n").unwrap(), - mk_object(&[("a".to_string(), Null), ("b".to_string(), Boolean(true))]) - ); - assert_eq!( - from_str("{\"a\" : 1.0 ,\"b\": [ true ]}").unwrap(), - mk_object(&[("a".to_string(), F64(1.0)), ("b".to_string(), Array(vec![Boolean(true)]))]) - ); - assert_eq!( - from_str( - "{\ - \"a\": 1.0, \ - \"b\": [\ - true,\ - \"foo\\nbar\", \ - { \"c\": {\"d\": null} } \ - ]\ - }" - ) - .unwrap(), - mk_object(&[ - ("a".to_string(), F64(1.0)), - ( - "b".to_string(), - Array(vec![ - Boolean(true), - String("foo\nbar".to_string()), - mk_object(&[("c".to_string(), mk_object(&[("d".to_string(), Null)]))]) - ]) - ) - ]) - ); -} - -#[test] -fn test_decode_struct() { - let s = "{ - \"inner\": [ - { \"a\": null, \"b\": 2, \"c\": [\"abc\", \"xyz\"] } - ] - }"; - - let v: Outer = json::decode(s).unwrap(); - assert_eq!( - v, - Outer { inner: vec![Inner { a: (), b: 2, c: vec!["abc".to_string(), "xyz".to_string()] }] } - ); -} - -#[derive(RustcDecodable)] -struct FloatStruct { - f: f64, - a: Vec, -} -#[test] -fn test_decode_struct_with_nan() { - let s = "{\"f\":null,\"a\":[null,123]}"; - let obj: FloatStruct = json::decode(s).unwrap(); - assert!(obj.f.is_nan()); - assert!(obj.a[0].is_nan()); - assert_eq!(obj.a[1], 123f64); -} - -#[test] -fn test_decode_option() { - let value: Option = json::decode("null").unwrap(); - assert_eq!(value, None); - - let value: Option = json::decode("\"jodhpurs\"").unwrap(); - assert_eq!(value, Some("jodhpurs".to_string())); -} - -#[test] -fn test_decode_enum() { - let value: Animal = json::decode("\"Dog\"").unwrap(); - assert_eq!(value, Dog); - - let s = "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}"; - let value: Animal = json::decode(s).unwrap(); - assert_eq!(value, Frog("Henry".to_string(), 349)); -} - -#[test] -fn test_decode_map() { - let s = "{\"a\": \"Dog\", \"b\": {\"variant\":\"Frog\",\ - \"fields\":[\"Henry\", 349]}}"; - let mut map: BTreeMap = json::decode(s).unwrap(); - - assert_eq!(map.remove(&"a".to_string()), Some(Dog)); - assert_eq!(map.remove(&"b".to_string()), Some(Frog("Henry".to_string(), 349))); -} - -#[test] -fn test_multiline_errors() { - assert_eq!(from_str("{\n \"foo\":\n \"bar\""), Err(SyntaxError(EOFWhileParsingObject, 3, 8))); -} - -#[derive(RustcDecodable)] -#[allow(dead_code)] -struct DecodeStruct { - x: f64, - y: bool, - z: string::String, - w: Vec, -} -#[derive(RustcDecodable)] -enum DecodeEnum { - A(f64), - B(string::String), -} -fn check_err(to_parse: &'static str, expected: DecoderError) { - let res: DecodeResult = match from_str(to_parse) { - Err(e) => Err(ParseError(e)), - Ok(json) => Decodable::decode(&mut Decoder::new(json)), - }; - match res { - Ok(_) => panic!("`{:?}` parsed & decoded ok, expecting error `{:?}`", to_parse, expected), - Err(ParseError(e)) => panic!("`{:?}` is not valid json: {:?}", to_parse, e), - Err(e) => { - assert_eq!(e, expected); - } - } -} -#[test] -fn test_decode_errors_struct() { - check_err::("[]", ExpectedError("Object".to_string(), "[]".to_string())); - check_err::( - "{\"x\": true, \"y\": true, \"z\": \"\", \"w\": []}", - ExpectedError("Number".to_string(), "true".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": [], \"z\": \"\", \"w\": []}", - ExpectedError("Boolean".to_string(), "[]".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": true, \"z\": {}, \"w\": []}", - ExpectedError("String".to_string(), "{}".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": true, \"z\": \"\", \"w\": null}", - ExpectedError("Array".to_string(), "null".to_string()), - ); - check_err::( - "{\"x\": 1, \"y\": true, \"z\": \"\"}", - MissingFieldError("w".to_string()), - ); -} -#[test] -fn test_decode_errors_enum() { - check_err::("{}", MissingFieldError("variant".to_string())); - check_err::( - "{\"variant\": 1}", - ExpectedError("String".to_string(), "1".to_string()), - ); - check_err::("{\"variant\": \"A\"}", MissingFieldError("fields".to_string())); - check_err::( - "{\"variant\": \"A\", \"fields\": null}", - ExpectedError("Array".to_string(), "null".to_string()), - ); - check_err::( - "{\"variant\": \"C\", \"fields\": []}", - UnknownVariantError("C".to_string()), - ); -} - -#[test] -fn test_find() { - let json_value = from_str("{\"dog\" : \"cat\"}").unwrap(); - let found_str = json_value.find("dog"); - assert!(found_str.unwrap().as_string().unwrap() == "cat"); -} - -#[test] -fn test_find_path() { - let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); - let found_str = json_value.find_path(&["dog", "cat", "mouse"]); - assert!(found_str.unwrap().as_string().unwrap() == "cheese"); -} - -#[test] -fn test_search() { - let json_value = from_str("{\"dog\":{\"cat\": {\"mouse\" : \"cheese\"}}}").unwrap(); - let found_str = json_value.search("mouse").and_then(|j| j.as_string()); - assert!(found_str.unwrap() == "cheese"); -} - -#[test] -fn test_index() { - let json_value = from_str("{\"animals\":[\"dog\",\"cat\",\"mouse\"]}").unwrap(); - let ref array = json_value["animals"]; - assert_eq!(array[0].as_string().unwrap(), "dog"); - assert_eq!(array[1].as_string().unwrap(), "cat"); - assert_eq!(array[2].as_string().unwrap(), "mouse"); -} - -#[test] -fn test_is_object() { - let json_value = from_str("{}").unwrap(); - assert!(json_value.is_object()); -} - -#[test] -fn test_as_object() { - let json_value = from_str("{}").unwrap(); - let json_object = json_value.as_object(); - assert!(json_object.is_some()); -} - -#[test] -fn test_is_array() { - let json_value = from_str("[1, 2, 3]").unwrap(); - assert!(json_value.is_array()); -} - -#[test] -fn test_as_array() { - let json_value = from_str("[1, 2, 3]").unwrap(); - let json_array = json_value.as_array(); - let expected_length = 3; - assert!(json_array.is_some() && json_array.unwrap().len() == expected_length); -} - -#[test] -fn test_is_string() { - let json_value = from_str("\"dog\"").unwrap(); - assert!(json_value.is_string()); -} - -#[test] -fn test_as_string() { - let json_value = from_str("\"dog\"").unwrap(); - let json_str = json_value.as_string(); - let expected_str = "dog"; - assert_eq!(json_str, Some(expected_str)); -} - -#[test] -fn test_is_number() { - let json_value = from_str("12").unwrap(); - assert!(json_value.is_number()); -} - -#[test] -fn test_is_i64() { - let json_value = from_str("-12").unwrap(); - assert!(json_value.is_i64()); - - let json_value = from_str("12").unwrap(); - assert!(!json_value.is_i64()); - - let json_value = from_str("12.0").unwrap(); - assert!(!json_value.is_i64()); -} - -#[test] -fn test_is_u64() { - let json_value = from_str("12").unwrap(); - assert!(json_value.is_u64()); - - let json_value = from_str("-12").unwrap(); - assert!(!json_value.is_u64()); - - let json_value = from_str("12.0").unwrap(); - assert!(!json_value.is_u64()); -} - -#[test] -fn test_is_f64() { - let json_value = from_str("12").unwrap(); - assert!(!json_value.is_f64()); - - let json_value = from_str("-12").unwrap(); - assert!(!json_value.is_f64()); - - let json_value = from_str("12.0").unwrap(); - assert!(json_value.is_f64()); - - let json_value = from_str("-12.0").unwrap(); - assert!(json_value.is_f64()); -} - -#[test] -fn test_as_i64() { - let json_value = from_str("-12").unwrap(); - let json_num = json_value.as_i64(); - assert_eq!(json_num, Some(-12)); -} - -#[test] -fn test_as_u64() { - let json_value = from_str("12").unwrap(); - let json_num = json_value.as_u64(); - assert_eq!(json_num, Some(12)); -} - -#[test] -fn test_as_f64() { - let json_value = from_str("12.0").unwrap(); - let json_num = json_value.as_f64(); - assert_eq!(json_num, Some(12f64)); -} - -#[test] -fn test_is_boolean() { - let json_value = from_str("false").unwrap(); - assert!(json_value.is_boolean()); -} - -#[test] -fn test_as_boolean() { - let json_value = from_str("false").unwrap(); - let json_bool = json_value.as_boolean(); - let expected_bool = false; - assert!(json_bool.is_some() && json_bool.unwrap() == expected_bool); -} - -#[test] -fn test_is_null() { - let json_value = from_str("null").unwrap(); - assert!(json_value.is_null()); -} - -#[test] -fn test_as_null() { - let json_value = from_str("null").unwrap(); - let json_null = json_value.as_null(); - let expected_null = (); - assert!(json_null.is_some() && json_null.unwrap() == expected_null); -} - -#[test] -fn test_encode_hashmap_with_numeric_key() { - use std::collections::HashMap; - use std::str::from_utf8; - let mut hm: HashMap = HashMap::new(); - hm.insert(1, true); - let mut mem_buf = Vec::new(); - write!(&mut mem_buf, "{}", json::as_pretty_json(&hm)).unwrap(); - let json_str = from_utf8(&mem_buf[..]).unwrap(); - match from_str(json_str) { - Err(_) => panic!("Unable to parse json_str: {:?}", json_str), - _ => {} // it parsed and we are good to go - } -} - -#[test] -fn test_prettyencode_hashmap_with_numeric_key() { - use std::collections::HashMap; - use std::str::from_utf8; - let mut hm: HashMap = HashMap::new(); - hm.insert(1, true); - let mut mem_buf = Vec::new(); - write!(&mut mem_buf, "{}", json::as_pretty_json(&hm)).unwrap(); - let json_str = from_utf8(&mem_buf[..]).unwrap(); - match from_str(json_str) { - Err(_) => panic!("Unable to parse json_str: {:?}", json_str), - _ => {} // it parsed and we are good to go - } -} - -#[test] -fn test_prettyencoder_indent_level_param() { - use std::collections::BTreeMap; - use std::str::from_utf8; - - let mut tree = BTreeMap::new(); - - tree.insert("hello".to_string(), String("guten tag".to_string())); - tree.insert("goodbye".to_string(), String("sayonara".to_string())); - - let json = Array( - // The following layout below should look a lot like - // the pretty-printed JSON (indent * x) - vec![ - // 0x - String("greetings".to_string()), // 1x - Object(tree), // 1x + 2x + 2x + 1x - ], // 0x - // End JSON array (7 lines) - ); - - // Helper function for counting indents - fn indents(source: &str) -> usize { - let trimmed = source.trim_start_matches(' '); - source.len() - trimmed.len() - } - - // Test up to 4 spaces of indents (more?) - for i in 0..4 { - let mut writer = Vec::new(); - write!(&mut writer, "{}", json::as_pretty_json(&json).indent(i)).unwrap(); - - let printed = from_utf8(&writer[..]).unwrap(); - - // Check for indents at each line - let lines: Vec<&str> = printed.lines().collect(); - assert_eq!(lines.len(), 7); // JSON should be 7 lines - - assert_eq!(indents(lines[0]), 0 * i); // [ - assert_eq!(indents(lines[1]), 1 * i); // "greetings", - assert_eq!(indents(lines[2]), 1 * i); // { - assert_eq!(indents(lines[3]), 2 * i); // "hello": "guten tag", - assert_eq!(indents(lines[4]), 2 * i); // "goodbye": "sayonara" - assert_eq!(indents(lines[5]), 1 * i); // }, - assert_eq!(indents(lines[6]), 0 * i); // ] - - // Finally, test that the pretty-printed JSON is valid - from_str(printed).ok().expect("Pretty-printed JSON is invalid!"); - } -} - -#[test] -fn test_hashmap_with_enum_key() { - use std::collections::HashMap; - #[derive(RustcEncodable, Eq, Hash, PartialEq, RustcDecodable, Debug)] - enum Enum { - Foo, - #[allow(dead_code)] - Bar, - } - let mut map = HashMap::new(); - map.insert(Enum::Foo, 0); - let result = json::encode(&map).unwrap(); - assert_eq!(&result[..], r#"{"Foo":0}"#); - let decoded: HashMap = json::decode(&result).unwrap(); - assert_eq!(map, decoded); -} - -#[test] -fn test_hashmap_with_numeric_key_can_handle_double_quote_delimited_key() { - use std::collections::HashMap; - let json_str = "{\"1\":true}"; - let json_obj = match from_str(json_str) { - Err(_) => panic!("Unable to parse json_str: {:?}", json_str), - Ok(o) => o, - }; - let mut decoder = Decoder::new(json_obj); - let _hm: HashMap = Decodable::decode(&mut decoder).unwrap(); -} - -#[test] -fn test_hashmap_with_numeric_key_will_error_with_string_keys() { - use std::collections::HashMap; - let json_str = "{\"a\":true}"; - let json_obj = match from_str(json_str) { - Err(_) => panic!("Unable to parse json_str: {:?}", json_str), - Ok(o) => o, - }; - let mut decoder = Decoder::new(json_obj); - let result: Result, DecoderError> = Decodable::decode(&mut decoder); - assert_eq!(result, Err(ExpectedError("Number".to_string(), "a".to_string()))); -} - -fn assert_stream_equal(src: &str, expected: Vec<(JsonEvent, Vec>)>) { - let mut parser = Parser::new(src.chars()); - let mut i = 0; - loop { - let evt = match parser.next() { - Some(e) => e, - None => { - break; - } - }; - let (ref expected_evt, ref expected_stack) = expected[i]; - if !parser.stack().is_equal_to(expected_stack) { - panic!("Parser stack is not equal to {:?}", expected_stack); - } - assert_eq!(&evt, expected_evt); - i += 1; - } -} -#[test] -fn test_streaming_parser() { - assert_stream_equal( - r#"{ "foo":"bar", "array" : [0, 1, 2, 3, 4, 5], "idents":[null,true,false]}"#, - vec![ - (ObjectStart, vec![]), - (StringValue("bar".to_string()), vec![StackElement::Key("foo")]), - (ArrayStart, vec![StackElement::Key("array")]), - (U64Value(0), vec![StackElement::Key("array"), StackElement::Index(0)]), - (U64Value(1), vec![StackElement::Key("array"), StackElement::Index(1)]), - (U64Value(2), vec![StackElement::Key("array"), StackElement::Index(2)]), - (U64Value(3), vec![StackElement::Key("array"), StackElement::Index(3)]), - (U64Value(4), vec![StackElement::Key("array"), StackElement::Index(4)]), - (U64Value(5), vec![StackElement::Key("array"), StackElement::Index(5)]), - (ArrayEnd, vec![StackElement::Key("array")]), - (ArrayStart, vec![StackElement::Key("idents")]), - (NullValue, vec![StackElement::Key("idents"), StackElement::Index(0)]), - (BooleanValue(true), vec![StackElement::Key("idents"), StackElement::Index(1)]), - (BooleanValue(false), vec![StackElement::Key("idents"), StackElement::Index(2)]), - (ArrayEnd, vec![StackElement::Key("idents")]), - (ObjectEnd, vec![]), - ], - ); -} -fn last_event(src: &str) -> JsonEvent { - let mut parser = Parser::new(src.chars()); - let mut evt = NullValue; - loop { - evt = match parser.next() { - Some(e) => e, - None => return evt, - } - } -} - -#[test] -fn test_read_object_streaming() { - assert_eq!(last_event("{ "), Error(SyntaxError(EOFWhileParsingObject, 1, 3))); - assert_eq!(last_event("{1"), Error(SyntaxError(KeyMustBeAString, 1, 2))); - assert_eq!(last_event("{ \"a\""), Error(SyntaxError(EOFWhileParsingObject, 1, 6))); - assert_eq!(last_event("{\"a\""), Error(SyntaxError(EOFWhileParsingObject, 1, 5))); - assert_eq!(last_event("{\"a\" "), Error(SyntaxError(EOFWhileParsingObject, 1, 6))); - - assert_eq!(last_event("{\"a\" 1"), Error(SyntaxError(ExpectedColon, 1, 6))); - assert_eq!(last_event("{\"a\":"), Error(SyntaxError(EOFWhileParsingValue, 1, 6))); - assert_eq!(last_event("{\"a\":1"), Error(SyntaxError(EOFWhileParsingObject, 1, 7))); - assert_eq!(last_event("{\"a\":1 1"), Error(SyntaxError(InvalidSyntax, 1, 8))); - assert_eq!(last_event("{\"a\":1,"), Error(SyntaxError(EOFWhileParsingObject, 1, 8))); - assert_eq!(last_event("{\"a\":1,}"), Error(SyntaxError(TrailingComma, 1, 8))); - - assert_stream_equal("{}", vec![(ObjectStart, vec![]), (ObjectEnd, vec![])]); - assert_stream_equal( - "{\"a\": 3}", - vec![ - (ObjectStart, vec![]), - (U64Value(3), vec![StackElement::Key("a")]), - (ObjectEnd, vec![]), - ], - ); - assert_stream_equal( - "{ \"a\": null, \"b\" : true }", - vec![ - (ObjectStart, vec![]), - (NullValue, vec![StackElement::Key("a")]), - (BooleanValue(true), vec![StackElement::Key("b")]), - (ObjectEnd, vec![]), - ], - ); - assert_stream_equal( - "{\"a\" : 1.0 ,\"b\": [ true ]}", - vec![ - (ObjectStart, vec![]), - (F64Value(1.0), vec![StackElement::Key("a")]), - (ArrayStart, vec![StackElement::Key("b")]), - (BooleanValue(true), vec![StackElement::Key("b"), StackElement::Index(0)]), - (ArrayEnd, vec![StackElement::Key("b")]), - (ObjectEnd, vec![]), - ], - ); - assert_stream_equal( - r#"{ - "a": 1.0, - "b": [ - true, - "foo\nbar", - { "c": {"d": null} } - ] - }"#, - vec![ - (ObjectStart, vec![]), - (F64Value(1.0), vec![StackElement::Key("a")]), - (ArrayStart, vec![StackElement::Key("b")]), - (BooleanValue(true), vec![StackElement::Key("b"), StackElement::Index(0)]), - ( - StringValue("foo\nbar".to_string()), - vec![StackElement::Key("b"), StackElement::Index(1)], - ), - (ObjectStart, vec![StackElement::Key("b"), StackElement::Index(2)]), - ( - ObjectStart, - vec![StackElement::Key("b"), StackElement::Index(2), StackElement::Key("c")], - ), - ( - NullValue, - vec![ - StackElement::Key("b"), - StackElement::Index(2), - StackElement::Key("c"), - StackElement::Key("d"), - ], - ), - ( - ObjectEnd, - vec![StackElement::Key("b"), StackElement::Index(2), StackElement::Key("c")], - ), - (ObjectEnd, vec![StackElement::Key("b"), StackElement::Index(2)]), - (ArrayEnd, vec![StackElement::Key("b")]), - (ObjectEnd, vec![]), - ], - ); -} -#[test] -fn test_read_array_streaming() { - assert_stream_equal("[]", vec![(ArrayStart, vec![]), (ArrayEnd, vec![])]); - assert_stream_equal("[ ]", vec![(ArrayStart, vec![]), (ArrayEnd, vec![])]); - assert_stream_equal( - "[true]", - vec![ - (ArrayStart, vec![]), - (BooleanValue(true), vec![StackElement::Index(0)]), - (ArrayEnd, vec![]), - ], - ); - assert_stream_equal( - "[ false ]", - vec![ - (ArrayStart, vec![]), - (BooleanValue(false), vec![StackElement::Index(0)]), - (ArrayEnd, vec![]), - ], - ); - assert_stream_equal( - "[null]", - vec![(ArrayStart, vec![]), (NullValue, vec![StackElement::Index(0)]), (ArrayEnd, vec![])], - ); - assert_stream_equal( - "[3, 1]", - vec![ - (ArrayStart, vec![]), - (U64Value(3), vec![StackElement::Index(0)]), - (U64Value(1), vec![StackElement::Index(1)]), - (ArrayEnd, vec![]), - ], - ); - assert_stream_equal( - "\n[3, 2]\n", - vec![ - (ArrayStart, vec![]), - (U64Value(3), vec![StackElement::Index(0)]), - (U64Value(2), vec![StackElement::Index(1)]), - (ArrayEnd, vec![]), - ], - ); - assert_stream_equal( - "[2, [4, 1]]", - vec![ - (ArrayStart, vec![]), - (U64Value(2), vec![StackElement::Index(0)]), - (ArrayStart, vec![StackElement::Index(1)]), - (U64Value(4), vec![StackElement::Index(1), StackElement::Index(0)]), - (U64Value(1), vec![StackElement::Index(1), StackElement::Index(1)]), - (ArrayEnd, vec![StackElement::Index(1)]), - (ArrayEnd, vec![]), - ], - ); - - assert_eq!(last_event("["), Error(SyntaxError(EOFWhileParsingValue, 1, 2))); - - assert_eq!(from_str("["), Err(SyntaxError(EOFWhileParsingValue, 1, 2))); - assert_eq!(from_str("[1"), Err(SyntaxError(EOFWhileParsingArray, 1, 3))); - assert_eq!(from_str("[1,"), Err(SyntaxError(EOFWhileParsingValue, 1, 4))); - assert_eq!(from_str("[1,]"), Err(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(from_str("[6 7]"), Err(SyntaxError(InvalidSyntax, 1, 4))); -} -#[test] -fn test_trailing_characters_streaming() { - assert_eq!(last_event("nulla"), Error(SyntaxError(TrailingCharacters, 1, 5))); - assert_eq!(last_event("truea"), Error(SyntaxError(TrailingCharacters, 1, 5))); - assert_eq!(last_event("falsea"), Error(SyntaxError(TrailingCharacters, 1, 6))); - assert_eq!(last_event("1a"), Error(SyntaxError(TrailingCharacters, 1, 2))); - assert_eq!(last_event("[]a"), Error(SyntaxError(TrailingCharacters, 1, 3))); - assert_eq!(last_event("{}a"), Error(SyntaxError(TrailingCharacters, 1, 3))); -} -#[test] -fn test_read_identifiers_streaming() { - assert_eq!(Parser::new("null".chars()).next(), Some(NullValue)); - assert_eq!(Parser::new("true".chars()).next(), Some(BooleanValue(true))); - assert_eq!(Parser::new("false".chars()).next(), Some(BooleanValue(false))); - - assert_eq!(last_event("n"), Error(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(last_event("nul"), Error(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(last_event("t"), Error(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(last_event("truz"), Error(SyntaxError(InvalidSyntax, 1, 4))); - assert_eq!(last_event("f"), Error(SyntaxError(InvalidSyntax, 1, 2))); - assert_eq!(last_event("faz"), Error(SyntaxError(InvalidSyntax, 1, 3))); -} - -#[test] -fn test_to_json() { - use json::ToJson; - use std::collections::{BTreeMap, HashMap}; - - let array2 = Array(vec![U64(1), U64(2)]); - let array3 = Array(vec![U64(1), U64(2), U64(3)]); - let object = { - let mut tree_map = BTreeMap::new(); - tree_map.insert("a".to_string(), U64(1)); - tree_map.insert("b".to_string(), U64(2)); - Object(tree_map) - }; - - assert_eq!(array2.to_json(), array2); - assert_eq!(object.to_json(), object); - assert_eq!(3_isize.to_json(), I64(3)); - assert_eq!(4_i8.to_json(), I64(4)); - assert_eq!(5_i16.to_json(), I64(5)); - assert_eq!(6_i32.to_json(), I64(6)); - assert_eq!(7_i64.to_json(), I64(7)); - assert_eq!(8_usize.to_json(), U64(8)); - assert_eq!(9_u8.to_json(), U64(9)); - assert_eq!(10_u16.to_json(), U64(10)); - assert_eq!(11_u32.to_json(), U64(11)); - assert_eq!(12_u64.to_json(), U64(12)); - assert_eq!(13.0_f32.to_json(), F64(13.0_f64)); - assert_eq!(14.0_f64.to_json(), F64(14.0_f64)); - assert_eq!(().to_json(), Null); - assert_eq!(f32::INFINITY.to_json(), Null); - assert_eq!(f64::NAN.to_json(), Null); - assert_eq!(true.to_json(), Boolean(true)); - assert_eq!(false.to_json(), Boolean(false)); - assert_eq!("abc".to_json(), String("abc".to_string())); - assert_eq!("abc".to_string().to_json(), String("abc".to_string())); - assert_eq!((1_usize, 2_usize).to_json(), array2); - assert_eq!((1_usize, 2_usize, 3_usize).to_json(), array3); - assert_eq!([1_usize, 2_usize].to_json(), array2); - assert_eq!((&[1_usize, 2_usize, 3_usize]).to_json(), array3); - assert_eq!((vec![1_usize, 2_usize]).to_json(), array2); - assert_eq!(vec![1_usize, 2_usize, 3_usize].to_json(), array3); - let mut tree_map = BTreeMap::new(); - tree_map.insert("a".to_string(), 1 as usize); - tree_map.insert("b".to_string(), 2); - assert_eq!(tree_map.to_json(), object); - let mut hash_map = HashMap::new(); - hash_map.insert("a".to_string(), 1 as usize); - hash_map.insert("b".to_string(), 2); - assert_eq!(hash_map.to_json(), object); - assert_eq!(Some(15).to_json(), I64(15)); - assert_eq!(Some(15 as usize).to_json(), U64(15)); - assert_eq!(None::.to_json(), Null); -} - -#[test] -fn test_encode_hashmap_with_arbitrary_key() { - use std::collections::HashMap; - #[derive(PartialEq, Eq, Hash, RustcEncodable)] - struct ArbitraryType(usize); - let mut hm: HashMap = HashMap::new(); - hm.insert(ArbitraryType(1), true); - let mut mem_buf = string::String::new(); - let mut encoder = Encoder::new(&mut mem_buf); - let result = hm.encode(&mut encoder); - match result.unwrap_err() { - EncoderError::BadHashmapKey => (), - _ => panic!("expected bad hash map key"), - } -} diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index b147aa55b2a61..490afb5a0438f 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -20,14 +20,14 @@ panic_unwind = { path = "../libpanic_unwind", optional = true } panic_abort = { path = "../libpanic_abort" } core = { path = "../libcore" } libc = { version = "0.2.51", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.16" } +compiler_builtins = { version = "0.1.32" } profiler_builtins = { path = "../libprofiler_builtins", optional = true } unwind = { path = "../libunwind" } hashbrown = { version = "0.6.2", default-features = false, features = ['rustc-dep-of-std'] } [dependencies.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.14", 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 @@ -74,3 +74,8 @@ std_detect_dlsym_getauxval = [] threads = 125 # Maximum heap size heap_size = 0x8000000 + +[[bench]] +name = "stdbenches" +path = "benches/lib.rs" +test = true diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs 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/backtrace.rs b/src/libstd/backtrace.rs index 97db0ff3791d7..e10d466030f0b 100644 --- a/src/libstd/backtrace.rs +++ b/src/libstd/backtrace.rs @@ -92,6 +92,7 @@ // a backtrace or actually symbolizing it. use crate::env; +use crate::ffi::c_void; use crate::fmt; use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use crate::sync::Mutex; @@ -144,10 +145,16 @@ fn _assert_send_sync() { } struct BacktraceFrame { - frame: backtrace::Frame, + frame: RawFrame, symbols: Vec, } +enum RawFrame { + Actual(backtrace::Frame), + #[cfg(test)] + Fake, +} + struct BacktraceSymbol { name: Option>, filename: Option, @@ -162,8 +169,8 @@ enum BytesOrWide { impl fmt::Debug for Backtrace { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut capture = match &self.inner { - Inner::Unsupported => return fmt.write_str("unsupported backtrace"), - Inner::Disabled => return fmt.write_str("disabled backtrace"), + Inner::Unsupported => return fmt.write_str(""), + Inner::Disabled => return fmt.write_str(""), Inner::Captured(c) => c.lock().unwrap(), }; capture.resolve(); @@ -193,11 +200,11 @@ impl fmt::Debug for BacktraceSymbol { if let Some(fn_name) = self.name.as_ref().map(|b| backtrace::SymbolName::new(b)) { write!(fmt, "fn: \"{:#}\"", fn_name)?; } else { - write!(fmt, "fn: \"\"")?; + write!(fmt, "fn: ")?; } if let Some(fname) = self.filename.as_ref() { - write!(fmt, ", file: {:?}", fname)?; + write!(fmt, ", file: \"{:?}\"", fname)?; } if let Some(line) = self.lineno.as_ref() { @@ -243,7 +250,7 @@ impl Backtrace { }, }; ENABLED.store(enabled as usize + 1, SeqCst); - return enabled; + enabled } /// Capture a stack backtrace of the current thread. @@ -293,7 +300,10 @@ impl Backtrace { let mut actual_start = None; unsafe { backtrace::trace_unsynchronized(|frame| { - frames.push(BacktraceFrame { frame: frame.clone(), symbols: Vec::new() }); + frames.push(BacktraceFrame { + frame: RawFrame::Actual(frame.clone()), + symbols: Vec::new(), + }); if frame.symbol_address() as usize == ip && actual_start.is_none() { actual_start = Some(frames.len()); } @@ -393,8 +403,13 @@ impl Capture { let _lock = lock(); for frame in self.frames.iter_mut() { let symbols = &mut frame.symbols; + let frame = match &frame.frame { + RawFrame::Actual(frame) => frame, + #[cfg(test)] + RawFrame::Fake => unimplemented!(), + }; unsafe { - backtrace::resolve_frame_unsynchronized(&frame.frame, |symbol| { + backtrace::resolve_frame_unsynchronized(frame, |symbol| { symbols.push(BacktraceSymbol { name: symbol.name().map(|m| m.as_bytes().to_vec()), filename: symbol.filename_raw().map(|b| match b { @@ -408,3 +423,65 @@ impl Capture { } } } + +impl RawFrame { + fn ip(&self) -> *mut c_void { + match self { + RawFrame::Actual(frame) => frame.ip(), + #[cfg(test)] + RawFrame::Fake => 1 as *mut c_void, + } + } +} + +#[test] +fn test_debug() { + let backtrace = Backtrace { + inner: Inner::Captured(Mutex::new(Capture { + actual_start: 1, + resolved: true, + frames: vec![ + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![BacktraceSymbol { + name: Some(b"std::backtrace::Backtrace::create".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())), + lineno: Some(100), + }], + }, + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![BacktraceSymbol { + name: Some(b"__rust_maybe_catch_panic".to_vec()), + filename: None, + lineno: None, + }], + }, + BacktraceFrame { + frame: RawFrame::Fake, + symbols: vec![ + BacktraceSymbol { + name: Some(b"std::rt::lang_start_internal".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), + lineno: Some(300), + }, + BacktraceSymbol { + name: Some(b"std::rt::lang_start".to_vec()), + filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), + lineno: Some(400), + }, + ], + }, + ], + })), + }; + + #[rustfmt::skip] + let expected = "Backtrace [\ + \n { fn: \"__rust_maybe_catch_panic\" },\ + \n { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\ + \n { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\ + \n]"; + + assert_eq!(format!("{:#?}", backtrace), expected); +} diff --git a/src/libstd/build.rs b/src/libstd/build.rs 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..5ba5eff44076b 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 @@ -2387,6 +2426,24 @@ where fn extend>(&mut self, iter: T) { self.base.extend(iter) } + + #[inline] + fn extend_one(&mut self, (k, v): (K, V)) { + self.base.insert(k, v); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + // self.base.extend_reserve(additional); + // FIXME: hashbrown should implement this method. + // But until then, use the same reservation logic: + + // Reserve the entire hint lower bound if the map is empty. + // Otherwise reserve half the hint (rounded up), so the map + // will only resize twice in the worst case. + let reserve = if self.is_empty() { additional } else { (additional + 1) / 2 }; + self.base.reserve(reserve); + } } #[stable(feature = "hash_extend_copy", since = "1.4.0")] @@ -2400,6 +2457,16 @@ where fn extend>(&mut self, iter: T) { self.base.extend(iter) } + + #[inline] + fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { + self.base.insert(k, v); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + Extend::<(K, V)>::extend_reserve(self, additional) + } } /// `RandomState` is the default state for [`HashMap`] types. @@ -2617,7 +2684,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..cb2f829803b85 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 { @@ -959,6 +970,16 @@ where fn extend>(&mut self, iter: I) { self.map.extend(iter.into_iter().map(|k| (k, ()))); } + + #[inline] + fn extend_one(&mut self, item: T) { + self.map.insert(item, ()); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.map.extend_reserve(additional); + } } #[stable(feature = "hash_extend_copy", since = "1.4.0")] @@ -971,6 +992,16 @@ where fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.map.insert(item, ()); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + Extend::::extend_reserve(self, additional) + } } #[stable(feature = "rust1", since = "1.0.0")] 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 af35a5d9b7c47..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 { @@ -723,8 +723,8 @@ pub struct ArgsOs { /// (such as `*` and `?`). On Windows this is not done, and such arguments are /// passed as-is. /// -/// On glibc Linux, arguments are retrieved by placing a function in .init_array. -/// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. +/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array". +/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension. /// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS /// and Windows. /// @@ -758,8 +758,8 @@ pub fn args() -> Args { /// set to arbitrary text, and it may not even exist, so this property should /// not be relied upon for security purposes. /// -/// On glibc Linux, arguments are retrieved by placing a function in .init_array. -/// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. +/// On glibc Linux systems, arguments are retrieved by placing a function in ".init_array". +/// Glibc passes argc, argv, and envp to functions in ".init_array", as a non-standard extension. /// This allows `std::env::args` to work even in a `cdylib` or `staticlib`, as it does on macOS /// and Windows. /// diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 2a370f192964f..3b4cb859dd425 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -14,8 +14,9 @@ // reconsider what crate these items belong in. use core::array; +use core::convert::Infallible; -use crate::alloc::{AllocErr, CannotReallocInPlace, LayoutErr}; +use crate::alloc::{AllocErr, LayoutErr}; use crate::any::TypeId; use crate::backtrace::Backtrace; use crate::borrow::Cow; @@ -88,7 +89,7 @@ pub trait Error: Debug + Display { /// fn main() { /// match get_super_error() { /// Err(e) => { - /// println!("Error: {}", e.description()); + /// println!("Error: {}", e); /// println!("Caused by: {}", e.source().unwrap()); /// } /// _ => println!("No error"), @@ -409,13 +410,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)] @@ -481,7 +475,7 @@ impl Error for string::FromUtf16Error { } #[stable(feature = "str_parse_error2", since = "1.8.0")] -impl Error for string::ParseError { +impl Error for Infallible { fn description(&self) -> &str { match *self {} } diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 20425aea8d517..b392d6e7226d2 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,11 +832,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f32 { - if self == NEG_INFINITY { - NEG_INFINITY - } else { - (self + ((self * self) + 1.0).sqrt()).ln().copysign(self) - } + (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) } /// Inverse hyperbolic cosine function. @@ -906,8 +840,6 @@ impl f32 { /// # Examples /// /// ``` - /// use std::f32; - /// /// let x = 1.0f32; /// let f = x.cosh().acosh(); /// @@ -919,7 +851,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 +859,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(); @@ -948,7 +878,7 @@ impl f32 { /// Returns `max` if `self` is greater than `max`, and `min` if `self` is /// less than `min`. Otherwise this returns `self`. /// - /// Not that this function returns NaN if the initial value was NaN as + /// Note that this function returns NaN if the initial value was NaN as /// well. /// /// # Panics @@ -962,7 +892,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")] @@ -982,8 +912,7 @@ impl f32 { #[cfg(test)] mod tests { - use crate::f32; - use crate::f32::*; + use crate::f32::consts; use crate::num::FpCategory as Fp; use crate::num::*; @@ -994,14 +923,14 @@ mod tests { #[test] fn test_min_nan() { - assert_eq!(NAN.min(2.0), 2.0); - assert_eq!(2.0f32.min(NAN), 2.0); + assert_eq!(f32::NAN.min(2.0), 2.0); + assert_eq!(2.0f32.min(f32::NAN), 2.0); } #[test] fn test_max_nan() { - assert_eq!(NAN.max(2.0), 2.0); - assert_eq!(2.0f32.max(NAN), 2.0); + assert_eq!(f32::NAN.max(2.0), 2.0); + assert_eq!(2.0f32.max(f32::NAN), 2.0); } #[test] @@ -1224,52 +1153,52 @@ mod tests { #[test] fn test_abs() { - assert_eq!(INFINITY.abs(), INFINITY); + assert_eq!(f32::INFINITY.abs(), f32::INFINITY); assert_eq!(1f32.abs(), 1f32); assert_eq!(0f32.abs(), 0f32); assert_eq!((-0f32).abs(), 0f32); assert_eq!((-1f32).abs(), 1f32); - assert_eq!(NEG_INFINITY.abs(), INFINITY); - assert_eq!((1f32 / NEG_INFINITY).abs(), 0f32); - assert!(NAN.abs().is_nan()); + assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); + assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); + assert!(f32::NAN.abs().is_nan()); } #[test] fn test_signum() { - assert_eq!(INFINITY.signum(), 1f32); + assert_eq!(f32::INFINITY.signum(), 1f32); assert_eq!(1f32.signum(), 1f32); assert_eq!(0f32.signum(), 1f32); assert_eq!((-0f32).signum(), -1f32); assert_eq!((-1f32).signum(), -1f32); - assert_eq!(NEG_INFINITY.signum(), -1f32); - assert_eq!((1f32 / NEG_INFINITY).signum(), -1f32); - assert!(NAN.signum().is_nan()); + assert_eq!(f32::NEG_INFINITY.signum(), -1f32); + assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); + assert!(f32::NAN.signum().is_nan()); } #[test] fn test_is_sign_positive() { - assert!(INFINITY.is_sign_positive()); + assert!(f32::INFINITY.is_sign_positive()); assert!(1f32.is_sign_positive()); assert!(0f32.is_sign_positive()); assert!(!(-0f32).is_sign_positive()); assert!(!(-1f32).is_sign_positive()); - assert!(!NEG_INFINITY.is_sign_positive()); - assert!(!(1f32 / NEG_INFINITY).is_sign_positive()); - assert!(NAN.is_sign_positive()); - assert!(!(-NAN).is_sign_positive()); + assert!(!f32::NEG_INFINITY.is_sign_positive()); + assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive()); + assert!(f32::NAN.is_sign_positive()); + assert!(!(-f32::NAN).is_sign_positive()); } #[test] fn test_is_sign_negative() { - assert!(!INFINITY.is_sign_negative()); + assert!(!f32::INFINITY.is_sign_negative()); assert!(!1f32.is_sign_negative()); assert!(!0f32.is_sign_negative()); assert!((-0f32).is_sign_negative()); assert!((-1f32).is_sign_negative()); - assert!(NEG_INFINITY.is_sign_negative()); - assert!((1f32 / NEG_INFINITY).is_sign_negative()); - assert!(!NAN.is_sign_negative()); - assert!((-NAN).is_sign_negative()); + assert!(f32::NEG_INFINITY.is_sign_negative()); + assert!((1f32 / f32::NEG_INFINITY).is_sign_negative()); + assert!(!f32::NAN.is_sign_negative()); + assert!((-f32::NAN).is_sign_negative()); } #[test] @@ -1334,13 +1263,13 @@ mod tests { #[test] fn test_sqrt_domain() { - assert!(NAN.sqrt().is_nan()); - assert!(NEG_INFINITY.sqrt().is_nan()); + assert!(f32::NAN.sqrt().is_nan()); + assert!(f32::NEG_INFINITY.sqrt().is_nan()); assert!((-1.0f32).sqrt().is_nan()); assert_eq!((-0.0f32).sqrt(), -0.0); assert_eq!(0.0f32.sqrt(), 0.0); assert_eq!(1.0f32.sqrt(), 1.0); - assert_eq!(INFINITY.sqrt(), INFINITY); + assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); } #[test] @@ -1480,6 +1409,8 @@ mod tests { assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32); } #[test] @@ -1589,12 +1520,155 @@ mod tests { #[test] #[should_panic] fn test_clamp_min_is_nan() { - let _ = 1.0f32.clamp(NAN, 1.0); + let _ = 1.0f32.clamp(f32::NAN, 1.0); } #[test] #[should_panic] fn test_clamp_max_is_nan() { - let _ = 1.0f32.clamp(3.0, NAN); + let _ = 1.0f32.clamp(3.0, f32::NAN); + } + + #[test] + fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u32 { + 1 << (f32::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f32 { + f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0) + } + + fn max_subnorm() -> f32 { + f32::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f32 { + f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f32 { + f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); } } diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index a1128a589a64a..72268d2cc2f98 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,11 +834,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f64 { - if self == NEG_INFINITY { - NEG_INFINITY - } else { - (self + ((self * self) + 1.0).sqrt()).ln().copysign(self) - } + (self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self) } /// Inverse hyperbolic cosine function. @@ -885,7 +853,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 +861,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(); @@ -914,7 +880,7 @@ impl f64 { /// Returns `max` if `self` is greater than `max`, and `min` if `self` is /// less than `min`. Otherwise this returns `self`. /// - /// Not that this function returns NaN if the initial value was NaN as + /// Note that this function returns NaN if the initial value was NaN as /// well. /// /// # Panics @@ -928,7 +894,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 +915,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 } } } @@ -973,8 +939,7 @@ impl f64 { #[cfg(test)] mod tests { - use crate::f64; - use crate::f64::*; + use crate::f64::consts; use crate::num::FpCategory as Fp; use crate::num::*; @@ -985,19 +950,19 @@ mod tests { #[test] fn test_min_nan() { - assert_eq!(NAN.min(2.0), 2.0); - assert_eq!(2.0f64.min(NAN), 2.0); + assert_eq!(f64::NAN.min(2.0), 2.0); + assert_eq!(2.0f64.min(f64::NAN), 2.0); } #[test] fn test_max_nan() { - assert_eq!(NAN.max(2.0), 2.0); - assert_eq!(2.0f64.max(NAN), 2.0); + assert_eq!(f64::NAN.max(2.0), 2.0); + assert_eq!(2.0f64.max(f64::NAN), 2.0); } #[test] fn test_nan() { - let nan: f64 = NAN; + let nan: f64 = f64::NAN; assert!(nan.is_nan()); assert!(!nan.is_infinite()); assert!(!nan.is_finite()); @@ -1009,7 +974,7 @@ mod tests { #[test] fn test_infinity() { - let inf: f64 = INFINITY; + let inf: f64 = f64::INFINITY; assert!(inf.is_infinite()); assert!(!inf.is_finite()); assert!(inf.is_sign_positive()); @@ -1021,7 +986,7 @@ mod tests { #[test] fn test_neg_infinity() { - let neg_inf: f64 = NEG_INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert!(neg_inf.is_infinite()); assert!(!neg_inf.is_finite()); assert!(!neg_inf.is_sign_positive()); @@ -1073,9 +1038,9 @@ mod tests { #[test] fn test_is_nan() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert!(nan.is_nan()); assert!(!0.0f64.is_nan()); assert!(!5.3f64.is_nan()); @@ -1086,9 +1051,9 @@ mod tests { #[test] fn test_is_infinite() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert!(!nan.is_infinite()); assert!(inf.is_infinite()); assert!(neg_inf.is_infinite()); @@ -1099,9 +1064,9 @@ mod tests { #[test] fn test_is_finite() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert!(!nan.is_finite()); assert!(!inf.is_finite()); assert!(!neg_inf.is_finite()); @@ -1113,9 +1078,9 @@ mod tests { #[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_is_normal() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; let zero: f64 = 0.0f64; let neg_zero: f64 = -0.0; assert!(!nan.is_normal()); @@ -1131,9 +1096,9 @@ mod tests { #[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_classify() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; let zero: f64 = 0.0f64; let neg_zero: f64 = -0.0; assert_eq!(nan.classify(), Fp::Nan); @@ -1217,59 +1182,59 @@ mod tests { #[test] fn test_abs() { - assert_eq!(INFINITY.abs(), INFINITY); + assert_eq!(f64::INFINITY.abs(), f64::INFINITY); assert_eq!(1f64.abs(), 1f64); assert_eq!(0f64.abs(), 0f64); assert_eq!((-0f64).abs(), 0f64); assert_eq!((-1f64).abs(), 1f64); - assert_eq!(NEG_INFINITY.abs(), INFINITY); - assert_eq!((1f64 / NEG_INFINITY).abs(), 0f64); - assert!(NAN.abs().is_nan()); + assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); + assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); + assert!(f64::NAN.abs().is_nan()); } #[test] fn test_signum() { - assert_eq!(INFINITY.signum(), 1f64); + assert_eq!(f64::INFINITY.signum(), 1f64); assert_eq!(1f64.signum(), 1f64); assert_eq!(0f64.signum(), 1f64); assert_eq!((-0f64).signum(), -1f64); assert_eq!((-1f64).signum(), -1f64); - assert_eq!(NEG_INFINITY.signum(), -1f64); - assert_eq!((1f64 / NEG_INFINITY).signum(), -1f64); - assert!(NAN.signum().is_nan()); + assert_eq!(f64::NEG_INFINITY.signum(), -1f64); + assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); + assert!(f64::NAN.signum().is_nan()); } #[test] fn test_is_sign_positive() { - assert!(INFINITY.is_sign_positive()); + assert!(f64::INFINITY.is_sign_positive()); assert!(1f64.is_sign_positive()); assert!(0f64.is_sign_positive()); assert!(!(-0f64).is_sign_positive()); assert!(!(-1f64).is_sign_positive()); - assert!(!NEG_INFINITY.is_sign_positive()); - assert!(!(1f64 / NEG_INFINITY).is_sign_positive()); - assert!(NAN.is_sign_positive()); - assert!(!(-NAN).is_sign_positive()); + assert!(!f64::NEG_INFINITY.is_sign_positive()); + assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive()); + assert!(f64::NAN.is_sign_positive()); + assert!(!(-f64::NAN).is_sign_positive()); } #[test] fn test_is_sign_negative() { - assert!(!INFINITY.is_sign_negative()); + assert!(!f64::INFINITY.is_sign_negative()); assert!(!1f64.is_sign_negative()); assert!(!0f64.is_sign_negative()); assert!((-0f64).is_sign_negative()); assert!((-1f64).is_sign_negative()); - assert!(NEG_INFINITY.is_sign_negative()); - assert!((1f64 / NEG_INFINITY).is_sign_negative()); - assert!(!NAN.is_sign_negative()); - assert!((-NAN).is_sign_negative()); + assert!(f64::NEG_INFINITY.is_sign_negative()); + assert!((1f64 / f64::NEG_INFINITY).is_sign_negative()); + assert!(!f64::NAN.is_sign_negative()); + assert!((-f64::NAN).is_sign_negative()); } #[test] fn test_mul_add() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05); assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65); assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); @@ -1283,9 +1248,9 @@ mod tests { #[test] fn test_recip() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_eq!(1.0f64.recip(), 1.0); assert_eq!(2.0f64.recip(), 0.5); assert_eq!((-0.4f64).recip(), -2.5); @@ -1297,9 +1262,9 @@ mod tests { #[test] fn test_powi() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_eq!(1.0f64.powi(1), 1.0); assert_approx_eq!((-3.1f64).powi(2), 9.61); assert_approx_eq!(5.9f64.powi(-2), 0.028727); @@ -1311,9 +1276,9 @@ mod tests { #[test] fn test_powf() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_eq!(1.0f64.powf(1.0), 1.0); assert_approx_eq!(3.4f64.powf(4.5), 246.408183); assert_approx_eq!(2.7f64.powf(-3.2), 0.041652); @@ -1327,13 +1292,13 @@ mod tests { #[test] fn test_sqrt_domain() { - assert!(NAN.sqrt().is_nan()); - assert!(NEG_INFINITY.sqrt().is_nan()); + assert!(f64::NAN.sqrt().is_nan()); + assert!(f64::NEG_INFINITY.sqrt().is_nan()); assert!((-1.0f64).sqrt().is_nan()); assert_eq!((-0.0f64).sqrt(), -0.0); assert_eq!(0.0f64.sqrt(), 0.0); assert_eq!(1.0f64.sqrt(), 1.0); - assert_eq!(INFINITY.sqrt(), INFINITY); + assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); } #[test] @@ -1342,9 +1307,9 @@ mod tests { assert_approx_eq!(2.718282, 1.0f64.exp()); assert_approx_eq!(148.413159, 5.0f64.exp()); - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; - let nan: f64 = NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; assert_eq!(inf, inf.exp()); assert_eq!(0.0, neg_inf.exp()); assert!(nan.exp().is_nan()); @@ -1355,9 +1320,9 @@ mod tests { assert_eq!(32.0, 5.0f64.exp2()); assert_eq!(1.0, 0.0f64.exp2()); - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; - let nan: f64 = NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; assert_eq!(inf, inf.exp2()); assert_eq!(0.0, neg_inf.exp2()); assert!(nan.exp2().is_nan()); @@ -1365,9 +1330,9 @@ mod tests { #[test] fn test_ln() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_approx_eq!(1.0f64.exp().ln(), 1.0); assert!(nan.ln().is_nan()); assert_eq!(inf.ln(), inf); @@ -1380,9 +1345,9 @@ mod tests { #[test] fn test_log() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_eq!(10.0f64.log(10.0), 1.0); assert_approx_eq!(2.3f64.log(3.5), 0.664858); assert_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0); @@ -1398,9 +1363,9 @@ mod tests { #[test] fn test_log2() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_approx_eq!(10.0f64.log2(), 3.321928); assert_approx_eq!(2.3f64.log2(), 1.201634); assert_approx_eq!(1.0f64.exp().log2(), 1.442695); @@ -1414,9 +1379,9 @@ mod tests { #[test] fn test_log10() { - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_eq!(10.0f64.log10(), 1.0); assert_approx_eq!(2.3f64.log10(), 0.361728); assert_approx_eq!(1.0f64.exp().log10(), 0.434294); @@ -1432,9 +1397,9 @@ mod tests { #[test] fn test_to_degrees() { let pi: f64 = consts::PI; - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_eq!(0.0f64.to_degrees(), 0.0); assert_approx_eq!((-5.8f64).to_degrees(), -332.315521); assert_eq!(pi.to_degrees(), 180.0); @@ -1446,9 +1411,9 @@ mod tests { #[test] fn test_to_radians() { let pi: f64 = consts::PI; - let nan: f64 = NAN; - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; assert_eq!(0.0f64.to_radians(), 0.0); assert_approx_eq!(154.6f64.to_radians(), 2.698279); assert_approx_eq!((-332.31f64).to_radians(), -5.799903); @@ -1463,9 +1428,9 @@ mod tests { assert_eq!(0.0f64.asinh(), 0.0f64); assert_eq!((-0.0f64).asinh(), -0.0f64); - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; - let nan: f64 = NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; assert_eq!(inf.asinh(), inf); assert_eq!(neg_inf.asinh(), neg_inf); assert!(nan.asinh().is_nan()); @@ -1473,6 +1438,8 @@ mod tests { // issue 63271 assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); } #[test] @@ -1480,9 +1447,9 @@ mod tests { assert_eq!(1.0f64.acosh(), 0.0f64); assert!(0.999f64.acosh().is_nan()); - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; - let nan: f64 = NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; assert_eq!(inf.acosh(), inf); assert!(neg_inf.acosh().is_nan()); assert!(nan.acosh().is_nan()); @@ -1495,9 +1462,9 @@ mod tests { assert_eq!(0.0f64.atanh(), 0.0f64); assert_eq!((-0.0f64).atanh(), -0.0f64); - let inf: f64 = INFINITY; - let neg_inf: f64 = NEG_INFINITY; - let nan: f64 = NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; assert_eq!(1.0f64.atanh(), inf); assert_eq!((-1.0f64).atanh(), neg_inf); assert!(2f64.atanh().atanh().is_nan()); @@ -1576,12 +1543,155 @@ mod tests { #[test] #[should_panic] fn test_clamp_min_is_nan() { - let _ = 1.0f64.clamp(NAN, 1.0); + let _ = 1.0f64.clamp(f64::NAN, 1.0); } #[test] #[should_panic] fn test_clamp_max_is_nan() { - let _ = 1.0f64.clamp(3.0, NAN); + let _ = 1.0f64.clamp(3.0, f64::NAN); + } + + #[test] + fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u64 { + 1 << (f64::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f64 { + f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0) + } + + fn max_subnorm() -> f64 { + f64::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f64 { + f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f64 { + f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); } } diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 04eaba515ff22..dca1fdde48242 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -234,15 +234,14 @@ pub struct NulError(usize, Vec); /// An error indicating that a nul byte was not in the expected position. /// -/// The slice used to create a [`CStr`] must have one and only one nul -/// byte at the end of the slice. +/// The slice used to create a [`CStr`] must have one and only one nul byte, +/// positioned at the end. /// -/// This error is created by the -/// [`from_bytes_with_nul`][`CStr::from_bytes_with_nul`] method on -/// [`CStr`]. See its documentation for more. +/// This error is created by the [`from_bytes_with_nul`] method on [`CStr`]. +/// See its documentation for more. /// /// [`CStr`]: struct.CStr.html -/// [`CStr::from_bytes_with_nul`]: struct.CStr.html#method.from_bytes_with_nul +/// [`from_bytes_with_nul`]: struct.CStr.html#method.from_bytes_with_nul /// /// # Examples /// @@ -257,6 +256,32 @@ pub struct FromBytesWithNulError { kind: FromBytesWithNulErrorKind, } +/// An error indicating that a nul byte was not in the expected position. +/// +/// The vector used to create a [`CString`] must have one and only one nul byte, +/// positioned at the end. +/// +/// This error is created by the [`from_vec_with_nul`] method on [`CString`]. +/// See its documentation for more. +/// +/// [`CString`]: struct.CString.html +/// [`from_vec_with_nul`]: struct.CString.html#method.from_vec_with_nul +/// +/// # Examples +/// +/// ``` +/// #![feature(cstring_from_vec_with_nul)] +/// use std::ffi::{CString, FromVecWithNulError}; +/// +/// let _: FromVecWithNulError = CString::from_vec_with_nul(b"f\0oo".to_vec()).unwrap_err(); +/// ``` +#[derive(Clone, PartialEq, Eq, Debug)] +#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +pub struct FromVecWithNulError { + error_kind: FromBytesWithNulErrorKind, + bytes: Vec, +} + #[derive(Clone, PartialEq, Eq, Debug)] enum FromBytesWithNulErrorKind { InteriorNul(usize), @@ -272,6 +297,59 @@ impl FromBytesWithNulError { } } +#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +impl FromVecWithNulError { + /// Returns a slice of [`u8`]s bytes that were attempted to convert to a [`CString`]. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(cstring_from_vec_with_nul)] + /// use std::ffi::CString; + /// + /// // Some invalid bytes in a vector + /// let bytes = b"f\0oo".to_vec(); + /// + /// let value = CString::from_vec_with_nul(bytes.clone()); + /// + /// assert_eq!(&bytes[..], value.unwrap_err().as_bytes()); + /// ``` + /// + /// [`CString`]: struct.CString.html + pub fn as_bytes(&self) -> &[u8] { + &self.bytes[..] + } + + /// Returns the bytes that were attempted to convert to a [`CString`]. + /// + /// This method is carefully constructed to avoid allocation. It will + /// consume the error, moving out the bytes, so that a copy of the bytes + /// does not need to be made. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(cstring_from_vec_with_nul)] + /// use std::ffi::CString; + /// + /// // Some invalid bytes in a vector + /// let bytes = b"f\0oo".to_vec(); + /// + /// let value = CString::from_vec_with_nul(bytes.clone()); + /// + /// assert_eq!(bytes, value.unwrap_err().into_bytes()); + /// ``` + /// + /// [`CString`]: struct.CString.html + pub fn into_bytes(self) -> Vec { + self.bytes + } +} + /// An error indicating invalid UTF-8 when converting a [`CString`] into a [`String`]. /// /// `CString` is just a wrapper over a buffer of bytes with a nul @@ -395,6 +473,12 @@ impl CString { /// ownership of a string that was allocated by foreign code) is likely to lead /// to undefined behavior or allocator corruption. /// + /// It should be noted that the length isn't just "recomputed," but that + /// the recomputed length must match the original length from the + /// [`into_raw`] call. This means the [`into_raw`]/`from_raw` methods + /// should not be used when passing the string to C functions that can + /// modify the string's length. + /// /// > **Note:** If you need to borrow a string that was allocated by /// > foreign code, use [`CStr`]. If you need to take ownership of /// > a string that was allocated by foreign code, you will need to @@ -440,6 +524,11 @@ impl CString { /// /// Failure to call [`from_raw`] will lead to a memory leak. /// + /// The C side must **not** modify the length of the string (by writing a + /// `NULL` somewhere inside the string or removing the final one) before + /// it makes it back into Rust using [`from_raw`]. See the safety section + /// in [`from_raw`]. + /// /// [`from_raw`]: #method.from_raw /// /// # Examples @@ -632,6 +721,86 @@ impl CString { let this = mem::ManuallyDrop::new(self); unsafe { ptr::read(&this.inner) } } + + /// Converts a `Vec` of `u8` to a `CString` without checking the invariants + /// on the given `Vec`. + /// + /// # Safety + /// + /// The given `Vec` **must** have one nul byte as its last element. + /// This means it cannot be empty nor have any other nul byte anywhere else. + /// + /// # Example + /// + /// ``` + /// #![feature(cstring_from_vec_with_nul)] + /// use std::ffi::CString; + /// assert_eq!( + /// unsafe { CString::from_vec_with_nul_unchecked(b"abc\0".to_vec()) }, + /// unsafe { CString::from_vec_unchecked(b"abc".to_vec()) } + /// ); + /// ``` + #[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] + pub unsafe fn from_vec_with_nul_unchecked(v: Vec) -> Self { + Self { inner: v.into_boxed_slice() } + } + + /// Attempts to converts a `Vec` of `u8` to a `CString`. + /// + /// Runtime checks are present to ensure there is only one nul byte in the + /// `Vec`, its last element. + /// + /// # Errors + /// + /// If a nul byte is present and not the last element or no nul bytes + /// is present, an error will be returned. + /// + /// # Examples + /// + /// A successful conversion will produce the same result as [`new`] when + /// called without the ending nul byte. + /// + /// ``` + /// #![feature(cstring_from_vec_with_nul)] + /// use std::ffi::CString; + /// assert_eq!( + /// CString::from_vec_with_nul(b"abc\0".to_vec()) + /// .expect("CString::from_vec_with_nul failed"), + /// CString::new(b"abc".to_vec()).expect("CString::new failed") + /// ); + /// ``` + /// + /// A incorrectly formatted vector will produce an error. + /// + /// ``` + /// #![feature(cstring_from_vec_with_nul)] + /// use std::ffi::{CString, FromVecWithNulError}; + /// // Interior nul byte + /// let _: FromVecWithNulError = CString::from_vec_with_nul(b"a\0bc".to_vec()).unwrap_err(); + /// // No nul byte + /// let _: FromVecWithNulError = CString::from_vec_with_nul(b"abc".to_vec()).unwrap_err(); + /// ``` + /// + /// [`new`]: #method.new + #[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] + pub fn from_vec_with_nul(v: Vec) -> Result { + let nul_pos = memchr::memchr(0, &v); + match nul_pos { + Some(nul_pos) if nul_pos + 1 == v.len() => { + // SAFETY: We know there is only one nul byte, at the end + // of the vec. + Ok(unsafe { Self::from_vec_with_nul_unchecked(v) }) + } + Some(nul_pos) => Err(FromVecWithNulError { + error_kind: FromBytesWithNulErrorKind::InteriorNul(nul_pos), + bytes: v, + }), + None => Err(FromVecWithNulError { + error_kind: FromBytesWithNulErrorKind::NotNulTerminated, + bytes: v, + }), + } + } } // Turns this `CString` into an empty string to prevent @@ -730,6 +899,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. @@ -954,6 +1134,23 @@ impl fmt::Display for FromBytesWithNulError { } } +#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +impl Error for FromVecWithNulError {} + +#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +impl fmt::Display for FromVecWithNulError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.error_kind { + FromBytesWithNulErrorKind::InteriorNul(pos) => { + write!(f, "data provided contains an interior nul byte at pos {}", pos) + } + FromBytesWithNulErrorKind::NotNulTerminated => { + write!(f, "data provided is not nul terminated") + } + } + } +} + impl IntoStringError { /// Consumes this error, returning original [`CString`] which generated the /// error. @@ -1329,6 +1526,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 +1713,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..f442d7fde1a5e 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. //! @@ -157,6 +157,8 @@ #[stable(feature = "cstr_from_bytes", since = "1.10.0")] pub use self::c_str::FromBytesWithNulError; +#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")] +pub use self::c_str::FromVecWithNulError; #[stable(feature = "rust1", since = "1.0.0")] pub use self::c_str::{CStr, CString, IntoStringError, NulError}; 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 7b1beb1ecda80..89dd9fb9b2cd5 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -1,103 +1,17 @@ //! Asynchronous values. -use core::cell::Cell; -use core::marker::Unpin; -use core::ops::{Drop, Generator, GeneratorState}; -use core::option::Option; -use core::pin::Pin; -use core::ptr::NonNull; -use core::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` -#[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. -#[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. -impl> !Unpin for GenFuture {} - -#[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( - #[cfg(not(bootstrap))] - (), - ) { - GeneratorState::Yielded(()) => Poll::Pending, - GeneratorState::Complete(x) => Poll::Ready(x), - } - } -} +pub use core::future::Future; -thread_local! { - static TLS_CX: Cell>>> = Cell::new(None); -} - -struct SetOnDrop(Option>>); - -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. -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) -} - -#[doc(hidden)] +#[doc(inline)] #[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); +pub use core::future::{from_generator, get_context, ResumeTy}; - 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.", - ); +#[doc(inline)] +#[unstable(feature = "future_readiness_fns", issue = "70921")] +pub use core::future::{pending, ready, Pending, Ready}; - // 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()) } -} +#[doc(inline)] +#[unstable(feature = "into_future", issue = "67644")] +pub use core::future::IntoFuture; diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 8862226adbbd3..0737008a94c9a 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -184,7 +184,6 @@ impl BufReader { /// # Examples /// /// ```no_run - /// #![feature(buffered_io_capacity)] /// use std::io::{BufReader, BufRead}; /// use std::fs::File; /// @@ -198,7 +197,7 @@ impl BufReader { /// Ok(()) /// } /// ``` - #[unstable(feature = "buffered_io_capacity", issue = "68833")] + #[stable(feature = "buffered_io_capacity", since = "1.46.0")] pub fn capacity(&self) -> usize { self.buf.len() } @@ -292,6 +291,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() @@ -363,7 +366,7 @@ impl Seek for BufReader { // it should be safe to assume that remainder fits within an i64 as the alternative // means we managed to allocate 8 exbibytes and that's absurd. // But it's not out of the realm of possibility for some weird underlying reader to - // support seeking by i64::min_value() so we need to handle underflow when subtracting + // support seeking by i64::MIN so we need to handle underflow when subtracting // remainder. if let Some(offset) = n.checked_sub(remainder) { result = self.inner.seek(SeekFrom::Current(offset))?; @@ -605,7 +608,6 @@ impl BufWriter { /// # Examples /// /// ```no_run - /// #![feature(buffered_io_capacity)] /// use std::io::BufWriter; /// use std::net::TcpStream; /// @@ -616,7 +618,7 @@ impl BufWriter { /// // Calculate how many bytes can be written without flushing /// let without_flush = capacity - buf_writer.buffer().len(); /// ``` - #[unstable(feature = "buffered_io_capacity", issue = "68833")] + #[stable(feature = "buffered_io_capacity", since = "1.46.0")] pub fn capacity(&self) -> usize { self.buf.capacity() } @@ -680,6 +682,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 +1049,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), @@ -1267,7 +1268,7 @@ mod tests { self.pos = self.pos.wrapping_add(n as u64); } SeekFrom::End(n) => { - self.pos = u64::max_value().wrapping_add(n as u64); + self.pos = u64::MAX.wrapping_add(n as u64); } } Ok(self.pos) @@ -1276,11 +1277,11 @@ mod tests { let mut reader = BufReader::with_capacity(5, PositionReader { pos: 0 }); assert_eq!(reader.fill_buf().ok(), Some(&[0, 1, 2, 3, 4][..])); - assert_eq!(reader.seek(SeekFrom::End(-5)).ok(), Some(u64::max_value() - 5)); + assert_eq!(reader.seek(SeekFrom::End(-5)).ok(), Some(u64::MAX - 5)); assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); // the following seek will require two underlying seeks let expected = 9223372036854775802; - assert_eq!(reader.seek(SeekFrom::Current(i64::min_value())).ok(), Some(expected)); + assert_eq!(reader.seek(SeekFrom::Current(i64::MIN)).ok(), Some(expected)); assert_eq!(reader.fill_buf().ok().map(|s| s.len()), Some(5)); // seeking to 0 should empty the buffer. assert_eq!(reader.seek(SeekFrom::Current(0)).ok(), Some(expected)); @@ -1318,7 +1319,7 @@ mod tests { // The following seek will require two underlying seeks. The first will // succeed but the second will fail. This should still invalidate the // buffer. - assert!(reader.seek(SeekFrom::Current(i64::min_value())).is_err()); + assert!(reader.seek(SeekFrom::Current(i64::MIN)).is_err()); assert_eq!(reader.buffer().len(), 0); } diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index f36aa1846a16c..f4db5f8145060 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(()) @@ -939,7 +963,7 @@ mod tests { #[cfg(target_pointer_width = "32")] fn vec_seek_and_write_past_usize_max() { let mut c = Cursor::new(Vec::new()); - c.set_position(::max_value() as u64 + 1); + c.set_position(usize::MAX as u64 + 1); assert!(c.write_all(&[1, 2, 3]).is_err()); } diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs index 3b55d9b900235..f7248e7547e27 100644 --- a/src/libstd/io/error.rs +++ b/src/libstd/io/error.rs @@ -160,6 +160,11 @@ pub enum ErrorKind { #[stable(feature = "rust1", since = "1.0.0")] Interrupted, /// Any I/O error not part of this list. + /// + /// Errors that are `Other` now may move to a different or a new + /// [`ErrorKind`] variant in the future. It is not recommended to match + /// an error against `Other` and to expect any additional characteristics, + /// e.g., a specific [`Error::raw_os_error`] return value. #[stable(feature = "rust1", since = "1.0.0")] Other, @@ -487,9 +492,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 dc831432c176f..717d2868abf98 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; @@ -502,7 +504,7 @@ pub trait Read { /// how many bytes were read. /// /// This function does not provide any guarantees about whether it blocks - /// waiting for data, but if an object needs to block for a read but cannot + /// waiting for data, but if an object needs to block for a read and cannot, /// it will typically signal this via an [`Err`] return value. /// /// If the return value of this method is [`Ok(n)`], then it must be @@ -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. /// @@ -1773,6 +1883,10 @@ pub trait BufRead: Read { /// /// If successful, this function will return the total number of bytes read. /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending the delimiter + /// or EOF. + /// /// # Errors /// /// This function will ignore all instances of [`ErrorKind::Interrupted`] and @@ -1835,6 +1949,10 @@ pub trait BufRead: Read { /// /// If this function returns `Ok(0)`, the stream has reached EOF. /// + /// This function is blocking and should be used carefully: it is possible for + /// an attacker to continuously send bytes without ever sending a newline + /// or EOF. + /// /// # Errors /// /// This function has the same error semantics as [`read_until`] and will @@ -2411,7 +2529,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 +2918,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 d410faca30d9e..b65b150d2c3a1 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -6,7 +6,7 @@ use crate::cell::RefCell; use crate::fmt; use crate::io::lazy::Lazy; use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter}; -use crate::sync::{Arc, Mutex, MutexGuard}; +use crate::sync::{Arc, Mutex, MutexGuard, Once}; use crate::sys::stdio; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use crate::thread::LocalKey; @@ -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() @@ -493,7 +533,11 @@ pub fn stdout() -> Stdout { Ok(stdout) => Maybe::Real(stdout), _ => Maybe::Fake, }; - Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout)))) + unsafe { + let ret = Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout)))); + ret.init(); + ret + } } } @@ -520,7 +564,7 @@ impl Stdout { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> StdoutLock<'_> { - StdoutLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } + StdoutLock { inner: self.inner.lock() } } } @@ -539,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() } @@ -557,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() } @@ -581,7 +633,7 @@ impl fmt::Debug for StdoutLock<'_> { /// an error. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { - inner: Arc>>>, + inner: &'static ReentrantMutex>>, } /// A locked reference to the `Stderr` handle. @@ -639,19 +691,28 @@ pub struct StderrLock<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn stderr() -> Stderr { - static INSTANCE: Lazy>>> = Lazy::new(); - return Stderr { - inner: unsafe { INSTANCE.get(stderr_init).expect("cannot access stderr during shutdown") }, - }; - - fn stderr_init() -> Arc>>> { - // This must not reentrantly access `INSTANCE` - let stderr = match stderr_raw() { - Ok(stderr) => Maybe::Real(stderr), - _ => Maybe::Fake, - }; - Arc::new(ReentrantMutex::new(RefCell::new(stderr))) - } + // Note that unlike `stdout()` we don't use `Lazy` here which registers a + // destructor. Stderr is not buffered nor does the `stderr_raw` type consume + // any owned resources, so there's no need to run any destructors at some + // point in the future. + // + // This has the added benefit of allowing `stderr` to be usable during + // process shutdown as well! + static INSTANCE: ReentrantMutex>> = + unsafe { ReentrantMutex::new(RefCell::new(Maybe::Fake)) }; + + // When accessing stderr we need one-time initialization of the reentrant + // mutex, followed by one-time detection of whether we actually have a + // stderr handle or not. Afterwards we can just always use the now-filled-in + // `INSTANCE` value. + static INIT: Once = Once::new(); + INIT.call_once(|| unsafe { + INSTANCE.init(); + if let Ok(stderr) = stderr_raw() { + *INSTANCE.lock().borrow_mut() = Maybe::Real(stderr); + } + }); + Stderr { inner: &INSTANCE } } impl Stderr { @@ -677,7 +738,7 @@ impl Stderr { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> StderrLock<'_> { - StderrLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } + StderrLock { inner: self.inner.lock() } } } @@ -696,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() } @@ -714,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() } @@ -792,10 +861,14 @@ fn print_to( { let result = local_s .try_with(|s| { - if let Ok(mut borrowed) = s.try_borrow_mut() { - if let Some(w) = borrowed.as_mut() { - return w.write_fmt(args); - } + // Note that we completely remove a local sink to write to in case + // our printing recursively panics/prints, so the recursive + // panic/print goes to the global sink instead of our local sink. + let prev = s.borrow_mut().take(); + if let Some(mut w) = prev { + let result = w.write_fmt(args); + *s.borrow_mut() = Some(w); + return result; } global_s().write_fmt(args) }) 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 2702de15b858a..6d98c4d01c040 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -234,12 +234,55 @@ mod crate_keyword {} #[doc(keyword = "else")] // -/// What to do when an [`if`] condition does not hold. +/// What expression to evaluate when an [`if`] condition evaluates to [`false`]. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// `else` expressions are optional. When no else expressions are supplied it is assumed to evaluate +/// to the unit type `()`. +/// +/// The type that the `else` blocks evaluate to must be compatible with the type that the `if` block +/// evaluates to. +/// +/// As can be seen below, `else` must be followed by either: `if`, `if let`, or a block `{}` and it +/// will return the value of that expression. +/// +/// ```rust +/// let result = if true == false { +/// "oh no" +/// } else if "something" == "other thing" { +/// "oh dear" +/// } else if let Some(200) = "blarg".parse::().ok() { +/// "uh oh" +/// } else { +/// println!("Sneaky side effect."); +/// "phew, nothing's broken" +/// }; +/// ``` +/// +/// Here's another example but here we do not try and return an expression: +/// +/// ```rust +/// if true == false { +/// println!("oh no"); +/// } else if "something" == "other thing" { +/// println!("oh dear"); +/// } else if let Some(200) = "blarg".parse::().ok() { +/// println!("uh oh"); +/// } else { +/// println!("phew, nothing's broken"); +/// } +/// ``` +/// +/// The above is _still_ an expression but it will always evaluate to `()`. /// +/// There is possibly no limit to the number of `else` blocks that could follow an `if` expression +/// however if you have several then a [`match`] expression might be preferable. +/// +/// Read more about control flow in the [Rust Book]. +/// +/// [Rust Book]: ../book/ch03-05-control-flow.html#handling-multiple-conditions-with-else-if +/// [`match`]: keyword.match.html +/// [`false`]: keyword.false.html /// [`if`]: keyword.if.html -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 mod else_keyword {} #[doc(keyword = "enum")] @@ -637,10 +680,18 @@ mod impl_keyword {} // /// Iterate over a series of values with [`for`]. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// The expression immediately following `in` must implement the [`Iterator`] trait. /// +/// ## 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 including 3. +/// +/// (Read more about [range patterns]) +/// +/// [`Iterator`]: ../book/ch13-04-performance.html +/// [range patterns]: ../reference/patterns.html?highlight=range#range-patterns /// [`for`]: keyword.for.html -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 mod in_keyword {} #[doc(keyword = "let")] @@ -958,9 +1009,93 @@ mod return_keyword {} // /// The receiver of a method, or the current module. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// `self` is used in two situations: referencing the current module and marking +/// the receiver of a method. /// -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +/// In paths, `self` can be used to refer to the current module, either in a +/// [`use`] statement or in a path to access an element: +/// +/// ``` +/// # #![allow(unused_imports)] +/// use std::io::{self, Read}; +/// ``` +/// +/// Is functionally the same as: +/// +/// ``` +/// # #![allow(unused_imports)] +/// use std::io; +/// use std::io::Read; +/// ``` +/// +/// Using `self` to access an element in the current module: +/// +/// ``` +/// # #![allow(dead_code)] +/// # fn main() {} +/// fn foo() {} +/// fn bar() { +/// self::foo() +/// } +/// ``` +/// +/// `self` as the current receiver for a method allows to omit the parameter +/// type most of the time. With the exception of this particularity, `self` is +/// used much like any other parameter: +/// +/// ``` +/// struct Foo(i32); +/// +/// impl Foo { +/// // No `self`. +/// fn new() -> Self { +/// Self(0) +/// } +/// +/// // Consuming `self`. +/// fn consume(self) -> Self { +/// Self(self.0 + 1) +/// } +/// +/// // Borrowing `self`. +/// fn borrow(&self) -> &i32 { +/// &self.0 +/// } +/// +/// // Borrowing `self` mutably. +/// fn borrow_mut(&mut self) -> &mut i32 { +/// &mut self.0 +/// } +/// } +/// +/// // This method must be called with a `Type::` prefix. +/// let foo = Foo::new(); +/// assert_eq!(foo.0, 0); +/// +/// // Those two calls produces the same result. +/// let foo = Foo::consume(foo); +/// assert_eq!(foo.0, 1); +/// let foo = foo.consume(); +/// assert_eq!(foo.0, 2); +/// +/// // Borrowing is handled automatically with the second syntax. +/// let borrow_1 = Foo::borrow(&foo); +/// let borrow_2 = foo.borrow(); +/// assert_eq!(borrow_1, borrow_2); +/// +/// // Borrowing mutably is handled automatically too with the second syntax. +/// let mut foo = Foo::new(); +/// *Foo::borrow_mut(&mut foo) += 1; +/// assert_eq!(foo.0, 1); +/// *foo.borrow_mut() += 1; +/// assert_eq!(foo.0, 2); +/// ``` +/// +/// Note that this automatic conversion when calling `foo.method()` is not +/// limited to the examples above. See the [Reference] for more information. +/// +/// [`use`]: keyword.use.html +/// [Reference]: ../reference/items/associated-items.html#methods mod self_keyword {} #[doc(keyword = "Self")] @@ -1162,9 +1297,61 @@ mod unsafe_keyword {} // /// Import or rename items from other crates or modules. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// Usually a `use` keyword is used to shorten the path required to refer to a module item. +/// The keyword may appear in modules, blocks and even functions, usually at the top. +/// +/// The most basic usage of the keyword is `use path::to::item;`, +/// though a number of convenient shortcuts are supported: +/// +/// * Simultaneously binding a list of paths with a common prefix, +/// using the glob-like brace syntax `use a::b::{c, d, e::f, g::h::i};` +/// * Simultaneously binding a list of paths with a common prefix and their common parent module, +/// using the [`self`] keyword, such as `use a::b::{self, c, d::e};` +/// * Rebinding the target name as a new local name, using the syntax `use p::q::r as x;`. +/// This can also be used with the last two features: `use a::b::{self as ab, c as abc}`. +/// * Binding all paths matching a given prefix, +/// using the asterisk wildcard syntax `use a::b::*;`. +/// * Nesting groups of the previous features multiple times, +/// such as `use a::b::{self as ab, c, d::{*, e::f}};` +/// * Reexporting with visibility modifiers such as `pub use a::b;` +/// * Importing with `_` to only import the methods of a trait without binding it to a name +/// (to avoid conflict for example): `use ::std::io::Read as _;`. +/// +/// Using path qualifiers like [`crate`], [`super`] or [`self`] is supported: `use crate::a::b;`. +/// +/// Note that when the wildcard `*` is used on a type, it does not import its methods (though +/// for `enum`s it imports the variants, as shown in the example below). +/// +/// ```compile_fail,edition2018 +/// enum ExampleEnum { +/// VariantA, +/// VariantB, +/// } /// -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 +/// impl ExampleEnum { +/// fn new() -> Self { +/// Self::VariantA +/// } +/// } +/// +/// use ExampleEnum::*; +/// +/// // Compiles. +/// let _ = VariantA; +/// +/// // Does not compile ! +/// let n = new(); +/// ``` +/// +/// For more information on `use` and paths in general, see the [Reference]. +/// +/// The differences about paths and the `use` keyword between the 2015 and 2018 editions +/// can also be found in the [Reference]. +/// +/// [`crate`]: keyword.crate.html +/// [`self`]: keyword.self.html +/// [`super`]: keyword.super.html +/// [Reference]: ../reference/items/use-declarations.html mod use_keyword {} #[doc(keyword = "where")] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 231908ddda0ae..ef699ede2a140 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,9 +242,12 @@ #![feature(atomic_mut_ptr)] #![feature(box_syntax)] #![feature(c_variadic)] +#![feature(cfg_accessible)] +#![feature(can_vector)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] +#![feature(char_internals)] #![feature(clamp)] #![feature(concat_idents)] #![feature(const_cstr_unchecked)] @@ -259,40 +264,48 @@ #![feature(duration_constants)] #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] +#![feature(extend_one)] #![feature(external_doc)] #![feature(fn_traits)] #![feature(format_args_nl)] +#![feature(future_readiness_fns)] +#![feature(gen_future)] #![feature(generator_trait)] #![feature(global_asm)] #![feature(hash_raw_entry)] #![feature(hashmap_internals)] #![feature(int_error_internals)] #![feature(int_error_matching)] +#![feature(into_future)] #![feature(integer_atomics)] #![feature(lang_items)] #![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)] #![feature(prelude_import)] #![feature(ptr_internals)] #![feature(raw)] +#![feature(raw_ref_macros)] #![feature(renamed_spin_loop)] #![feature(rustc_attrs)] #![feature(rustc_private)] #![feature(shrink_to)] #![feature(slice_concat_ext)] #![feature(slice_internals)] -#![feature(specialization)] +#![feature(min_specialization)] #![feature(staged_api)] #![feature(std_internals)] #![feature(stdsimd)] @@ -301,6 +314,7 @@ #![feature(test)] #![feature(thread_local)] #![feature(toowned_clone_into)] +#![feature(total_cmp)] #![feature(trace_macros)] #![feature(track_caller)] #![feature(try_reserve)] @@ -308,6 +322,7 @@ #![feature(untagged_unions)] #![feature(unwind_attributes)] #![feature(vec_into_raw_parts)] +#![feature(wake_trait)] // NB: the above list is sorted to minimize merge conflicts. #![default_lib_allocator] @@ -461,9 +476,14 @@ pub mod time; #[stable(feature = "futures_api", since = "1.36.0")] pub mod task { //! Types and Traits for working with asynchronous tasks. + #[doc(inline)] #[stable(feature = "futures_api", since = "1.36.0")] pub use core::task::*; + + #[doc(inline)] + #[unstable(feature = "wake_trait", issue = "69912")] + pub use alloc::task::*; } #[stable(feature = "futures_api", since = "1.36.0")] @@ -505,48 +525,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/addr.rs b/src/libstd/net/addr.rs index 57cba6b1f7a1b..8c8d1aadf48e2 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -1,7 +1,8 @@ +use crate::cmp::Ordering; use crate::convert::TryInto; use crate::fmt; use crate::hash; -use crate::io; +use crate::io::{self, Write}; use crate::iter; use crate::mem; use crate::net::{htons, ntohs, IpAddr, Ipv4Addr, Ipv6Addr}; @@ -36,7 +37,7 @@ use crate::vec; /// assert_eq!(socket.port(), 8080); /// assert_eq!(socket.is_ipv4(), true); /// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] pub enum SocketAddr { /// An IPv4 socket address. @@ -599,7 +600,26 @@ impl fmt::Display for SocketAddr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for SocketAddrV4 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}:{}", self.ip(), self.port()) + // Fast path: if there's no alignment stuff, write to the output buffer + // directly + if f.precision().is_none() && f.width().is_none() { + write!(f, "{}:{}", self.ip(), self.port()) + } else { + const IPV4_SOCKET_BUF_LEN: usize = (3 * 4) // the segments + + 3 // the separators + + 1 + 5; // the port + let mut buf = [0; IPV4_SOCKET_BUF_LEN]; + let mut buf_slice = &mut buf[..]; + + // Unwrap is fine because writing to a sufficiently-sized + // buffer is infallible + write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap(); + let len = IPV4_SOCKET_BUF_LEN - buf_slice.len(); + + // This unsafe is OK because we know what is being written to the buffer + let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; + f.pad(buf) + } } } @@ -613,7 +633,28 @@ impl fmt::Debug for SocketAddrV4 { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for SocketAddrV6 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "[{}]:{}", self.ip(), self.port()) + // Fast path: if there's no alignment stuff, write to the output + // buffer directly + if f.precision().is_none() && f.width().is_none() { + write!(f, "[{}]:{}", self.ip(), self.port()) + } else { + const IPV6_SOCKET_BUF_LEN: usize = (4 * 8) // The address + + 7 // The colon separators + + 2 // The brackets + + 1 + 5; // The port + + let mut buf = [0; IPV6_SOCKET_BUF_LEN]; + let mut buf_slice = &mut buf[..]; + + // Unwrap is fine because writing to a sufficiently-sized + // buffer is infallible + write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap(); + let len = IPV6_SOCKET_BUF_LEN - buf_slice.len(); + + // This unsafe is OK because we know what is being written to the buffer + let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; + f.pad(buf) + } } } @@ -658,6 +699,34 @@ impl Eq for SocketAddrV4 {} #[stable(feature = "rust1", since = "1.0.0")] impl Eq for SocketAddrV6 {} +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl PartialOrd for SocketAddrV4 { + fn partial_cmp(&self, other: &SocketAddrV4) -> Option { + Some(self.cmp(other)) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl PartialOrd for SocketAddrV6 { + fn partial_cmp(&self, other: &SocketAddrV6) -> Option { + Some(self.cmp(other)) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl Ord for SocketAddrV4 { + fn cmp(&self, other: &SocketAddrV4) -> Ordering { + self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl Ord for SocketAddrV6 { + fn cmp(&self, other: &SocketAddrV6) -> Ordering { + self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl hash::Hash for SocketAddrV4 { fn hash(&self, s: &mut H) { @@ -895,6 +964,14 @@ impl ToSocketAddrs for (&str, u16) { } } +#[stable(feature = "string_u16_to_socket_addrs", since = "1.46.0")] +impl ToSocketAddrs for (String, u16) { + type Iter = vec::IntoIter; + fn to_socket_addrs(&self) -> io::Result> { + (&*self.0, self.1).to_socket_addrs() + } +} + // accepts strings like 'localhost:12345' #[stable(feature = "rust1", since = "1.0.0")] impl ToSocketAddrs for str { @@ -989,11 +1066,26 @@ mod tests { // s has been moved into the tsa call } - // FIXME: figure out why this fails on openbsd and fix it #[test] - #[cfg(not(any(windows, target_os = "openbsd")))] - fn to_socket_addr_str_bad() { - assert!(tsa("1200::AB00:1234::2552:7777:1313:34300").is_err()); + fn bind_udp_socket_bad() { + // rust-lang/rust#53957: This is a regression test for a parsing problem + // discovered as part of issue rust-lang/rust#23076, where we were + // incorrectly parsing invalid input and then that would result in a + // successful `UdpSocket` binding when we would expect failure. + // + // At one time, this test was written as a call to `tsa` with + // INPUT_23076. However, that structure yields an unreliable test, + // because it ends up passing junk input to the DNS server, and some DNS + // servers will respond with `Ok` to such input, with the ip address of + // the DNS server itself. + // + // This form of the test is more robust: even when the DNS server + // returns its own address, it is still an error to bind a UDP socket to + // a non-local address, and so we still get an error here in that case. + + const INPUT_23076: &'static str = "1200::AB00:1234::2552:7777:1313:34300"; + + assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err()) } #[test] @@ -1087,4 +1179,67 @@ mod tests { assert!(!v6.is_ipv4()); assert!(v6.is_ipv6()); } + + #[test] + fn socket_v4_to_str() { + let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080); + + assert_eq!(format!("{}", socket), "192.168.0.1:8080"); + assert_eq!(format!("{:<20}", socket), "192.168.0.1:8080 "); + assert_eq!(format!("{:>20}", socket), " 192.168.0.1:8080"); + assert_eq!(format!("{:^20}", socket), " 192.168.0.1:8080 "); + assert_eq!(format!("{:.10}", socket), "192.168.0."); + } + + #[test] + fn socket_v6_to_str() { + let socket: SocketAddrV6 = "[2a02:6b8:0:1::1]:53".parse().unwrap(); + + assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1]:53"); + assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1]:53 "); + assert_eq!(format!("{:>24}", socket), " [2a02:6b8:0:1::1]:53"); + assert_eq!(format!("{:^24}", socket), " [2a02:6b8:0:1::1]:53 "); + assert_eq!(format!("{:.15}", socket), "[2a02:6b8:0:1::"); + } + + #[test] + fn compare() { + let v4_1 = "224.120.45.1:23456".parse::().unwrap(); + let v4_2 = "224.210.103.5:12345".parse::().unwrap(); + let v4_3 = "224.210.103.5:23456".parse::().unwrap(); + let v6_1 = "[2001:db8:f00::1002]:23456".parse::().unwrap(); + let v6_2 = "[2001:db8:f00::2001]:12345".parse::().unwrap(); + let v6_3 = "[2001:db8:f00::2001]:23456".parse::().unwrap(); + + // equality + assert_eq!(v4_1, v4_1); + assert_eq!(v6_1, v6_1); + assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1)); + assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1)); + assert!(v4_1 != v4_2); + assert!(v6_1 != v6_2); + + // compare different addresses + assert!(v4_1 < v4_2); + assert!(v6_1 < v6_2); + assert!(v4_2 > v4_1); + assert!(v6_2 > v6_1); + + // compare the same address with different ports + assert!(v4_2 < v4_3); + assert!(v6_2 < v6_3); + assert!(v4_3 > v4_2); + assert!(v6_3 > v6_2); + + // compare different addresses with the same port + assert!(v4_1 < v4_3); + assert!(v6_1 < v6_3); + assert!(v4_3 > v4_1); + assert!(v6_3 > v6_1); + + // compare with an inferred right-hand side + assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap()); + assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap()); + assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap()); + } } diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index edc28033c9b83..0f0be2c488314 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -7,9 +7,9 @@ )] use crate::cmp::Ordering; -use crate::fmt; +use crate::fmt::{self, Write as FmtWrite}; use crate::hash; -use crate::io::Write; +use crate::io::Write as IoWrite; use crate::sys::net::netc as c; use crate::sys_common::{AsInner, FromInner}; @@ -856,16 +856,23 @@ impl From for IpAddr { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Ipv4Addr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - const IPV4_BUF_LEN: usize = 15; // Long enough for the longest possible IPv4 address - let mut buf = [0u8; IPV4_BUF_LEN]; - let mut buf_slice = &mut buf[..]; let octets = self.octets(); - // Note: The call to write should never fail, hence the unwrap - write!(buf_slice, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap(); - let len = IPV4_BUF_LEN - buf_slice.len(); - // This unsafe is OK because we know what is being written to the buffer - let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; - fmt.pad(buf) + // Fast Path: if there's no alignment stuff, write directly to the buffer + if fmt.precision().is_none() && fmt.width().is_none() { + write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) + } else { + const IPV4_BUF_LEN: usize = 15; // Long enough for the longest possible IPv4 address + let mut buf = [0u8; IPV4_BUF_LEN]; + let mut buf_slice = &mut buf[..]; + + // Note: The call to write should never fail, hence the unwrap + write!(buf_slice, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap(); + let len = IPV4_BUF_LEN - buf_slice.len(); + + // This unsafe is OK because we know what is being written to the buffer + let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; + fmt.pad(buf) + } } } @@ -1525,102 +1532,100 @@ impl Ipv6Addr { } } +/// Write an Ipv6Addr, conforming to the canonical style described by +/// [RFC 5952](https://tools.ietf.org/html/rfc5952). #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Ipv6Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - // Note: The calls to write should never fail, hence the unwraps in the function - // Long enough for the longest possible IPv6: 39 - const IPV6_BUF_LEN: usize = 39; - let mut buf = [0u8; IPV6_BUF_LEN]; - let mut buf_slice = &mut buf[..]; + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // If there are no alignment requirements, write out the IP address to + // f. Otherwise, write it to a local buffer, then use f.pad. + if f.precision().is_none() && f.width().is_none() { + let segments = self.segments(); + + // Special case for :: and ::1; otherwise they get written with the + // IPv4 formatter + if self.is_unspecified() { + f.write_str("::") + } else if self.is_loopback() { + f.write_str("::1") + } else if let Some(ipv4) = self.to_ipv4() { + match segments[5] { + // IPv4 Compatible address + 0 => write!(f, "::{}", ipv4), + // IPv4 Mapped address + 0xffff => write!(f, "::ffff:{}", ipv4), + _ => unreachable!(), + } + } else { + #[derive(Copy, Clone, Default)] + struct Span { + start: usize, + len: usize, + } - match self.segments() { - // We need special cases for :: and ::1, otherwise they're formatted - // as ::0.0.0.[01] - [0, 0, 0, 0, 0, 0, 0, 0] => write!(buf_slice, "::").unwrap(), - [0, 0, 0, 0, 0, 0, 0, 1] => write!(buf_slice, "::1").unwrap(), - // Ipv4 Compatible address - [0, 0, 0, 0, 0, 0, g, h] => { - write!( - buf_slice, - "::{}.{}.{}.{}", - (g >> 8) as u8, - g as u8, - (h >> 8) as u8, - h as u8 - ) - .unwrap(); - } - // Ipv4-Mapped address - [0, 0, 0, 0, 0, 0xffff, g, h] => { - write!( - buf_slice, - "::ffff:{}.{}.{}.{}", - (g >> 8) as u8, - g as u8, - (h >> 8) as u8, - h as u8 - ) - .unwrap(); - } - _ => { - fn find_zero_slice(segments: &[u16; 8]) -> (usize, usize) { - let mut longest_span_len = 0; - let mut longest_span_at = 0; - let mut cur_span_len = 0; - let mut cur_span_at = 0; - - for i in 0..8 { - if segments[i] == 0 { - if cur_span_len == 0 { - cur_span_at = i; + // Find the inner 0 span + let zeroes = { + let mut longest = Span::default(); + let mut current = Span::default(); + + for (i, &segment) in segments.iter().enumerate() { + if segment == 0 { + if current.len == 0 { + current.start = i; } - cur_span_len += 1; + current.len += 1; - if cur_span_len > longest_span_len { - longest_span_len = cur_span_len; - longest_span_at = cur_span_at; + if current.len > longest.len { + longest = current; } } else { - cur_span_len = 0; - cur_span_at = 0; + current = Span::default(); } } - (longest_span_at, longest_span_len) - } - - let (zeros_at, zeros_len) = find_zero_slice(&self.segments()); - - if zeros_len > 1 { - fn fmt_subslice(segments: &[u16], buf: &mut &mut [u8]) { - if !segments.is_empty() { - write!(*buf, "{:x}", segments[0]).unwrap(); - for &seg in &segments[1..] { - write!(*buf, ":{:x}", seg).unwrap(); - } + longest + }; + + /// Write a colon-separated part of the address + #[inline] + fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { + if let Some(first) = chunk.first() { + fmt::LowerHex::fmt(first, f)?; + for segment in &chunk[1..] { + f.write_char(':')?; + fmt::LowerHex::fmt(segment, f)?; } } + Ok(()) + } - fmt_subslice(&self.segments()[..zeros_at], &mut buf_slice); - write!(buf_slice, "::").unwrap(); - fmt_subslice(&self.segments()[zeros_at + zeros_len..], &mut buf_slice); + if zeroes.len > 1 { + fmt_subslice(f, &segments[..zeroes.start])?; + f.write_str("::")?; + fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) } else { - let &[a, b, c, d, e, f, g, h] = &self.segments(); - write!( - buf_slice, - "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}", - a, b, c, d, e, f, g, h - ) - .unwrap(); + fmt_subslice(f, &segments) } } + } else { + // Slow path: write the address to a local buffer, the use f.pad. + // Defined recursively by using the fast path to write to the + // buffer. + + // This is the largest possible size of an IPv6 address + const IPV6_BUF_LEN: usize = (4 * 8) + 7; + let mut buf = [0u8; IPV6_BUF_LEN]; + let mut buf_slice = &mut buf[..]; + + // Note: This call to write should never fail, so unwrap is okay. + write!(buf_slice, "{}", self).unwrap(); + let len = IPV6_BUF_LEN - buf_slice.len(); + + // This is safe because we know exactly what can be in this buffer + let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; + f.pad(buf) } - let len = IPV6_BUF_LEN - buf_slice.len(); - // This is safe because we know exactly what can be in this buffer - let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) }; - fmt.pad(buf) } } 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/num.rs b/src/libstd/num.rs index de8acf8d9d472..b496c16a749cf 100644 --- a/src/libstd/num.rs +++ b/src/libstd/num.rs @@ -52,52 +52,43 @@ where #[cfg(test)] mod tests { use crate::ops::Mul; - use crate::u16; - use crate::u32; - use crate::u64; - use crate::u8; - use crate::usize; #[test] fn test_saturating_add_uint() { - use crate::usize::MAX; assert_eq!(3_usize.saturating_add(5_usize), 8_usize); - assert_eq!(3_usize.saturating_add(MAX - 1), MAX); - assert_eq!(MAX.saturating_add(MAX), MAX); - assert_eq!((MAX - 2).saturating_add(1), MAX - 1); + assert_eq!(3_usize.saturating_add(usize::MAX - 1), usize::MAX); + assert_eq!(usize::MAX.saturating_add(usize::MAX), usize::MAX); + assert_eq!((usize::MAX - 2).saturating_add(1), usize::MAX - 1); } #[test] fn test_saturating_sub_uint() { - use crate::usize::MAX; assert_eq!(5_usize.saturating_sub(3_usize), 2_usize); assert_eq!(3_usize.saturating_sub(5_usize), 0_usize); assert_eq!(0_usize.saturating_sub(1_usize), 0_usize); - assert_eq!((MAX - 1).saturating_sub(MAX), 0); + assert_eq!((usize::MAX - 1).saturating_sub(usize::MAX), 0); } #[test] fn test_saturating_add_int() { - use crate::isize::{MAX, MIN}; assert_eq!(3i32.saturating_add(5), 8); - assert_eq!(3isize.saturating_add(MAX - 1), MAX); - assert_eq!(MAX.saturating_add(MAX), MAX); - assert_eq!((MAX - 2).saturating_add(1), MAX - 1); + assert_eq!(3isize.saturating_add(isize::MAX - 1), isize::MAX); + assert_eq!(isize::MAX.saturating_add(isize::MAX), isize::MAX); + assert_eq!((isize::MAX - 2).saturating_add(1), isize::MAX - 1); assert_eq!(3i32.saturating_add(-5), -2); - assert_eq!(MIN.saturating_add(-1), MIN); - assert_eq!((-2isize).saturating_add(-MAX), MIN); + assert_eq!(isize::MIN.saturating_add(-1), isize::MIN); + assert_eq!((-2isize).saturating_add(-isize::MAX), isize::MIN); } #[test] fn test_saturating_sub_int() { - use crate::isize::{MAX, MIN}; assert_eq!(3i32.saturating_sub(5), -2); - assert_eq!(MIN.saturating_sub(1), MIN); - assert_eq!((-2isize).saturating_sub(MAX), MIN); + assert_eq!(isize::MIN.saturating_sub(1), isize::MIN); + assert_eq!((-2isize).saturating_sub(isize::MAX), isize::MIN); assert_eq!(3i32.saturating_sub(-5), 8); - assert_eq!(3isize.saturating_sub(-(MAX - 1)), MAX); - assert_eq!(MAX.saturating_sub(-MAX), MAX); - assert_eq!((MAX - 2).saturating_sub(-1), MAX - 1); + assert_eq!(3isize.saturating_sub(-(isize::MAX - 1)), isize::MAX); + assert_eq!(isize::MAX.saturating_sub(-isize::MAX), isize::MAX); + assert_eq!((isize::MAX - 2).saturating_sub(-1), isize::MAX - 1); } #[test] diff --git a/src/libstd/os/illumos/fs.rs b/src/libstd/os/illumos/fs.rs new file mode 100644 index 0000000000000..b668aa2595d67 --- /dev/null +++ b/src/libstd/os/illumos/fs.rs @@ -0,0 +1,116 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::illumos::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: ../../../../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/linux/raw.rs b/src/libstd/os/linux/raw.rs index 0caec97bb7b90..eb8589eb58f47 100644 --- a/src/libstd/os/linux/raw.rs +++ b/src/libstd/os/linux/raw.rs @@ -170,7 +170,7 @@ mod arch { #[cfg(target_arch = "hexagon")] mod arch { - use crate::os::raw::{c_int, c_long, c_longlong, culonglong}; + use crate::os::raw::{c_int, c_long, c_longlong, c_ulonglong}; #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = c_longlong; 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..d22ac1d538584 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -201,8 +201,7 @@ fn default_hook(info: &PanicInfo<'_>) { if FIRST_PANIC.swap(false, Ordering::SeqCst) { let _ = writeln!( err, - "note: run with `RUST_BACKTRACE=1` \ - environment variable to display a backtrace" + "note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace" ); } } @@ -271,44 +270,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 +287,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 +331,7 @@ 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() } + intrinsics::abort() } let info = PanicInfo::internal_constructor(Some(msg), Location::caller()); @@ -432,7 +397,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { #[track_caller] pub fn begin_panic(msg: M) -> ! { if cfg!(feature = "panic_immediate_abort") { - unsafe { intrinsics::abort() } + intrinsics::abort() } rust_panic_with_hook(&mut PanicPayload::new(msg), None, Location::caller()); @@ -488,11 +453,8 @@ fn rust_panic_with_hook( // process real quickly as we don't want to try calling it again as it'll // probably just panic again. if panics > 2 { - util::dumb_print(format_args!( - "thread panicked while processing \ - panic. aborting.\n" - )); - unsafe { intrinsics::abort() } + util::dumb_print(format_args!("thread panicked while processing panic. aborting.\n")); + intrinsics::abort() } unsafe { @@ -523,11 +485,8 @@ fn rust_panic_with_hook( // have limited options. Currently our preference is to // just abort. In the future we may consider resuming // unwinding or otherwise exiting the thread cleanly. - util::dumb_print(format_args!( - "thread panicked while panicking. \ - aborting.\n" - )); - unsafe { intrinsics::abort() } + util::dumb_print(format_args!("thread panicked while panicking. aborting.\n")); + intrinsics::abort() } rust_panic(payload) diff --git a/src/libstd/path.rs b/src/libstd/path.rs index b8361d3e82599..8ff7508ba6457 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` @@ -1524,6 +1534,11 @@ impl> iter::Extend

` (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-67651.rs b/src/test/ui/async-await/issue-67651.rs new file mode 100644 index 0000000000000..bd96a3b709bae --- /dev/null +++ b/src/test/ui/async-await/issue-67651.rs @@ -0,0 +1,20 @@ +// edition:2018 + +trait From { + fn from(); +} + +impl From for () { + fn from() {} +} + +impl From for () { +//~^ ERROR conflicting implementations of trait + fn from() {} +} + +fn bar() -> impl core::future::Future { + async move { From::from() } +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-67651.stderr b/src/test/ui/async-await/issue-67651.stderr new file mode 100644 index 0000000000000..99857c215eb8f --- /dev/null +++ b/src/test/ui/async-await/issue-67651.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `From` for type `()`: + --> $DIR/issue-67651.rs:11:1 + | +LL | impl From for () { + | ---------------- first implementation here +... +LL | impl From for () { + | ^^^^^^^^^^^^^^^^ conflicting implementation for `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. 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-69446-fnmut-capture.rs b/src/test/ui/async-await/issue-69446-fnmut-capture.rs new file mode 100644 index 0000000000000..842115538c9d8 --- /dev/null +++ b/src/test/ui/async-await/issue-69446-fnmut-capture.rs @@ -0,0 +1,22 @@ +// Regression test for issue #69446 - we should display +// which variable is captured +// edition:2018 + +use core::future::Future; + +struct Foo; +impl Foo { + fn foo(&mut self) {} +} + +async fn bar(_: impl FnMut() -> T) +where + T: Future, +{} + +fn main() { + let mut x = Foo; + bar(move || async { //~ ERROR captured + x.foo(); + }); +} diff --git a/src/test/ui/async-await/issue-69446-fnmut-capture.stderr b/src/test/ui/async-await/issue-69446-fnmut-capture.stderr new file mode 100644 index 0000000000000..3d2b0402bc52c --- /dev/null +++ b/src/test/ui/async-await/issue-69446-fnmut-capture.stderr @@ -0,0 +1,19 @@ +error: captured variable cannot escape `FnMut` closure body + --> $DIR/issue-69446-fnmut-capture.rs:19:17 + | +LL | let mut x = Foo; + | ----- variable defined here +LL | bar(move || async { + | _______________-_^ + | | | + | | inferred to be a `FnMut` closure +LL | | x.foo(); + | | - variable captured here +LL | | }); + | |_____^ returns an `async` block that contains a reference to a captured variable, which then escapes the closure body + | + = note: `FnMut` closures only have access to their captured variables while they are executing... + = note: ...therefore, they cannot allow references to captured variables to escape + +error: aborting due to previous error + 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/issue-72442.rs b/src/test/ui/async-await/issue-72442.rs new file mode 100644 index 0000000000000..61c8c8c1594d3 --- /dev/null +++ b/src/test/ui/async-await/issue-72442.rs @@ -0,0 +1,26 @@ +// edition:2018 +// compile-flags:-Cincremental=tmp/issue-72442 + +use std::fs::File; +use std::future::Future; +use std::io::prelude::*; + +fn main() -> Result<(), Box> { + block_on(async { + { + let path = std::path::Path::new("."); + let mut f = File::open(path.to_str())?; + //~^ ERROR the trait bound + let mut src = String::new(); + f.read_to_string(&mut src)?; + Ok(()) + } + }) +} + +fn block_on(f: F) -> F::Output +where + F: Future>>, +{ + Ok(()) +} diff --git a/src/test/ui/async-await/issue-72442.stderr b/src/test/ui/async-await/issue-72442.stderr new file mode 100644 index 0000000000000..5685433357871 --- /dev/null +++ b/src/test/ui/async-await/issue-72442.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `std::option::Option<&str>: std::convert::AsRef` is not satisfied + --> $DIR/issue-72442.rs:12:36 + | +LL | let mut f = File::open(path.to_str())?; + | ^^^^^^^^^^^^^ the trait `std::convert::AsRef` is not implemented for `std::option::Option<&str>` + | + ::: $SRC_DIR/libstd/fs.rs:LL:COL + | +LL | pub fn open>(path: P) -> io::Result { + | ----------- required by this bound in `std::fs::File::open` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/issue-72590-type-error-sized.rs b/src/test/ui/async-await/issue-72590-type-error-sized.rs new file mode 100644 index 0000000000000..00e098d43e073 --- /dev/null +++ b/src/test/ui/async-await/issue-72590-type-error-sized.rs @@ -0,0 +1,22 @@ +// Regression test for issue #72590 +// Tests that we don't emit a spurious "size cannot be statically determined" error +// edition:2018 + +struct Foo { + foo: Nonexistent, //~ ERROR cannot find + other: str +} + +struct Bar { + test: Missing //~ ERROR cannot find +} + +impl Foo { + async fn frob(self) {} //~ ERROR the size +} + +impl Bar { + async fn myfn(self) {} +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-72590-type-error-sized.stderr b/src/test/ui/async-await/issue-72590-type-error-sized.stderr new file mode 100644 index 0000000000000..603895b598c16 --- /dev/null +++ b/src/test/ui/async-await/issue-72590-type-error-sized.stderr @@ -0,0 +1,28 @@ +error[E0412]: cannot find type `Nonexistent` in this scope + --> $DIR/issue-72590-type-error-sized.rs:6:10 + | +LL | foo: Nonexistent, + | ^^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `Missing` in this scope + --> $DIR/issue-72590-type-error-sized.rs:11:11 + | +LL | test: Missing + | ^^^^^^^ not found in this scope + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/issue-72590-type-error-sized.rs:15:19 + | +LL | async fn frob(self) {} + | ^^^^ doesn't have a size known at compile-time + | + = help: within `Foo`, the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + = note: required because it appears within the type `Foo` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0412. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/issue-73050.rs b/src/test/ui/async-await/issue-73050.rs new file mode 100644 index 0000000000000..790f24a230b7e --- /dev/null +++ b/src/test/ui/async-await/issue-73050.rs @@ -0,0 +1,12 @@ +// check-pass +// edition:2018 + +#[allow(unused)] +async fn foo<'a>() { + let _data = &mut [0u8; { 1 + 4 }]; + bar().await +} + +async fn bar() {} + +fn main() {} diff --git a/src/test/ui/async-await/issue-73137.rs b/src/test/ui/async-await/issue-73137.rs new file mode 100644 index 0000000000000..18374460df79b --- /dev/null +++ b/src/test/ui/async-await/issue-73137.rs @@ -0,0 +1,42 @@ +// Regression test for + +// run-pass +// edition:2018 + +#![allow(dead_code)] +#![feature(wake_trait)] +use std::future::Future; +use std::task::{Waker, Wake, Context}; +use std::sync::Arc; + +struct DummyWaker; +impl Wake for DummyWaker { + fn wake(self: Arc) {} +} + +struct Foo { + a: usize, + b: &'static u32, +} + +#[inline(never)] +fn nop(_: T) {} + +fn main() { + let mut fut = Box::pin(async { + let action = Foo { + b: &42, + a: async { 0 }.await, + }; + + // An error in the generator transform caused `b` to be overwritten with `a` when `b` was + // borrowed. + nop(&action.b); + assert_ne!(0usize, unsafe { std::mem::transmute(action.b) }); + + async {}.await; + }); + let waker = Waker::from(Arc::new(DummyWaker)); + let mut cx = Context::from_waker(&waker); + let _ = fut.as_mut().poll(&mut cx); +} 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 cd6670923c2c6..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/libstd/future.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_tls_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-62097.stderr b/src/test/ui/async-await/issues/issue-62097.stderr index 94afccc06a9e7..0f58b158904db 100644 --- a/src/test/ui/async-await/issues/issue-62097.stderr +++ b/src/test/ui/async-await/issues/issue-62097.stderr @@ -1,16 +1,14 @@ -error: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/issue-62097.rs:12:31 | LL | pub async fn run_dummy_fn(&self) { - | ^^^^^ ...but this borrow... + | ^^^^^ + | | + | this data with an anonymous lifetime `'_`... + | ...is captured here... LL | foo(|| self.bar()).await; - | --- this return type evaluates to the `'static` lifetime... - | -note: ...can't outlive the lifetime `'_` as defined on the method body at 12:31 - --> $DIR/issue-62097.rs:12:31 - | -LL | pub async fn run_dummy_fn(&self) { - | ^ + | --- ...and is required to live as long as `'static` here error: aborting due to previous error +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/async-await/issues/issue-63388-2.nll.stderr b/src/test/ui/async-await/issues/issue-63388-2.nll.stderr deleted file mode 100644 index 6edb9e63d480a..0000000000000 --- a/src/test/ui/async-await/issues/issue-63388-2.nll.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0106]: missing lifetime specifier - --> $DIR/issue-63388-2.rs:12:10 - | -LL | foo: &dyn Foo, bar: &'a dyn Foo - | -------- ----------- -LL | ) -> &dyn Foo - | ^ help: consider using the named lifetime: `&'a` - | - = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `foo` or `bar` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/async-await/issues/issue-63388-2.rs b/src/test/ui/async-await/issues/issue-63388-2.rs index 73e7f25f97d0d..458bc9faeaf27 100644 --- a/src/test/ui/async-await/issues/issue-63388-2.rs +++ b/src/test/ui/async-await/issues/issue-63388-2.rs @@ -8,7 +8,7 @@ trait Foo {} impl Xyz { async fn do_sth<'a>( - foo: &dyn Foo, bar: &'a dyn Foo //~ ERROR cannot infer + foo: &dyn Foo, bar: &'a dyn Foo ) -> &dyn Foo //~ ERROR missing lifetime specifier { foo 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 9f51ced9c3f49..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,25 +4,14 @@ 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` - -error: cannot infer an appropriate lifetime - --> $DIR/issue-63388-2.rs:11:9 - | -LL | foo: &dyn Foo, bar: &'a dyn Foo - | ^^^ ...but this borrow... -... -LL | foo - | --- this return type evaluates to the `'static` lifetime... +help: consider using the `'a` lifetime | -note: ...can't outlive the lifetime `'_` as defined on the method body at 11:14 - --> $DIR/issue-63388-2.rs:11:14 - | -LL | foo: &dyn Foo, bar: &'a dyn Foo - | ^ +LL | ) -> &'a dyn Foo + | ^^^ -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/async-await/issues/issue-65159.rs b/src/test/ui/async-await/issues/issue-65159.rs index b5fee061f277e..2f80435046bdf 100644 --- a/src/test/ui/async-await/issues/issue-65159.rs +++ b/src/test/ui/async-await/issues/issue-65159.rs @@ -5,6 +5,7 @@ async fn copy() -> Result<()> //~ ERROR wrong number of type arguments { Ok(()) + //~^ type annotations needed } fn main() { } diff --git a/src/test/ui/async-await/issues/issue-65159.stderr b/src/test/ui/async-await/issues/issue-65159.stderr index 56d2c38b302e9..04cfa5249982e 100644 --- a/src/test/ui/async-await/issues/issue-65159.stderr +++ b/src/test/ui/async-await/issues/issue-65159.stderr @@ -4,6 +4,13 @@ error[E0107]: wrong number of type arguments: expected 2, found 1 LL | async fn copy() -> Result<()> | ^^^^^^^^^^ expected 2 type arguments -error: aborting due to previous error +error[E0282]: type annotations needed + --> $DIR/issue-65159.rs:7:5 + | +LL | Ok(()) + | ^^ cannot infer type for type parameter `E` declared on the enum `Result` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0107`. +Some errors have detailed explanations: E0107, E0282. +For more information about an error, try `rustc --explain E0107`. 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.rs b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs index b12d7bccecead..05960c0c7f636 100644 --- a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs @@ -9,6 +9,9 @@ impl Trait<'_, '_> for T { } async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { //~^ ERROR ambiguous lifetime bound //~| ERROR ambiguous lifetime bound + //~| ERROR ambiguous lifetime bound + //~| ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds + //~| ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds (a, b) } 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 f9a1b4b3394c1..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 @@ -14,5 +14,42 @@ LL | async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<' | = help: add #![feature(member_constraints)] to the crate attributes to enable -error: aborting due to 2 previous errors +error: ambiguous lifetime bound in `impl Trait` + --> $DIR/ret-impl-trait-no-fg.rs:9:64 + | +LL | async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^ the elided lifetimes here do not outlive one another + | + = help: add #![feature(member_constraints)] to the crate attributes to enable + +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ret-impl-trait-no-fg.rs:9:1 + | +LL | / async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { +LL | | +LL | | +LL | | +... | +LL | | (a, b) +LL | | } + | |_^ + | + = note: hidden type `(&u8, &u8)` captures lifetime '_#5r + +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ret-impl-trait-no-fg.rs:9:1 + | +LL | / async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { +LL | | +LL | | +LL | | +... | +LL | | (a, b) +LL | | } + | |_^ + | + = note: hidden type `(&u8, &u8)` captures lifetime '_#6r + +error: aborting due to 5 previous errors +For more information about this error, try `rustc --explain E0700`. 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/no-std.rs b/src/test/ui/async-await/no-std.rs new file mode 100644 index 0000000000000..63e93cdff7e77 --- /dev/null +++ b/src/test/ui/async-await/no-std.rs @@ -0,0 +1,13 @@ +// edition:2018 +// check-pass + +#![no_std] +#![crate_type = "rlib"] + +use core::future::Future; + +async fn a(f: impl Future) { + f.await; +} + +fn main() {} 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/auto-is-contextual.rs b/src/test/ui/auto-traits/auto-is-contextual.rs similarity index 100% rename from src/test/ui/auto-is-contextual.rs rename to src/test/ui/auto-traits/auto-is-contextual.rs diff --git a/src/test/ui/auto-traits/auto-trait-projection-recursion.rs b/src/test/ui/auto-traits/auto-trait-projection-recursion.rs new file mode 100644 index 0000000000000..a36f26f02e9f4 --- /dev/null +++ b/src/test/ui/auto-traits/auto-trait-projection-recursion.rs @@ -0,0 +1,34 @@ +// Checking the `Send` bound in `main` requires: +// +// checking as Y>::P: Send +// which normalizes to Box>>: Send +// which needs X>: Send +// which needs as Y>::P: Send +// +// At this point we used to normalize the predicate to `Box>>: Send` +// and continue in a loop where we created new region variables to the +// recursion limit. To avoid this we now "canonicalize" region variables to +// lowest unified region vid. This means we instead have to prove +// `Box>>: Send`, which we can because auto traits are coinductive. + +// check-pass + +// Avoid a really long error message if this regresses. +#![recursion_limit="20"] + +trait Y { + type P; +} + +impl<'a> Y for C<'a> { + type P = Box>>; +} + +struct C<'a>(&'a ()); +struct X(T::P); + +fn is_send() {} + +fn main() { + is_send::>>(); +} diff --git a/src/test/ui/auto-trait-validation.rs b/src/test/ui/auto-traits/auto-trait-validation.rs similarity index 100% rename from src/test/ui/auto-trait-validation.rs rename to src/test/ui/auto-traits/auto-trait-validation.rs diff --git a/src/test/ui/auto-trait-validation.stderr b/src/test/ui/auto-traits/auto-trait-validation.stderr similarity index 100% rename from src/test/ui/auto-trait-validation.stderr rename to src/test/ui/auto-traits/auto-trait-validation.stderr diff --git a/src/test/ui/traits/auto-traits.rs b/src/test/ui/auto-traits/auto-traits.rs similarity index 95% rename from src/test/ui/traits/auto-traits.rs rename to src/test/ui/auto-traits/auto-traits.rs index c495b97b25bfe..15fdddc5f3f5c 100644 --- a/src/test/ui/traits/auto-traits.rs +++ b/src/test/ui/auto-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/issues/issue-23080-2.rs b/src/test/ui/auto-traits/issue-23080-2.rs similarity index 87% rename from src/test/ui/issues/issue-23080-2.rs rename to src/test/ui/auto-traits/issue-23080-2.rs index d20bb4bd90726..7f6b9e3fba79f 100644 --- a/src/test/ui/issues/issue-23080-2.rs +++ b/src/test/ui/auto-traits/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/auto-traits/issue-23080-2.stderr similarity index 94% rename from src/test/ui/issues/issue-23080-2.stderr rename to src/test/ui/auto-traits/issue-23080-2.stderr index fcd1ecfa98288..48ce09aaa34da 100644 --- a/src/test/ui/issues/issue-23080-2.stderr +++ b/src/test/ui/auto-traits/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/auto-traits/issue-23080.rs similarity index 89% rename from src/test/ui/issues/issue-23080.rs rename to src/test/ui/auto-traits/issue-23080.rs index fa5c35316bc28..035db82ba5de0 100644 --- a/src/test/ui/issues/issue-23080.rs +++ b/src/test/ui/auto-traits/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/auto-traits/issue-23080.stderr similarity index 91% rename from src/test/ui/issues/issue-23080.stderr rename to src/test/ui/auto-traits/issue-23080.stderr index dbb9861b5784a..73ecb1c362e17 100644 --- a/src/test/ui/issues/issue-23080.stderr +++ b/src/test/ui/auto-traits/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/typeck/typeck-auto-trait-no-supertraits-2.rs b/src/test/ui/auto-traits/typeck-auto-trait-no-supertraits-2.rs similarity index 91% rename from src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.rs rename to src/test/ui/auto-traits/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/auto-traits/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/auto-traits/typeck-auto-trait-no-supertraits-2.stderr similarity index 86% rename from src/test/ui/typeck/typeck-auto-trait-no-supertraits-2.stderr rename to src/test/ui/auto-traits/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/auto-traits/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/auto-traits/typeck-auto-trait-no-supertraits.rs similarity index 97% rename from src/test/ui/typeck/typeck-auto-trait-no-supertraits.rs rename to src/test/ui/auto-traits/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/auto-traits/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/auto-traits/typeck-auto-trait-no-supertraits.stderr similarity index 85% rename from src/test/ui/typeck/typeck-auto-trait-no-supertraits.stderr rename to src/test/ui/auto-traits/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/auto-traits/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-constituent-types-2.rs b/src/test/ui/auto-traits/typeck-default-trait-impl-constituent-types-2.rs similarity index 90% rename from src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.rs rename to src/test/ui/auto-traits/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/auto-traits/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/auto-traits/typeck-default-trait-impl-constituent-types-2.stderr similarity index 80% rename from src/test/ui/typeck/typeck-default-trait-impl-constituent-types-2.stderr rename to src/test/ui/auto-traits/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/auto-traits/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/auto-traits/typeck-default-trait-impl-constituent-types.rs similarity index 91% rename from src/test/ui/typeck/typeck-default-trait-impl-constituent-types.rs rename to src/test/ui/auto-traits/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/auto-traits/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/auto-traits/typeck-default-trait-impl-constituent-types.stderr similarity index 76% rename from src/test/ui/typeck/typeck-default-trait-impl-constituent-types.stderr rename to src/test/ui/auto-traits/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/auto-traits/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-negation.rs b/src/test/ui/auto-traits/typeck-default-trait-impl-negation.rs similarity index 95% rename from src/test/ui/typeck/typeck-default-trait-impl-negation.rs rename to src/test/ui/auto-traits/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/auto-traits/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/auto-traits/typeck-default-trait-impl-negation.stderr similarity index 79% rename from src/test/ui/typeck/typeck-default-trait-impl-negation.stderr rename to src/test/ui/auto-traits/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/auto-traits/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/auto-traits/typeck-default-trait-impl-precedence.rs similarity index 95% rename from src/test/ui/typeck/typeck-default-trait-impl-precedence.rs rename to src/test/ui/auto-traits/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/auto-traits/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/auto-traits/typeck-default-trait-impl-precedence.stderr similarity index 79% rename from src/test/ui/typeck/typeck-default-trait-impl-precedence.stderr rename to src/test/ui/auto-traits/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/auto-traits/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/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..a3f6452477146 100644 --- a/src/test/ui/borrowck/borrowck-asm.rs +++ b/src/test/ui/borrowck/borrowck-asm.rs @@ -3,10 +3,11 @@ // ignore-powerpc // ignore-powerpc64 // ignore-powerpc64le +// ignore-riscv64 // ignore-sparc // ignore-sparc64 -#![feature(asm)] +#![feature(llvm_asm)] #[cfg(any(target_arch = "x86", target_arch = "x86_64", @@ -19,7 +20,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 +29,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 +37,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 +51,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 +66,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 +74,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..3dccca784151e 100644 --- a/src/test/ui/borrowck/borrowck-asm.stderr +++ b/src/test/ui/borrowck/borrowck-asm.stderr @@ -1,29 +1,29 @@ error[E0382]: use of moved value: `x` - --> $DIR/borrowck-asm.rs:24:17 + --> $DIR/borrowck-asm.rs:25:17 | 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:32: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:40: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:54: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:61: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:69: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:77: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/ui/borrowck/borrowck-describe-lvalue.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr index 075e0e2e4515e..4144d70cc1601 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.stderr +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.stderr @@ -21,10 +21,13 @@ LL | *y = 1; error: captured variable cannot escape `FnMut` closure body --> $DIR/borrowck-describe-lvalue.rs:264:16 | +LL | let mut x = 0; + | ----- variable defined here LL | || { | - inferred to be a `FnMut` closure LL | / || { LL | | let y = &mut x; + | | - variable captured here LL | | &mut x; LL | | *y = 1; LL | | drop(y); 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/issue-45983.migrate.stderr b/src/test/ui/borrowck/issue-45983.migrate.stderr deleted file mode 100644 index c1564cf07e68a..0000000000000 --- a/src/test/ui/borrowck/issue-45983.migrate.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: borrowed data cannot be stored outside of its closure - --> $DIR/issue-45983.rs:20:27 - | -LL | let x = None; - | - borrowed data cannot be stored into here... -LL | give_any(|y| x = Some(y)); - | --- ^ cannot be stored outside of its closure - | | - | ...because it cannot outlive this closure - -error: aborting due to previous error - diff --git a/src/test/ui/borrowck/issue-45983.nll.stderr b/src/test/ui/borrowck/issue-45983.nll.stderr deleted file mode 100644 index 51bb4dee6762a..0000000000000 --- a/src/test/ui/borrowck/issue-45983.nll.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0521]: borrowed data escapes outside of closure - --> $DIR/issue-45983.rs:20:18 - | -LL | let x = None; - | - `x` declared here, outside of the closure body -LL | give_any(|y| x = Some(y)); - | - ^^^^^^^^^^^ `y` escapes the closure body here - | | - | `y` is a reference that is only valid in the closure body - -error[E0594]: cannot assign to `x`, as it is not declared as mutable - --> $DIR/issue-45983.rs:20:18 - | -LL | let x = None; - | - help: consider changing this to be mutable: `mut x` -LL | give_any(|y| x = Some(y)); - | ^^^^^^^^^^^ cannot assign - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/borrowck/issue-45983.rs b/src/test/ui/borrowck/issue-45983.rs index 3cd282077424b..6784f6f86a010 100644 --- a/src/test/ui/borrowck/issue-45983.rs +++ b/src/test/ui/borrowck/issue-45983.rs @@ -1,24 +1,12 @@ // As documented in Issue #45983, this test is evaluating the quality // of our diagnostics on erroneous code using higher-ranked closures. -// revisions: migrate nll - -// Since we are testing nll (and migration) explicitly as a separate -// revisions, don't worry about the --compare-mode=nll on this test. - -// ignore-compare-mode-nll -// ignore-compare-mode-polonius - -//[nll]compile-flags: -Z borrowck=mir - fn give_any FnOnce(&'r ())>(f: F) { f(&()); } fn main() { - let x = None; + let mut x = None; give_any(|y| x = Some(y)); - //[migrate]~^ ERROR borrowed data cannot be stored outside of its closure - //[nll]~^^ ERROR borrowed data escapes outside of closure - //[nll]~| ERROR cannot assign to `x`, as it is not declared as mutable + //~^ ERROR borrowed data escapes outside of closure } diff --git a/src/test/ui/borrowck/issue-45983.stderr b/src/test/ui/borrowck/issue-45983.stderr new file mode 100644 index 0000000000000..efd414a2d44ff --- /dev/null +++ b/src/test/ui/borrowck/issue-45983.stderr @@ -0,0 +1,12 @@ +error[E0521]: borrowed data escapes outside of closure + --> $DIR/issue-45983.rs:10:18 + | +LL | let mut x = None; + | ----- `x` declared here, outside of the closure body +LL | give_any(|y| x = Some(y)); + | - ^^^^^^^^^^^ `y` escapes the closure body here + | | + | `y` is a reference that is only valid in the closure body + +error: aborting due to previous error + diff --git a/src/test/ui/borrowck/issue-69789-iterator-mut-suggestion.rs b/src/test/ui/borrowck/issue-69789-iterator-mut-suggestion.rs new file mode 100644 index 0000000000000..f6d0e9e04d321 --- /dev/null +++ b/src/test/ui/borrowck/issue-69789-iterator-mut-suggestion.rs @@ -0,0 +1,11 @@ +// Regression test for #69789: rustc generated an invalid suggestion +// when `&` reference from `&mut` iterator is mutated. + +fn main() { + for item in &mut std::iter::empty::<&'static ()>() { + //~^ NOTE this iterator yields `&` references + *item = (); + //~^ ERROR cannot assign + //~| NOTE cannot be written + } +} diff --git a/src/test/ui/borrowck/issue-69789-iterator-mut-suggestion.stderr b/src/test/ui/borrowck/issue-69789-iterator-mut-suggestion.stderr new file mode 100644 index 0000000000000..d2865ffd196a5 --- /dev/null +++ b/src/test/ui/borrowck/issue-69789-iterator-mut-suggestion.stderr @@ -0,0 +1,12 @@ +error[E0594]: cannot assign to `*item` which is behind a `&` reference + --> $DIR/issue-69789-iterator-mut-suggestion.rs:7:9 + | +LL | for item in &mut std::iter::empty::<&'static ()>() { + | -------------------------------------- this iterator yields `&` references +LL | +LL | *item = (); + | ^^^^^^^^^^ `item` is a `&` reference, so the data it refers to cannot be written + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/borrowck/issue-7573.nll.stderr b/src/test/ui/borrowck/issue-7573.nll.stderr deleted file mode 100644 index 20afecfe5de79..0000000000000 --- a/src/test/ui/borrowck/issue-7573.nll.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0521]: borrowed data escapes outside of closure - --> $DIR/issue-7573.rs:21:9 - | -LL | let mut lines_to_use: Vec<&CrateId> = Vec::new(); - | ---------------- `lines_to_use` declared here, outside of the closure body -LL | -LL | let push_id = |installed_id: &CrateId| { - | ------------ `installed_id` is a reference that is only valid in the closure body -... -LL | lines_to_use.push(installed_id); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `installed_id` escapes the closure body here - -error: aborting due to previous error - diff --git a/src/test/ui/borrowck/issue-7573.rs b/src/test/ui/borrowck/issue-7573.rs index 20a6a5c92f149..7c07411533ff0 100644 --- a/src/test/ui/borrowck/issue-7573.rs +++ b/src/test/ui/borrowck/issue-7573.rs @@ -1,36 +1,34 @@ pub struct CrateId { local_path: String, - junk: String + junk: String, } impl CrateId { fn new(s: &str) -> CrateId { - CrateId { - local_path: s.to_string(), - junk: "wutevs".to_string() - } + CrateId { local_path: s.to_string(), junk: "wutevs".to_string() } } } pub fn remove_package_from_database() { let mut lines_to_use: Vec<&CrateId> = Vec::new(); - //~^ NOTE cannot infer an appropriate lifetime + //~^ NOTE `lines_to_use` declared here, outside of the closure body let push_id = |installed_id: &CrateId| { - //~^ NOTE borrowed data cannot outlive this closure - //~| NOTE ...so that variable is valid at time of its declaration + //~^ NOTE `installed_id` is a reference that is only valid in the closure body lines_to_use.push(installed_id); - //~^ ERROR borrowed data cannot be stored outside of its closure - //~| NOTE cannot be stored outside of its closure + //~^ ERROR borrowed data escapes outside of closure + //~| NOTE `installed_id` escapes the closure body here }; list_database(push_id); for l in &lines_to_use { println!("{}", l.local_path); } - } -pub fn list_database(mut f: F) where F: FnMut(&CrateId) { +pub fn list_database(mut f: F) +where + F: FnMut(&CrateId), +{ let stuff = ["foo", "bar"]; for l in &stuff { diff --git a/src/test/ui/borrowck/issue-7573.stderr b/src/test/ui/borrowck/issue-7573.stderr index 32b3ef72d8bda..815419db833e5 100644 --- a/src/test/ui/borrowck/issue-7573.stderr +++ b/src/test/ui/borrowck/issue-7573.stderr @@ -1,16 +1,14 @@ -error: borrowed data cannot be stored outside of its closure - --> $DIR/issue-7573.rs:21:27 +error[E0521]: borrowed data escapes outside of closure + --> $DIR/issue-7573.rs:17:9 | LL | let mut lines_to_use: Vec<&CrateId> = Vec::new(); - | - cannot infer an appropriate lifetime... + | ---------------- `lines_to_use` declared here, outside of the closure body LL | LL | let push_id = |installed_id: &CrateId| { - | ------- ------------------------ borrowed data cannot outlive this closure - | | - | ...so that variable is valid at time of its declaration -... + | ------------ `installed_id` is a reference that is only valid in the closure body +LL | LL | lines_to_use.push(installed_id); - | ^^^^^^^^^^^^ cannot be stored outside of its closure + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `installed_id` escapes the closure body here error: aborting due to previous error 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/regions-escape-bound-fn-2.nll.stderr b/src/test/ui/borrowck/regions-escape-bound-fn-2.nll.stderr deleted file mode 100644 index 68a0fe0b4f07b..0000000000000 --- a/src/test/ui/borrowck/regions-escape-bound-fn-2.nll.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0521]: borrowed data escapes outside of closure - --> $DIR/regions-escape-bound-fn-2.rs:8:18 - | -LL | let mut x = None; - | ----- `x` declared here, outside of the closure body -LL | with_int(|y| x = Some(y)); - | - ^^^^^^^^^^^ `y` escapes the closure body here - | | - | `y` is a reference that is only valid in the closure body - -error: aborting due to previous error - diff --git a/src/test/ui/borrowck/regions-escape-bound-fn-2.rs b/src/test/ui/borrowck/regions-escape-bound-fn-2.rs index cb423032b4610..0e98d98cf87b3 100644 --- a/src/test/ui/borrowck/regions-escape-bound-fn-2.rs +++ b/src/test/ui/borrowck/regions-escape-bound-fn-2.rs @@ -1,4 +1,7 @@ -fn with_int(f: F) where F: FnOnce(&isize) { +fn with_int(f: F) +where + F: FnOnce(&isize), +{ let x = 3; f(&x); } @@ -6,5 +9,5 @@ fn with_int(f: F) where F: FnOnce(&isize) { fn main() { let mut x = None; with_int(|y| x = Some(y)); - //~^ ERROR borrowed data cannot be stored outside of its closure + //~^ ERROR borrowed data escapes outside of closure } diff --git a/src/test/ui/borrowck/regions-escape-bound-fn-2.stderr b/src/test/ui/borrowck/regions-escape-bound-fn-2.stderr index 4b37edafa1273..1dc60bb155452 100644 --- a/src/test/ui/borrowck/regions-escape-bound-fn-2.stderr +++ b/src/test/ui/borrowck/regions-escape-bound-fn-2.stderr @@ -1,12 +1,12 @@ -error: borrowed data cannot be stored outside of its closure - --> $DIR/regions-escape-bound-fn-2.rs:8:27 +error[E0521]: borrowed data escapes outside of closure + --> $DIR/regions-escape-bound-fn-2.rs:11:18 | LL | let mut x = None; - | ----- borrowed data cannot be stored into here... + | ----- `x` declared here, outside of the closure body LL | with_int(|y| x = Some(y)); - | --- ^ cannot be stored outside of its closure - | | - | ...because it cannot outlive this closure + | - ^^^^^^^^^^^ `y` escapes the closure body here + | | + | `y` is a reference that is only valid in the closure body error: aborting due to previous error diff --git a/src/test/ui/borrowck/regions-escape-bound-fn.nll.stderr b/src/test/ui/borrowck/regions-escape-bound-fn.nll.stderr deleted file mode 100644 index d304de92c7e18..0000000000000 --- a/src/test/ui/borrowck/regions-escape-bound-fn.nll.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0521]: borrowed data escapes outside of closure - --> $DIR/regions-escape-bound-fn.rs:8:18 - | -LL | let mut x: Option<&isize> = None; - | ----- `x` declared here, outside of the closure body -LL | with_int(|y| x = Some(y)); - | - ^^^^^^^^^^^ `y` escapes the closure body here - | | - | `y` is a reference that is only valid in the closure body - -error: aborting due to previous error - diff --git a/src/test/ui/borrowck/regions-escape-bound-fn.rs b/src/test/ui/borrowck/regions-escape-bound-fn.rs index 772df3e6c5822..f896ae7bdada2 100644 --- a/src/test/ui/borrowck/regions-escape-bound-fn.rs +++ b/src/test/ui/borrowck/regions-escape-bound-fn.rs @@ -1,4 +1,7 @@ -fn with_int(f: F) where F: FnOnce(&isize) { +fn with_int(f: F) +where + F: FnOnce(&isize), +{ let x = 3; f(&x); } @@ -6,5 +9,5 @@ fn with_int(f: F) where F: FnOnce(&isize) { fn main() { let mut x: Option<&isize> = None; with_int(|y| x = Some(y)); - //~^ ERROR borrowed data cannot be stored outside of its closure + //~^ ERROR borrowed data escapes outside of closure } diff --git a/src/test/ui/borrowck/regions-escape-bound-fn.stderr b/src/test/ui/borrowck/regions-escape-bound-fn.stderr index 4973d5306f959..5c548ec2876a3 100644 --- a/src/test/ui/borrowck/regions-escape-bound-fn.stderr +++ b/src/test/ui/borrowck/regions-escape-bound-fn.stderr @@ -1,12 +1,12 @@ -error: borrowed data cannot be stored outside of its closure - --> $DIR/regions-escape-bound-fn.rs:8:27 +error[E0521]: borrowed data escapes outside of closure + --> $DIR/regions-escape-bound-fn.rs:11:18 | LL | let mut x: Option<&isize> = None; - | ----- borrowed data cannot be stored into here... + | ----- `x` declared here, outside of the closure body LL | with_int(|y| x = Some(y)); - | --- ^ cannot be stored outside of its closure - | | - | ...because it cannot outlive this closure + | - ^^^^^^^^^^^ `y` escapes the closure body here + | | + | `y` is a reference that is only valid in the closure body error: aborting due to previous error diff --git a/src/test/ui/borrowck/regions-escape-unboxed-closure.nll.stderr b/src/test/ui/borrowck/regions-escape-unboxed-closure.nll.stderr deleted file mode 100644 index d9931302f75fc..0000000000000 --- a/src/test/ui/borrowck/regions-escape-unboxed-closure.nll.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0521]: borrowed data escapes outside of closure - --> $DIR/regions-escape-unboxed-closure.rs:6:23 - | -LL | let mut x: Option<&isize> = None; - | ----- `x` declared here, outside of the closure body -LL | with_int(&mut |y| x = Some(y)); - | - ^^^^^^^^^^^ `y` escapes the closure body here - | | - | `y` is a reference that is only valid in the closure body - -error: aborting due to previous error - diff --git a/src/test/ui/borrowck/regions-escape-unboxed-closure.rs b/src/test/ui/borrowck/regions-escape-unboxed-closure.rs index d8bef927fd722..f01e47122d1e1 100644 --- a/src/test/ui/borrowck/regions-escape-unboxed-closure.rs +++ b/src/test/ui/borrowck/regions-escape-unboxed-closure.rs @@ -1,8 +1,7 @@ -fn with_int(f: &mut dyn FnMut(&isize)) { -} +fn with_int(f: &mut dyn FnMut(&isize)) {} fn main() { let mut x: Option<&isize> = None; with_int(&mut |y| x = Some(y)); - //~^ ERROR borrowed data cannot be stored outside of its closure + //~^ ERROR borrowed data escapes outside of closure } diff --git a/src/test/ui/borrowck/regions-escape-unboxed-closure.stderr b/src/test/ui/borrowck/regions-escape-unboxed-closure.stderr index 047e290acae14..f2a49e70d2716 100644 --- a/src/test/ui/borrowck/regions-escape-unboxed-closure.stderr +++ b/src/test/ui/borrowck/regions-escape-unboxed-closure.stderr @@ -1,12 +1,12 @@ -error: borrowed data cannot be stored outside of its closure - --> $DIR/regions-escape-unboxed-closure.rs:6:32 +error[E0521]: borrowed data escapes outside of closure + --> $DIR/regions-escape-unboxed-closure.rs:5:23 | LL | let mut x: Option<&isize> = None; - | ----- borrowed data cannot be stored into here... + | ----- `x` declared here, outside of the closure body LL | with_int(&mut |y| x = Some(y)); - | --- ^ cannot be stored outside of its closure - | | - | ...because it cannot outlive this closure + | - ^^^^^^^^^^^ `y` escapes the closure body here + | | + | `y` is a reference that is only valid in the closure body error: aborting due to previous error 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.rs b/src/test/ui/bound-suggestions.rs index 605a6df838658..562dec9f080de 100644 --- a/src/test/ui/bound-suggestions.rs +++ b/src/test/ui/bound-suggestions.rs @@ -19,7 +19,7 @@ fn test_one_bound(t: T) { } #[allow(dead_code)] -fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug { +fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, { println!("{:?} {:?}", x, y); //~^ 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-self-type.stderr b/src/test/ui/builtin-superkinds/builtin-superkinds-self-type.stderr index 999a5839ba690..2dac4a22ae713 100644 --- a/src/test/ui/builtin-superkinds/builtin-superkinds-self-type.stderr +++ b/src/test/ui/builtin-superkinds/builtin-superkinds-self-type.stderr @@ -2,15 +2,9 @@ error[E0310]: the parameter type `T` may not live long enough --> $DIR/builtin-superkinds-self-type.rs:10:16 | LL | impl Foo for T { } - | -- ^^^ + | -- ^^^ ...so that the type `T` will meet its required lifetime bounds | | | help: consider adding an explicit lifetime bound...: `T: 'static +` - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/builtin-superkinds-self-type.rs:10:16 - | -LL | impl Foo for T { } - | ^^^ 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-1.rs b/src/test/ui/c-variadic/variadic-ffi-1.rs index e7197a9d16859..a7824d919674d 100644 --- a/src/test/ui/c-variadic/variadic-ffi-1.rs +++ b/src/test/ui/c-variadic/variadic-ffi-1.rs @@ -1,5 +1,6 @@ // ignore-arm stdcall isn't supported // ignore-aarch64 stdcall isn't supported +// ignore-riscv64 stdcall isn't supported extern "stdcall" { fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling diff --git a/src/test/ui/c-variadic/variadic-ffi-1.stderr b/src/test/ui/c-variadic/variadic-ffi-1.stderr index 318b8aabafb49..89ea65fd43fe4 100644 --- a/src/test/ui/c-variadic/variadic-ffi-1.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-1.stderr @@ -1,11 +1,11 @@ error[E0045]: C-variadic function must have C or cdecl calling convention - --> $DIR/variadic-ffi-1.rs:5:5 + --> $DIR/variadic-ffi-1.rs:6:5 | LL | fn printf(_: *const u8, ...); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention error[E0060]: this function takes at least 2 arguments but 0 arguments were supplied - --> $DIR/variadic-ffi-1.rs:16:9 + --> $DIR/variadic-ffi-1.rs:17:9 | LL | fn foo(f: isize, x: u8, ...); | ----------------------------- defined here @@ -16,7 +16,7 @@ LL | foo(); | expected at least 2 arguments error[E0060]: this function takes at least 2 arguments but 1 argument was supplied - --> $DIR/variadic-ffi-1.rs:17:9 + --> $DIR/variadic-ffi-1.rs:18:9 | LL | fn foo(f: isize, x: u8, ...); | ----------------------------- defined here @@ -27,7 +27,7 @@ LL | foo(1); | expected at least 2 arguments error[E0308]: mismatched types - --> $DIR/variadic-ffi-1.rs:19:56 + --> $DIR/variadic-ffi-1.rs:20:56 | LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo; | ------------------------------------- ^^^ expected non-variadic fn, found variadic function @@ -38,7 +38,7 @@ LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo; found fn item `unsafe extern "C" fn(_, _, ...) {foo}` error[E0308]: mismatched types - --> $DIR/variadic-ffi-1.rs:20:54 + --> $DIR/variadic-ffi-1.rs:21:54 | LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar; | ----------------------------------- ^^^ expected variadic fn, found non-variadic function @@ -49,37 +49,37 @@ LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar; found fn item `extern "C" fn(_, _) {bar}` error[E0617]: can't pass `f32` to variadic function - --> $DIR/variadic-ffi-1.rs:22:19 + --> $DIR/variadic-ffi-1.rs:23:19 | LL | foo(1, 2, 3f32); | ^^^^ help: cast the value to `c_double`: `3f32 as c_double` error[E0617]: can't pass `bool` to variadic function - --> $DIR/variadic-ffi-1.rs:23:19 + --> $DIR/variadic-ffi-1.rs:24:19 | LL | foo(1, 2, true); | ^^^^ help: cast the value to `c_int`: `true as c_int` error[E0617]: can't pass `i8` to variadic function - --> $DIR/variadic-ffi-1.rs:24:19 + --> $DIR/variadic-ffi-1.rs:25:19 | LL | foo(1, 2, 1i8); | ^^^ help: cast the value to `c_int`: `1i8 as c_int` error[E0617]: can't pass `u8` to variadic function - --> $DIR/variadic-ffi-1.rs:25:19 + --> $DIR/variadic-ffi-1.rs:26:19 | LL | foo(1, 2, 1u8); | ^^^ help: cast the value to `c_uint`: `1u8 as c_uint` error[E0617]: can't pass `i16` to variadic function - --> $DIR/variadic-ffi-1.rs:26:19 + --> $DIR/variadic-ffi-1.rs:27:19 | LL | foo(1, 2, 1i16); | ^^^^ help: cast the value to `c_int`: `1i16 as c_int` error[E0617]: can't pass `u16` to variadic function - --> $DIR/variadic-ffi-1.rs:27:19 + --> $DIR/variadic-ffi-1.rs:28:19 | LL | foo(1, 2, 1u16); | ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint` diff --git a/src/test/ui/c-variadic/variadic-ffi-4.nll.stderr b/src/test/ui/c-variadic/variadic-ffi-4.nll.stderr deleted file mode 100644 index 89107e799bd22..0000000000000 --- a/src/test/ui/c-variadic/variadic-ffi-4.nll.stderr +++ /dev/null @@ -1,123 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:8:5 - | -LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | -- -- has type `core::ffi::VaListImpl<'1>` - | | - | lifetime `'f` defined here -LL | ap - | ^^ function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'f` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:8:5 - | -LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | -- -- has type `core::ffi::VaListImpl<'1>` - | | - | lifetime `'f` defined here -LL | ap - | ^^ returning this value requires that `'1` must outlive `'f` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:12:5 - | -LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> { - | -- has type `core::ffi::VaListImpl<'1>` -LL | ap - | ^^ returning this value requires that `'1` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:16:33 - | -LL | let _ = ap.with_copy(|ap| { ap }); - | --- ^^ returning this value requires that `'1` must outlive `'2` - | | | - | | return type of closure is core::ffi::VaList<'2, '_> - | has type `core::ffi::VaList<'1, '_>` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:20:5 - | -LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` - | | - | has type `&mut core::ffi::VaListImpl<'1>` -LL | *ap0 = ap1; - | ^^^^ assignment requires that `'1` must outlive `'2` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:20:5 - | -LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` - | | - | has type `&mut core::ffi::VaListImpl<'1>` -LL | *ap0 = ap1; - | ^^^^ assignment requires that `'2` must outlive `'1` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:24:5 - | -LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { - | --- ------- has type `core::ffi::VaListImpl<'2>` - | | - | has type `&mut core::ffi::VaListImpl<'1>` -LL | ap0 = &mut ap1; - | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:24:5 - | -LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { - | --- ------- has type `core::ffi::VaListImpl<'2>` - | | - | has type `&mut core::ffi::VaListImpl<'1>` -LL | ap0 = &mut ap1; - | ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1` - -error[E0384]: cannot assign to immutable argument `ap0` - --> $DIR/variadic-ffi-4.rs:24:5 - | -LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { - | --- help: make this binding mutable: `mut ap0` -LL | ap0 = &mut ap1; - | ^^^^^^^^^^^^^^ cannot assign to immutable argument - -error[E0597]: `ap1` does not live long enough - --> $DIR/variadic-ffi-4.rs:24:11 - | -LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { - | - let's call the lifetime of this reference `'3` -LL | ap0 = &mut ap1; - | ------^^^^^^^^ - | | | - | | borrowed value does not live long enough - | assignment requires that `ap1` is borrowed for `'3` -... -LL | } - | - `ap1` dropped here while still borrowed - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:31:12 - | -LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` - | | - | has type `&mut core::ffi::VaListImpl<'1>` -LL | *ap0 = ap1.clone(); - | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:31:12 - | -LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `core::ffi::VaListImpl<'2>` - | | - | has type `&mut core::ffi::VaListImpl<'1>` -LL | *ap0 = ap1.clone(); - | ^^^^^^^^^^^ argument requires that `'2` must outlive `'1` - -error: aborting due to 12 previous errors - -Some errors have detailed explanations: E0384, E0597. -For more information about an error, try `rustc --explain E0384`. diff --git a/src/test/ui/c-variadic/variadic-ffi-4.rs b/src/test/ui/c-variadic/variadic-ffi-4.rs index a4d658cef1630..8064037942259 100644 --- a/src/test/ui/c-variadic/variadic-ffi-4.rs +++ b/src/test/ui/c-variadic/variadic-ffi-4.rs @@ -1,32 +1,38 @@ -#![crate_type="lib"] +#![crate_type = "lib"] #![no_std] #![feature(c_variadic)] use core::ffi::{VaList, VaListImpl}; pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - ap //~ ERROR: mismatched types + ap + //~^ ERROR: lifetime may not live long enough + //~| ERROR: lifetime may not live long enough } pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> { - ap //~ ERROR: mismatched types + ap //~ ERROR: lifetime may not live long enough } pub unsafe extern "C" fn no_escape2(_: usize, ap: ...) { - let _ = ap.with_copy(|ap| { ap }); //~ ERROR: cannot infer an appropriate lifetime + let _ = ap.with_copy(|ap| ap); //~ ERROR: lifetime may not live long enough } pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - *ap0 = ap1; //~ ERROR: mismatched types + *ap0 = ap1; + //~^ ERROR: lifetime may not live long enough + //~| ERROR: lifetime may not live long enough } -pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { +pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { ap0 = &mut ap1; - //~^ ERROR: a value of type `core::ffi::VaListImpl<'_>` is borrowed for too long - //~| ERROR: mismatched types - //~| ERROR: cannot infer an appropriate lifetime + //~^ ERROR: `ap1` does not live long enough + //~| ERROR: lifetime may not live long enough + //~| ERROR: lifetime may not live long enough } pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - *ap0 = ap1.clone(); //~ ERROR: mismatched types + *ap0 = ap1.clone(); + //~^ ERROR: lifetime may not live long enough + //~| ERROR: lifetime may not live long enough } diff --git a/src/test/ui/c-variadic/variadic-ffi-4.stderr b/src/test/ui/c-variadic/variadic-ffi-4.stderr index cd4cd8b198de8..65623501569e1 100644 --- a/src/test/ui/c-variadic/variadic-ffi-4.stderr +++ b/src/test/ui/c-variadic/variadic-ffi-4.stderr @@ -1,217 +1,114 @@ -error[E0308]: mismatched types +error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:8:5 | +LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { + | -- -- has type `core::ffi::VaListImpl<'1>` + | | + | lifetime `'f` defined here LL | ap - | ^^ lifetime mismatch - | - = note: expected struct `core::ffi::VaListImpl<'f>` - found struct `core::ffi::VaListImpl<'_>` -note: the scope of call-site for function at 7:78... - --> $DIR/variadic-ffi-4.rs:7:78 - | -LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | ______________________________________________________________________________^ -LL | | ap -LL | | } - | |_^ -note: ...does not necessarily outlive the lifetime `'f` as defined on the function body at 7:37 - --> $DIR/variadic-ffi-4.rs:7:37 + | ^^ function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'f` + +error: lifetime may not live long enough + --> $DIR/variadic-ffi-4.rs:8:5 | LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | ^^ + | -- -- has type `core::ffi::VaListImpl<'1>` + | | + | lifetime `'f` defined here +LL | ap + | ^^ returning this value requires that `'1` must outlive `'f` -error[E0308]: mismatched types - --> $DIR/variadic-ffi-4.rs:12:5 +error: lifetime may not live long enough + --> $DIR/variadic-ffi-4.rs:14:5 | +LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> { + | -- has type `core::ffi::VaListImpl<'1>` LL | ap - | ^^ lifetime mismatch - | - = note: expected struct `core::ffi::VaListImpl<'static>` - found struct `core::ffi::VaListImpl<'_>` -note: the scope of call-site for function at 11:79... - --> $DIR/variadic-ffi-4.rs:11:79 - | -LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> { - | _______________________________________________________________________________^ -LL | | ap -LL | | } - | |_^ - = note: ...does not necessarily outlive the static lifetime + | ^^ returning this value requires that `'1` must outlive `'static` -error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements - --> $DIR/variadic-ffi-4.rs:16:33 - | -LL | let _ = ap.with_copy(|ap| { ap }); - | ^^ - | -note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 16:26... - --> $DIR/variadic-ffi-4.rs:16:26 - | -LL | let _ = ap.with_copy(|ap| { ap }); - | ^^^^^^^^^^^ -note: ...so that the expression is assignable - --> $DIR/variadic-ffi-4.rs:16:33 - | -LL | let _ = ap.with_copy(|ap| { ap }); - | ^^ - = note: expected `core::ffi::VaList<'_, '_>` - found `core::ffi::VaList<'_, '_>` -note: but, the lifetime must be valid for the method call at 16:13... - --> $DIR/variadic-ffi-4.rs:16:13 - | -LL | let _ = ap.with_copy(|ap| { ap }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...so type `core::ffi::VaList<'_, '_>` of expression is valid during the expression - --> $DIR/variadic-ffi-4.rs:16:13 - | -LL | let _ = ap.with_copy(|ap| { ap }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +error: lifetime may not live long enough + --> $DIR/variadic-ffi-4.rs:18:31 + | +LL | let _ = ap.with_copy(|ap| ap); + | --- ^^ returning this value requires that `'1` must outlive `'2` + | | | + | | return type of closure is core::ffi::VaList<'2, '_> + | has type `core::ffi::VaList<'1, '_>` -error[E0308]: mismatched types - --> $DIR/variadic-ffi-4.rs:20:12 +error: lifetime may not live long enough + --> $DIR/variadic-ffi-4.rs:22:5 | +LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { + | ------- ------- has type `core::ffi::VaListImpl<'2>` + | | + | has type `&mut core::ffi::VaListImpl<'1>` LL | *ap0 = ap1; - | ^^^ lifetime mismatch - | - = note: expected struct `core::ffi::VaListImpl<'_>` - found struct `core::ffi::VaListImpl<'_>` -note: the scope of call-site for function at 19:87... - --> $DIR/variadic-ffi-4.rs:19:87 - | -LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | _______________________________________________________________________________________^ -LL | | *ap0 = ap1; -LL | | } - | |_^ -note: ...does not necessarily outlive the anonymous lifetime #2 defined on the function body at 19:1 - --> $DIR/variadic-ffi-4.rs:19:1 - | -LL | / pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { -LL | | *ap0 = ap1; -LL | | } - | |_^ + | ^^^^ assignment requires that `'1` must outlive `'2` -error[E0490]: a value of type `core::ffi::VaListImpl<'_>` is borrowed for too long - --> $DIR/variadic-ffi-4.rs:24:11 +error: lifetime may not live long enough + --> $DIR/variadic-ffi-4.rs:22:5 | -LL | ap0 = &mut ap1; - | ^^^^^^^^ - | -note: the type is valid for the anonymous lifetime #1 defined on the function body at 23:1 - --> $DIR/variadic-ffi-4.rs:23:1 - | -LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { -LL | | ap0 = &mut ap1; -LL | | -LL | | -LL | | -LL | | } - | |_^ -note: but the borrow lasts for the scope of call-site for function at 23:83 - --> $DIR/variadic-ffi-4.rs:23:83 - | -LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { - | ___________________________________________________________________________________^ -LL | | ap0 = &mut ap1; -LL | | -LL | | -LL | | -LL | | } - | |_^ +LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { + | ------- ------- has type `core::ffi::VaListImpl<'2>` + | | + | has type `&mut core::ffi::VaListImpl<'1>` +LL | *ap0 = ap1; + | ^^^^ assignment requires that `'2` must outlive `'1` -error[E0308]: mismatched types - --> $DIR/variadic-ffi-4.rs:24:11 +error: lifetime may not live long enough + --> $DIR/variadic-ffi-4.rs:28:5 | +LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { + | ------- ------- has type `core::ffi::VaListImpl<'2>` + | | + | has type `&mut core::ffi::VaListImpl<'1>` LL | ap0 = &mut ap1; - | ^^^^^^^^ lifetime mismatch - | - = note: expected mutable reference `&mut core::ffi::VaListImpl<'_>` - found mutable reference `&mut core::ffi::VaListImpl<'_>` -note: the scope of call-site for function at 23:83... - --> $DIR/variadic-ffi-4.rs:23:83 - | -LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { - | ___________________________________________________________________________________^ -LL | | ap0 = &mut ap1; -LL | | -LL | | -LL | | -LL | | } - | |_^ -note: ...does not necessarily outlive the anonymous lifetime #2 defined on the function body at 23:1 - --> $DIR/variadic-ffi-4.rs:23:1 - | -LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { -LL | | ap0 = &mut ap1; -LL | | -LL | | -LL | | -LL | | } - | |_^ + | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2` -error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements - --> $DIR/variadic-ffi-4.rs:24:11 +error: lifetime may not live long enough + --> $DIR/variadic-ffi-4.rs:28:5 | +LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { + | ------- ------- has type `core::ffi::VaListImpl<'2>` + | | + | has type `&mut core::ffi::VaListImpl<'1>` LL | ap0 = &mut ap1; - | ^^^^^^^^ - | -note: first, the lifetime cannot outlive the scope of call-site for function at 23:83... - --> $DIR/variadic-ffi-4.rs:23:83 - | -LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { - | ___________________________________________________________________________________^ -LL | | ap0 = &mut ap1; -LL | | -LL | | -LL | | -LL | | } - | |_^ -note: ...so that the type `core::ffi::VaListImpl<'_>` is not borrowed for too long - --> $DIR/variadic-ffi-4.rs:24:11 - | -LL | ap0 = &mut ap1; - | ^^^^^^^^ -note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the function body at 23:1... - --> $DIR/variadic-ffi-4.rs:23:1 - | -LL | / pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) { -LL | | ap0 = &mut ap1; -LL | | -LL | | -LL | | -LL | | } - | |_^ -note: ...so that reference does not outlive borrowed content - --> $DIR/variadic-ffi-4.rs:24:11 + | ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1` + +error[E0597]: `ap1` does not live long enough + --> $DIR/variadic-ffi-4.rs:28:11 | +LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { + | - let's call the lifetime of this reference `'3` LL | ap0 = &mut ap1; - | ^^^^^^^^ + | ------^^^^^^^^ + | | | + | | borrowed value does not live long enough + | assignment requires that `ap1` is borrowed for `'3` +... +LL | } + | - `ap1` dropped here while still borrowed -error[E0308]: mismatched types - --> $DIR/variadic-ffi-4.rs:31:12 +error: lifetime may not live long enough + --> $DIR/variadic-ffi-4.rs:35:12 | +LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { + | ------- ------- has type `core::ffi::VaListImpl<'2>` + | | + | has type `&mut core::ffi::VaListImpl<'1>` LL | *ap0 = ap1.clone(); - | ^^^^^^^^^^^ lifetime mismatch - | - = note: expected struct `core::ffi::VaListImpl<'_>` - found struct `core::ffi::VaListImpl<'_>` -note: the scope of call-site for function at 30:87... - --> $DIR/variadic-ffi-4.rs:30:87 - | -LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | _______________________________________________________________________________________^ -LL | | *ap0 = ap1.clone(); -LL | | } - | |_^ -note: ...does not necessarily outlive the anonymous lifetime #2 defined on the function body at 30:1 - --> $DIR/variadic-ffi-4.rs:30:1 + | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + +error: lifetime may not live long enough + --> $DIR/variadic-ffi-4.rs:35:12 | -LL | / pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { -LL | | *ap0 = ap1.clone(); -LL | | } - | |_^ +LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { + | ------- ------- has type `core::ffi::VaListImpl<'2>` + | | + | has type `&mut core::ffi::VaListImpl<'1>` +LL | *ap0 = ap1.clone(); + | ^^^^^^^^^^^ argument requires that `'2` must outlive `'1` -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors -Some errors have detailed explanations: E0308, E0495. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0597`. 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/cast/cast-from-nil.stderr b/src/test/ui/cast/cast-from-nil.stderr index c8e3628a7ded8..dab133cfb4b67 100644 --- a/src/test/ui/cast/cast-from-nil.stderr +++ b/src/test/ui/cast/cast-from-nil.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `u32` --> $DIR/cast-from-nil.rs:2:21 | LL | fn main() { let u = (assert!(true) as u32); } - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/cast/cast-to-bare-fn.stderr b/src/test/ui/cast/cast-to-bare-fn.stderr index 84933dca929a4..d97b0c5f8aadc 100644 --- a/src/test/ui/cast/cast-to-bare-fn.stderr +++ b/src/test/ui/cast/cast-to-bare-fn.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `fn(isize) {foo}` as `extern "C" fn() -> isize --> $DIR/cast-to-bare-fn.rs:5:13 | LL | let x = foo as extern "C" fn() -> isize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)` --> $DIR/cast-to-bare-fn.rs:7:13 | LL | let y = v as extern "Rust" fn(isize) -> (isize, isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error: aborting due to 2 previous errors diff --git a/src/test/ui/cast/cast-to-nil.stderr b/src/test/ui/cast/cast-to-nil.stderr index 478f6b69dafc8..29a9baffd71d7 100644 --- a/src/test/ui/cast/cast-to-nil.stderr +++ b/src/test/ui/cast/cast-to-nil.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `u32` as `()` --> $DIR/cast-to-nil.rs:2:21 | LL | fn main() { let u = 0u32 as (); } - | ^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr index ffa02533d8b66..9b86f8d4def86 100644 --- a/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr +++ b/src/test/ui/cast/cast-to-unsized-trait-object-suggestion.stderr @@ -12,7 +12,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box<{integer}>` as `dyn std::ma LL | Box::new(1) as dyn Send; | ^^^^^^^^^^^^^^^-------- | | - | help: try casting to a `Box` instead: `Box` + | help: you can cast to a `Box` instead: `Box` error: aborting due to 2 previous errors diff --git a/src/test/ui/cenum_impl_drop_cast.rs b/src/test/ui/cenum_impl_drop_cast.rs new file mode 100644 index 0000000000000..96e3d967e2c61 --- /dev/null +++ b/src/test/ui/cenum_impl_drop_cast.rs @@ -0,0 +1,18 @@ +#![deny(cenum_impl_drop_cast)] + +enum E { + A = 0, +} + +impl Drop for E { + fn drop(&mut self) { + println!("Drop"); + } +} + +fn main() { + let e = E::A; + let i = e as u32; + //~^ ERROR cannot cast enum `E` into integer `u32` because it implements `Drop` + //~| WARN this was previously accepted +} diff --git a/src/test/ui/cenum_impl_drop_cast.stderr b/src/test/ui/cenum_impl_drop_cast.stderr new file mode 100644 index 0000000000000..8d847a0c80b16 --- /dev/null +++ b/src/test/ui/cenum_impl_drop_cast.stderr @@ -0,0 +1,16 @@ +error: cannot cast enum `E` into integer `u32` because it implements `Drop` + --> $DIR/cenum_impl_drop_cast.rs:15:13 + | +LL | let i = e as u32; + | ^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/cenum_impl_drop_cast.rs:1:9 + | +LL | #![deny(cenum_impl_drop_cast)] + | ^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #73333 + +error: aborting due to previous error + diff --git a/src/test/ui/cfg/conditional-compile-arch.rs b/src/test/ui/cfg/conditional-compile-arch.rs index ea3affee4066e..7de561df1361f 100644 --- a/src/test/ui/cfg/conditional-compile-arch.rs +++ b/src/test/ui/cfg/conditional-compile-arch.rs @@ -36,3 +36,6 @@ pub fn main() { } #[cfg(target_arch = "sparc64")] pub fn main() { } + +#[cfg(target_arch = "riscv64")] +pub fn main() { } 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..fdc94f69bf21a --- /dev/null +++ b/src/test/ui/chalkify/impl_wf.rs @@ -0,0 +1,44 @@ +// compile-flags: -Z chalk + +trait Foo: Sized { } + +trait Bar { + type Item: Foo; +} + +impl Foo for i32 { } + +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..5293bbaecd389 --- /dev/null +++ b/src/test/ui/chalkify/impl_wf.stderr @@ -0,0 +1,24 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/impl_wf.rs:11:6 + | +LL | trait Foo: Sized { } + | ----- required by this bound in `Foo` +... +LL | impl Foo for str { } + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `str` + = note: to learn more, visit + +error[E0277]: the trait bound `f32: Foo` is not satisfied + --> $DIR/impl_wf.rs:40: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 2 previous errors + +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..9dd9eb320ddd3 --- /dev/null +++ b/src/test/ui/chalkify/inherent_impl.rs @@ -0,0 +1,57 @@ +// run-pass +// compile-flags: -Z chalk +// FIXME(chalk): remove when uncommented +#![allow(dead_code, unused_variables)] + +trait Foo { } + +impl Foo for i32 { } + +struct S { + x: T, +} + +// FIXME(chalk): need late-bound regions on FnDefs +/* +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. + // FIXME(chalk): need late-bound regions on FnDefs + /* + fn dummy_bar(&self) { + only_foo(&self.x); + only_bar::(); + } + */ + fn dummy_bar() { + only_bar::(); + } +} + +fn main() { + let s = S { + x: 5, + }; + + // FIXME(chalk): need late-bound regions on FnDefs + /* + s.dummy_foo(); + s.dummy_bar::(); + */ + 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..2b62bf18a71ce --- /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): order of these two errors is non-deterministic, + // so let's just hide one for now + //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. + only_bar(x); //~ ERROR the trait bound `f64: 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..5cfb968404df6 --- /dev/null +++ b/src/test/ui/chalkify/type_inference.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `f64: Bar` is not satisfied + --> $DIR/type_inference.rs:27:5 + | +LL | fn only_bar(_x: T) { } + | --- required by this bound in `only_bar` +... +LL | only_bar(x); + | ^^^^^^^^ the trait `Bar` is not implemented for `f64` + +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..7c469d99c5799 --- /dev/null +++ b/src/test/ui/chalkify/type_wf.rs @@ -0,0 +1,25 @@ +// check-fail +// 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, + }; + + let s = S { //~ ERROR the trait bound `f64: Foo` is not satisfied + x: 5.0, + }; + + let s = S { + x: Some(5.0), + }; +} diff --git a/src/test/ui/chalkify/type_wf.stderr b/src/test/ui/chalkify/type_wf.stderr new file mode 100644 index 0000000000000..ab585a6ed2140 --- /dev/null +++ b/src/test/ui/chalkify/type_wf.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `f64: Foo` is not satisfied + --> $DIR/type_wf.rs:18:13 + | +LL | struct S { + | ---------------- required by `S` +... +LL | let s = S { + | ^ the trait `Foo` is not implemented for `f64` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. 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-immutable-mut-slices.rs b/src/test/ui/check-static-immutable-mut-slices.rs index d5e9fb2dede93..3be02f6a0f674 100644 --- a/src/test/ui/check-static-immutable-mut-slices.rs +++ b/src/test/ui/check-static-immutable-mut-slices.rs @@ -1,6 +1,6 @@ // Checks that immutable static items can't have mutable slices static TEST: &'static mut [isize] = &mut []; -//~^ ERROR references in statics may only refer to immutable values +//~^ ERROR mutable references are not allowed in statics pub fn main() { } diff --git a/src/test/ui/check-static-immutable-mut-slices.stderr b/src/test/ui/check-static-immutable-mut-slices.stderr index 66fe8646e1016..9ffbb483d139d 100644 --- a/src/test/ui/check-static-immutable-mut-slices.stderr +++ b/src/test/ui/check-static-immutable-mut-slices.stderr @@ -1,12 +1,9 @@ -error[E0658]: references in statics may only refer to immutable values +error[E0764]: mutable references are not allowed in statics --> $DIR/check-static-immutable-mut-slices.rs:3:37 | LL | static TEST: &'static mut [isize] = &mut []; - | ^^^^^^^ statics require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^^ `&mut` is only allowed in `const fn` error: aborting due to previous error -For more information about this error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0764`. diff --git a/src/test/ui/check-static-values-constraints.stderr b/src/test/ui/check-static-values-constraints.stderr index 7d7ecbd1a26a5..b00affdca850a 100644 --- a/src/test/ui/check-static-values-constraints.stderr +++ b/src/test/ui/check-static-values-constraints.stderr @@ -5,7 +5,9 @@ LL | ..SafeStruct{field1: SafeEnum::Va | ___________________________________________^ LL | | LL | | field2: SafeEnum::Variant1}}; - | |________________________________________________________________________________^ statics cannot evaluate destructors + | | ^- value is dropped here + | |________________________________________________________________________________| + | statics cannot evaluate destructors error[E0010]: allocations are not allowed in statics --> $DIR/check-static-values-constraints.rs:79:33 @@ -18,6 +20,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 +40,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 +54,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 +68,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 +82,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 +96,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 +119,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..64a0b52a1fac5 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 @@ -1,42 +1,43 @@ -error[E0631]: type mismatch in closure arguments - --> $DIR/expect-fn-supply-fn.rs:30:5 +error: lifetime may not live long enough + --> $DIR/expect-fn-supply-fn.rs:16:49 | -LL | fn with_closure_expecting_fn_with_free_region(_: F) - | ------------------------------------------ -LL | where F: for<'a> FnOnce(fn(&'a u32), &i32) - | ------------------------- required by this bound in `with_closure_expecting_fn_with_free_region` +LL | fn expect_free_supply_free_from_fn<'x>(x: &'x u32) { + | -- lifetime `'x` defined here ... -LL | with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---------------- found signature of `fn(for<'r> fn(&'r u32), _) -> _` - | | - | expected signature of `fn(fn(&'a u32), &i32) -> _` +LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {}); + | ^ + | | + | has type `fn(&'1 u32)` + | requires that `'1` must outlive `'x` -error[E0631]: type mismatch in closure arguments - --> $DIR/expect-fn-supply-fn.rs:37:5 +error: lifetime may not live long enough + --> $DIR/expect-fn-supply-fn.rs:16:49 | -LL | fn with_closure_expecting_fn_with_bound_region(_: F) - | ------------------------------------------- -LL | where F: FnOnce(fn(&u32), &i32) - | ---------------------- required by this bound in `with_closure_expecting_fn_with_bound_region` +LL | fn expect_free_supply_free_from_fn<'x>(x: &'x u32) { + | -- lifetime `'x` defined here ... +LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {}); + | ^ requires that `'x` must outlive `'static` + | + = help: consider replacing `'x` with `'static` + +error: higher-ranked subtype error + --> $DIR/expect-fn-supply-fn.rs:32:49 + | +LL | with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {}); + | ^ + +error: higher-ranked subtype error + --> $DIR/expect-fn-supply-fn.rs:39:50 + | LL | with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------------------- found signature of `fn(fn(&'x u32), _) -> _` - | | - | expected signature of `fn(for<'r> fn(&'r u32), &i32) -> _` + | ^ -error[E0631]: type mismatch in closure arguments - --> $DIR/expect-fn-supply-fn.rs:46:5 +error: higher-ranked subtype error + --> $DIR/expect-fn-supply-fn.rs:48:50 | -LL | fn with_closure_expecting_fn_with_bound_region(_: F) - | ------------------------------------------- -LL | where F: FnOnce(fn(&u32), &i32) - | ---------------------- required by this bound in `with_closure_expecting_fn_with_bound_region` -... LL | with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --------------- found signature of `for<'r> fn(fn(&'r u32), _) -> _` - | | - | expected signature of `fn(for<'r> fn(&'r u32), &i32) -> _` + | ^ -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0631`. diff --git a/src/test/ui/closure-expected-type/expect-fn-supply-fn.rs b/src/test/ui/closure-expected-type/expect-fn-supply-fn.rs index a4e43da91baf8..c81c40c18b45b 100644 --- a/src/test/ui/closure-expected-type/expect-fn-supply-fn.rs +++ b/src/test/ui/closure-expected-type/expect-fn-supply-fn.rs @@ -1,10 +1,12 @@ fn with_closure_expecting_fn_with_free_region(_: F) - where F: for<'a> FnOnce(fn(&'a u32), &i32) +where + F: for<'a> FnOnce(fn(&'a u32), &i32), { } fn with_closure_expecting_fn_with_bound_region(_: F) - where F: FnOnce(fn(&u32), &i32) +where + F: FnOnce(fn(&u32), &i32), { } @@ -28,14 +30,14 @@ fn expect_free_supply_bound() { // Here, we are given a function whose region is bound at closure level, // but we expect one bound in the argument. Error results. with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {}); - //~^ ERROR type mismatch + //~^ ERROR mismatched types } fn expect_bound_supply_free_from_fn<'x>(x: &'x u32) { // Here, we are given a `fn(&u32)` but we expect a `fn(&'x // u32)`. In principle, this could be ok, but we demand equality. with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {}); - //~^ ERROR type mismatch + //~^ ERROR mismatched types } fn expect_bound_supply_free_from_closure() { @@ -44,7 +46,7 @@ fn expect_bound_supply_free_from_closure() { // the argument level. type Foo<'a> = fn(&'a u32); with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| { - //~^ ERROR type mismatch + //~^ ERROR mismatched types }); } @@ -52,8 +54,7 @@ fn expect_bound_supply_bound<'x>(x: &'x u32) { // No error in this case. The supplied type supplies the bound // regions, and hence we are able to figure out the type of `y` // from the expected type - with_closure_expecting_fn_with_bound_region(|x: for<'z> fn(&'z u32), y| { - }); + with_closure_expecting_fn_with_bound_region(|x: for<'z> fn(&'z u32), y| {}); } -fn main() { } +fn main() {} 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..0de15dfa7357d 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 @@ -1,81 +1,68 @@ error[E0308]: mismatched types - --> $DIR/expect-fn-supply-fn.rs:14:52 + --> $DIR/expect-fn-supply-fn.rs:16:52 | LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {}); | ^^^^^^^^^^^ lifetime mismatch | = note: expected fn pointer `fn(&u32)` found fn pointer `fn(&'x u32)` -note: the anonymous lifetime #2 defined on the body at 14:48... - --> $DIR/expect-fn-supply-fn.rs:14:48 +note: the anonymous lifetime #2 defined on the body at 16:48... + --> $DIR/expect-fn-supply-fn.rs:16:48 | LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {}); | ^^^^^^^^^^^^^^^^^^^^^^ -note: ...does not necessarily outlive the lifetime `'x` as defined on the function body at 11:36 - --> $DIR/expect-fn-supply-fn.rs:11:36 +note: ...does not necessarily outlive the lifetime `'x` as defined on the function body at 13:36 + --> $DIR/expect-fn-supply-fn.rs:13:36 | LL | fn expect_free_supply_free_from_fn<'x>(x: &'x u32) { | ^^ error[E0308]: mismatched types - --> $DIR/expect-fn-supply-fn.rs:14:52 + --> $DIR/expect-fn-supply-fn.rs:16:52 | LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {}); | ^^^^^^^^^^^ lifetime mismatch | = note: expected fn pointer `fn(&u32)` found fn pointer `fn(&'x u32)` -note: the lifetime `'x` as defined on the function body at 11:36... - --> $DIR/expect-fn-supply-fn.rs:11:36 +note: the lifetime `'x` as defined on the function body at 13:36... + --> $DIR/expect-fn-supply-fn.rs:13:36 | LL | fn expect_free_supply_free_from_fn<'x>(x: &'x u32) { | ^^ -note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 14:48 - --> $DIR/expect-fn-supply-fn.rs:14:48 +note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 16:48 + --> $DIR/expect-fn-supply-fn.rs:16:48 | LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {}); | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0631]: type mismatch in closure arguments - --> $DIR/expect-fn-supply-fn.rs:30:5 +error[E0308]: mismatched types + --> $DIR/expect-fn-supply-fn.rs:32:52 | -LL | fn with_closure_expecting_fn_with_free_region(_: F) - | ------------------------------------------ -LL | where F: for<'a> FnOnce(fn(&'a u32), &i32) - | ------------------------- required by this bound in `with_closure_expecting_fn_with_free_region` -... LL | with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---------------- found signature of `fn(for<'r> fn(&'r u32), _) -> _` - | | - | expected signature of `fn(fn(&'a u32), &i32) -> _` + | ^^^^^^^^ one type is more general than the other + | + = note: expected fn pointer `fn(&u32)` + found fn pointer `for<'r> fn(&'r u32)` -error[E0631]: type mismatch in closure arguments - --> $DIR/expect-fn-supply-fn.rs:37:5 +error[E0308]: mismatched types + --> $DIR/expect-fn-supply-fn.rs:39:53 | -LL | fn with_closure_expecting_fn_with_bound_region(_: F) - | ------------------------------------------- -LL | where F: FnOnce(fn(&u32), &i32) - | ---------------------- required by this bound in `with_closure_expecting_fn_with_bound_region` -... LL | with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------------------- found signature of `fn(fn(&'x u32), _) -> _` - | | - | expected signature of `fn(for<'r> fn(&'r u32), &i32) -> _` + | ^^^^^^^^^^^ one type is more general than the other + | + = note: expected fn pointer `for<'r> fn(&'r u32)` + found fn pointer `fn(&'x u32)` -error[E0631]: type mismatch in closure arguments - --> $DIR/expect-fn-supply-fn.rs:46:5 +error[E0308]: mismatched types + --> $DIR/expect-fn-supply-fn.rs:48:53 | -LL | fn with_closure_expecting_fn_with_bound_region(_: F) - | ------------------------------------------- -LL | where F: FnOnce(fn(&u32), &i32) - | ---------------------- required by this bound in `with_closure_expecting_fn_with_bound_region` -... LL | with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --------------- found signature of `for<'r> fn(fn(&'r u32), _) -> _` - | | - | expected signature of `fn(for<'r> fn(&'r u32), &i32) -> _` + | ^^^^^^^ one type is more general than the other + | + = note: expected fn pointer `for<'r> fn(&'r u32)` + found fn pointer `fn(&u32)` error: aborting due to 5 previous errors -Some errors have detailed explanations: E0308, E0631. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0308`. 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/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr b/src/test/ui/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr index 2005bd4dd5ca7..0c6d11cd3211d 100644 --- a/src/test/ui/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr +++ b/src/test/ui/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr @@ -1,8 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/expect-two-infer-vars-supply-ty-with-bound-region.rs:8:27 + --> $DIR/expect-two-infer-vars-supply-ty-with-bound-region.rs:8:5 | LL | with_closure(|x: u32, y| {}); - | ^ consider giving this closure parameter a type + | ^^^^^^^^^^^^ cannot infer type for type parameter `B` declared on the function `with_closure` error: aborting due to previous error 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-2.nll.stderr b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.nll.stderr new file mode 100644 index 0000000000000..52bca8dd63e1f --- /dev/null +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.nll.stderr @@ -0,0 +1,24 @@ +error: lifetime may not live long enough + --> $DIR/expect-region-supply-region-2.rs:14:30 + | +LL | fn expect_bound_supply_named<'x>() { + | -- lifetime `'x` defined here +... +LL | closure_expecting_bound(|x: &'x u32| { + | ^ - let's call the lifetime of this reference `'1` + | | + | requires that `'1` must outlive `'x` + +error: lifetime may not live long enough + --> $DIR/expect-region-supply-region-2.rs:14:30 + | +LL | fn expect_bound_supply_named<'x>() { + | -- lifetime `'x` defined here +... +LL | closure_expecting_bound(|x: &'x u32| { + | ^ requires that `'x` must outlive `'static` + | + = help: consider replacing `'x` with `'static` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.rs b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.rs new file mode 100644 index 0000000000000..7405b1a1e3a28 --- /dev/null +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.rs @@ -0,0 +1,24 @@ +#![allow(warnings)] + +fn closure_expecting_bound(_: F) +where + F: FnOnce(&u32), +{ +} + +fn expect_bound_supply_named<'x>() { + let mut f: Option<&u32> = None; + + // Here we give a type annotation that `x` should be free. We get + // an error because of that. + closure_expecting_bound(|x: &'x u32| { + //~^ ERROR mismatched types + //~| ERROR mismatched types + + // Borrowck doesn't get a chance to run, but if it did it should error + // here. + f = Some(x); + }); +} + +fn main() {} diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr new file mode 100644 index 0000000000000..7f527904a69e5 --- /dev/null +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr @@ -0,0 +1,55 @@ +error[E0308]: mismatched types + --> $DIR/expect-region-supply-region-2.rs:14:33 + | +LL | closure_expecting_bound(|x: &'x u32| { + | ^^^^^^^ lifetime mismatch + | + = note: expected reference `&u32` + found reference `&'x u32` +note: the anonymous lifetime #2 defined on the body at 14:29... + --> $DIR/expect-region-supply-region-2.rs:14:29 + | +LL | closure_expecting_bound(|x: &'x u32| { + | _____________________________^ +LL | | +LL | | +LL | | +... | +LL | | f = Some(x); +LL | | }); + | |_____^ +note: ...does not necessarily outlive the lifetime `'x` as defined on the function body at 9:30 + --> $DIR/expect-region-supply-region-2.rs:9:30 + | +LL | fn expect_bound_supply_named<'x>() { + | ^^ + +error[E0308]: mismatched types + --> $DIR/expect-region-supply-region-2.rs:14:33 + | +LL | closure_expecting_bound(|x: &'x u32| { + | ^^^^^^^ lifetime mismatch + | + = note: expected reference `&u32` + found reference `&'x u32` +note: the lifetime `'x` as defined on the function body at 9:30... + --> $DIR/expect-region-supply-region-2.rs:9:30 + | +LL | fn expect_bound_supply_named<'x>() { + | ^^ +note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 14:29 + --> $DIR/expect-region-supply-region-2.rs:14:29 + | +LL | closure_expecting_bound(|x: &'x u32| { + | _____________________________^ +LL | | +LL | | +LL | | +... | +LL | | f = Some(x); +LL | | }); + | |_____^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region.nll.stderr b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.nll.stderr deleted file mode 100644 index d7d716ed4cb0a..0000000000000 --- a/src/test/ui/closures/closure-expected-type/expect-region-supply-region.nll.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error[E0521]: borrowed data escapes outside of closure - --> $DIR/expect-region-supply-region.rs:18:9 - | -LL | let mut f: Option<&u32> = None; - | ----- `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); - | ^^^^^^^^^^^ `x` escapes the closure body here - -error[E0521]: borrowed data escapes outside of closure - --> $DIR/expect-region-supply-region.rs:28:9 - | -LL | let mut f: Option<&u32> = None; - | ----- `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); - | ^^^^^^^^^^^ `x` escapes the closure body here - -error: lifetime may not live long enough - --> $DIR/expect-region-supply-region.rs:37:30 - | -LL | fn expect_bound_supply_named<'x>() { - | -- lifetime `'x` defined here -... -LL | closure_expecting_bound(|x: &'x u32| { - | ^ - let's call the lifetime of this reference `'1` - | | - | requires that `'1` must outlive `'x` - -error: lifetime may not live long enough - --> $DIR/expect-region-supply-region.rs:37:30 - | -LL | fn expect_bound_supply_named<'x>() { - | -- lifetime `'x` defined here -... -LL | closure_expecting_bound(|x: &'x u32| { - | ^ requires that `'x` must outlive `'static` - | - = help: consider replacing `'x` with `'static` - -error: aborting due to 4 previous errors - 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-expected-type/expect-region-supply-region.rs b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.rs index 28a6ab77a915e..55c6aa795c26a 100644 --- a/src/test/ui/closures/closure-expected-type/expect-region-supply-region.rs +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.rs @@ -1,12 +1,14 @@ #![allow(warnings)] fn closure_expecting_bound(_: F) - where F: FnOnce(&u32) +where + F: FnOnce(&u32), { } fn closure_expecting_free<'a, F>(_: F) - where F: FnOnce(&'a u32) +where + F: FnOnce(&'a u32), { } @@ -15,7 +17,7 @@ fn expect_bound_supply_nothing() { // it to escape into `f`: let mut f: Option<&u32> = None; closure_expecting_bound(|x| { - f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure + f = Some(x); //~ ERROR borrowed data escapes outside of closure }); } @@ -25,22 +27,7 @@ fn expect_bound_supply_bound() { // closure: let mut f: Option<&u32> = None; closure_expecting_bound(|x: &u32| { - f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure - }); -} - -fn expect_bound_supply_named<'x>() { - let mut f: Option<&u32> = None; - - // Here we give a type annotation that `x` should be free. We get - // an error because of that. - closure_expecting_bound(|x: &'x u32| { - //~^ ERROR mismatched types - //~| ERROR mismatched types - - // And we still cannot let `x` escape into `f`. - f = Some(x); - //~^ ERROR borrowed data cannot be stored outside of its closure + f = Some(x); //~ ERROR borrowed data escapes outside of closure }); } @@ -67,4 +54,4 @@ fn expect_free_supply_named<'x>() { closure_expecting_free(|x: &'x u32| f = Some(x)); // OK } -fn main() { } +fn main() {} diff --git a/src/test/ui/closures/closure-expected-type/expect-region-supply-region.stderr b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.stderr index eb860f9aef243..213071abfffc3 100644 --- a/src/test/ui/closures/closure-expected-type/expect-region-supply-region.stderr +++ b/src/test/ui/closures/closure-expected-type/expect-region-supply-region.stderr @@ -1,87 +1,22 @@ -error: borrowed data cannot be stored outside of its closure - --> $DIR/expect-region-supply-region.rs:18:18 +error[E0521]: borrowed data escapes outside of closure + --> $DIR/expect-region-supply-region.rs:20:9 | LL | let mut f: Option<&u32> = None; - | ----- borrowed data cannot be stored into here... + | ----- `f` declared here, outside of the closure body LL | closure_expecting_bound(|x| { - | --- ...because it cannot outlive this closure + | - `x` is a reference that is only valid in the closure body LL | f = Some(x); - | ^ cannot be stored outside of its closure + | ^^^^^^^^^^^ `x` escapes the closure body here -error: borrowed data cannot be stored outside of its closure - --> $DIR/expect-region-supply-region.rs:28:18 +error[E0521]: borrowed data escapes outside of closure + --> $DIR/expect-region-supply-region.rs:30:9 | LL | let mut f: Option<&u32> = None; - | ----- borrowed data cannot be stored into here... + | ----- `f` declared here, outside of the closure body LL | closure_expecting_bound(|x: &u32| { - | --------- ...because it cannot outlive this closure + | - `x` is a reference that is only valid in the closure body LL | f = Some(x); - | ^ cannot be stored outside of its closure + | ^^^^^^^^^^^ `x` escapes the closure body here -error[E0308]: mismatched types - --> $DIR/expect-region-supply-region.rs:37:33 - | -LL | closure_expecting_bound(|x: &'x u32| { - | ^^^^^^^ lifetime mismatch - | - = note: expected reference `&u32` - found reference `&'x u32` -note: the anonymous lifetime #2 defined on the body at 37:29... - --> $DIR/expect-region-supply-region.rs:37:29 - | -LL | closure_expecting_bound(|x: &'x u32| { - | _____________________________^ -LL | | -LL | | -LL | | -... | -LL | | -LL | | }); - | |_____^ -note: ...does not necessarily outlive the lifetime `'x` as defined on the function body at 32:30 - --> $DIR/expect-region-supply-region.rs:32:30 - | -LL | fn expect_bound_supply_named<'x>() { - | ^^ - -error[E0308]: mismatched types - --> $DIR/expect-region-supply-region.rs:37:33 - | -LL | closure_expecting_bound(|x: &'x u32| { - | ^^^^^^^ lifetime mismatch - | - = note: expected reference `&u32` - found reference `&'x u32` -note: the lifetime `'x` as defined on the function body at 32:30... - --> $DIR/expect-region-supply-region.rs:32:30 - | -LL | fn expect_bound_supply_named<'x>() { - | ^^ -note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 37:29 - --> $DIR/expect-region-supply-region.rs:37:29 - | -LL | closure_expecting_bound(|x: &'x u32| { - | _____________________________^ -LL | | -LL | | -LL | | -... | -LL | | -LL | | }); - | |_____^ - -error: borrowed data cannot be stored outside of its closure - --> $DIR/expect-region-supply-region.rs:42:18 - | -LL | let mut f: Option<&u32> = None; - | ----- borrowed data cannot be stored into here... -... -LL | closure_expecting_bound(|x: &'x u32| { - | ------------ ...because it cannot outlive this closure -... -LL | f = Some(x); - | ^ cannot be stored outside of its closure - -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/closures/closure-immutable-outer-variable.fixed b/src/test/ui/closures/closure-immutable-outer-variable.fixed index 102f1f94a36e1..1b0feede34ecf 100644 --- a/src/test/ui/closures/closure-immutable-outer-variable.fixed +++ b/src/test/ui/closures/closure-immutable-outer-variable.fixed @@ -8,6 +8,6 @@ fn foo(mut f: Box) { fn main() { let mut y = true; - foo(Box::new(move || y = false) as Box<_>); + foo(Box::new(move || y = !y) as Box<_>); //~^ ERROR cannot assign to `y`, as it is not declared as mutable } diff --git a/src/test/ui/closures/closure-immutable-outer-variable.rs b/src/test/ui/closures/closure-immutable-outer-variable.rs index 6eb43b372c96c..50ec1c6148a04 100644 --- a/src/test/ui/closures/closure-immutable-outer-variable.rs +++ b/src/test/ui/closures/closure-immutable-outer-variable.rs @@ -8,6 +8,6 @@ fn foo(mut f: Box) { fn main() { let y = true; - foo(Box::new(move || y = false) as Box<_>); + foo(Box::new(move || y = !y) as Box<_>); //~^ ERROR cannot assign to `y`, as it is not declared as mutable } diff --git a/src/test/ui/closures/closure-immutable-outer-variable.stderr b/src/test/ui/closures/closure-immutable-outer-variable.stderr index 7e60f3cd8ffa4..799097889cd30 100644 --- a/src/test/ui/closures/closure-immutable-outer-variable.stderr +++ b/src/test/ui/closures/closure-immutable-outer-variable.stderr @@ -3,8 +3,8 @@ error[E0594]: cannot assign to `y`, as it is not declared as mutable | LL | let y = true; | - help: consider changing this to be mutable: `mut y` -LL | foo(Box::new(move || y = false) as Box<_>); - | ^^^^^^^^^ cannot assign +LL | foo(Box::new(move || y = !y) as Box<_>); + | ^^^^^^ cannot assign error: aborting due to previous error diff --git a/src/test/ui/closures/closure-move-sync.rs b/src/test/ui/closures/closure-move-sync.rs index 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-no-fn-3.stderr b/src/test/ui/closures/closure-no-fn-3.stderr index ab6056b65473e..4b3b4be798fc1 100644 --- a/src/test/ui/closures/closure-no-fn-3.stderr +++ b/src/test/ui/closures/closure-no-fn-3.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:27: 6:37 b --> $DIR/closure-no-fn-3.rs:6:27 | LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast error: aborting due to previous error 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-41366.rs b/src/test/ui/closures/issue-41366.rs index 5cae0e76d1acb..af1e37ba867de 100644 --- a/src/test/ui/closures/issue-41366.rs +++ b/src/test/ui/closures/issue-41366.rs @@ -7,7 +7,6 @@ impl<'g> T<'g> for u32 { } fn main() { - (&|_|()) as &dyn for<'x> Fn(>::V); + (&|_| ()) as &dyn for<'x> Fn(>::V); //~^ ERROR: type mismatch in closure arguments - //~| ERROR: type mismatch resolving } diff --git a/src/test/ui/closures/issue-41366.stderr b/src/test/ui/closures/issue-41366.stderr index 2f2871e9f0e90..9c4b7d529ef4d 100644 --- a/src/test/ui/closures/issue-41366.stderr +++ b/src/test/ui/closures/issue-41366.stderr @@ -1,23 +1,14 @@ error[E0631]: type mismatch in closure arguments --> $DIR/issue-41366.rs:10:5 | -LL | (&|_|()) as &dyn for<'x> Fn(>::V); - | ^^-----^ +LL | (&|_| ()) as &dyn for<'x> Fn(>::V); + | ^^------^ | | | - | | found signature of `fn(_) -> _` - | expected signature of `for<'x> fn(>::V) -> _` + | | found signature of `fn(u16) -> _` + | expected signature of `fn(>::V) -> _` | = note: required for the cast to the object type `dyn for<'x> std::ops::Fn(>::V)` -error[E0271]: type mismatch resolving `for<'x> <[closure@$DIR/issue-41366.rs:10:7: 10:12] as std::ops::FnOnce<(>::V,)>>::Output == ()` - --> $DIR/issue-41366.rs:10:5 - | -LL | (&|_|()) as &dyn for<'x> Fn(>::V); - | ^^^^^^^^ expected bound lifetime parameter 'x, found concrete lifetime - | - = note: required for the cast to the object type `dyn for<'x> std::ops::Fn(>::V)` - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0271, E0631. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0631`. 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-52437.rs b/src/test/ui/closures/issue-52437.rs index 1e649a556e01d..634638e1335b3 100644 --- a/src/test/ui/closures/issue-52437.rs +++ b/src/test/ui/closures/issue-52437.rs @@ -3,4 +3,5 @@ fn main() { //~^ ERROR: invalid label name `'static` //~| ERROR: `loop` is not allowed in a `const` //~| ERROR: type annotations needed + //~| ERROR mismatched types } diff --git a/src/test/ui/closures/issue-52437.stderr b/src/test/ui/closures/issue-52437.stderr index b9225e55fe5c7..acb59c7b02d1b 100644 --- a/src/test/ui/closures/issue-52437.stderr +++ b/src/test/ui/closures/issue-52437.stderr @@ -19,7 +19,15 @@ error[E0282]: type annotations needed LL | [(); &(&'static: loop { |x| {}; }) as *const _ as usize] | ^ consider giving this closure parameter a type -error: aborting due to 3 previous errors +error[E0308]: mismatched types + --> $DIR/issue-52437.rs:2:5 + | +LL | fn main() { + | - expected `()` because of default return type +LL | [(); &(&'static: loop { |x| {}; }) as *const _ as usize] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found array `[(); _]` + +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0282, E0658. +Some errors have detailed explanations: E0282, E0308, E0658. For more information about an error, try `rustc --explain E0282`. 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/tab_2.stderr b/src/test/ui/codemap_tests/tab_2.stderr index 70414bbd953d6..0bfdc3ac2651d 100644 --- a/src/test/ui/codemap_tests/tab_2.stderr +++ b/src/test/ui/codemap_tests/tab_2.stderr @@ -1,4 +1,4 @@ -error: unterminated double quote string +error[E0765]: unterminated double quote string --> $DIR/tab_2.rs:4:7 | LL | """; @@ -8,3 +8,4 @@ LL | | } error: aborting due to previous error +For more information about this error, try `rustc --explain E0765`. 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..8b85adb58453f 100644 --- a/src/test/ui/codemap_tests/unicode.stderr +++ b/src/test/ui/codemap_tests/unicode.stderr @@ -4,7 +4,8 @@ error[E0703]: invalid ABI: found `路濫狼á́́` LL | extern "路濫狼á́́" fn foo() {} | ^^^^^^^^^ invalid ABI | - = help: valid ABIs: cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, Rust, C, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted + = help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted 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-overloaded-autoderef.rs b/src/test/ui/coerce/coerce-overloaded-autoderef.rs deleted file mode 100644 index 3fe18103ef8c3..0000000000000 --- a/src/test/ui/coerce/coerce-overloaded-autoderef.rs +++ /dev/null @@ -1,67 +0,0 @@ -// run-pass -#![allow(dead_code)] -// pretty-expanded FIXME #23616 - -use std::rc::Rc; - -// Examples from the "deref coercions" RFC, at rust-lang/rfcs#241. - -fn use_ref(_: &T) {} -fn use_mut(_: &mut T) {} - -fn use_rc(t: Rc) { - use_ref(&*t); // what you have to write today - use_ref(&t); // what you'd be able to write - use_ref(&&&&&&t); - use_ref(&mut &&&&&t); - use_ref(&&&mut &&&t); -} - -fn use_mut_box(mut t: &mut Box) { - use_mut(&mut *t); // what you have to write today - use_mut(t); // what you'd be able to write - use_mut(&mut &mut &mut t); - - use_ref(&*t); // what you have to write today - use_ref(t); // what you'd be able to write - use_ref(&&&&&&t); - use_ref(&mut &&&&&t); - use_ref(&&&mut &&&t); -} - -fn use_nested(t: &Box) { - use_ref(&**t); // what you have to write today - use_ref(t); // what you'd be able to write (note: recursive deref) - use_ref(&&&&&&t); - use_ref(&mut &&&&&t); - use_ref(&&&mut &&&t); -} - -fn use_slice(_: &[u8]) {} -fn use_slice_mut(_: &mut [u8]) {} - -fn use_vec(mut v: Vec) { - use_slice_mut(&mut v[..]); // what you have to write today - use_slice_mut(&mut v); // what you'd be able to write - use_slice_mut(&mut &mut &mut v); - - use_slice(&v[..]); // what you have to write today - use_slice(&v); // what you'd be able to write - use_slice(&&&&&&v); - use_slice(&mut &&&&&v); - use_slice(&&&mut &&&v); -} - -fn use_vec_ref(v: &Vec) { - use_slice(&v[..]); // what you have to write today - use_slice(v); // what you'd be able to write - use_slice(&&&&&&v); - use_slice(&mut &&&&&v); - use_slice(&&&mut &&&v); -} - -fn use_op_rhs(s: &mut String) { - *s += {&String::from(" ")}; -} - -pub fn main() {} diff --git a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr b/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr index 44e5c6a99f727..93e16bac13b87 100644 --- a/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr +++ b/src/test/ui/coercion/coerce-expect-unsized-ascribed.stderr @@ -121,7 +121,7 @@ error[E0308]: mismatched types LL | let _ = Box::new(|x| (x as u8)): Box _>; | ^^^^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::ops::Fn`, found closure | - = note: expected struct `std::boxed::Box _>` + = note: expected struct `std::boxed::Box u8>` found struct `std::boxed::Box<[closure@$DIR/coerce-expect-unsized-ascribed.rs:26:22: 26:35]>` error: aborting due to 14 previous errors diff --git a/src/test/ui/coerce/coerce-expect-unsized.rs b/src/test/ui/coercion/coerce-expect-unsized.rs similarity index 98% rename from src/test/ui/coerce/coerce-expect-unsized.rs rename to src/test/ui/coercion/coerce-expect-unsized.rs index b44aa6ab37760..d486fdf73aba8 100644 --- a/src/test/ui/coerce/coerce-expect-unsized.rs +++ b/src/test/ui/coercion/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/coercion/coerce-overloaded-autoderef-fail.rs b/src/test/ui/coercion/coerce-overloaded-autoderef-fail.rs new file mode 100644 index 0000000000000..01d9c1e486a42 --- /dev/null +++ b/src/test/ui/coercion/coerce-overloaded-autoderef-fail.rs @@ -0,0 +1,32 @@ +fn borrow_mut(x: &mut T) -> &mut T { x } +fn borrow(x: &T) -> &T { x } + +fn borrow_mut2(_: &mut T, _: &mut T) {} +fn borrow2(_: &mut T, _: &T) {} + +fn double_mut_borrow(x: &mut Box) { + let y = borrow_mut(x); + let z = borrow_mut(x); + //~^ ERROR cannot borrow `*x` as mutable more than once at a time + drop((y, z)); +} + +fn double_imm_borrow(x: &mut Box) { + let y = borrow(x); + let z = borrow(x); + **x += 1; + //~^ ERROR cannot assign to `**x` because it is borrowed + drop((y, z)); +} + +fn double_mut_borrow2(x: &mut Box) { + borrow_mut2(x, x); + //~^ ERROR cannot borrow `*x` as mutable more than once at a time +} + +fn double_borrow2(x: &mut Box) { + borrow2(x, x); + //~^ ERROR cannot borrow `*x` as mutable because it is also borrowed as immutable +} + +pub fn main() {} diff --git a/src/test/ui/coercion/coerce-overloaded-autoderef-fail.stderr b/src/test/ui/coercion/coerce-overloaded-autoderef-fail.stderr new file mode 100644 index 0000000000000..d067c3b3a1805 --- /dev/null +++ b/src/test/ui/coercion/coerce-overloaded-autoderef-fail.stderr @@ -0,0 +1,46 @@ +error[E0499]: cannot borrow `*x` as mutable more than once at a time + --> $DIR/coerce-overloaded-autoderef-fail.rs:9:24 + | +LL | let y = borrow_mut(x); + | - first mutable borrow occurs here +LL | let z = borrow_mut(x); + | ^ second mutable borrow occurs here +LL | +LL | drop((y, z)); + | - first borrow later used here + +error[E0506]: cannot assign to `**x` because it is borrowed + --> $DIR/coerce-overloaded-autoderef-fail.rs:17:5 + | +LL | let y = borrow(x); + | - borrow of `**x` occurs here +LL | let z = borrow(x); +LL | **x += 1; + | ^^^^^^^^ assignment to borrowed `**x` occurs here +LL | +LL | drop((y, z)); + | - borrow later used here + +error[E0499]: cannot borrow `*x` as mutable more than once at a time + --> $DIR/coerce-overloaded-autoderef-fail.rs:23:20 + | +LL | borrow_mut2(x, x); + | ----------- - ^ second mutable borrow occurs here + | | | + | | first mutable borrow occurs here + | first borrow later used by call + +error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable + --> $DIR/coerce-overloaded-autoderef-fail.rs:28:5 + | +LL | borrow2(x, x); + | -------^^^^-^ + | | | + | | immutable borrow occurs here + | mutable borrow occurs here + | immutable borrow later used by call + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0499, E0502, E0506. +For more information about an error, try `rustc --explain E0499`. diff --git a/src/test/ui/coercion/coerce-overloaded-autoderef.rs b/src/test/ui/coercion/coerce-overloaded-autoderef.rs index 01d9c1e486a42..d5484607c8b52 100644 --- a/src/test/ui/coercion/coerce-overloaded-autoderef.rs +++ b/src/test/ui/coercion/coerce-overloaded-autoderef.rs @@ -1,32 +1,68 @@ -fn borrow_mut(x: &mut T) -> &mut T { x } -fn borrow(x: &T) -> &T { x } +// run-pass +#![allow(unused_braces)] +#![allow(dead_code)] +// pretty-expanded FIXME #23616 -fn borrow_mut2(_: &mut T, _: &mut T) {} -fn borrow2(_: &mut T, _: &T) {} +use std::rc::Rc; -fn double_mut_borrow(x: &mut Box) { - let y = borrow_mut(x); - let z = borrow_mut(x); - //~^ ERROR cannot borrow `*x` as mutable more than once at a time - drop((y, z)); +// Examples from the "deref coercions" RFC, at rust-lang/rfcs#241. + +fn use_ref(_: &T) {} +fn use_mut(_: &mut T) {} + +fn use_rc(t: Rc) { + use_ref(&*t); // what you have to write today + use_ref(&t); // what you'd be able to write + use_ref(&&&&&&t); + use_ref(&mut &&&&&t); + use_ref(&&&mut &&&t); +} + +fn use_mut_box(mut t: &mut Box) { + use_mut(&mut *t); // what you have to write today + use_mut(t); // what you'd be able to write + use_mut(&mut &mut &mut t); + + use_ref(&*t); // what you have to write today + use_ref(t); // what you'd be able to write + use_ref(&&&&&&t); + use_ref(&mut &&&&&t); + use_ref(&&&mut &&&t); } -fn double_imm_borrow(x: &mut Box) { - let y = borrow(x); - let z = borrow(x); - **x += 1; - //~^ ERROR cannot assign to `**x` because it is borrowed - drop((y, z)); +fn use_nested(t: &Box) { + use_ref(&**t); // what you have to write today + use_ref(t); // what you'd be able to write (note: recursive deref) + use_ref(&&&&&&t); + use_ref(&mut &&&&&t); + use_ref(&&&mut &&&t); +} + +fn use_slice(_: &[u8]) {} +fn use_slice_mut(_: &mut [u8]) {} + +fn use_vec(mut v: Vec) { + use_slice_mut(&mut v[..]); // what you have to write today + use_slice_mut(&mut v); // what you'd be able to write + use_slice_mut(&mut &mut &mut v); + + use_slice(&v[..]); // what you have to write today + use_slice(&v); // what you'd be able to write + use_slice(&&&&&&v); + use_slice(&mut &&&&&v); + use_slice(&&&mut &&&v); } -fn double_mut_borrow2(x: &mut Box) { - borrow_mut2(x, x); - //~^ ERROR cannot borrow `*x` as mutable more than once at a time +fn use_vec_ref(v: &Vec) { + use_slice(&v[..]); // what you have to write today + use_slice(v); // what you'd be able to write + use_slice(&&&&&&v); + use_slice(&mut &&&&&v); + use_slice(&&&mut &&&v); } -fn double_borrow2(x: &mut Box) { - borrow2(x, x); - //~^ ERROR cannot borrow `*x` as mutable because it is also borrowed as immutable +fn use_op_rhs(s: &mut String) { + *s += {&String::from(" ")}; } pub fn main() {} diff --git a/src/test/ui/coercion/coerce-overloaded-autoderef.stderr b/src/test/ui/coercion/coerce-overloaded-autoderef.stderr deleted file mode 100644 index 7cdfcb5f4fc63..0000000000000 --- a/src/test/ui/coercion/coerce-overloaded-autoderef.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error[E0499]: cannot borrow `*x` as mutable more than once at a time - --> $DIR/coerce-overloaded-autoderef.rs:9:24 - | -LL | let y = borrow_mut(x); - | - first mutable borrow occurs here -LL | let z = borrow_mut(x); - | ^ second mutable borrow occurs here -LL | -LL | drop((y, z)); - | - first borrow later used here - -error[E0506]: cannot assign to `**x` because it is borrowed - --> $DIR/coerce-overloaded-autoderef.rs:17:5 - | -LL | let y = borrow(x); - | - borrow of `**x` occurs here -LL | let z = borrow(x); -LL | **x += 1; - | ^^^^^^^^ assignment to borrowed `**x` occurs here -LL | -LL | drop((y, z)); - | - borrow later used here - -error[E0499]: cannot borrow `*x` as mutable more than once at a time - --> $DIR/coerce-overloaded-autoderef.rs:23:20 - | -LL | borrow_mut2(x, x); - | ----------- - ^ second mutable borrow occurs here - | | | - | | first mutable borrow occurs here - | first borrow later used by call - -error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable - --> $DIR/coerce-overloaded-autoderef.rs:28:5 - | -LL | borrow2(x, x); - | -------^^^^-^ - | | | - | | immutable borrow occurs here - | mutable borrow occurs here - | immutable borrow later used by call - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0499, E0502, E0506. -For more information about an error, try `rustc --explain E0499`. diff --git a/src/test/ui/coerce/coerce-reborrow-imm-ptr-arg.rs b/src/test/ui/coercion/coerce-reborrow-imm-ptr-arg.rs similarity index 100% rename from src/test/ui/coerce/coerce-reborrow-imm-ptr-arg.rs rename to src/test/ui/coercion/coerce-reborrow-imm-ptr-arg.rs diff --git a/src/test/ui/coerce/coerce-reborrow-imm-ptr-rcvr.rs b/src/test/ui/coercion/coerce-reborrow-imm-ptr-rcvr.rs similarity index 100% rename from src/test/ui/coerce/coerce-reborrow-imm-ptr-rcvr.rs rename to src/test/ui/coercion/coerce-reborrow-imm-ptr-rcvr.rs diff --git a/src/test/ui/coerce/coerce-reborrow-imm-vec-arg.rs b/src/test/ui/coercion/coerce-reborrow-imm-vec-arg.rs similarity index 100% rename from src/test/ui/coerce/coerce-reborrow-imm-vec-arg.rs rename to src/test/ui/coercion/coerce-reborrow-imm-vec-arg.rs diff --git a/src/test/ui/coerce/coerce-reborrow-imm-vec-rcvr.rs b/src/test/ui/coercion/coerce-reborrow-imm-vec-rcvr.rs similarity index 100% rename from src/test/ui/coerce/coerce-reborrow-imm-vec-rcvr.rs rename to src/test/ui/coercion/coerce-reborrow-imm-vec-rcvr.rs diff --git a/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.rs b/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.rs new file mode 100644 index 0000000000000..48be2d3146b81 --- /dev/null +++ b/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.rs @@ -0,0 +1,6 @@ +fn test(_a: T, _b: T) {} + +fn main() { + test(&mut 7, &7); + //~^ mismatched types +} diff --git a/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr b/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr new file mode 100644 index 0000000000000..59b0ec496f16f --- /dev/null +++ b/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/coerce-reborrow-multi-arg-fail.rs:4:18 + | +LL | test(&mut 7, &7); + | ^^ types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&{integer}` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/coercion/coerce-reborrow-multi-arg.rs b/src/test/ui/coercion/coerce-reborrow-multi-arg.rs new file mode 100644 index 0000000000000..93cd0bb3e27f6 --- /dev/null +++ b/src/test/ui/coercion/coerce-reborrow-multi-arg.rs @@ -0,0 +1,9 @@ +// build-pass +fn test(_a: T, _b: T) {} + +fn main() { + test(&7, &7); + test(&7, &mut 7); + test::<&i32>(&mut 7, &7); + test::<&i32>(&mut 7, &mut 7); +} diff --git a/src/test/ui/coerce/coerce-reborrow-mut-ptr-arg.rs b/src/test/ui/coercion/coerce-reborrow-mut-ptr-arg.rs similarity index 100% rename from src/test/ui/coerce/coerce-reborrow-mut-ptr-arg.rs rename to src/test/ui/coercion/coerce-reborrow-mut-ptr-arg.rs diff --git a/src/test/ui/coerce/coerce-reborrow-mut-ptr-rcvr.rs b/src/test/ui/coercion/coerce-reborrow-mut-ptr-rcvr.rs similarity index 100% rename from src/test/ui/coerce/coerce-reborrow-mut-ptr-rcvr.rs rename to src/test/ui/coercion/coerce-reborrow-mut-ptr-rcvr.rs diff --git a/src/test/ui/coerce/coerce-reborrow-mut-vec-arg.rs b/src/test/ui/coercion/coerce-reborrow-mut-vec-arg.rs similarity index 100% rename from src/test/ui/coerce/coerce-reborrow-mut-vec-arg.rs rename to src/test/ui/coercion/coerce-reborrow-mut-vec-arg.rs diff --git a/src/test/ui/coerce/coerce-reborrow-mut-vec-rcvr.rs b/src/test/ui/coercion/coerce-reborrow-mut-vec-rcvr.rs similarity index 100% rename from src/test/ui/coerce/coerce-reborrow-mut-vec-rcvr.rs rename to src/test/ui/coercion/coerce-reborrow-mut-vec-rcvr.rs diff --git a/src/test/ui/coercion/coerce-to-bang-cast.rs b/src/test/ui/coercion/coerce-to-bang-cast.rs index 8ef1948084654..85598a42eccd9 100644 --- a/src/test/ui/coercion/coerce-to-bang-cast.rs +++ b/src/test/ui/coercion/coerce-to-bang-cast.rs @@ -1,7 +1,5 @@ #![feature(never_type)] -fn foo(x: usize, y: !, z: usize) { } - fn cast_a() { let y = {return; 22} as !; //~^ ERROR non-primitive cast diff --git a/src/test/ui/coercion/coerce-to-bang-cast.stderr b/src/test/ui/coercion/coerce-to-bang-cast.stderr index ff30ebc09c63a..50e009aa25bb1 100644 --- a/src/test/ui/coercion/coerce-to-bang-cast.stderr +++ b/src/test/ui/coercion/coerce-to-bang-cast.stderr @@ -1,18 +1,14 @@ error[E0605]: non-primitive cast: `i32` as `!` - --> $DIR/coerce-to-bang-cast.rs:6:13 + --> $DIR/coerce-to-bang-cast.rs:4:13 | LL | let y = {return; 22} as !; - | ^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `i32` as `!` - --> $DIR/coerce-to-bang-cast.rs:11:13 + --> $DIR/coerce-to-bang-cast.rs:9:13 | LL | let y = 22 as !; - | ^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/coerce/coerce-unify-return.rs b/src/test/ui/coercion/coerce-unify-return.rs similarity index 100% rename from src/test/ui/coerce/coerce-unify-return.rs rename to src/test/ui/coercion/coerce-unify-return.rs diff --git a/src/test/ui/coerce/coerce-unify.rs b/src/test/ui/coercion/coerce-unify.rs similarity index 100% rename from src/test/ui/coerce/coerce-unify.rs rename to src/test/ui/coercion/coerce-unify.rs diff --git a/src/test/ui/coerce/coerce-unsize-subtype.rs b/src/test/ui/coercion/coerce-unsize-subtype.rs similarity index 100% rename from src/test/ui/coerce/coerce-unsize-subtype.rs rename to src/test/ui/coercion/coerce-unsize-subtype.rs 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-fn-covariant-bound-vs-static.rs b/src/test/ui/coherence/coherence-fn-covariant-bound-vs-static.rs new file mode 100644 index 0000000000000..99f805f7f0f63 --- /dev/null +++ b/src/test/ui/coherence/coherence-fn-covariant-bound-vs-static.rs @@ -0,0 +1,26 @@ +// Test that impls for these two types are considered ovelapping: +// +// * `for<'r> fn(fn(&'r u32))` +// * `fn(fn(&'a u32)` where `'a` is free +// +// This is because, for `'a = 'static`, the two types overlap. +// Effectively for them to be equal to you get: +// +// * `for<'r> fn(fn(&'r u32)) <: fn(fn(&'static u32))` +// * true if `exists<'r> { 'r: 'static }` (obviously true) +// * `fn(fn(&'static u32)) <: for<'r> fn(fn(&'r u32))` +// * true if `forall<'r> { 'static: 'r }` (also true) + +trait Trait {} + +impl Trait for for<'r> fn(fn(&'r ())) {} +impl<'a> Trait for fn(fn(&'a ())) {} +//~^ ERROR conflicting implementations +// +// Note in particular that we do NOT get a future-compatibility warning +// here. This is because the new leak-check proposed in [MCP 295] does not +// "error" when these two types are equated. +// +// [MCP 295]: https://github.com/rust-lang/compiler-team/issues/295 + +fn main() {} diff --git a/src/test/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr b/src/test/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr new file mode 100644 index 0000000000000..49271edf8e5e4 --- /dev/null +++ b/src/test/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr @@ -0,0 +1,13 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))`: + --> $DIR/coherence-fn-covariant-bound-vs-static.rs:17:1 + | +LL | impl Trait for for<'r> fn(fn(&'r ())) {} + | ------------------------------------- first implementation here +LL | impl<'a> Trait for fn(fn(&'a ())) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'r> fn(fn(&'r ()))` + | + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/coherence/coherence-fn-implied-bounds.rs b/src/test/ui/coherence/coherence-fn-implied-bounds.rs new file mode 100644 index 0000000000000..4539af9a32e38 --- /dev/null +++ b/src/test/ui/coherence/coherence-fn-implied-bounds.rs @@ -0,0 +1,26 @@ +// Test that our leak-check is not smart enough to take implied bounds +// into account (yet). Here we have two types that look like they +// should not be equivalent, but because of the rules on implied +// bounds we ought to know that, in fact, `'a = 'b` must always hold, +// and hence they are. +// +// Rustc can't figure this out and hence it accepts the impls but +// gives a future-compatibility warning (because we'd like to make +// this an error someday). +// +// Note that while we would like to make this a hard error, we also +// give the same warning for `coherence-wasm-bindgen.rs`, which ought +// to be accepted. + +#![deny(coherence_leak_check)] + +trait Trait {} + +impl Trait for for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32 {} + +impl Trait for for<'c> fn(&'c &'c u32, &'c &'c u32) -> &'c u32 { + //~^ ERROR conflicting implementations + //~| WARNING this was previously accepted by the compiler +} + +fn main() {} diff --git a/src/test/ui/coherence/coherence-fn-implied-bounds.stderr b/src/test/ui/coherence/coherence-fn-implied-bounds.stderr new file mode 100644 index 0000000000000..a3e7f0bcde376 --- /dev/null +++ b/src/test/ui/coherence/coherence-fn-implied-bounds.stderr @@ -0,0 +1,20 @@ +error: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32`: + --> $DIR/coherence-fn-implied-bounds.rs:21:1 + | +LL | impl Trait for for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32 {} + | ------------------------------------------------------------------ first implementation here +LL | +LL | impl Trait for for<'c> fn(&'c &'c u32, &'c &'c u32) -> &'c u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a &'b u32, &'b &'a u32) -> &'b u32` + | +note: the lint level is defined here + --> $DIR/coherence-fn-implied-bounds.rs:15:9 + | +LL | #![deny(coherence_leak_check)] + | ^^^^^^^^^^^^^^^^^^^^ + = 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 #56105 + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + +error: aborting due to previous error + diff --git a/src/test/ui/coherence/coherence-fn-inputs.rs b/src/test/ui/coherence/coherence-fn-inputs.rs new file mode 100644 index 0000000000000..3afec5c5459af --- /dev/null +++ b/src/test/ui/coherence/coherence-fn-inputs.rs @@ -0,0 +1,25 @@ +// Test that we consider these two types completely equal: +// +// * `for<'a, 'b> fn(&'a u32, &'b u32)` +// * `for<'c> fn(&'c u32, &'c u32)` +// +// For a long time we considered these to be distinct types. But in fact they +// are equivalent, if you work through the implications of subtyping -- this is +// because: +// +// * `'c` can be the intersection of `'a` and `'b` (and there is always an intersection) +// * `'a` and `'b` can both be equal to `'c` + +trait Trait {} +impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {} +impl Trait for for<'c> fn(&'c u32, &'c u32) { + //~^ ERROR conflicting implementations + // + // Note in particular that we do NOT get a future-compatibility warning + // here. This is because the new leak-check proposed in [MCP 295] does not + // "error" when these two types are equated. + // + // [MCP 295]: https://github.com/rust-lang/compiler-team/issues/295 +} + +fn main() {} diff --git a/src/test/ui/coherence/coherence-fn-inputs.stderr b/src/test/ui/coherence/coherence-fn-inputs.stderr new file mode 100644 index 0000000000000..56ab873a39320 --- /dev/null +++ b/src/test/ui/coherence/coherence-fn-inputs.stderr @@ -0,0 +1,13 @@ +error[E0119]: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)`: + --> $DIR/coherence-fn-inputs.rs:15:1 + | +LL | impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {} + | ----------------------------------------------- first implementation here +LL | impl Trait for for<'c> fn(&'c u32, &'c u32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u32, &'b u32)` + | + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/coherence/coherence-free-vs-bound-region.rs b/src/test/ui/coherence/coherence-free-vs-bound-region.rs new file mode 100644 index 0000000000000..2f5c49d293d5d --- /dev/null +++ b/src/test/ui/coherence/coherence-free-vs-bound-region.rs @@ -0,0 +1,21 @@ +// Capture a coherence pattern from wasm-bindgen that we discovered as part of +// future-compatibility warning #56105. This pattern currently receives a lint +// warning but we probably want to support it long term. +// +// Key distinction: we are implementing once for `A` (take ownership) and one +// for `&A` (borrow). +// +// c.f. #56105 + +#![deny(coherence_leak_check)] + +trait TheTrait {} + +impl<'a> TheTrait for fn(&'a u8) {} + +impl TheTrait for fn(&u8) { + //~^ ERROR conflicting implementations of trait + //~| WARNING this was previously accepted by the compiler +} + +fn main() {} diff --git a/src/test/ui/coherence/coherence-free-vs-bound-region.stderr b/src/test/ui/coherence/coherence-free-vs-bound-region.stderr new file mode 100644 index 0000000000000..97aa491272143 --- /dev/null +++ b/src/test/ui/coherence/coherence-free-vs-bound-region.stderr @@ -0,0 +1,20 @@ +error: conflicting implementations of trait `TheTrait` for type `fn(&u8)`: + --> $DIR/coherence-free-vs-bound-region.rs:16:1 + | +LL | impl<'a> TheTrait for fn(&'a u8) {} + | -------------------------------- first implementation here +LL | +LL | impl TheTrait for fn(&u8) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `fn(&u8)` + | +note: the lint level is defined here + --> $DIR/coherence-free-vs-bound-region.rs:10:9 + | +LL | #![deny(coherence_leak_check)] + | ^^^^^^^^^^^^^^^^^^^^ + = 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 #56105 + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + +error: aborting due to previous error + 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-inherited-assoc-ty-cycle-err.rs b/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.rs index 7f0e5472c3c2e..d74d3a2a52351 100644 --- a/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.rs +++ b/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.rs @@ -4,6 +4,7 @@ // // No we expect to run into a more user-friendly cycle error instead. #![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete trait Trait { type Assoc; } //~^ ERROR E0391 diff --git a/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr b/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr index 71f997c54c6f2..7e140480b77d4 100644 --- a/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr +++ b/src/test/ui/coherence/coherence-inherited-assoc-ty-cycle-err.stderr @@ -1,16 +1,25 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/coherence-inherited-assoc-ty-cycle-err.rs:6:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0391]: cycle detected when building specialization graph of trait `Trait` - --> $DIR/coherence-inherited-assoc-ty-cycle-err.rs:8:1 + --> $DIR/coherence-inherited-assoc-ty-cycle-err.rs:9:1 | LL | trait Trait { type Assoc; } | ^^^^^^^^^^^^^^ | = note: ...which again requires building specialization graph of trait `Trait`, completing the cycle note: cycle used when coherence checking all impls of trait `Trait` - --> $DIR/coherence-inherited-assoc-ty-cycle-err.rs:8:1 + --> $DIR/coherence-inherited-assoc-ty-cycle-err.rs:9:1 | LL | trait Trait { type Assoc; } | ^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0391`. 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 deleted file mode 100644 index 76f5cc1b78232..0000000000000 --- a/src/test/ui/coherence/coherence-subtyping.old.stderr +++ /dev/null @@ -1,14 +0,0 @@ -warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`: - --> $DIR/coherence-subtyping.rs:16:1 - | -LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {} - | ---------------------------------------------------------- first implementation here -LL | -LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8` - | - = note: `#[warn(coherence_leak_check)]` 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 #56105 - = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details - diff --git a/src/test/ui/coherence/coherence-subtyping.re.stderr b/src/test/ui/coherence/coherence-subtyping.re.stderr deleted file mode 100644 index 76f5cc1b78232..0000000000000 --- a/src/test/ui/coherence/coherence-subtyping.re.stderr +++ /dev/null @@ -1,14 +0,0 @@ -warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`: - --> $DIR/coherence-subtyping.rs:16:1 - | -LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {} - | ---------------------------------------------------------- first implementation here -LL | -LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8` - | - = note: `#[warn(coherence_leak_check)]` 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 #56105 - = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details - diff --git a/src/test/ui/coherence/coherence-subtyping.rs b/src/test/ui/coherence/coherence-subtyping.rs index f5c1d92411baa..b3ed728a81c06 100644 --- a/src/test/ui/coherence/coherence-subtyping.rs +++ b/src/test/ui/coherence/coherence-subtyping.rs @@ -4,7 +4,6 @@ // Note: This scenario is currently accepted, but as part of the // universe transition (#56105) may eventually become an error. -// revisions: old re // check-pass trait TheTrait { @@ -14,10 +13,8 @@ trait TheTrait { impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {} impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { - //[re]~^ WARNING conflicting implementation - //[re]~^^ WARNING this was previously accepted by the compiler but is being phased out - //[old]~^^^ WARNING conflicting implementation - //[old]~^^^^ WARNING this was previously accepted by the compiler but is being phased out + //~^ WARNING conflicting implementation + //~^^ WARNING this was previously accepted by the compiler but is being phased out } fn main() {} diff --git a/src/test/ui/coherence/coherence-subtyping.stderr b/src/test/ui/coherence/coherence-subtyping.stderr new file mode 100644 index 0000000000000..7f751a24c75c9 --- /dev/null +++ b/src/test/ui/coherence/coherence-subtyping.stderr @@ -0,0 +1,16 @@ +warning: conflicting implementations of trait `TheTrait` for type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8`: + --> $DIR/coherence-subtyping.rs:15:1 + | +LL | impl TheTrait for for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8 {} + | ---------------------------------------------------------- first implementation here +LL | +LL | impl TheTrait for for<'a> fn(&'a u8, &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8` + | + = note: `#[warn(coherence_leak_check)]` 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 #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/coherence-wasm-bindgen.rs b/src/test/ui/coherence/coherence-wasm-bindgen.rs new file mode 100644 index 0000000000000..ee09a72449be1 --- /dev/null +++ b/src/test/ui/coherence/coherence-wasm-bindgen.rs @@ -0,0 +1,37 @@ +// Capture a coherence pattern from wasm-bindgen that we discovered as part of +// future-compatibility warning #56105. This pattern currently receives a lint +// warning but we probably want to support it long term. +// +// Key distinction: we are implementing once for `A` (take ownership) and one +// for `&A` (borrow). +// +// c.f. #56105 + +#![deny(coherence_leak_check)] + +trait IntoWasmAbi { + fn some_method(&self) {} +} + +trait FromWasmAbi {} +trait RefFromWasmAbi {} +trait ReturnWasmAbi {} + +impl<'a, 'b, A, R> IntoWasmAbi for &'a (dyn Fn(A) -> R + 'b) +where + A: FromWasmAbi, + R: ReturnWasmAbi, +{ +} + +// Explicitly writing the bound lifetime. +impl<'a, 'b, A, R> IntoWasmAbi for &'a (dyn for<'x> Fn(&'x A) -> R + 'b) +where + A: RefFromWasmAbi, + R: ReturnWasmAbi, +{ + //~^^^^^ ERROR conflicting implementation + //~| WARNING this was previously accepted +} + +fn main() {} diff --git a/src/test/ui/coherence/coherence-wasm-bindgen.stderr b/src/test/ui/coherence/coherence-wasm-bindgen.stderr new file mode 100644 index 0000000000000..c77483bb847f5 --- /dev/null +++ b/src/test/ui/coherence/coherence-wasm-bindgen.stderr @@ -0,0 +1,32 @@ +error: conflicting implementations of trait `IntoWasmAbi` for type `&dyn std::ops::Fn(&_) -> _`: + --> $DIR/coherence-wasm-bindgen.rs:28:1 + | +LL | / impl<'a, 'b, A, R> IntoWasmAbi for &'a (dyn Fn(A) -> R + 'b) +LL | | where +LL | | A: FromWasmAbi, +LL | | R: ReturnWasmAbi, +LL | | { +LL | | } + | |_- first implementation here +... +LL | / impl<'a, 'b, A, R> IntoWasmAbi for &'a (dyn for<'x> Fn(&'x A) -> R + 'b) +LL | | where +LL | | A: RefFromWasmAbi, +LL | | R: ReturnWasmAbi, +... | +LL | | +LL | | } + | |_^ conflicting implementation for `&dyn std::ops::Fn(&_) -> _` + | +note: the lint level is defined here + --> $DIR/coherence-wasm-bindgen.rs:10:9 + | +LL | #![deny(coherence_leak_check)] + | ^^^^^^^^^^^^^^^^^^^^ + = 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 #56105 + = note: downstream crates may implement trait `FromWasmAbi` for type `&_` + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + +error: aborting due to previous error + 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/command/command-argv0-debug.rs b/src/test/ui/command/command-argv0-debug.rs index 133d2ada2b263..cb948a91c1054 100644 --- a/src/test/ui/command/command-argv0-debug.rs +++ b/src/test/ui/command/command-argv0-debug.rs @@ -4,8 +4,6 @@ // ignore-cloudabi no processes // ignore-emscripten no processes // ignore-sgx no processes -#![feature(process_set_argv0)] - use std::os::unix::process::CommandExt; use std::process::Command; diff --git a/src/test/ui/command/command-argv0.rs b/src/test/ui/command/command-argv0.rs index 56a9fb4d39125..e3394e0567cb8 100644 --- a/src/test/ui/command/command-argv0.rs +++ b/src/test/ui/command/command-argv0.rs @@ -4,8 +4,6 @@ // ignore-cloudabi no processes // ignore-emscripten no processes // ignore-sgx no processes -#![feature(process_set_argv0)] - use std::env; use std::os::unix::process::CommandExt; use std::process::Command; 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/conditional-compilation/cfg_accessible-input-validation.rs b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.rs new file mode 100644 index 0000000000000..c51c908a4262e --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.rs @@ -0,0 +1,24 @@ +#![feature(cfg_accessible)] + +#[cfg_accessible] //~ ERROR malformed `cfg_accessible` attribute input +struct S1; + +#[cfg_accessible = "value"] //~ ERROR malformed `cfg_accessible` attribute input +struct S2; + +#[cfg_accessible()] //~ ERROR `cfg_accessible` path is not specified +struct S3; + +#[cfg_accessible(std, core)] //~ ERROR multiple `cfg_accessible` paths are specified +struct S4; + +#[cfg_accessible("std")] //~ ERROR `cfg_accessible` path cannot be a literal +struct S5; + +#[cfg_accessible(std = "value")] //~ ERROR `cfg_accessible` path cannot accept arguments +struct S6; + +#[cfg_accessible(std(value))] //~ ERROR `cfg_accessible` path cannot accept arguments +struct S7; + +fn main() {} diff --git a/src/test/ui/conditional-compilation/cfg_accessible-input-validation.stderr b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.stderr new file mode 100644 index 0000000000000..86706c766356e --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-input-validation.stderr @@ -0,0 +1,44 @@ +error: malformed `cfg_accessible` attribute input + --> $DIR/cfg_accessible-input-validation.rs:3:1 + | +LL | #[cfg_accessible] + | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]` + +error: malformed `cfg_accessible` attribute input + --> $DIR/cfg_accessible-input-validation.rs:6:1 + | +LL | #[cfg_accessible = "value"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]` + +error: `cfg_accessible` path is not specified + --> $DIR/cfg_accessible-input-validation.rs:9:1 + | +LL | #[cfg_accessible()] + | ^^^^^^^^^^^^^^^^^^^ + +error: multiple `cfg_accessible` paths are specified + --> $DIR/cfg_accessible-input-validation.rs:12:23 + | +LL | #[cfg_accessible(std, core)] + | ^^^^ + +error: `cfg_accessible` path cannot be a literal + --> $DIR/cfg_accessible-input-validation.rs:15:18 + | +LL | #[cfg_accessible("std")] + | ^^^^^ + +error: `cfg_accessible` path cannot accept arguments + --> $DIR/cfg_accessible-input-validation.rs:18:18 + | +LL | #[cfg_accessible(std = "value")] + | ^^^^^^^^^^^^^ + +error: `cfg_accessible` path cannot accept arguments + --> $DIR/cfg_accessible-input-validation.rs:21:18 + | +LL | #[cfg_accessible(std(value))] + | ^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/test/ui/conditional-compilation/cfg_accessible-stuck.rs b/src/test/ui/conditional-compilation/cfg_accessible-stuck.rs new file mode 100644 index 0000000000000..8bc93fa324378 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-stuck.rs @@ -0,0 +1,9 @@ +#![feature(cfg_accessible)] + +#[cfg_accessible(Z)] //~ ERROR cannot determine whether the path is accessible or not +struct S; + +#[cfg_accessible(S)] //~ ERROR cannot determine whether the path is accessible or not +struct Z; + +fn main() {} diff --git a/src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr b/src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr new file mode 100644 index 0000000000000..9641441a819b0 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr @@ -0,0 +1,14 @@ +error: cannot determine whether the path is accessible or not + --> $DIR/cfg_accessible-stuck.rs:6:1 + | +LL | #[cfg_accessible(S)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: cannot determine whether the path is accessible or not + --> $DIR/cfg_accessible-stuck.rs:3:1 + | +LL | #[cfg_accessible(Z)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/conditional-compilation/cfg_accessible-unstable.rs b/src/test/ui/conditional-compilation/cfg_accessible-unstable.rs new file mode 100644 index 0000000000000..e9247e67a2a26 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-unstable.rs @@ -0,0 +1,2 @@ +#[cfg_accessible(std)] //~ ERROR use of unstable library feature 'cfg_accessible' +fn main() {} diff --git a/src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr b/src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr new file mode 100644 index 0000000000000..2f55b9559c78f --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr @@ -0,0 +1,12 @@ +error[E0658]: use of unstable library feature 'cfg_accessible': `cfg_accessible` is not fully implemented + --> $DIR/cfg_accessible-unstable.rs:1:3 + | +LL | #[cfg_accessible(std)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #64797 for more information + = help: add `#![feature(cfg_accessible)]` 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/conditional-compilation/cfg_accessible.rs b/src/test/ui/conditional-compilation/cfg_accessible.rs new file mode 100644 index 0000000000000..07b0be5b1ae26 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible.rs @@ -0,0 +1,43 @@ +#![feature(cfg_accessible)] + +mod m { + pub struct ExistingPublic; + struct ExistingPrivate; +} + +#[cfg_accessible(m::ExistingPublic)] +struct ExistingPublic; + +// FIXME: Not implemented yet. +#[cfg_accessible(m::ExistingPrivate)] //~ ERROR not sure whether the path is accessible or not +struct ExistingPrivate; + +// FIXME: Not implemented yet. +#[cfg_accessible(m::NonExistent)] //~ ERROR not sure whether the path is accessible or not +struct ExistingPrivate; + +#[cfg_accessible(n::AccessibleExpanded)] // OK, `cfg_accessible` can wait and retry. +struct AccessibleExpanded; + +macro_rules! generate_accessible_expanded { + () => { + mod n { + pub struct AccessibleExpanded; + } + }; +} + +generate_accessible_expanded!(); + +struct S { + field: u8, +} + +// FIXME: Not implemented yet. +#[cfg_accessible(S::field)] //~ ERROR not sure whether the path is accessible or not +struct Field; + +fn main() { + ExistingPublic; + AccessibleExpanded; +} diff --git a/src/test/ui/conditional-compilation/cfg_accessible.stderr b/src/test/ui/conditional-compilation/cfg_accessible.stderr new file mode 100644 index 0000000000000..167765cd66ee6 --- /dev/null +++ b/src/test/ui/conditional-compilation/cfg_accessible.stderr @@ -0,0 +1,38 @@ +error: not sure whether the path is accessible or not + --> $DIR/cfg_accessible.rs:12:18 + | +LL | #[cfg_accessible(m::ExistingPrivate)] + | ^^^^^^^^^^^^^^^^^^ + | +note: `cfg_accessible` is not fully implemented + --> $DIR/cfg_accessible.rs:12:18 + | +LL | #[cfg_accessible(m::ExistingPrivate)] + | ^^^^^^^^^^^^^^^^^^ + +error: not sure whether the path is accessible or not + --> $DIR/cfg_accessible.rs:16:18 + | +LL | #[cfg_accessible(m::NonExistent)] + | ^^^^^^^^^^^^^^ + | +note: `cfg_accessible` is not fully implemented + --> $DIR/cfg_accessible.rs:16:18 + | +LL | #[cfg_accessible(m::NonExistent)] + | ^^^^^^^^^^^^^^ + +error: not sure whether the path is accessible or not + --> $DIR/cfg_accessible.rs:37:18 + | +LL | #[cfg_accessible(S::field)] + | ^^^^^^^^ + | +note: `cfg_accessible` is not fully implemented + --> $DIR/cfg_accessible.rs:37:18 + | +LL | #[cfg_accessible(S::field)] + | ^^^^^^^^ + +error: aborting due to 3 previous errors + 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 new file mode 100644 index 0000000000000..6110d16c070d9 --- /dev/null +++ b/src/test/ui/const-generics/argument_order.rs @@ -0,0 +1,9 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +struct Bad { //~ ERROR type parameters must be declared prior + arr: [u8; { N }], + another: T, +} + +fn main() { } diff --git a/src/test/ui/const-generics/argument_order.stderr b/src/test/ui/const-generics/argument_order.stderr new file mode 100644 index 0000000000000..f77ae49cf10b1 --- /dev/null +++ b/src/test/ui/const-generics/argument_order.stderr @@ -0,0 +1,17 @@ +error: type parameters must be declared prior to const parameters + --> $DIR/argument_order.rs:4:28 + | +LL | struct Bad { + | -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `` + +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/argument_order.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs b/src/test/ui/const-generics/array-impls/alloc-traits-impls-length-32.rs index db941a440e104..b4a083636b64f 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,14 @@ where Vec::::new() } +pub fn yes_array_into_vec() -> Vec { + [].into() +} + +pub fn yes_array_into_box() -> Box<[T]> { + [].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..48cf21d489ada 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,11 +2,18 @@ use std::{convert::TryFrom, rc::Rc, sync::Arc}; +pub fn no_vec() { + let v: Vec<_> = [0; 33].into(); + //~^ ERROR arrays only have std trait implementations for lengths 0..=32 +} + pub fn no_box() { let boxed_slice = Box::new([0; 33]) as Box<[i32]>; let boxed_array = >::try_from(boxed_slice); //~^ ERROR the trait bound `std::boxed::Box<[i32; 33]>: std::convert::From>` is not satisfied //~^^ ERROR the trait bound `std::boxed::Box<[i32; 33]>: std::convert::TryFrom>` is not satisfied + let boxed_slice = >::from([0; 33]); + //~^ 15:42: 15:49: arrays only have std trait implementations for lengths 0..=32 [E0277] } pub fn no_rc() { 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..5c01603ab881c 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,80 @@ 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 22 others = note: required because of the requirements on the impl of `std::convert::Into>` for `std::boxed::Box<[i32]>` = note: required because of the requirements on the impl of `std::convert::TryFrom>` for `std::boxed::Box<[i32; 33]>` +error[E0277]: arrays only have std trait implementations for lengths 0..=32 + --> $DIR/alloc-types-no-impls-length-33.rs:15:42 + | +LL | let boxed_slice = >::from([0; 33]); + | ^^^^^^^ + | | + | expected an implementor of trait `std::convert::From<[{integer}; 33]>` + | help: consider borrowing here: `&[0; 33]` + | + = note: the trait bound `[i32; 33]: std::convert::From<[{integer}; 33]>` is not satisfied + = note: required because of the requirements on the impl of `std::convert::From<[i32; 33]>` for `std::boxed::Box<[i32]>` + = note: required by `std::convert::From::from` + error[E0277]: the trait bound `std::boxed::Box<[i32; 33]>: std::convert::TryFrom>` is not satisfied - --> $DIR/alloc-types-no-impls-length-33.rs: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:21:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::rc::Rc<[i32; 33]>` | = help: the following implementations were found: + as std::convert::From>> as std::convert::From> as std::convert::From>> as std::convert::From<&[T]>> - 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:21:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::rc::Rc<[i32; 33]>` | = help: the following implementations were found: - as std::convert::TryFrom>> + 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:28:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From>` is not implemented for `std::sync::Arc<[i32; 33]>` | = help: the following implementations were found: + as std::convert::From>> as std::convert::From> as std::convert::From>> as std::convert::From<&[T]>> - 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:28:23 | LL | let boxed_array = >::try_from(boxed_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::TryFrom>` is not implemented for `std::sync::Arc<[i32; 33]>` | = help: the following implementations were found: - as std::convert::TryFrom>> + as std::convert::TryFrom>> -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/const-generics/array-impls/core-traits-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/auxiliary/impl-const.rs b/src/test/ui/const-generics/auxiliary/impl-const.rs new file mode 100644 index 0000000000000..fc993d63927c3 --- /dev/null +++ b/src/test/ui/const-generics/auxiliary/impl-const.rs @@ -0,0 +1,9 @@ +#![feature(const_generics)] + +pub struct Num; + +// Braces around const expression causes crash +impl Num<{5}> { + pub fn five(&self) { + } +} 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 8379cbd4908e9..b29d27e524751 100644 --- a/src/test/ui/const-generics/cannot-infer-const-args.stderr +++ b/src/test/ui/const-generics/cannot-infer-const-args.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/cannot-infer-const-args.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information error[E0282]: type annotations needed --> $DIR/cannot-infer-const-args.rs:9:5 | LL | foo(); - | ^^^ cannot infer type for fn item `fn() -> usize {foo::<_: usize>}` + | ^^^ + | + = note: unable to infer the value 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 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..4a6241de1b453 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 @@ -13,7 +14,8 @@ LL | fn foo() -> Array { | ^ | = note: type arguments must be provided before constant arguments + = help: reorder the arguments: types, then consts: `` -error: aborting due to previous error +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 28586426b44e9..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,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 #[derive(Debug)] struct S; fn main() { - assert_eq!(std::any::type_name::>(), "const_generic_type_name::S<3usize>"); + assert_eq!(std::any::type_name::>(), "const_generic_type_name::S<3>"); } 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..90c438b05cb81 100644 --- a/src/test/ui/const-generics/fn-const-param-call.rs +++ b/src/test/ui/const-generics/fn-const-param-call.rs @@ -1,15 +1,14 @@ -// run-pass - -#![feature(const_generics, const_compare_raw_pointers)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete fn function() -> u32 { 17 } -struct Wrapper u32>; +struct Wrapper u32>; //~ ERROR: using function pointers as const generic parameters impl u32> Wrapper { +//~^ ERROR: using function pointers as const generic parameters fn call() -> u32 { F() } 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..b5811243caa8a 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,23 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash - --> $DIR/fn-const-param-call.rs:3:12 +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/fn-const-param-call.rs:1:12 | -LL | #![feature(const_generics, const_compare_raw_pointers)] +LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: using function pointers as const generic parameters is forbidden + --> $DIR/fn-const-param-call.rs:8:25 + | +LL | struct Wrapper u32>; + | ^^^^^^^^^^^ + +error: using function pointers as const generic parameters is forbidden + --> $DIR/fn-const-param-call.rs:10:15 + | +LL | impl u32> Wrapper { + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/const-generics/fn-const-param-infer.rs b/src/test/ui/const-generics/fn-const-param-infer.rs index dc69fa9eea585..14fa3b494b3fc 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.rs +++ b/src/test/ui/const-generics/fn-const-param-infer.rs @@ -1,7 +1,8 @@ -#![feature(const_generics, const_compare_raw_pointers)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete struct Checked bool>; +//~^ ERROR: using function pointers as const generic parameters fn not_one(val: usize) -> bool { val != 1 } fn not_two(val: usize) -> bool { val != 2 } @@ -13,14 +14,14 @@ fn generic(val: usize) -> bool { val != 1 } fn main() { let _: Option> = None; let _: Checked = Checked::; - let _: Checked = Checked::; //~ mismatched types + let _: Checked = Checked::; let _ = Checked::; let _ = Checked::<{generic_arg::}>; - let _ = Checked::<{generic_arg::}>; //~ mismatched types + let _ = Checked::<{generic_arg::}>; - let _ = Checked::; //~ type annotations needed + let _ = Checked::; let _ = Checked::<{generic::}>; let _: Checked<{generic::}> = Checked::<{generic::}>; - let _: Checked<{generic::}> = Checked::<{generic::}>; //~ mismatched types + let _: Checked<{generic::}> = Checked::<{generic::}>; } 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 44eab8baa40a6..7aaa41eb7d7b1 100644 --- a/src/test/ui/const-generics/fn-const-param-infer.stderr +++ b/src/test/ui/const-generics/fn-const-param-infer.stderr @@ -1,49 +1,17 @@ -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)] +LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = 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 +error: using function pointers as const generic parameters is forbidden + --> $DIR/fn-const-param-infer.rs:4:25 | -LL | let _: Checked = Checked::; - | ---------------- ^^^^^^^^^^^^^^^^^^ expected `not_one`, found `not_two` - | | - | expected due to this - | - = note: expected struct `Checked` - found struct `Checked` - -error[E0308]: mismatched types - --> $DIR/fn-const-param-infer.rs:20:24 - | -LL | let _ = Checked::<{generic_arg::}>; - | ^^^^^^^^^^^^^^^^^^ expected `usize`, found `u32` - | - = note: expected fn pointer `fn(usize) -> _` - found fn item `fn(u32) -> _ {generic_arg::}` - -error[E0282]: type annotations needed - --> $DIR/fn-const-param-infer.rs:22:23 - | -LL | let _ = Checked::; - | ^^^^^^^ cannot infer type for type parameter `T` declared on the function `generic` - -error[E0308]: mismatched types - --> $DIR/fn-const-param-infer.rs:25:40 - | -LL | let _: Checked<{generic::}> = Checked::<{generic::}>; - | ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `generic::`, found `generic::` - | | - | expected due to this - | - = note: expected struct `Checked>` - found struct `Checked>` +LL | struct Checked bool>; + | ^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to previous error; 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/issue-68104-print-stack-overflow.rs b/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs new file mode 100644 index 0000000000000..bda9ce8767d08 --- /dev/null +++ b/src/test/ui/const-generics/issue-68104-print-stack-overflow.rs @@ -0,0 +1,14 @@ +// aux-build:impl-const.rs +// run-pass + +#![feature(const_generics)] +#![allow(incomplete_features)] + +extern crate impl_const; + +use impl_const::*; + +pub fn main() { + let n = Num::<5>; + n.five(); +} diff --git a/src/test/ui/const-generics/issue-70180-1-stalled_on.rs b/src/test/ui/const-generics/issue-70180-1-stalled_on.rs new file mode 100644 index 0000000000000..ff2a5250263d5 --- /dev/null +++ b/src/test/ui/const-generics/issue-70180-1-stalled_on.rs @@ -0,0 +1,35 @@ +// build-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +pub fn works() { + let array/*: [_; _]*/ = default_array(); + let _: [_; 4] = array; + Foo::foo(&array); +} + +pub fn didnt_work() { + let array/*: [_; _]*/ = default_array(); + Foo::foo(&array); + let _: [_; 4] = array; +} + +trait Foo { + fn foo(&self) {} +} + +impl Foo for [i32; 4] {} +impl Foo for [i64; 8] {} + +// Only needed because `[_; _]` is not valid type syntax. +fn default_array() -> [T; N] +where + [T; N]: Default, +{ + Default::default() +} + +fn main() { + works(); + didnt_work(); +} diff --git a/src/test/ui/const-generics/issue-70180-2-stalled_on.rs b/src/test/ui/const-generics/issue-70180-2-stalled_on.rs new file mode 100644 index 0000000000000..83338668f4ffd --- /dev/null +++ b/src/test/ui/const-generics/issue-70180-2-stalled_on.rs @@ -0,0 +1,35 @@ +// build-pass +#![feature(const_generics)] +#![allow(incomplete_features)] + +fn works() { + let array/*: [u8; _]*/ = default_byte_array(); + let _: [_; 4] = array; + Foo::foo(&array); +} + +fn didnt_work() { + let array/*: [u8; _]*/ = default_byte_array(); + Foo::foo(&array); + let _: [_; 4] = array; +} + +trait Foo { + fn foo(&self) {} +} + +impl Foo for [u8; 4] {} +impl Foo for [u8; 8] {} + +// Only needed because `[u8; _]` is not valid type syntax. +fn default_byte_array() -> [u8; N] +where + [u8; N]: Default, +{ + Default::default() +} + +fn main() { + works(); + didnt_work(); +} 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 5b5e431bf2ff6..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,9 +1,10 @@ #![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 fn f(x: T) -> [T; N] { [x; N] - //~^ ERROR array lengths can't depend on generic parameters } fn main() { 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 949fa896d8780..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,16 +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 -error: array lengths can't depend on generic parameters - --> $DIR/issue-61336-1.rs:5:9 - | -LL | [x; N] - | ^ - -error: aborting due to previous error +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 7bb36f41b8f9d..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,14 +1,13 @@ #![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}] - //~^ ERROR array lengths can't depend on generic parameters + [x; { N }] } fn g(x: T) -> [T; N] { - [x; {N}] - //~^ ERROR array lengths can't depend on generic parameters + [x; { N }] + //~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied } fn main() { diff --git a/src/test/ui/const-generics/issues/issue-61336-2.stderr b/src/test/ui/const-generics/issues/issue-61336-2.stderr index 63f86c81b1e7f..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,22 +1,24 @@ -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: array lengths can't depend on generic parameters - --> $DIR/issue-61336-2.rs:5:9 +error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied + --> $DIR/issue-61336-2.rs:9:5 | -LL | [x; {N}] - | ^^^ - -error: array lengths can't depend on generic parameters - --> $DIR/issue-61336-2.rs:10:9 +LL | [x; { N }] + | ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | + = note: the `Copy` trait is required because the repeated element will be copied +help: consider restricting type parameter `T` | -LL | [x; {N}] - | ^^^ +LL | fn g(x: T) -> [T; N] { + | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +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 edc012cbb3d13..eb0f309762764 100644 --- a/src/test/ui/const-generics/issues/issue-61336.rs +++ b/src/test/ui/const-generics/issues/issue-61336.rs @@ -1,14 +1,13 @@ #![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] - //~^ ERROR array lengths can't depend on generic parameters } fn g(x: T) -> [T; N] { [x; N] - //~^ ERROR array lengths can't depend on generic parameters + //~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied } fn main() { diff --git a/src/test/ui/const-generics/issues/issue-61336.stderr b/src/test/ui/const-generics/issues/issue-61336.stderr index f96e8e02d4ec0..0eee37df3dd52 100644 --- a/src/test/ui/const-generics/issues/issue-61336.stderr +++ b/src/test/ui/const-generics/issues/issue-61336.stderr @@ -1,22 +1,24 @@ -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: array lengths can't depend on generic parameters - --> $DIR/issue-61336.rs:5:9 +error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied + --> $DIR/issue-61336.rs:9:5 | LL | [x; N] - | ^ - -error: array lengths can't depend on generic parameters - --> $DIR/issue-61336.rs:10:9 + | ^^^^^^ the trait `std::marker::Copy` is not implemented for `T` | -LL | [x; 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 2 previous errors +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..cc671163e85a1 100644 --- a/src/test/ui/const-generics/issues/issue-61747.rs +++ b/src/test/ui/const-generics/issues/issue-61747.rs @@ -1,12 +1,11 @@ -// 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; impl Const<{C}> { fn successor() -> Const<{C + 1}> { + //~^ ERROR constant expression depends on a generic parameter 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..2685d9fdf167c 100644 --- a/src/test/ui/const-generics/issues/issue-61747.stderr +++ b/src/test/ui/const-generics/issues/issue-61747.stderr @@ -1,8 +1,19 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash - --> $DIR/issue-61747.rs:3:12 +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61747.rs:1:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: constant expression depends on a generic parameter + --> $DIR/issue-61747.rs:7:23 + | +LL | fn successor() -> Const<{C + 1}> { + | ^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/const-generics/issues/issue-61935.rs b/src/test/ui/const-generics/issues/issue-61935.rs new file mode 100644 index 0000000000000..0d42ff1895cdb --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61935.rs @@ -0,0 +1,23 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +trait Foo {} + +impl Foo for [(); N] + where + Self:FooImpl<{N==0}> +//~^ERROR constant expression depends on a generic parameter +{} + +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..a785af5f008ea --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-61935.stderr @@ -0,0 +1,19 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-61935.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: constant expression depends on a generic parameter + --> $DIR/issue-61935.rs:8:14 + | +LL | Self:FooImpl<{N==0}> + | ^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs b/src/test/ui/const-generics/issues/issue-62187-encountered-polymorphic-const.rs index 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 20347ac4b7dac..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,16 +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-62187-encountered-polymorphic-const.rs:3:12 | LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information warning: unused variable: `foo` --> $DIR/issue-62187-encountered-polymorphic-const.rs:15:9 | LL | let foo = <[u8; 2]>::BIT_LEN; - | ^^^ help: consider prefixing with an underscore: `_foo` + | ^^^ help: if this is intentional, prefix it with an underscore: `_foo` | = note: `#[warn(unused_variables)]` on by default +warning: 2 warnings emitted + diff --git a/src/test/ui/const-generics/issues/issue-62220.rs b/src/test/ui/const-generics/issues/issue-62220.rs new file mode 100644 index 0000000000000..5c4a0d31a895d --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62220.rs @@ -0,0 +1,22 @@ +#![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) { + //~^ ERROR constant expression depends on a generic parameter + 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-62220.stderr b/src/test/ui/const-generics/issues/issue-62220.stderr new file mode 100644 index 0000000000000..d91d2bb326fc5 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-62220.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-62220.rs:10:27 + | +LL | pub fn trunc(self) -> (TruncatedVector, T) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-62456.rs b/src/test/ui/const-generics/issues/issue-62456.rs index c5e6fe9104bc9..37947ad1b331c 100644 --- a/src/test/ui/const-generics/issues/issue-62456.rs +++ b/src/test/ui/const-generics/issues/issue-62456.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 foo() { let _ = [0u64; N + 1]; - //~^ ERROR array lengths can't depend on generic parameters + //~^ 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 9cdccf8407c9b..0454fed670598 100644 --- a/src/test/ui/const-generics/issues/issue-62456.stderr +++ b/src/test/ui/const-generics/issues/issue-62456.stderr @@ -1,16 +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: array lengths can't depend on generic parameters +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 +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 74ed3d354fc74..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: array lengths can't depend on generic parameters + //~^ 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 c2a752ec1715f..f09af76325e96 100644 --- a/src/test/ui/const-generics/issues/issue-62504.stderr +++ b/src/test/ui/const-generics/issues/issue-62504.stderr @@ -1,8 +1,10 @@ -error: array lengths can't depend on generic parameters +error: constant expression depends on a generic parameter --> $DIR/issue-62504.rs:18:25 | LL | ArrayHolder([0; Self::SIZE]) | ^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-62579-no-match.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..7cedf51ca0404 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)] +#![allow(dead_code, unconditional_recursion)] #![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete fn fact() { fact::<{ N - 1 }>(); + //~^ ERROR constant expression depends on a generic parameter } 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..1e9c0f2f3d9eb --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-66205.stderr @@ -0,0 +1,19 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-66205.rs:2:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: constant expression depends on a generic parameter + --> $DIR/issue-66205.rs:6:12 + | +LL | fact::<{ N - 1 }>(); + | ^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-66906.rs b/src/test/ui/const-generics/issues/issue-66906.rs new file mode 100644 index 0000000000000..486c72d8a349f --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-66906.rs @@ -0,0 +1,12 @@ +// check-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +pub struct Tuple; + +pub trait Trait { + type Input: From<>::Input>; +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-66906.stderr b/src/test/ui/const-generics/issues/issue-66906.stderr new file mode 100644 index 0000000000000..8e8b552f90eb5 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-66906.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-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 79c5ac9dd187e..c8ee182123985 100644 --- a/src/test/ui/const-generics/issues/issue-67739.rs +++ b/src/test/ui/const-generics/issues/issue-67739.rs @@ -10,7 +10,7 @@ pub trait Trait { fn associated_size(&self) -> usize { [0u8; mem::size_of::()]; - //~^ ERROR: array lengths can't depend on generic parameters + //~^ 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 index a31b556c086f8..27a56b8eb02b2 100644 --- a/src/test/ui/const-generics/issues/issue-67739.stderr +++ b/src/test/ui/const-generics/issues/issue-67739.stderr @@ -1,8 +1,10 @@ -error: array lengths can't depend on generic parameters +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-68977.rs b/src/test/ui/const-generics/issues/issue-68977.rs new file mode 100644 index 0000000000000..346ea3c204244 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68977.rs @@ -0,0 +1,40 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +struct PhantomU8; + +trait FxpStorage { + type SInt; // Add arithmetic traits as needed. +} + +macro_rules! fxp_storage_impls { + ($($($n:literal)|+ => $sint:ty),* $(,)?) => { + $($(impl FxpStorage for PhantomU8<$n> { + type SInt = $sint; + })*)* + } +} + +fxp_storage_impls! { + 1 => i8, + 2 => i16, + 3 | 4 => i32, + 5 | 6 | 7 | 8 => i64, + 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 => i128, +} + +type FxpStorageHelper = + PhantomU8<{(INT_BITS + FRAC_BITS + 7) / 8}>; + +struct Fxp +where + FxpStorageHelper: FxpStorage, + //~^ ERROR constant expression depends on a generic parameter +{ + storage: as FxpStorage>::SInt, +} + +fn main() { + Fxp::<1, 15> { storage: 0i16 }; + Fxp::<2, 15> { storage: 0i32 }; +} diff --git a/src/test/ui/const-generics/issues/issue-68977.stderr b/src/test/ui/const-generics/issues/issue-68977.stderr new file mode 100644 index 0000000000000..e1190d9026da9 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-68977.stderr @@ -0,0 +1,19 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-68977.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: constant expression depends on a generic parameter + --> $DIR/issue-68977.rs:31:44 + | +LL | FxpStorageHelper: FxpStorage, + | ^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-70125-1.rs b/src/test/ui/const-generics/issues/issue-70125-1.rs new file mode 100644 index 0000000000000..08a8309d4319f --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-70125-1.rs @@ -0,0 +1,19 @@ +// run-pass +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +const L: usize = 4; + +pub trait Print { + fn print(&self) -> usize { + N + } +} + +pub struct Printer; +impl Print for Printer {} + +fn main() { + let p = Printer; + assert_eq!(p.print(), 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 new file mode 100644 index 0000000000000..8ad4b25ae5bc0 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-70125-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-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 new file mode 100644 index 0000000000000..fb7d4886a7c17 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-70125-2.rs @@ -0,0 +1,16 @@ +// run-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +fn main() { + <()>::foo(); +} + +trait Foo { + fn foo() -> usize { + X + } +} + +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 new file mode 100644 index 0000000000000..c1f9634810e48 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-70125-2.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-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 new file mode 100644 index 0000000000000..b53cec80071fd --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-70167.rs @@ -0,0 +1,10 @@ +// check-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +pub trait Trait: From<>::Item> { + type Item; +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-70167.stderr b/src/test/ui/const-generics/issues/issue-70167.stderr new file mode 100644 index 0000000000000..5d647e933c4c5 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-70167.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-70167.rs:3:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs new file mode 100644 index 0000000000000..225593c3178a5 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.rs @@ -0,0 +1,20 @@ +// Regression test for #72819: ICE due to failure in resolving the const generic in `Arr`'s type +// bounds. + +#![feature(const_generics)] +#![allow(incomplete_features)] +struct Arr +where Assert::<{N < usize::max_value() / 2}>: IsTrue, +//~^ ERROR constant expression depends on a generic parameter +{ +} + +enum Assert {} + +trait IsTrue {} + +impl IsTrue for Assert {} + +fn main() { + let x: Arr<{usize::max_value()}> = Arr {}; +} diff --git a/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.stderr b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.stderr new file mode 100644 index 0000000000000..a9f664d0ac8c5 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-72819-generic-in-const-eval.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/issue-72819-generic-in-const-eval.rs:7:47 + | +LL | where Assert::<{N < usize::max_value() / 2}>: IsTrue, + | ^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs b/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs new file mode 100644 index 0000000000000..c22e61d0ce337 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue70273-assoc-fn.rs @@ -0,0 +1,17 @@ +// check-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +trait T { + fn f(); +} +struct S; + +impl T<0usize> for S { + fn f() {} +} + +fn main() { + let _err = >::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 new file mode 100644 index 0000000000000..931701b64b481 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue70273-assoc-fn.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/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..0d392ddcaedcc --- /dev/null +++ b/src/test/ui/const-generics/lazy-normalization/issue-71922.rs @@ -0,0 +1,19 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete +trait Foo {} + +impl Foo for [(); N] where Self: FooImpl<{ N == 0 }> {} +//~^ ERROR constant expression depends on a generic parameter + +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-71922.stderr b/src/test/ui/const-generics/lazy-normalization/issue-71922.stderr new file mode 100644 index 0000000000000..00917571e716d --- /dev/null +++ b/src/test/ui/const-generics/lazy-normalization/issue-71922.stderr @@ -0,0 +1,19 @@ +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-71922.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-71922.rs:5:50 + | +LL | impl Foo for [(); N] where Self: FooImpl<{ N == 0 }> {} + | ^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/test/ui/const-generics/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..97ca9d6a44c9e 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,12 +1,11 @@ -// run-pass -#![feature(const_generics, const_compare_raw_pointers)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete const A: u32 = 3; -struct Const; +struct Const; //~ ERROR: using raw pointers as const generic parameters -impl Const

{ +impl Const

{ //~ ERROR: using raw pointers as const generic parameters fn get() -> u32 { unsafe { *P 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..1ce8bb9c05423 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,23 @@ -warning: the feature `const_generics` is incomplete and may cause the compiler to crash - --> $DIR/raw-ptr-const-param-deref.rs:2:12 +warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/raw-ptr-const-param-deref.rs:1:12 | -LL | #![feature(const_generics, const_compare_raw_pointers)] +LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: using raw pointers as const generic parameters is forbidden + --> $DIR/raw-ptr-const-param-deref.rs:6:23 + | +LL | struct Const; + | ^^^^^^^^^^ + +error: using raw pointers as const generic parameters is forbidden + --> $DIR/raw-ptr-const-param-deref.rs:8:15 + | +LL | impl Const

{ + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/ui/const-generics/raw-ptr-const-param.rs b/src/test/ui/const-generics/raw-ptr-const-param.rs index f69c37fbb8f3d..237b410e073d6 100644 --- a/src/test/ui/const-generics/raw-ptr-const-param.rs +++ b/src/test/ui/const-generics/raw-ptr-const-param.rs @@ -1,9 +1,9 @@ -#![feature(const_generics, const_compare_raw_pointers)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete -struct Const; +struct Const; //~ ERROR: using raw pointers as const generic parameters fn main() { - let _: Const<{15 as *const _}> = Const::<{10 as *const _}>; //~ mismatched types - let _: Const<{10 as *const _}> = Const::<{10 as *const _}>; + let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>; + let _: Const<{ 10 as *const _ }> = Const::<{ 10 as *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 9cd39b61dc936..6e64f8a327fd5 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,17 @@ -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)] +LL | #![feature(const_generics)] | ^^^^^^^^^^^^^^ | = 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:38 +error: using raw pointers as const generic parameters is forbidden + --> $DIR/raw-ptr-const-param.rs:4:23 | -LL | let _: Const<{15 as *const _}> = Const::<{10 as *const _}>; - | ----------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{pointer}`, found `{pointer}` - | | - | expected due to this - | - = note: expected struct `Const<{pointer}>` - found struct `Const<{pointer}>` +LL | struct Const; + | ^^^^^^^^^^ -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/std/const-generics-range.rs b/src/test/ui/const-generics/std/const-generics-range.rs new file mode 100644 index 0000000000000..6d56fe0d7b8e3 --- /dev/null +++ b/src/test/ui/const-generics/std/const-generics-range.rs @@ -0,0 +1,30 @@ +// check-pass +#![allow(incomplete_features)] +#![feature(const_generics)] + +// `Range` should be usable within const generics: +struct _Range>; +const RANGE : _Range<{ 0 .. 1000 }> = _Range; + +// `RangeFrom` should be usable within const generics: +struct _RangeFrom>; +const RANGE_FROM : _RangeFrom<{ 0 .. }> = _RangeFrom; + +// `RangeFull` should be usable within const generics: +struct _RangeFull; +const RANGE_FULL : _RangeFull<{ .. }> = _RangeFull; + +// Regression test for #70155 +// `RangeInclusive` should be usable within const generics: +struct _RangeInclusive>; +const RANGE_INCLUSIVE : _RangeInclusive<{ 0 ..= 999 }> = _RangeInclusive; + +// `RangeTo` should be usable within const generics: +struct _RangeTo>; +const RANGE_TO : _RangeTo<{ .. 1000 }> = _RangeTo; + +// `RangeToInclusive` should be usable within const generics: +struct _RangeToInclusive>; +const RANGE_TO_INCLUSIVE : _RangeToInclusive<{ ..= 999 }> = _RangeToInclusive; + +pub fn main() {} 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 new file mode 100644 index 0000000000000..588c7b9523aad --- /dev/null +++ b/src/test/ui/const-generics/type_of_anon_const.rs @@ -0,0 +1,21 @@ +// run-pass + +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +trait T { + fn l() -> usize; + fn r() -> bool; +} + +struct S; + +impl T for S { + fn l() -> usize { N } + fn r() -> bool { M } +} + +fn main() { + assert_eq!(>::l::(), 123); + assert!(>::r::()); +} diff --git a/src/test/ui/const-generics/type_of_anon_const.stderr b/src/test/ui/const-generics/type_of_anon_const.stderr new file mode 100644 index 0000000000000..8afed0d39866a --- /dev/null +++ b/src/test/ui/const-generics/type_of_anon_const.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/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-generics/wf-misc.rs b/src/test/ui/const-generics/wf-misc.rs new file mode 100644 index 0000000000000..4ff1b9e2da5b2 --- /dev/null +++ b/src/test/ui/const-generics/wf-misc.rs @@ -0,0 +1,16 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete + +pub fn arr_len() { + let _: [u8; N + 1]; + //~^ ERROR constant expression depends on a generic parameter +} + +struct Const; + +pub fn func_call() { + let _: Const::<{N + 1}>; + //~^ ERROR constant expression depends on a generic parameter +} + +fn main() {} diff --git a/src/test/ui/const-generics/wf-misc.stderr b/src/test/ui/const-generics/wf-misc.stderr new file mode 100644 index 0000000000000..03f2bf3f52699 --- /dev/null +++ b/src/test/ui/const-generics/wf-misc.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/wf-misc.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44580 for more information + +error: constant expression depends on a generic parameter + --> $DIR/wf-misc.rs:5:12 + | +LL | let _: [u8; N + 1]; + | ^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: constant expression depends on a generic parameter + --> $DIR/wf-misc.rs:12:12 + | +LL | let _: Const::<{N + 1}>; + | ^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/src/test/ui/const-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/cast-discriminant-zst-enum.rs b/src/test/ui/consts/cast-discriminant-zst-enum.rs new file mode 100644 index 0000000000000..a77258120111e --- /dev/null +++ b/src/test/ui/consts/cast-discriminant-zst-enum.rs @@ -0,0 +1,47 @@ +// run-pass +// Test a ZST enum whose dicriminant is ~0i128. This caused an ICE when casting to a i32. + +#[derive(Copy, Clone)] +enum Nums { + NegOne = -1, +} + +const NEG_ONE_I8: i8 = Nums::NegOne as i8; +const NEG_ONE_I16: i16 = Nums::NegOne as i16; +const NEG_ONE_I32: i32 = Nums::NegOne as i32; +const NEG_ONE_I64: i64 = Nums::NegOne as i64; +const NEG_ONE_I128: i128 = Nums::NegOne as i128; + +#[inline(never)] +fn identity(t: T) -> T { t } + +fn test_as_arg(n: Nums) { + assert_eq!(-1i8, n as i8); + assert_eq!(-1i16, n as i16); + assert_eq!(-1i32, n as i32); + assert_eq!(-1i64, n as i64); + assert_eq!(-1i128, n as i128); +} + +fn main() { + let kind = Nums::NegOne; + assert_eq!(-1i8, kind as i8); + assert_eq!(-1i16, kind as i16); + assert_eq!(-1i32, kind as i32); + assert_eq!(-1i64, kind as i64); + assert_eq!(-1i128, kind as i128); + + assert_eq!(-1i8, identity(kind) as i8); + assert_eq!(-1i16, identity(kind) as i16); + assert_eq!(-1i32, identity(kind) as i32); + assert_eq!(-1i64, identity(kind) as i64); + assert_eq!(-1i128, identity(kind) as i128); + + test_as_arg(Nums::NegOne); + + assert_eq!(-1i8, NEG_ONE_I8); + assert_eq!(-1i16, NEG_ONE_I16); + assert_eq!(-1i32, NEG_ONE_I32); + assert_eq!(-1i64, NEG_ONE_I64); + assert_eq!(-1i128, NEG_ONE_I128); +} 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-4b.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr index 5b2c4116c4b1d..e4d256c0ad192 100644 --- a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr +++ b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr @@ -16,7 +16,7 @@ error[E0604]: only `u8` can be cast as `char`, not `i8` --> $DIR/const-eval-overflow-4b.rs:25:13 | LL | : [u32; 5i8 as char as usize] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ invalid cast error: aborting due to 3 previous errors 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 e0df787f80a44..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. @@ -12,7 +12,7 @@ error: any use of this value will cause an error LL | const I32_REF_U8_UNION: u8 = unsafe { Nonsense { int_32_ref: &3 }.uint_8 }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes | = note: `#[deny(const_err)]` on by default @@ -22,7 +22,7 @@ error: any use of this value will cause an error LL | const I32_REF_U16_UNION: u16 = unsafe { Nonsense { int_32_ref: &3 }.uint_16 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:34:45 @@ -30,13 +30,13 @@ error: any use of this value will cause an error LL | const I32_REF_U32_UNION: u32 = unsafe { Nonsense { int_32_ref: &3 }.uint_32 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes 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. @@ -54,7 +54,7 @@ error: any use of this value will cause an error LL | const I32_REF_I8_UNION: i8 = unsafe { Nonsense { int_32_ref: &3 }.int_8 }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:46:45 @@ -62,7 +62,7 @@ error: any use of this value will cause an error LL | const I32_REF_I16_UNION: i16 = unsafe { Nonsense { int_32_ref: &3 }.int_16 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:49:45 @@ -70,13 +70,13 @@ error: any use of this value will cause an error LL | const I32_REF_I32_UNION: i32 = unsafe { Nonsense { int_32_ref: &3 }.int_32 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes 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. @@ -94,13 +94,13 @@ error: any use of this value will cause an error LL | const I32_REF_F32_UNION: f32 = unsafe { Nonsense { int_32_ref: &3 }.float_32 }; | ----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes 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. @@ -110,7 +110,7 @@ error: any use of this value will cause an error LL | const I32_REF_BOOL_UNION: bool = unsafe { Nonsense { int_32_ref: &3 }.truthy_falsey }; | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:67:47 @@ -118,7 +118,7 @@ error: any use of this value will cause an error LL | const I32_REF_CHAR_UNION: char = unsafe { Nonsense { int_32_ref: &3 }.character }; | ------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:70:39 @@ -126,7 +126,7 @@ error: any use of this value will cause an error LL | const STR_U8_UNION: u8 = unsafe { Nonsense { stringy: "3" }.uint_8 }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:73:41 @@ -134,7 +134,7 @@ error: any use of this value will cause an error LL | const STR_U16_UNION: u16 = unsafe { Nonsense { stringy: "3" }.uint_16 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:76:41 @@ -142,13 +142,13 @@ error: any use of this value will cause an error LL | const STR_U32_UNION: u32 = unsafe { Nonsense { stringy: "3" }.uint_32 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes 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. @@ -158,7 +158,7 @@ error: any use of this value will cause an error LL | const STR_U128_UNION: u128 = unsafe { Nonsense { stringy: "3" }.uint_128 }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:85:39 @@ -166,7 +166,7 @@ error: any use of this value will cause an error LL | const STR_I8_UNION: i8 = unsafe { Nonsense { stringy: "3" }.int_8 }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:88:41 @@ -174,7 +174,7 @@ error: any use of this value will cause an error LL | const STR_I16_UNION: i16 = unsafe { Nonsense { stringy: "3" }.int_16 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:91:41 @@ -182,13 +182,13 @@ error: any use of this value will cause an error LL | const STR_I32_UNION: i32 = unsafe { Nonsense { stringy: "3" }.int_32 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes 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. @@ -198,7 +198,7 @@ error: any use of this value will cause an error LL | const STR_I128_UNION: i128 = unsafe { Nonsense { stringy: "3" }.int_128 }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:100:41 @@ -206,13 +206,13 @@ error: any use of this value will cause an error LL | const STR_F32_UNION: f32 = unsafe { Nonsense { stringy: "3" }.float_32 }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes 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. @@ -222,7 +222,7 @@ error: any use of this value will cause an error LL | const STR_BOOL_UNION: bool = unsafe { Nonsense { stringy: "3" }.truthy_falsey }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: any use of this value will cause an error --> $DIR/const-pointer-values-in-various-types.rs:109:43 @@ -230,7 +230,7 @@ error: any use of this value will cause an error LL | const STR_CHAR_UNION: char = unsafe { Nonsense { stringy: "3" }.character }; | --------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | a raw memory access tried to access part of a pointer value as raw bytes + | unable to turn pointer into raw bytes error: aborting due to 29 previous errors 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/const_let.stderr b/src/test/ui/consts/const-eval/const_let.stderr index 4753222a7c07d..47f39b703e460 100644 --- a/src/test/ui/consts/const-eval/const_let.stderr +++ b/src/test/ui/consts/const-eval/const_let.stderr @@ -2,25 +2,33 @@ error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/const_let.rs:16:32 | LL | const Y: FakeNeedsDrop = { let mut x = FakeNeedsDrop; x = FakeNeedsDrop; x }; - | ^^^^^ constants cannot evaluate destructors + | ^^^^^ - value is dropped here + | | + | constants cannot evaluate destructors error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/const_let.rs:20:33 | LL | const Y2: FakeNeedsDrop = { let mut x; x = FakeNeedsDrop; x = FakeNeedsDrop; x }; - | ^^^^^ constants cannot evaluate destructors + | ^^^^^ - value is dropped here + | | + | constants cannot evaluate destructors error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/const_let.rs:24:21 | LL | const Z: () = { let mut x = None; x = Some(FakeNeedsDrop); }; - | ^^^^^ constants cannot evaluate destructors + | ^^^^^ - value is dropped here + | | + | constants cannot evaluate destructors error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/const_let.rs:28:22 | LL | const Z2: () = { let mut x; x = None; x = Some(FakeNeedsDrop); }; - | ^^^^^ constants cannot evaluate destructors + | ^^^^^ - value is dropped here + | | + | constants cannot evaluate destructors error: aborting due to 4 previous errors diff --git a/src/test/ui/consts/const-eval/const_raw_ptr_ops.rs b/src/test/ui/consts/const-eval/const_raw_ptr_ops.rs index 9be1374f85d99..e238e13b8e2da 100644 --- a/src/test/ui/consts/const-eval/const_raw_ptr_ops.rs +++ b/src/test/ui/consts/const-eval/const_raw_ptr_ops.rs @@ -1,17 +1,6 @@ -#![feature(const_raw_ptr_to_usize_cast, const_compare_raw_pointers, const_raw_ptr_deref)] - fn main() {} // unconst and bad, will thus error in miri -const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; //~ ERROR any use of this -// unconst and bad, will thus error in miri -const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; //~ ERROR any use of this -// unconst and fine -const Y: usize = unsafe { 42usize as *const i32 as usize + 1 }; -// unconst and bad, will thus error in miri -const Y2: usize = unsafe { &1 as *const i32 as usize + 1 }; //~ ERROR any use of this -// unconst and fine -const Z: i32 = unsafe { *(&1 as *const i32) }; +const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; //~ ERROR cannot be reliably // unconst and bad, will thus error in miri -const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR any use of this value will cause -const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR any use of this value will cause +const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; //~ ERROR cannot be reliably diff --git a/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr b/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr index 2cba833a74896..21d3f5e7e8536 100644 --- a/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr +++ b/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr @@ -1,44 +1,18 @@ -error: any use of this value will cause an error - --> $DIR/const_raw_ptr_ops.rs:6:26 +error: pointers cannot be reliably compared during const eval. + --> $DIR/const_raw_ptr_ops.rs:4:26 | LL | const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; - | -------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | "pointer arithmetic or comparison" needs an rfc before being allowed inside constants + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[deny(const_err)]` on by default + = note: see issue #53020 for more information -error: any use of this value will cause an error - --> $DIR/const_raw_ptr_ops.rs:8:27 +error: pointers cannot be reliably compared during const eval. + --> $DIR/const_raw_ptr_ops.rs:6:27 | LL | const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; - | --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | "pointer arithmetic or comparison" needs an rfc before being allowed inside constants - -error: any use of this value will cause an error - --> $DIR/const_raw_ptr_ops.rs:12:28 - | -LL | const Y2: usize = unsafe { &1 as *const i32 as usize + 1 }; - | ---------------------------^^^^^^^^^^^^^^^^^^^^^^^^^------- - | | - | "pointer-to-integer cast" needs an rfc before being allowed inside constants - -error: any use of this value will cause an error - --> $DIR/const_raw_ptr_ops.rs:16:26 - | -LL | const Z2: i32 = unsafe { *(42 as *const i32) }; - | -------------------------^^^^^^^^^^^^^^^^^^^--- - | | - | a memory access tried to interpret some bytes as a pointer - -error: any use of this value will cause an error - --> $DIR/const_raw_ptr_ops.rs:17:26 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -LL | const Z3: i32 = unsafe { *(44 as *const i32) }; - | -------------------------^^^^^^^^^^^^^^^^^^^--- - | | - | a memory access tried to interpret some bytes as a pointer + = note: see issue #53020 for more information -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const-eval/const_raw_ptr_ops2.rs b/src/test/ui/consts/const-eval/const_raw_ptr_ops2.rs new file mode 100644 index 0000000000000..d2a7623837a23 --- /dev/null +++ b/src/test/ui/consts/const-eval/const_raw_ptr_ops2.rs @@ -0,0 +1,13 @@ +#![feature(const_raw_ptr_to_usize_cast, const_raw_ptr_deref)] + +fn main() {} + +// unconst and fine +const Y: usize = unsafe { 42usize as *const i32 as usize + 1 }; +// unconst and bad, will thus error in miri +const Y2: usize = unsafe { &1 as *const i32 as usize + 1 }; //~ ERROR any use of this +// unconst and fine +const Z: i32 = unsafe { *(&1 as *const i32) }; +// unconst and bad, will thus error in miri +const Z2: i32 = unsafe { *(42 as *const i32) }; //~ ERROR any use of this value will cause +const Z3: i32 = unsafe { *(44 as *const i32) }; //~ ERROR any use of this value will cause diff --git a/src/test/ui/consts/const-eval/const_raw_ptr_ops2.stderr b/src/test/ui/consts/const-eval/const_raw_ptr_ops2.stderr new file mode 100644 index 0000000000000..93f2261745d6f --- /dev/null +++ b/src/test/ui/consts/const-eval/const_raw_ptr_ops2.stderr @@ -0,0 +1,28 @@ +error: any use of this value will cause an error + --> $DIR/const_raw_ptr_ops2.rs:8:28 + | +LL | const Y2: usize = unsafe { &1 as *const i32 as usize + 1 }; + | ---------------------------^^^^^^^^^^^^^^^^^^^^^^^^^------- + | | + | "pointer-to-integer cast" needs an rfc before being allowed inside constants + | + = note: `#[deny(const_err)]` on by default + +error: any use of this value will cause an error + --> $DIR/const_raw_ptr_ops2.rs:12:26 + | +LL | const Z2: i32 = unsafe { *(42 as *const i32) }; + | -------------------------^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn bytes into a pointer + +error: any use of this value will cause an error + --> $DIR/const_raw_ptr_ops2.rs:13:26 + | +LL | const Z3: i32 = unsafe { *(44 as *const i32) }; + | -------------------------^^^^^^^^^^^^^^^^^^^--- + | | + | unable to turn bytes into a pointer + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/consts/const-eval/double_check2.stderr b/src/test/ui/consts/const-eval/double_check2.stderr index 28e0922ecafa6..93dd9a53ec99f 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 tag | = 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/infinite_loop.rs b/src/test/ui/consts/const-eval/infinite_loop.rs index af5e7658d48d2..c8de259354eac 100644 --- a/src/test/ui/consts/const-eval/infinite_loop.rs +++ b/src/test/ui/consts/const-eval/infinite_loop.rs @@ -2,7 +2,6 @@ fn main() { // Tests the Collatz conjecture with an incorrect base case (0 instead of 1). // The value of `n` will loop indefinitely (4 - 2 - 1 - 4). let _ = [(); { - //~^ WARNING Constant evaluating a complex constant, this might take some time let mut n = 113383; // #20 in https://oeis.org/A006884 while n != 0 { //~^ ERROR `while` is not allowed in a `const` diff --git a/src/test/ui/consts/const-eval/infinite_loop.stderr b/src/test/ui/consts/const-eval/infinite_loop.stderr index e04c31cf39782..3386e6e588e71 100644 --- a/src/test/ui/consts/const-eval/infinite_loop.stderr +++ b/src/test/ui/consts/const-eval/infinite_loop.stderr @@ -1,5 +1,5 @@ error[E0658]: `while` is not allowed in a `const` - --> $DIR/infinite_loop.rs:7:9 + --> $DIR/infinite_loop.rs:6:9 | LL | / while n != 0 { LL | | @@ -14,7 +14,7 @@ LL | | } = help: add `#![feature(const_if_match)]` to the crate attributes to enable error[E0658]: `if` is not allowed in a `const` - --> $DIR/infinite_loop.rs:9:17 + --> $DIR/infinite_loop.rs:8:17 | LL | n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,24 +22,11 @@ LL | n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; = note: see issue #49146 for more information = help: add `#![feature(const_if_match)]` to the crate attributes to enable -warning: Constant evaluating a complex constant, this might take some time - --> $DIR/infinite_loop.rs:4:18 - | -LL | let _ = [(); { - | __________________^ -LL | | -LL | | let mut n = 113383; // #20 in https://oeis.org/A006884 -LL | | while n != 0 { -... | -LL | | n -LL | | }]; - | |_____^ - error[E0080]: evaluation of constant value failed - --> $DIR/infinite_loop.rs:9:20 + --> $DIR/infinite_loop.rs:8:17 | LL | n = if n % 2 == 0 { n/2 } else { 3*n + 1 }; - | ^^^^^^^^^^ duplicate interpreter state observed here, const evaluation will never terminate + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) error: aborting due to 3 previous errors 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-49296.stderr b/src/test/ui/consts/const-eval/issue-49296.stderr index 48809e0ae649c..798f130a4baf6 100644 --- a/src/test/ui/consts/const-eval/issue-49296.stderr +++ b/src/test/ui/consts/const-eval/issue-49296.stderr @@ -4,7 +4,7 @@ error: any use of this value will cause an error LL | const X: u64 = *wat(42); | ---------------^^^^^^^^- | | - | dangling pointer was dereferenced + | pointer to alloc2 was dereferenced after this allocation got freed | = note: `#[deny(const_err)]` on by default diff --git a/src/test/ui/consts/const-eval/issue-52442.rs b/src/test/ui/consts/const-eval/issue-52442.rs index d820c70516124..07fb491015a85 100644 --- a/src/test/ui/consts/const-eval/issue-52442.rs +++ b/src/test/ui/consts/const-eval/issue-52442.rs @@ -1,6 +1,6 @@ fn main() { [(); { &loop { break } as *const _ as usize } ]; - //~^ ERROR casting pointers to integers in constants is unstable - //~| ERROR `loop` is not allowed in a `const` + //~^ 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-52475.rs b/src/test/ui/consts/const-eval/issue-52475.rs index 3788167f44902..869f0b981af7d 100644 --- a/src/test/ui/consts/const-eval/issue-52475.rs +++ b/src/test/ui/consts/const-eval/issue-52475.rs @@ -1,6 +1,5 @@ fn main() { let _ = [(); { - //~^ WARNING Constant evaluating a complex constant, this might take some time let mut x = &0; let mut n = 0; while n < 5 { diff --git a/src/test/ui/consts/const-eval/issue-52475.stderr b/src/test/ui/consts/const-eval/issue-52475.stderr index 31d87925b2cfc..b069537ead963 100644 --- a/src/test/ui/consts/const-eval/issue-52475.stderr +++ b/src/test/ui/consts/const-eval/issue-52475.stderr @@ -1,5 +1,5 @@ error[E0658]: `while` is not allowed in a `const` - --> $DIR/issue-52475.rs:6:9 + --> $DIR/issue-52475.rs:5:9 | LL | / while n < 5 { LL | | @@ -12,24 +12,11 @@ LL | | } = help: add `#![feature(const_loop)]` to the crate attributes to enable = help: add `#![feature(const_if_match)]` to the crate attributes to enable -warning: Constant evaluating a complex constant, this might take some time - --> $DIR/issue-52475.rs:2:18 - | -LL | let _ = [(); { - | __________________^ -LL | | -LL | | let mut x = &0; -LL | | let mut n = 0; -... | -LL | | 0 -LL | | }]; - | |_____^ - error[E0080]: evaluation of constant value failed - --> $DIR/issue-52475.rs:8:17 + --> $DIR/issue-52475.rs:7:17 | LL | n = (n + 1) % 5; - | ^^^^^^^^^^^ duplicate interpreter state observed here, const evaluation will never terminate + | ^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`) error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const-eval/issue-65394.rs b/src/test/ui/consts/const-eval/issue-65394.rs index b1c058eac9e4b..2518e4ed40b30 100644 --- a/src/test/ui/consts/const-eval/issue-65394.rs +++ b/src/test/ui/consts/const-eval/issue-65394.rs @@ -5,7 +5,7 @@ const _: Vec = { let mut x = Vec::::new(); //~ ERROR destructors cannot be evaluated at compile-time - let r = &mut x; //~ ERROR references in constants may only refer to immutable values + let r = &mut x; //~ ERROR mutable references are not allowed in constants let y = x; y }; diff --git a/src/test/ui/consts/const-eval/issue-65394.stderr b/src/test/ui/consts/const-eval/issue-65394.stderr index d85a1a1a3c32b..771d368d78391 100644 --- a/src/test/ui/consts/const-eval/issue-65394.stderr +++ b/src/test/ui/consts/const-eval/issue-65394.stderr @@ -1,19 +1,19 @@ -error[E0658]: references in constants may only refer to immutable values +error[E0764]: mutable references are not allowed in constants --> $DIR/issue-65394.rs:8:13 | LL | let r = &mut x; - | ^^^^^^ constants require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/issue-65394.rs:7:9 | LL | let mut x = Vec::::new(); | ^^^^^ constants cannot evaluate destructors +... +LL | }; + | - value is dropped here error: aborting due to 2 previous errors -Some errors have detailed explanations: E0493, E0658. +Some errors have detailed explanations: E0493, E0764. For more information about an error, try `rustc --explain E0493`. 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/livedrop.rs b/src/test/ui/consts/const-eval/livedrop.rs new file mode 100644 index 0000000000000..f00d1c70659fd --- /dev/null +++ b/src/test/ui/consts/const-eval/livedrop.rs @@ -0,0 +1,20 @@ +#![feature(const_if_match)] +#![feature(const_loop)] + +const _: Option> = { + let mut never_returned = Some(Vec::new()); + let mut always_returned = None; //~ ERROR destructors cannot be evaluated at compile-time + + let mut i = 0; + loop { + always_returned = never_returned; + never_returned = None; + + i += 1; + if i == 10 { + break always_returned; + } + } +}; + +fn main() {} diff --git a/src/test/ui/consts/const-eval/livedrop.stderr b/src/test/ui/consts/const-eval/livedrop.stderr new file mode 100644 index 0000000000000..b802d23d9a89c --- /dev/null +++ b/src/test/ui/consts/const-eval/livedrop.stderr @@ -0,0 +1,12 @@ +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/livedrop.rs:6:9 + | +LL | let mut always_returned = None; + | ^^^^^^^^^^^^^^^^^^^ constants cannot evaluate destructors +... +LL | always_returned = never_returned; + | --------------- value is dropped here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0493`. 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/promoted_raw_ptr_ops.rs b/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.rs index c6fb5eeab5aec..d724fe3060b21 100644 --- a/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.rs +++ b/src/test/ui/consts/const-eval/promoted_raw_ptr_ops.rs @@ -1,4 +1,4 @@ -#![feature(const_raw_ptr_to_usize_cast, const_compare_raw_pointers, const_raw_ptr_deref)] +#![feature(const_raw_ptr_to_usize_cast, const_raw_ptr_deref)] fn main() { let x: &'static bool = &(42 as *const i32 == 43 as *const i32); 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/shift_overflow.rs b/src/test/ui/consts/const-eval/shift_overflow.rs index f7d0f6bd96144..e843584b69bb5 100644 --- a/src/test/ui/consts/const-eval/shift_overflow.rs +++ b/src/test/ui/consts/const-eval/shift_overflow.rs @@ -1,6 +1,6 @@ enum Foo { // test that we detect overflows for non-u32 discriminants - X = 1 << ((u32::max_value() as u64) + 1), //~ ERROR E0080 + X = 1 << ((u32::MAX as u64) + 1), //~ ERROR E0080 Y = 42, } diff --git a/src/test/ui/consts/const-eval/shift_overflow.stderr b/src/test/ui/consts/const-eval/shift_overflow.stderr index 5db231cd5b0df..f4840e9ac96bd 100644 --- a/src/test/ui/consts/const-eval/shift_overflow.stderr +++ b/src/test/ui/consts/const-eval/shift_overflow.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation of constant value failed --> $DIR/shift_overflow.rs:3:9 | -LL | X = 1 << ((u32::max_value() as u64) + 1), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to shift left with overflow +LL | X = 1 << ((u32::MAX as u64) + 1), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to shift left with overflow error: aborting due to previous error 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..1f7593c6db9b6 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 tag | = 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 tag | = 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 scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. 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 edfc7ac837fc7..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 allocation 8 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 2d48309b72722..f69f6a1109f76 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.rs +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.rs @@ -6,7 +6,7 @@ use std::mem; // normalize-stderr-test "offset \d+" -> "offset N" -// normalize-stderr-test "allocation \d+" -> "allocation N" +// normalize-stderr-test "alloc\d+" -> "allocN" // normalize-stderr-test "size \d+" -> "size N" #[repr(C)] @@ -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 a562c64b124f9..47d29ffc9b3c1 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:32:1 | LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (not entirely in bounds) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) | = 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. @@ -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. @@ -70,7 +70,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:62:1 | LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (not entirely in bounds) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) | = 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. @@ -86,7 +86,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-wide-ptr.rs:68:1 | LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (not entirely in bounds) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (going beyond the bounds of its allocation) | = 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 allocation N 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..9c94551f7440e 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)] @@ -35,8 +34,8 @@ suite!( C6: 5i8.checked_mul(122), None; C7: (-127i8).checked_mul(-99), None; - C8: (i8::min_value() + 1).checked_div(-1), Some(127); - C9: i8::min_value().checked_div(-1), None; + C8: (i8::MIN + 1).checked_div(-1), Some(127); + C9: i8::MIN.checked_div(-1), None; C10: 1i8.checked_div(0), None; C11: 5i8.checked_rem(2), Some(1); @@ -57,8 +56,8 @@ suite!( C21: i8::MIN.checked_abs(), None; // `const_euclidean_int_methods` - C22: (i8::min_value() + 1).checked_div_euclid(-1), Some(127); - C23: i8::min_value().checked_div_euclid(-1), None; + C22: (i8::MIN + 1).checked_div_euclid(-1), Some(127); + C23: i8::MIN.checked_div_euclid(-1), None; C24: (1i8).checked_div_euclid(0), None; C25: 5i8.checked_rem_euclid(2), Some(1); @@ -73,12 +72,12 @@ suite!( saturating_and_wrapping -> i8 { // `const_saturating_int_methods` C28: 100i8.saturating_add(1), 101; - C29: i8::max_value().saturating_add(100), i8::max_value(); - C30: i8::min_value().saturating_add(-1), i8::min_value(); + C29: i8::MAX.saturating_add(100), i8::MAX; + C30: i8::MIN.saturating_add(-1), i8::MIN; C31: 100i8.saturating_sub(127), -27; - C32: i8::min_value().saturating_sub(100), i8::min_value(); - C33: i8::max_value().saturating_sub(-1), i8::max_value(); + C32: i8::MIN.saturating_sub(100), i8::MIN; + C33: i8::MAX.saturating_sub(-1), i8::MAX; C34: 10i8.saturating_mul(12), 120; C35: i8::MAX.saturating_mul(10), i8::MAX; @@ -86,13 +85,13 @@ suite!( C37: 100i8.saturating_neg(), -100; C38: (-100i8).saturating_neg(), 100; - C39: i8::min_value().saturating_neg(), i8::max_value(); - C40: i8::max_value().saturating_neg(), i8::min_value() + 1; + C39: i8::MIN.saturating_neg(), i8::MAX; + C40: i8::MAX.saturating_neg(), i8::MIN + 1; C57: 100i8.saturating_abs(), 100; C58: (-100i8).saturating_abs(), 100; - C59: i8::min_value().saturating_abs(), i8::max_value(); - C60: (i8::min_value() + 1).saturating_abs(), i8::max_value(); + C59: i8::MIN.saturating_abs(), i8::MAX; + C60: (i8::MIN + 1).saturating_abs(), i8::MAX; // `const_wrapping_int_methods` C41: 100i8.wrapping_div(10), 10; diff --git a/src/test/ui/consts/const-int-conversion-rpass.rs b/src/test/ui/consts/const-int-conversion-rpass.rs index 6484169dd9ae1..4aaeeaa38853d 100644 --- a/src/test/ui/consts/const-int-conversion-rpass.rs +++ b/src/test/ui/consts/const-int-conversion-rpass.rs @@ -6,13 +6,13 @@ const FROM_LE_BYTES: i32 = i32::from_le_bytes([0x12, 0x34, 0x56, 0x78]); const FROM_NE_BYTES: i32 = i32::from_be(i32::from_ne_bytes([0x80, 0, 0, 0])); const TO_BE_BYTES: [u8; 4] = 0x12_34_56_78_i32.to_be_bytes(); const TO_LE_BYTES: [u8; 4] = 0x12_34_56_78_i32.to_le_bytes(); -const TO_NE_BYTES: [u8; 4] = i32::min_value().to_be().to_ne_bytes(); +const TO_NE_BYTES: [u8; 4] = i32::MIN.to_be().to_ne_bytes(); fn main() { assert_eq!(REVERSE, 0x1e6a2c48); assert_eq!(FROM_BE_BYTES, 0x12_34_56_78); assert_eq!(FROM_LE_BYTES, 0x78_56_34_12); - assert_eq!(FROM_NE_BYTES, i32::min_value()); + assert_eq!(FROM_NE_BYTES, i32::MIN); assert_eq!(TO_BE_BYTES, [0x12, 0x34, 0x56, 0x78]); assert_eq!(TO_LE_BYTES, [0x78, 0x56, 0x34, 0x12]); assert_eq!(TO_NE_BYTES, [0x80, 0, 0, 0]); diff --git a/src/test/ui/consts/const-int-conversion.rs b/src/test/ui/consts/const-int-conversion.rs index b80e616eae77e..5a05a2b35937a 100644 --- a/src/test/ui/consts/const-int-conversion.rs +++ b/src/test/ui/consts/const-int-conversion.rs @@ -11,6 +11,6 @@ fn main() { //~^ ERROR temporary value dropped while borrowed let c: &'static [u8] = &(0x12_34_56_78_i32.to_le_bytes()); //~^ ERROR temporary value dropped while borrowed - let d: &'static [u8] = &(i32::min_value().to_be().to_ne_bytes()); + let d: &'static [u8] = &(i32::MIN.to_be().to_ne_bytes()); //~^ ERROR temporary value dropped while borrowed } diff --git a/src/test/ui/consts/const-int-conversion.stderr b/src/test/ui/consts/const-int-conversion.stderr index 237f9627219bd..61162a792262b 100644 --- a/src/test/ui/consts/const-int-conversion.stderr +++ b/src/test/ui/consts/const-int-conversion.stderr @@ -67,8 +67,8 @@ LL | } error[E0716]: temporary value dropped while borrowed --> $DIR/const-int-conversion.rs:14:29 | -LL | let d: &'static [u8] = &(i32::min_value().to_be().to_ne_bytes()); - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use +LL | let d: &'static [u8] = &(i32::MIN.to_be().to_ne_bytes()); + | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use | | | type annotation requires that borrow lasts for `'static` LL | diff --git a/src/test/ui/consts/const-int-overflowing-rpass.rs b/src/test/ui/consts/const-int-overflowing-rpass.rs index 9be87a6447cda..eecb88becabca 100644 --- a/src/test/ui/consts/const-int-overflowing-rpass.rs +++ b/src/test/ui/consts/const-int-overflowing-rpass.rs @@ -1,7 +1,7 @@ // run-pass const ADD_A: (u32, bool) = 5u32.overflowing_add(2); -const ADD_B: (u32, bool) = u32::max_value().overflowing_add(1); +const ADD_B: (u32, bool) = u32::MAX.overflowing_add(1); const SUB_A: (u32, bool) = 5u32.overflowing_sub(2); const SUB_B: (u32, bool) = 0u32.overflowing_sub(1); @@ -20,14 +20,14 @@ const NEG_B: (u32, bool) = core::u32::MAX.overflowing_neg(); const ABS_POS: (i32, bool) = 10i32.overflowing_abs(); const ABS_NEG: (i32, bool) = (-10i32).overflowing_abs(); -const ABS_MIN: (i32, bool) = i32::min_value().overflowing_abs(); +const ABS_MIN: (i32, bool) = i32::MIN.overflowing_abs(); fn main() { assert_eq!(ADD_A, (7, false)); assert_eq!(ADD_B, (0, true)); assert_eq!(SUB_A, (3, false)); - assert_eq!(SUB_B, (u32::max_value(), true)); + assert_eq!(SUB_B, (u32::MAX, true)); assert_eq!(MUL_A, (10, false)); assert_eq!(MUL_B, (1410065408, true)); @@ -43,5 +43,5 @@ fn main() { assert_eq!(ABS_POS, (10, false)); assert_eq!(ABS_NEG, (10, false)); - assert_eq!(ABS_MIN, (i32::min_value(), true)); + assert_eq!(ABS_MIN, (i32::MIN, true)); } diff --git a/src/test/ui/consts/const-int-pow-rpass.rs b/src/test/ui/consts/const-int-pow-rpass.rs index b0fba19455ba8..4f936236dbb20 100644 --- a/src/test/ui/consts/const-int-pow-rpass.rs +++ b/src/test/ui/consts/const-int-pow-rpass.rs @@ -20,10 +20,10 @@ const NEXT_POWER_OF_TWO: u32 = 3u32.next_power_of_two(); const CHECKED_NEXT_POWER_OF_TWO_OK: Option = 3u32.checked_next_power_of_two(); const CHECKED_NEXT_POWER_OF_TWO_OVERFLOW: Option = - u32::max_value().checked_next_power_of_two(); + u32::MAX.checked_next_power_of_two(); const WRAPPING_NEXT_POWER_OF_TWO: u32 = - u32::max_value().wrapping_next_power_of_two(); + u32::MAX.wrapping_next_power_of_two(); fn main() { assert!(!IS_POWER_OF_TWO_A); @@ -37,7 +37,7 @@ fn main() { assert_eq!(WRAPPING_POW, 217); assert_eq!(OVERFLOWING_POW, (217, true)); - assert_eq!(SATURATING_POW, u8::max_value()); + assert_eq!(SATURATING_POW, u8::MAX); assert_eq!(NEXT_POWER_OF_TWO, 4); diff --git a/src/test/ui/consts/const-int-saturating-arith.rs b/src/test/ui/consts/const-int-saturating-arith.rs index d0a3eccd17763..4718120a51bd3 100644 --- a/src/test/ui/consts/const-int-saturating-arith.rs +++ b/src/test/ui/consts/const-int-saturating-arith.rs @@ -2,33 +2,33 @@ #![feature(const_saturating_int_methods)] const INT_U32_NO: u32 = (42 as u32).saturating_add(2); -const INT_U32: u32 = u32::max_value().saturating_add(1); -const INT_U128: u128 = u128::max_value().saturating_add(1); -const INT_I128: i128 = i128::max_value().saturating_add(1); -const INT_I128_NEG: i128 = i128::min_value().saturating_add(-1); +const INT_U32: u32 = u32::MAX.saturating_add(1); +const INT_U128: u128 = u128::MAX.saturating_add(1); +const INT_I128: i128 = i128::MAX.saturating_add(1); +const INT_I128_NEG: i128 = i128::MIN.saturating_add(-1); const INT_U32_NO_SUB: u32 = (42 as u32).saturating_sub(2); const INT_U32_SUB: u32 = (1 as u32).saturating_sub(2); const INT_I32_NO_SUB: i32 = (-42 as i32).saturating_sub(2); -const INT_I32_NEG_SUB: i32 = i32::min_value().saturating_sub(1); -const INT_I32_POS_SUB: i32 = i32::max_value().saturating_sub(-1); +const INT_I32_NEG_SUB: i32 = i32::MIN.saturating_sub(1); +const INT_I32_POS_SUB: i32 = i32::MAX.saturating_sub(-1); const INT_U128_SUB: u128 = (0 as u128).saturating_sub(1); -const INT_I128_NEG_SUB: i128 = i128::min_value().saturating_sub(1); -const INT_I128_POS_SUB: i128 = i128::max_value().saturating_sub(-1); +const INT_I128_NEG_SUB: i128 = i128::MIN.saturating_sub(1); +const INT_I128_POS_SUB: i128 = i128::MAX.saturating_sub(-1); fn main() { assert_eq!(INT_U32_NO, 44); - assert_eq!(INT_U32, u32::max_value()); - assert_eq!(INT_U128, u128::max_value()); - assert_eq!(INT_I128, i128::max_value()); - assert_eq!(INT_I128_NEG, i128::min_value()); + assert_eq!(INT_U32, u32::MAX); + assert_eq!(INT_U128, u128::MAX); + assert_eq!(INT_I128, i128::MAX); + assert_eq!(INT_I128_NEG, i128::MIN); assert_eq!(INT_U32_NO_SUB, 40); assert_eq!(INT_U32_SUB, 0); assert_eq!(INT_I32_NO_SUB, -44); - assert_eq!(INT_I32_NEG_SUB, i32::min_value()); - assert_eq!(INT_I32_POS_SUB, i32::max_value()); + assert_eq!(INT_I32_NEG_SUB, i32::MIN); + assert_eq!(INT_I32_POS_SUB, i32::MAX); assert_eq!(INT_U128_SUB, 0); - assert_eq!(INT_I128_NEG_SUB, i128::min_value()); - assert_eq!(INT_I128_POS_SUB, i128::max_value()); + assert_eq!(INT_I128_NEG_SUB, i128::MIN); + assert_eq!(INT_I128_POS_SUB, i128::MAX); } diff --git a/src/test/ui/consts/const-int-unchecked.rs b/src/test/ui/consts/const-int-unchecked.rs index fb09f62854d61..1596093b2c14b 100644 --- a/src/test/ui/consts/const-int-unchecked.rs +++ b/src/test/ui/consts/const-int-unchecked.rs @@ -131,12 +131,12 @@ const _: u16 = unsafe { std::intrinsics::unchecked_mul(300u16, 250u16) }; const _: i32 = unsafe { std::intrinsics::unchecked_div(1, 0) }; //~^ ERROR any use of this value will cause an error -const _: i32 = unsafe { std::intrinsics::unchecked_div(i32::min_value(), -1) }; +const _: i32 = unsafe { std::intrinsics::unchecked_div(i32::MIN, -1) }; //~^ ERROR any use of this value will cause an error const _: i32 = unsafe { std::intrinsics::unchecked_rem(1, 0) }; //~^ ERROR any use of this value will cause an error -const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::min_value(), -1) }; +const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::MIN, -1) }; //~^ ERROR any use of this value will cause an error fn main() {} diff --git a/src/test/ui/consts/const-int-unchecked.stderr b/src/test/ui/consts/const-int-unchecked.stderr index bf31e0b0732d8..0287b404e7d46 100644 --- a/src/test/ui/consts/const-int-unchecked.stderr +++ b/src/test/ui/consts/const-int-unchecked.stderr @@ -4,7 +4,7 @@ error: any use of this value will cause an error LL | const SHL_U8: u8 = unsafe { intrinsics::unchecked_shl(5_u8, 8) }; | ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 8 in `unchecked_shl` + | overflowing shift by 8 in `unchecked_shl` | = note: `#[deny(const_err)]` on by default @@ -14,7 +14,7 @@ error: any use of this value will cause an error LL | const SHL_U16: u16 = unsafe { intrinsics::unchecked_shl(5_u16, 16) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 16 in `unchecked_shl` + | overflowing shift by 16 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:19:31 @@ -22,7 +22,7 @@ error: any use of this value will cause an error LL | const SHL_U32: u32 = unsafe { intrinsics::unchecked_shl(5_u32, 32) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 32 in `unchecked_shl` + | overflowing shift by 32 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:21:31 @@ -30,7 +30,7 @@ error: any use of this value will cause an error LL | const SHL_U64: u64 = unsafe { intrinsics::unchecked_shl(5_u64, 64) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 64 in `unchecked_shl` + | overflowing shift by 64 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:23:33 @@ -38,7 +38,7 @@ error: any use of this value will cause an error LL | const SHL_U128: u128 = unsafe { intrinsics::unchecked_shl(5_u128, 128) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 128 in `unchecked_shl` + | overflowing shift by 128 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:28:29 @@ -46,7 +46,7 @@ error: any use of this value will cause an error LL | const SHL_I8: i8 = unsafe { intrinsics::unchecked_shl(5_i8, 8) }; | ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 8 in `unchecked_shl` + | overflowing shift by 8 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:30:31 @@ -54,7 +54,7 @@ error: any use of this value will cause an error LL | const SHL_I16: i16 = unsafe { intrinsics::unchecked_shl(5_16, 16) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 16 in `unchecked_shl` + | overflowing shift by 16 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:32:31 @@ -62,7 +62,7 @@ error: any use of this value will cause an error LL | const SHL_I32: i32 = unsafe { intrinsics::unchecked_shl(5_i32, 32) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 32 in `unchecked_shl` + | overflowing shift by 32 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:34:31 @@ -70,7 +70,7 @@ error: any use of this value will cause an error LL | const SHL_I64: i64 = unsafe { intrinsics::unchecked_shl(5_i64, 64) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 64 in `unchecked_shl` + | overflowing shift by 64 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:36:33 @@ -78,7 +78,7 @@ error: any use of this value will cause an error LL | const SHL_I128: i128 = unsafe { intrinsics::unchecked_shl(5_i128, 128) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 128 in `unchecked_shl` + | overflowing shift by 128 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:41:33 @@ -86,7 +86,7 @@ error: any use of this value will cause an error LL | const SHL_I8_NEG: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -1) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 255 in `unchecked_shl` + | overflowing shift by 255 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:43:35 @@ -94,7 +94,7 @@ error: any use of this value will cause an error LL | const SHL_I16_NEG: i16 = unsafe { intrinsics::unchecked_shl(5_16, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 65535 in `unchecked_shl` + | overflowing shift by 65535 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:45:35 @@ -102,7 +102,7 @@ error: any use of this value will cause an error LL | const SHL_I32_NEG: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 4294967295 in `unchecked_shl` + | overflowing shift by 4294967295 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:47:35 @@ -110,7 +110,7 @@ error: any use of this value will cause an error LL | const SHL_I64_NEG: i64 = unsafe { intrinsics::unchecked_shl(5_i64, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 18446744073709551615 in `unchecked_shl` + | overflowing shift by 18446744073709551615 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:49:37 @@ -118,7 +118,7 @@ error: any use of this value will cause an error LL | const SHL_I128_NEG: i128 = unsafe { intrinsics::unchecked_shl(5_i128, -1) }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shl` + | overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:55:40 @@ -126,7 +126,7 @@ error: any use of this value will cause an error LL | const SHL_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shl(5_i8, -6) }; | ---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 250 in `unchecked_shl` + | overflowing shift by 250 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:57:42 @@ -134,7 +134,7 @@ error: any use of this value will cause an error LL | const SHL_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shl(5_16, -13) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 65523 in `unchecked_shl` + | overflowing shift by 65523 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:59:42 @@ -142,7 +142,7 @@ error: any use of this value will cause an error LL | const SHL_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shl(5_i32, -25) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 4294967271 in `unchecked_shl` + | overflowing shift by 4294967271 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:61:42 @@ -150,7 +150,7 @@ error: any use of this value will cause an error LL | const SHL_I64_NEG_RANDOM: i64 = unsafe { intrinsics::unchecked_shl(5_i64, -30) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 18446744073709551586 in `unchecked_shl` + | overflowing shift by 18446744073709551586 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:63:44 @@ -158,7 +158,7 @@ error: any use of this value will cause an error LL | const SHL_I128_NEG_RANDOM: i128 = unsafe { intrinsics::unchecked_shl(5_i128, -93) }; | -------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shl` + | overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shl` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:70:29 @@ -166,7 +166,7 @@ error: any use of this value will cause an error LL | const SHR_U8: u8 = unsafe { intrinsics::unchecked_shr(5_u8, 8) }; | ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 8 in `unchecked_shr` + | overflowing shift by 8 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:72:31 @@ -174,7 +174,7 @@ error: any use of this value will cause an error LL | const SHR_U16: u16 = unsafe { intrinsics::unchecked_shr(5_u16, 16) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 16 in `unchecked_shr` + | overflowing shift by 16 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:74:31 @@ -182,7 +182,7 @@ error: any use of this value will cause an error LL | const SHR_U32: u32 = unsafe { intrinsics::unchecked_shr(5_u32, 32) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 32 in `unchecked_shr` + | overflowing shift by 32 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:76:31 @@ -190,7 +190,7 @@ error: any use of this value will cause an error LL | const SHR_U64: u64 = unsafe { intrinsics::unchecked_shr(5_u64, 64) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 64 in `unchecked_shr` + | overflowing shift by 64 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:78:33 @@ -198,7 +198,7 @@ error: any use of this value will cause an error LL | const SHR_U128: u128 = unsafe { intrinsics::unchecked_shr(5_u128, 128) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 128 in `unchecked_shr` + | overflowing shift by 128 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:83:29 @@ -206,7 +206,7 @@ error: any use of this value will cause an error LL | const SHR_I8: i8 = unsafe { intrinsics::unchecked_shr(5_i8, 8) }; | ----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 8 in `unchecked_shr` + | overflowing shift by 8 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:85:31 @@ -214,7 +214,7 @@ error: any use of this value will cause an error LL | const SHR_I16: i16 = unsafe { intrinsics::unchecked_shr(5_16, 16) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 16 in `unchecked_shr` + | overflowing shift by 16 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:87:31 @@ -222,7 +222,7 @@ error: any use of this value will cause an error LL | const SHR_I32: i32 = unsafe { intrinsics::unchecked_shr(5_i32, 32) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 32 in `unchecked_shr` + | overflowing shift by 32 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:89:31 @@ -230,7 +230,7 @@ error: any use of this value will cause an error LL | const SHR_I64: i64 = unsafe { intrinsics::unchecked_shr(5_i64, 64) }; | ------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 64 in `unchecked_shr` + | overflowing shift by 64 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:91:33 @@ -238,7 +238,7 @@ error: any use of this value will cause an error LL | const SHR_I128: i128 = unsafe { intrinsics::unchecked_shr(5_i128, 128) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 128 in `unchecked_shr` + | overflowing shift by 128 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:96:33 @@ -246,7 +246,7 @@ error: any use of this value will cause an error LL | const SHR_I8_NEG: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -1) }; | --------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 255 in `unchecked_shr` + | overflowing shift by 255 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:98:35 @@ -254,7 +254,7 @@ error: any use of this value will cause an error LL | const SHR_I16_NEG: i16 = unsafe { intrinsics::unchecked_shr(5_16, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 65535 in `unchecked_shr` + | overflowing shift by 65535 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:100:35 @@ -262,7 +262,7 @@ error: any use of this value will cause an error LL | const SHR_I32_NEG: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 4294967295 in `unchecked_shr` + | overflowing shift by 4294967295 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:102:35 @@ -270,7 +270,7 @@ error: any use of this value will cause an error LL | const SHR_I64_NEG: i64 = unsafe { intrinsics::unchecked_shr(5_i64, -1) }; | ----------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 18446744073709551615 in `unchecked_shr` + | overflowing shift by 18446744073709551615 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:104:37 @@ -278,7 +278,7 @@ error: any use of this value will cause an error LL | const SHR_I128_NEG: i128 = unsafe { intrinsics::unchecked_shr(5_i128, -1) }; | ------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shr` + | overflowing shift by 340282366920938463463374607431768211455 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:110:40 @@ -286,7 +286,7 @@ error: any use of this value will cause an error LL | const SHR_I8_NEG_RANDOM: i8 = unsafe { intrinsics::unchecked_shr(5_i8, -6) }; | ---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 250 in `unchecked_shr` + | overflowing shift by 250 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:112:42 @@ -294,7 +294,7 @@ error: any use of this value will cause an error LL | const SHR_I16_NEG_RANDOM: i16 = unsafe { intrinsics::unchecked_shr(5_16, -13) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 65523 in `unchecked_shr` + | overflowing shift by 65523 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:114:42 @@ -302,7 +302,7 @@ error: any use of this value will cause an error LL | const SHR_I32_NEG_RANDOM: i32 = unsafe { intrinsics::unchecked_shr(5_i32, -25) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 4294967271 in `unchecked_shr` + | overflowing shift by 4294967271 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:116:42 @@ -310,7 +310,7 @@ error: any use of this value will cause an error LL | const SHR_I64_NEG_RANDOM: i64 = unsafe { intrinsics::unchecked_shr(5_i64, -30) }; | -----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 18446744073709551586 in `unchecked_shr` + | overflowing shift by 18446744073709551586 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:118:44 @@ -318,7 +318,7 @@ error: any use of this value will cause an error LL | const SHR_I128_NEG_RANDOM: i128 = unsafe { intrinsics::unchecked_shr(5_i128, -93) }; | -------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shr` + | overflowing shift by 340282366920938463463374607431768211363 in `unchecked_shr` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:123:25 @@ -326,7 +326,7 @@ error: any use of this value will cause an error LL | const _: u16 = unsafe { std::intrinsics::unchecked_add(40000u16, 30000) }; | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_add` + | overflow executing `unchecked_add` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:126:25 @@ -334,7 +334,7 @@ error: any use of this value will cause an error LL | const _: u32 = unsafe { std::intrinsics::unchecked_sub(14u32, 22) }; | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_sub` + | overflow executing `unchecked_sub` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:129:25 @@ -342,7 +342,7 @@ error: any use of this value will cause an error LL | const _: u16 = unsafe { std::intrinsics::unchecked_mul(300u16, 250u16) }; | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_mul` + | overflow executing `unchecked_mul` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:132:25 @@ -355,10 +355,10 @@ LL | const _: i32 = unsafe { std::intrinsics::unchecked_div(1, 0) }; error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:134:25 | -LL | const _: i32 = unsafe { std::intrinsics::unchecked_div(i32::min_value(), -1) }; - | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- +LL | const _: i32 = unsafe { std::intrinsics::unchecked_div(i32::MIN, -1) }; + | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_div` + | overflow executing `unchecked_div` error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:137:25 @@ -371,10 +371,10 @@ LL | const _: i32 = unsafe { std::intrinsics::unchecked_rem(1, 0) }; error: any use of this value will cause an error --> $DIR/const-int-unchecked.rs:139:25 | -LL | const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::min_value(), -1) }; - | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- +LL | const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::MIN, -1) }; + | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- | | - | Overflow executing `unchecked_rem` + | overflow executing `unchecked_rem` error: aborting due to 47 previous errors diff --git a/src/test/ui/consts/const-int-wrapping-rpass.rs b/src/test/ui/consts/const-int-wrapping-rpass.rs index 2bbad99a52a90..225d1e9393db4 100644 --- a/src/test/ui/consts/const-int-wrapping-rpass.rs +++ b/src/test/ui/consts/const-int-wrapping-rpass.rs @@ -1,10 +1,10 @@ // run-pass const ADD_A: u32 = 200u32.wrapping_add(55); -const ADD_B: u32 = 200u32.wrapping_add(u32::max_value()); +const ADD_B: u32 = 200u32.wrapping_add(u32::MAX); const SUB_A: u32 = 100u32.wrapping_sub(100); -const SUB_B: u32 = 100u32.wrapping_sub(u32::max_value()); +const SUB_B: u32 = 100u32.wrapping_sub(u32::MAX); const MUL_A: u8 = 10u8.wrapping_mul(12); const MUL_B: u8 = 25u8.wrapping_mul(12); @@ -20,7 +20,7 @@ const NEG_B: u32 = 1234567890u32.wrapping_neg(); const ABS_POS: i32 = 10i32.wrapping_abs(); const ABS_NEG: i32 = (-10i32).wrapping_abs(); -const ABS_MIN: i32 = i32::min_value().wrapping_abs(); +const ABS_MIN: i32 = i32::MIN.wrapping_abs(); fn main() { assert_eq!(ADD_A, 255); @@ -43,5 +43,5 @@ fn main() { assert_eq!(ABS_POS, 10); assert_eq!(ABS_NEG, 10); - assert_eq!(ABS_MIN, i32::min_value()); + assert_eq!(ABS_MIN, i32::MIN); } 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..12ba9cacabf7c 100644 --- a/src/test/ui/consts/const-match-check.eval1.stderr +++ b/src/test/ui/consts/const-match-check.eval1.stderr @@ -1,11 +1,12 @@ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered +error[E0005]: refutable pattern in local binding: `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:25:15 | LL | A = { let 0 = 0; 0 }, - | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered + | ^ patterns `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `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..2eed7abdc6570 100644 --- a/src/test/ui/consts/const-match-check.eval2.stderr +++ b/src/test/ui/consts/const-match-check.eval2.stderr @@ -1,11 +1,12 @@ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered +error[E0005]: refutable pattern in local binding: `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:31:24 | LL | let x: [i32; { let 0 = 0; 0 }] = []; - | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered + | ^ patterns `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `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..1fa0cb17fe66e 100644 --- a/src/test/ui/consts/const-match-check.matchck.stderr +++ b/src/test/ui/consts/const-match-check.matchck.stderr @@ -1,50 +1,54 @@ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered +error[E0005]: refutable pattern in local binding: `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:4:22 | LL | const X: i32 = { let 0 = 0; 0 }; - | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered + | ^ patterns `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `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 }; | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered +error[E0005]: refutable pattern in local binding: `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:8:23 | LL | static Y: i32 = { let 0 = 0; 0 }; - | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered + | ^ patterns `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `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 }; | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered +error[E0005]: refutable pattern in local binding: `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:13:26 | LL | const X: i32 = { let 0 = 0; 0 }; - | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered + | ^ patterns `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `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 }; | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered +error[E0005]: refutable pattern in local binding: `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:19:26 | LL | const X: i32 = { let 0 = 0; 0 }; - | ^ patterns `std::i32::MIN..=-1i32` and `1i32..=std::i32::MAX` not covered + | ^ patterns `i32::MIN..=-1i32` and `1i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `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-multi-ref.rs b/src/test/ui/consts/const-multi-ref.rs index 5e2be0d4f3f02..18645efc88715 100644 --- a/src/test/ui/consts/const-multi-ref.rs +++ b/src/test/ui/consts/const-multi-ref.rs @@ -3,7 +3,7 @@ const _: i32 = { let mut a = 5; - let p = &mut a; //~ ERROR references in constants may only refer to immutable values + let p = &mut a; //~ ERROR mutable references are not allowed in constants let reborrow = {p}; let pp = &reborrow; diff --git a/src/test/ui/consts/const-multi-ref.stderr b/src/test/ui/consts/const-multi-ref.stderr index e01dd4e574738..9a7914b458874 100644 --- a/src/test/ui/consts/const-multi-ref.stderr +++ b/src/test/ui/consts/const-multi-ref.stderr @@ -1,11 +1,8 @@ -error[E0658]: references in constants may only refer to immutable values +error[E0764]: mutable references are not allowed in constants --> $DIR/const-multi-ref.rs:6:13 | LL | let p = &mut a; - | ^^^^^^ constants require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead --> $DIR/const-multi-ref.rs:16:13 @@ -15,5 +12,5 @@ LL | let p = &a; error: aborting due to 2 previous errors -Some errors have detailed explanations: E0492, E0658. +Some errors have detailed explanations: E0492, E0764. For more information about an error, try `rustc --explain E0492`. diff --git a/src/test/ui/consts/const-mut-refs/const_mut_address_of.rs b/src/test/ui/consts/const-mut-refs/const_mut_address_of.rs index 130ba9283b1d9..5819daa817af0 100644 --- a/src/test/ui/consts/const-mut-refs/const_mut_address_of.rs +++ b/src/test/ui/consts/const-mut-refs/const_mut_address_of.rs @@ -1,5 +1,3 @@ -// check-pass - #![feature(const_mut_refs)] #![feature(const_fn)] #![feature(raw_ref_op)] @@ -24,7 +22,9 @@ const fn baz(foo: &mut Foo)-> *mut usize { const _: () = { foo().bar(); + //~^ ERROR mutable references are not allowed in constants baz(&mut foo()); + //~^ ERROR mutable references are not allowed in constants }; fn main() {} diff --git a/src/test/ui/consts/const-mut-refs/const_mut_address_of.stderr b/src/test/ui/consts/const-mut-refs/const_mut_address_of.stderr new file mode 100644 index 0000000000000..2214ce6ee1c87 --- /dev/null +++ b/src/test/ui/consts/const-mut-refs/const_mut_address_of.stderr @@ -0,0 +1,15 @@ +error[E0764]: mutable references are not allowed in constants + --> $DIR/const_mut_address_of.rs:24:5 + | +LL | foo().bar(); + | ^^^^^ `&mut` is only allowed in `const fn` + +error[E0764]: mutable references are not allowed in constants + --> $DIR/const_mut_address_of.rs:26:9 + | +LL | baz(&mut foo()); + | ^^^^^^^^^^ `&mut` is only allowed in `const fn` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0764`. diff --git a/src/test/ui/consts/const-mut-refs/const_mut_refs.rs b/src/test/ui/consts/const-mut-refs/const_mut_refs.rs index 99006a20b1bcb..9099d5a1b8ea6 100644 --- a/src/test/ui/consts/const-mut-refs/const_mut_refs.rs +++ b/src/test/ui/consts/const-mut-refs/const_mut_refs.rs @@ -1,5 +1,3 @@ -// run-pass - #![feature(const_mut_refs)] struct Foo { @@ -31,6 +29,9 @@ const fn bazz(foo: &mut Foo) -> usize { fn main() { let _: [(); foo().bar()] = [(); 1]; + //~^ ERROR mutable references are not allowed in constants let _: [(); baz(&mut foo())] = [(); 2]; + //~^ ERROR mutable references are not allowed in constants let _: [(); bazz(&mut foo())] = [(); 3]; + //~^ ERROR mutable references are not allowed in constants } diff --git a/src/test/ui/consts/const-mut-refs/const_mut_refs.stderr b/src/test/ui/consts/const-mut-refs/const_mut_refs.stderr new file mode 100644 index 0000000000000..4ca7b128b7c4b --- /dev/null +++ b/src/test/ui/consts/const-mut-refs/const_mut_refs.stderr @@ -0,0 +1,21 @@ +error[E0764]: mutable references are not allowed in constants + --> $DIR/const_mut_refs.rs:31:17 + | +LL | let _: [(); foo().bar()] = [(); 1]; + | ^^^^^ `&mut` is only allowed in `const fn` + +error[E0764]: mutable references are not allowed in constants + --> $DIR/const_mut_refs.rs:33:21 + | +LL | let _: [(); baz(&mut foo())] = [(); 2]; + | ^^^^^^^^^^ `&mut` is only allowed in `const fn` + +error[E0764]: mutable references are not allowed in constants + --> $DIR/const_mut_refs.rs:35:22 + | +LL | let _: [(); bazz(&mut foo())] = [(); 3]; + | ^^^^^^^^^^ `&mut` is only allowed in `const fn` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0764`. diff --git a/src/test/ui/consts/const-pattern-irrefutable.rs b/src/test/ui/consts/const-pattern-irrefutable.rs index 60e16aaf89532..65f09eb80098c 100644 --- a/src/test/ui/consts/const-pattern-irrefutable.rs +++ b/src/test/ui/consts/const-pattern-irrefutable.rs @@ -9,8 +9,8 @@ use foo::d; const a: u8 = 2; fn main() { - let a = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX - let c = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX - let d = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX + let a = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=u8::MAX + let c = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=u8::MAX + let d = 4; //~ ERROR refutable pattern in local binding: `0u8..=1u8` and `3u8..=u8::MAX fn f() {} // Check that the `NOTE`s still work with an item here (cf. issue #35115). } diff --git a/src/test/ui/consts/const-pattern-irrefutable.stderr b/src/test/ui/consts/const-pattern-irrefutable.stderr index 4814aa9a5b2ca..bb2fdec72ba0d 100644 --- a/src/test/ui/consts/const-pattern-irrefutable.stderr +++ b/src/test/ui/consts/const-pattern-irrefutable.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=std::u8::MAX` not covered +error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:12:9 | LL | const a: u8 = 2; @@ -9,8 +9,10 @@ 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 +error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:13:9 | LL | pub const b: u8 = 2; @@ -21,8 +23,10 @@ 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 +error[E0005]: refutable pattern in local binding: `0u8..=1u8` and `3u8..=u8::MAX` not covered --> $DIR/const-pattern-irrefutable.rs:14:9 | LL | pub const d: u8 = 2; @@ -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..0aa30665f5907 100644 --- a/src/test/ui/consts/const-size_of-cycle.stderr +++ b/src/test/ui/consts/const-size_of-cycle.stderr @@ -1,34 +1,34 @@ 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::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating `std::mem::size_of`... --> $SRC_DIR/libcore/mem/mod.rs:LL:COL | -LL | intrinsics::size_of::() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub const fn size_of() -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires const-evaluating + checking `std::intrinsics::size_of`... --> $SRC_DIR/libcore/intrinsics.rs:LL:COL | 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 +note: cycle used when checking that `Foo` is well-formed + --> $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_forget.rs b/src/test/ui/consts/const_forget.rs new file mode 100644 index 0000000000000..2dcb72a5a09cb --- /dev/null +++ b/src/test/ui/consts/const_forget.rs @@ -0,0 +1,22 @@ +// check-pass + +#![feature(const_forget)] + +use std::mem::forget; + +const _: () = forget(0i32); +const _: () = forget(Vec::>>::new()); + +// Writing this function signature without const-forget +// triggers compiler errors: +// 1) That we use a non-const fn inside a const fn +// 2) without the forget, it complains about the destructor of Box +// +// FIXME: this method cannot be called in const-eval yet, as Box isn't +// const constructable +#[allow(unused)] +const fn const_forget_box(b: Box) { + forget(b); +} + +fn main() {} 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/issue-73431.rs b/src/test/ui/consts/const_in_pattern/issue-73431.rs new file mode 100644 index 0000000000000..fa18a3af1b09f --- /dev/null +++ b/src/test/ui/consts/const_in_pattern/issue-73431.rs @@ -0,0 +1,29 @@ +// run-pass + +// Regression test for https://github.com/rust-lang/rust/issues/73431. + +pub trait Zero { + const ZERO: Self; +} + +impl Zero for usize { + const ZERO: Self = 0; +} + +impl Zero for Wrapper { + const ZERO: Self = Wrapper(T::ZERO); +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Wrapper(T); + +fn is_zero(x: Wrapper) -> bool { + match x { + Zero::ZERO => true, + _ => false, + } +} + +fn main() { + let _ = is_zero(Wrapper(42)); +} 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.rs b/src/test/ui/consts/const_let_assign3.rs index cbe73923e9c42..f993a427b4899 100644 --- a/src/test/ui/consts/const_let_assign3.rs +++ b/src/test/ui/consts/const_let_assign3.rs @@ -13,14 +13,14 @@ impl S { const FOO: S = { let mut s = S { state: 42 }; - s.foo(3); //~ ERROR references in constants may only refer to immutable values + s.foo(3); //~ ERROR mutable references are not allowed in constants s }; type Array = [u32; { let mut x = 2; let y = &mut x; -//~^ ERROR references in constants may only refer to immutable values +//~^ ERROR mutable references are not allowed in constants *y = 42; //~^ ERROR constant contains unimplemented expression type *y diff --git a/src/test/ui/consts/const_let_assign3.stderr b/src/test/ui/consts/const_let_assign3.stderr index 5e2a85cc03d9f..dd05a4c0bb069 100644 --- a/src/test/ui/consts/const_let_assign3.stderr +++ b/src/test/ui/consts/const_let_assign3.stderr @@ -3,32 +3,30 @@ 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 +error[E0764]: mutable references are not allowed in constants --> $DIR/const_let_assign3.rs:16:5 | LL | s.foo(3); - | ^ constants require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^ `&mut` is only allowed in `const fn` -error[E0658]: references in constants may only refer to immutable values +error[E0764]: mutable references are not allowed in constants --> $DIR/const_let_assign3.rs:22:13 | LL | let y = &mut x; - | ^^^^^^ constants require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error[E0019]: constant contains unimplemented expression type --> $DIR/const_let_assign3.rs:24:5 | LL | *y = 42; | ^^^^^^^ + | + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable error: aborting due to 4 previous errors -Some errors have detailed explanations: E0019, E0658. +Some errors have detailed explanations: E0019, E0764. For more information about an error, try `rustc --explain E0019`. 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/const_limit/const_eval_limit_not_reached.rs b/src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs index 4ed908312fb6d..34abcdf08dae0 100644 --- a/src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs +++ b/src/test/ui/consts/const_limit/const_eval_limit_not_reached.rs @@ -1,15 +1,21 @@ // check-pass + #![feature(const_eval_limit)] -#![const_eval_limit="1000"] +#![feature(const_loop, const_if_match)] -const CONSTANT: usize = limit(); +// This needs to be higher than the number of loop iterations since each pass through the loop may +// hit more than one terminator. +#![const_eval_limit="4000"] -fn main() { - assert_eq!(CONSTANT, 1764); -} +const X: usize = { + let mut x = 0; + while x != 1000 { + x += 1; + } -const fn limit() -> usize { - let x = 42; + x +}; - x * 42 +fn main() { + assert_eq!(X, 1000); } diff --git a/src/test/ui/consts/const_limit/const_eval_limit_reached.rs b/src/test/ui/consts/const_limit/const_eval_limit_reached.rs index d962398d4136e..b45aca0b13e42 100644 --- a/src/test/ui/consts/const_limit/const_eval_limit_reached.rs +++ b/src/test/ui/consts/const_limit/const_eval_limit_reached.rs @@ -1,21 +1,18 @@ -// ignore-tidy-linelength -// only-x86_64 -// check-pass -// NOTE: We always compile this test with -Copt-level=0 because higher opt-levels -// optimize away the const function -// compile-flags:-Copt-level=0 #![feature(const_eval_limit)] -#![const_eval_limit="2"] +#![feature(const_loop, const_if_match)] -const CONSTANT: usize = limit(); -//~^ WARNING Constant evaluating a complex constant, this might take some time +#![const_eval_limit="500"] -fn main() { - assert_eq!(CONSTANT, 1764); -} +const X: usize = { + let mut x = 0; + while x != 1000 { + //~^ ERROR any use of this value will cause an error + x += 1; + } -const fn limit() -> usize { //~ WARNING Constant evaluating a complex constant, this might take some time - let x = 42; + x +}; - x * 42 +fn main() { + assert_eq!(X, 1000); } diff --git a/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr b/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr index e0871ff718561..8c2190b4e591f 100644 --- a/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr +++ b/src/test/ui/consts/const_limit/const_eval_limit_reached.stderr @@ -1,16 +1,20 @@ -warning: Constant evaluating a complex constant, this might take some time - --> $DIR/const_eval_limit_reached.rs:17:1 +error: any use of this value will cause an error + --> $DIR/const_eval_limit_reached.rs:8:5 | -LL | / const fn limit() -> usize { -LL | | let x = 42; -LL | | -LL | | x * 42 -LL | | } - | |_^ - -warning: Constant evaluating a complex constant, this might take some time - --> $DIR/const_eval_limit_reached.rs:10:1 +LL | / const X: usize = { +LL | | let mut x = 0; +LL | | while x != 1000 { + | |_____^ +LL | || +LL | || x += 1; +LL | || } + | ||_____^ exceeded interpreter step limit (see `#[const_eval_limit]`) +LL | | +LL | | x +LL | | }; + | |__- | -LL | const CONSTANT: usize = limit(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(const_err)]` on by default + +error: aborting due to previous error diff --git a/src/test/ui/consts/control-flow/drop-fail.precise.stderr b/src/test/ui/consts/control-flow/drop-fail.precise.stderr new file mode 100644 index 0000000000000..b4b6be8a1e5f0 --- /dev/null +++ b/src/test/ui/consts/control-flow/drop-fail.precise.stderr @@ -0,0 +1,15 @@ +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/drop-fail.rs:10:9 + | +LL | let x = Some(Vec::new()); + | ^ constants cannot evaluate destructors + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/drop-fail.rs:41:9 + | +LL | let mut tmp = None; + | ^^^^^^^ constants cannot evaluate destructors + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0493`. diff --git a/src/test/ui/consts/control-flow/drop-fail.rs b/src/test/ui/consts/control-flow/drop-fail.rs new file mode 100644 index 0000000000000..7bd36726cead5 --- /dev/null +++ b/src/test/ui/consts/control-flow/drop-fail.rs @@ -0,0 +1,64 @@ +// revisions: stock precise + +#![feature(const_if_match)] +#![feature(const_loop)] +#![cfg_attr(precise, feature(const_precise_live_drops))] + +// `x` is *not* always moved into the final value and may be dropped inside the initializer. +const _: Option> = { + let y: Option> = None; + let x = Some(Vec::new()); + //[stock,precise]~^ ERROR destructors cannot be evaluated at compile-time + + if true { + x + } else { + y + } +}; + +// We only clear `NeedsDrop` if a local is moved from in entirely. This is a shortcoming of the +// existing analysis. +const _: Vec = { + let vec_tuple = (Vec::new(),); + //[stock]~^ ERROR destructors cannot be evaluated at compile-time + + vec_tuple.0 +}; + +// This applies to single-field enum variants as well. +const _: Vec = { + let x: Result<_, Vec> = Ok(Vec::new()); + //[stock]~^ ERROR destructors cannot be evaluated at compile-time + + match x { + Ok(x) | Err(x) => x, + } +}; + +const _: Option> = { + let mut some = Some(Vec::new()); + let mut tmp = None; + //[stock,precise]~^ ERROR destructors cannot be evaluated at compile-time + + let mut i = 0; + while i < 10 { + tmp = some; + some = None; + + // We can escape the loop with `Some` still in `tmp`, + // which would require that it be dropped at the end of the block. + if i > 100 { + break; + } + + some = tmp; + tmp = None; + + i += 1; + } + + some +}; + +fn main() {} diff --git a/src/test/ui/consts/control-flow/drop-fail.stock.stderr b/src/test/ui/consts/control-flow/drop-fail.stock.stderr new file mode 100644 index 0000000000000..6a9ea91d20e1f --- /dev/null +++ b/src/test/ui/consts/control-flow/drop-fail.stock.stderr @@ -0,0 +1,39 @@ +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/drop-fail.rs:10:9 + | +LL | let x = Some(Vec::new()); + | ^ constants cannot evaluate destructors +... +LL | }; + | - value is dropped here + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/drop-fail.rs:23:9 + | +LL | let vec_tuple = (Vec::new(),); + | ^^^^^^^^^ constants cannot evaluate destructors +... +LL | }; + | - value is dropped here + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/drop-fail.rs:31:9 + | +LL | let x: Result<_, Vec> = Ok(Vec::new()); + | ^ constants cannot evaluate destructors +... +LL | }; + | - value is dropped here + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/drop-fail.rs:41:9 + | +LL | let mut tmp = None; + | ^^^^^^^ constants cannot evaluate destructors +... +LL | }; + | - value is dropped here + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0493`. diff --git a/src/test/ui/consts/control-flow/drop-failure.rs b/src/test/ui/consts/control-flow/drop-failure.rs deleted file mode 100644 index 9da5546976c75..0000000000000 --- a/src/test/ui/consts/control-flow/drop-failure.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![feature(const_if_match)] -#![feature(const_loop)] - -// `x` is *not* always moved into the final value may be dropped inside the initializer. -const _: Option> = { - let y: Option> = None; - let x = Some(Vec::new()); - //~^ ERROR destructors cannot be evaluated at compile-time - - if true { - x - } else { - y - } -}; - -// We only clear `NeedsDrop` if a local is moved from in entirely. This is a shortcoming of the -// existing analysis. -const _: Vec = { - let vec_tuple = (Vec::new(),); - //~^ ERROR destructors cannot be evaluated at compile-time - - vec_tuple.0 -}; - -// This applies to single-field enum variants as well. -const _: Vec = { - let x: Result<_, Vec> = Ok(Vec::new()); - //~^ ERROR destructors cannot be evaluated at compile-time - - match x { - Ok(x) | Err(x) => x, - } -}; - -const _: Option> = { - let mut some = Some(Vec::new()); - let mut tmp = None; - //~^ ERROR destructors cannot be evaluated at compile-time - - let mut i = 0; - while i < 10 { - tmp = some; - some = None; - - // We can escape the loop with `Some` still in `tmp`, - // which would require that it be dropped at the end of the block. - if i > 100 { - break; - } - - some = tmp; - tmp = None; - - i += 1; - } - - some -}; - -fn main() {} diff --git a/src/test/ui/consts/control-flow/drop-failure.stderr b/src/test/ui/consts/control-flow/drop-failure.stderr deleted file mode 100644 index 3eec3a929a07f..0000000000000 --- a/src/test/ui/consts/control-flow/drop-failure.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0493]: destructors cannot be evaluated at compile-time - --> $DIR/drop-failure.rs:7:9 - | -LL | let x = Some(Vec::new()); - | ^ constants cannot evaluate destructors - -error[E0493]: destructors cannot be evaluated at compile-time - --> $DIR/drop-failure.rs:20:9 - | -LL | let vec_tuple = (Vec::new(),); - | ^^^^^^^^^ constants cannot evaluate destructors - -error[E0493]: destructors cannot be evaluated at compile-time - --> $DIR/drop-failure.rs:28:9 - | -LL | let x: Result<_, Vec> = Ok(Vec::new()); - | ^ constants cannot evaluate destructors - -error[E0493]: destructors cannot be evaluated at compile-time - --> $DIR/drop-failure.rs:38:9 - | -LL | let mut tmp = None; - | ^^^^^^^ constants cannot evaluate destructors - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0493`. diff --git a/src/test/ui/consts/control-flow/drop-pass.rs b/src/test/ui/consts/control-flow/drop-pass.rs new file mode 100644 index 0000000000000..b0afd76c4e6ef --- /dev/null +++ b/src/test/ui/consts/control-flow/drop-pass.rs @@ -0,0 +1,47 @@ +// run-pass +// revisions: stock precise + +#![feature(const_if_match)] +#![feature(const_loop)] +#![cfg_attr(precise, feature(const_precise_live_drops))] + +// `x` is always moved into the final value and is not dropped inside the initializer. +const _: Option> = { + let y: Option> = None; + let x = Some(Vec::new()); + + if true { + x + } else { + x + } +}; + +const _: Option> = { + let x = Some(Vec::new()); + match () { + () => x, + } +}; + +const _: Option> = { + let mut some = Some(Vec::new()); + let mut tmp = None; + + let mut i = 0; + while i < 10 { + tmp = some; + some = None; + + // We can never exit the loop with `Some` in `tmp`. + + some = tmp; + tmp = None; + + i += 1; + } + + some +}; + +fn main() {} diff --git a/src/test/ui/consts/control-flow/drop-precise.rs b/src/test/ui/consts/control-flow/drop-precise.rs new file mode 100644 index 0000000000000..95df76d990554 --- /dev/null +++ b/src/test/ui/consts/control-flow/drop-precise.rs @@ -0,0 +1,20 @@ +// run-pass +// gate-test-const_precise_live_drops + +#![feature(const_if_match)] +#![feature(const_loop)] +#![feature(const_precise_live_drops)] + +const _: Vec = { + let vec_tuple = (Vec::new(),); + vec_tuple.0 +}; + +const _: Vec = { + let x: Result<_, Vec> = Ok(Vec::new()); + match x { + Ok(x) | Err(x) => x, + } +}; + +fn main() {} diff --git a/src/test/ui/consts/control-flow/drop-success.rs b/src/test/ui/consts/control-flow/drop-success.rs deleted file mode 100644 index 185d6b639962b..0000000000000 --- a/src/test/ui/consts/control-flow/drop-success.rs +++ /dev/null @@ -1,45 +0,0 @@ -// run-pass - -#![feature(const_if_match)] -#![feature(const_loop)] - -// `x` is always moved into the final value and is not dropped inside the initializer. -const _: Option> = { - let y: Option> = None; - let x = Some(Vec::new()); - - if true { - x - } else { - x - } -}; - -const _: Option> = { - let x = Some(Vec::new()); - match () { - () => x, - } -}; - -const _: Option> = { - let mut some = Some(Vec::new()); - let mut tmp = None; - - let mut i = 0; - while i < 10 { - tmp = some; - some = None; - - // We can never exit the loop with `Some` in `tmp`. - - some = tmp; - tmp = None; - - i += 1; - } - - some -}; - -fn main() {} 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 bac9f555d271b..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 | | }; - | |__^ type validation failed: 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 4748be37dffcf..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 | | }; - | |__^ type validation failed: 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/enum-discr-type-err.stderr b/src/test/ui/consts/enum-discr-type-err.stderr index 492b79e2e6021..9834a99b79a0e 100644 --- a/src/test/ui/consts/enum-discr-type-err.stderr +++ b/src/test/ui/consts/enum-discr-type-err.stderr @@ -11,10 +11,6 @@ LL | | } | |_- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -help: you can convert an `i32` to `isize` and panic if the converted value wouldn't fit - | -LL | $( $v = $s::V.try_into().unwrap(), )* - | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types --> $DIR/enum-discr-type-err.rs:18:21 @@ -29,10 +25,6 @@ LL | | } | |_- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -help: you can convert an `i32` to `isize` and panic if the converted value wouldn't fit - | -LL | $( $v = $s::V.try_into().unwrap(), )* - | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors 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 2d4c939f47d79..d719bf1b97161 100644 --- a/src/test/ui/consts/issue-52432.rs +++ b/src/test/ui/consts/issue-52432.rs @@ -6,5 +6,5 @@ fn main() { //~| ERROR: type annotations needed [(); &(static || {}) as *const _ as usize]; //~^ ERROR: closures cannot be static - //~| ERROR: evaluation of constant value failed + //~| ERROR evaluation of constant value failed } 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-69310-array-size-lit-wrong-ty.rs b/src/test/ui/consts/issue-69310-array-size-lit-wrong-ty.rs index 98be8c345a9ed..f0d5fea8e0239 100644 --- a/src/test/ui/consts/issue-69310-array-size-lit-wrong-ty.rs +++ b/src/test/ui/consts/issue-69310-array-size-lit-wrong-ty.rs @@ -3,7 +3,7 @@ // we call the query `lit_to_const(input);`. // However, the literal `input.lit` would not be of the type expected by `input.ty`. // As a result, we immediately called `bug!(...)` instead of bubbling up the problem -// so that it could be handled by the caller of `lit_to_const` (`ast_const_to_const`). +// so that it could be handled by the caller of `lit_to_const` (`from_anon_const`). fn main() {} 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/min_const_fn/min_const_fn.stderr b/src/test/ui/consts/min_const_fn/min_const_fn.stderr index 512b343011b40..4b0401ebf9dba 100644 --- a/src/test/ui/consts/min_const_fn/min_const_fn.stderr +++ b/src/test/ui/consts/min_const_fn/min_const_fn.stderr @@ -2,7 +2,9 @@ error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/min_const_fn.rs:37:25 | LL | const fn into_inner(self) -> T { self.0 } - | ^^^^ constant functions cannot evaluate destructors + | ^^^^ - value is dropped here + | | + | constant functions cannot evaluate destructors error[E0723]: mutable references in const fn are unstable --> $DIR/min_const_fn.rs:39:36 @@ -17,7 +19,9 @@ error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/min_const_fn.rs:44:28 | LL | const fn into_inner_lt(self) -> T { self.0 } - | ^^^^ constant functions cannot evaluate destructors + | ^^^^ - value is dropped here + | | + | constant functions cannot evaluate destructors error[E0723]: mutable references in const fn are unstable --> $DIR/min_const_fn.rs:46:42 @@ -32,7 +36,9 @@ error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/min_const_fn.rs:51:27 | LL | const fn into_inner_s(self) -> T { self.0 } - | ^^^^ constant functions cannot evaluate destructors + | ^^^^ - value is dropped here + | | + | constant functions cannot evaluate destructors error[E0723]: mutable references in const fn are unstable --> $DIR/min_const_fn.rs:53:38 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 da00c49963eec..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(); | ^^^^^^^ - | | - | tried to call 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/feature-gate-unleash_the_miri_inside_of_you.stderr b/src/test/ui/consts/miri_unleashed/feature-gate-unleash_the_miri_inside_of_you.stderr index 37016664ac58f..0b6cb2fab46f1 100644 --- a/src/test/ui/consts/miri_unleashed/feature-gate-unleash_the_miri_inside_of_you.stderr +++ b/src/test/ui/consts/miri_unleashed/feature-gate-unleash_the_miri_inside_of_you.stderr @@ -2,7 +2,9 @@ error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/feature-gate-unleash_the_miri_inside_of_you.rs:11:20 | LL | const F: u32 = (U::X, 42).1; - | ^^^^^^^^^^ constants cannot evaluate destructors + | ^^^^^^^^^^ - value is dropped here + | | + | constants cannot evaluate destructors error: aborting due to previous error 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..aa9b3144f401b --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/inline_asm.rs @@ -0,0 +1,22 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +// only-x86_64 +#![feature(asm,llvm_asm)] +#![allow(const_err)] + +fn main() {} + +// Make sure we catch executing inline assembly. +static TEST_BAD1: () = { + 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! +}; + +// Make sure we catch executing inline assembly. +static TEST_BAD2: () = { + unsafe { asm!("nop"); } + //~^ ERROR could not evaluate static initializer + //~| NOTE inline assembly is not supported +}; 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..d372b4a5d25c0 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/inline_asm.stderr @@ -0,0 +1,31 @@ +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) + +error[E0080]: could not evaluate static initializer + --> $DIR/inline_asm.rs:19:14 + | +LL | unsafe { asm!("nop"); } + | ^^^^^^^^^^^^ inline assembly is not supported + +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"); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/inline_asm.rs:19:14 + | +LL | unsafe { asm!("nop"); } + | ^^^^^^^^^^^^ + = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +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/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 86f27784701c6..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 - | | ^^^^^^^^^^^^^^^^^^^^^^^^ tried to modify constant memory -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..65fc49c0b27a6 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.rs @@ -0,0 +1,21 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +#![feature(core_intrinsics)] +#![allow(const_err)] + +// During CTFE, we prevent pointer comparison and pointer-to-int casts. + +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 +}; + +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..21f11dda5a667 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/ptr_arith.stderr @@ -0,0 +1,28 @@ +error[E0080]: could not evaluate static initializer + --> $DIR/ptr_arith.rs:9: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:16:14 + | +LL | let _v = x + 0; + | ^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/ptr_arith.rs:9:14 + | +LL | let _v = x == x; + | ^^^^^^ +help: skipping check that does not even have a feature gate + --> $DIR/ptr_arith.rs:15:20 + | +LL | let x: usize = std::mem::transmute(&0); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +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/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/miri_unleashed/slice_eq.rs b/src/test/ui/consts/miri_unleashed/slice_eq.rs new file mode 100644 index 0000000000000..fd843105daf2a --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/slice_eq.rs @@ -0,0 +1,17 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +// run-pass + +#![feature(const_raw_ptr_comparison)] + +const EMPTY_SLICE: &[i32] = &[]; +const EMPTY_EQ: bool = EMPTY_SLICE.as_ptr().guaranteed_eq(&[] as *const _); +const EMPTY_EQ2: bool = EMPTY_SLICE.as_ptr().guaranteed_ne(&[] as *const _); +const EMPTY_NE: bool = EMPTY_SLICE.as_ptr().guaranteed_ne(&[1] as *const _); +const EMPTY_NE2: bool = EMPTY_SLICE.as_ptr().guaranteed_eq(&[1] as *const _); + +fn main() { + assert!(!EMPTY_EQ); + assert!(!EMPTY_EQ2); + assert!(!EMPTY_NE); + assert!(!EMPTY_NE2); +} diff --git a/src/test/ui/consts/miri_unleashed/tls.rs b/src/test/ui/consts/miri_unleashed/tls.rs new file mode 100644 index 0000000000000..ba86a554bbb68 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/tls.rs @@ -0,0 +1,17 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +#![feature(thread_local)] +#![allow(const_err)] + +use std::thread; + +#[thread_local] +static A: u8 = 0; + +// Make sure we catch accessing thread-local storage. +static TEST_BAD: () = { + unsafe { let _val = A; } + //~^ ERROR could not evaluate static initializer + //~| NOTE cannot access thread local static +}; + +fn main() {} diff --git a/src/test/ui/consts/miri_unleashed/tls.stderr b/src/test/ui/consts/miri_unleashed/tls.stderr new file mode 100644 index 0000000000000..d3e87f319acde --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/tls.stderr @@ -0,0 +1,17 @@ +error[E0080]: could not evaluate static initializer + --> $DIR/tls.rs:12:25 + | +LL | unsafe { let _val = A; } + | ^ cannot access thread local static (DefId(0:4 ~ tls[317d]::A[0])) + +warning: skipping const checks + | +help: skipping check that does not even have a feature gate + --> $DIR/tls.rs:12:25 + | +LL | unsafe { let _val = A; } + | ^ + +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/offset.rs b/src/test/ui/consts/offset.rs new file mode 100644 index 0000000000000..f64242d568e31 --- /dev/null +++ b/src/test/ui/consts/offset.rs @@ -0,0 +1,115 @@ +// run-pass +#![feature(const_ptr_offset)] +#![feature(const_ptr_offset_from)] +#![feature(ptr_offset_from)] +use std::ptr; + +#[repr(C)] +struct Struct { + a: u32, + b: u32, + c: u32, +} +static S: Struct = Struct { a: 0, b: 0, c: 0 }; + +// For these tests we use offset_from to check that two pointers are equal. +// Rust doesn't currently support comparing pointers in const fn. + +static OFFSET_NO_CHANGE: bool = unsafe { + let p1 = &S.b as *const u32; + let p2 = p1.offset(2).offset(-2); + p1.offset_from(p2) == 0 +}; +static OFFSET_MIDDLE: bool = unsafe { + let p1 = (&S.a as *const u32).offset(1); + let p2 = (&S.c as *const u32).offset(-1); + p1.offset_from(p2) == 0 +}; +// Pointing to the end of the allocation is OK +static OFFSET_END: bool = unsafe { + let p1 = (&S.a as *const u32).offset(3); + let p2 = (&S.c as *const u32).offset(1); + p1.offset_from(p2) == 0 +}; +// Casting though a differently sized type is OK +static OFFSET_U8_PTR: bool = unsafe { + let p1 = (&S.a as *const u32 as *const u8).offset(5); + let p2 = (&S.c as *const u32 as *const u8).offset(-3); + p1.offset_from(p2) == 0 +}; +// Any offset with a ZST does nothing +const OFFSET_ZST: bool = unsafe { + let pz = &() as *const (); + // offset_from can't work with ZSTs, so cast to u8 ptr + let p1 = pz.offset(5) as *const u8; + let p2 = pz.offset(isize::MIN) as *const u8; + p1.offset_from(p2) == 0 +}; +const OFFSET_ZERO: bool = unsafe { + let p = [0u8; 0].as_ptr(); + p.offset(0).offset_from(p) == 0 +}; +const OFFSET_ONE: bool = unsafe { + let p = &42u32 as *const u32; + p.offset(1).offset_from(p) == 1 +}; +const OFFSET_DANGLING: bool = unsafe { + let p = ptr::NonNull::::dangling().as_ptr(); + p.offset(0).offset_from(p) == 0 +}; +const OFFSET_UNALIGNED: bool = unsafe { + let arr = [0u8; 32]; + let p1 = arr.as_ptr(); + let p2 = (p1.offset(2) as *const u32).offset(1); + (p2 as *const u8).offset_from(p1) == 6 +}; + +const WRAP_OFFSET_NO_CHANGE: bool = unsafe { + let p1 = &42u32 as *const u32; + let p2 = p1.wrapping_offset(1000).wrapping_offset(-1000); + let p3 = p1.wrapping_offset(-1000).wrapping_offset(1000); + (p1.offset_from(p2) == 0) & (p1.offset_from(p3) == 0) +}; +const WRAP_ADDRESS_SPACE: bool = unsafe { + let p1 = &42u8 as *const u8; + let p2 = p1.wrapping_offset(isize::MIN).wrapping_offset(isize::MIN); + p1.offset_from(p2) == 0 +}; +// Wrap on the count*size_of::() calculation. +const WRAP_SIZE_OF: bool = unsafe { + // Make sure that if p1 moves backwards, we are still in range + let arr = [0u32; 2]; + let p = &arr[1] as *const u32; + // With wrapping arithmetic, isize::MAX * 4 == -4 + let wrapped = p.wrapping_offset(isize::MAX); + let backward = p.wrapping_offset(-1); + wrapped.offset_from(backward) == 0 +}; +const WRAP_INTEGER_POINTER: bool = unsafe { + let p1 = (0x42 as *const u32).wrapping_offset(4); + let p2 = 0x52 as *const u32; + p1.offset_from(p2) == 0 +}; +const WRAP_NULL: bool = unsafe { + let p1 = ptr::null::().wrapping_offset(1); + let p2 = 0x4 as *const u32; + p1.offset_from(p2) == 0 +}; + +fn main() { + assert!(OFFSET_NO_CHANGE); + assert!(OFFSET_MIDDLE); + assert!(OFFSET_END); + assert!(OFFSET_U8_PTR); + assert!(OFFSET_ZST); + assert!(OFFSET_ZERO); + assert!(OFFSET_ONE); + assert!(OFFSET_DANGLING); + assert!(OFFSET_UNALIGNED); + + assert!(WRAP_OFFSET_NO_CHANGE); + assert!(WRAP_ADDRESS_SPACE); + assert!(WRAP_SIZE_OF); + assert!(WRAP_INTEGER_POINTER); + assert!(WRAP_NULL); +} 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 24da983cf0868..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 | | @@ -26,10 +27,11 @@ error: any use of this value will cause an error LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | a memory access tried to interpret some bytes as a pointer - | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:28:14 + | unable to turn bytes into a pointer + | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `NOT_PTR` at $DIR/offset_from_ub.rs:23:14 | - ::: $DIR/offset_from_ub.rs:26:1 + ::: $DIR/offset_from_ub.rs:21:1 | LL | / pub const NOT_PTR: usize = { LL | | @@ -43,10 +45,11 @@ error: any use of this value will cause an error LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | exact_div: 1 cannot be divided by 2 without remainder - | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:36:14 + | exact_div: 1isize cannot be divided by 2isize without remainder + | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `NOT_MULTIPLE_OF_SIZE` at $DIR/offset_from_ub.rs:31:14 | - ::: $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 | | @@ -81,10 +85,11 @@ error: any use of this value will cause an error LL | intrinsics::ptr_offset_from(self, origin) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | - | a memory access tried to interpret some bytes as a pointer - | inside call to `std::ptr::const_ptr::::offset_from` at $DIR/offset_from_ub.rs:49:14 + | unable to turn bytes into a pointer + | inside `std::ptr::const_ptr::::offset_from` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `DIFFERENT_INT` at $DIR/offset_from_ub.rs:44:14 | - ::: $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/offset_ub.rs b/src/test/ui/consts/offset_ub.rs new file mode 100644 index 0000000000000..4f943ed9ad194 --- /dev/null +++ b/src/test/ui/consts/offset_ub.rs @@ -0,0 +1,25 @@ +// ignore-tidy-linelength +#![feature(const_ptr_offset)] +use std::ptr; + +// normalize-stderr-test "alloc\d+" -> "allocN" + +pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; //~NOTE +pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; //~NOTE +pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; //~NOTE + +pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) }; //~NOTE +pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; //~NOTE +pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; //~NOTE +pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; //~NOTE + +pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE +pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; //~NOTE + +// Right now, a zero offset from null is UB +pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; //~NOTE + +// Make sure that we don't panic when computing abs(offset*size_of::()) +pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; //~NOTE + +fn main() {} diff --git a/src/test/ui/consts/offset_ub.stderr b/src/test/ui/consts/offset_ub.stderr new file mode 100644 index 0000000000000..0ab81cc0c5b31 --- /dev/null +++ b/src/test/ui/consts/offset_ub.stderr @@ -0,0 +1,169 @@ +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `BEFORE_START` at $DIR/offset_ub.rs:7:46 + | + ::: $DIR/offset_ub.rs:7:1 + | +LL | pub const BEFORE_START: *const u8 = unsafe { (&0u8 as *const u8).offset(-1) }; + | ------------------------------------------------------------------------------ + | + = note: `#[deny(const_err)]` on by default + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset 2, but is outside bounds of allocN which has size 1 + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `AFTER_END` at $DIR/offset_ub.rs:8:43 + | + ::: $DIR/offset_ub.rs:8:1 + | +LL | pub const AFTER_END: *const u8 = unsafe { (&0u8 as *const u8).offset(2) }; + | -------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset 101, but is outside bounds of allocN which has size 100 + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `AFTER_ARRAY` at $DIR/offset_ub.rs:9:45 + | + ::: $DIR/offset_ub.rs:9:1 + | +LL | pub const AFTER_ARRAY: *const u8 = unsafe { [0u8; 100].as_ptr().offset(101) }; + | ------------------------------------------------------------------------------ + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `OVERFLOW` at $DIR/offset_ub.rs:11:43 + | + ::: $DIR/offset_ub.rs:11:1 + | +LL | pub const OVERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MAX) }; + | ---------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `UNDERFLOW` at $DIR/offset_ub.rs:12:44 + | + ::: $DIR/offset_ub.rs:12:1 + | +LL | pub const UNDERFLOW: *const u16 = unsafe { [0u16; 1].as_ptr().offset(isize::MIN) }; + | ----------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `OVERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:13:56 + | + ::: $DIR/offset_ub.rs:13:1 + | +LL | pub const OVERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (usize::MAX as *const u8).offset(2) }; + | --------------------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | overflowing in-bounds pointer arithmetic + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `UNDERFLOW_ADDRESS_SPACE` at $DIR/offset_ub.rs:14:57 + | + ::: $DIR/offset_ub.rs:14:1 + | +LL | pub const UNDERFLOW_ADDRESS_SPACE: *const u8 = unsafe { (1 as *const u8).offset(-2) }; + | -------------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: pointer must be in-bounds at offset 1, but is outside bounds of allocN which has size 0 + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `ZERO_SIZED_ALLOC` at $DIR/offset_ub.rs:16:50 + | + ::: $DIR/offset_ub.rs:16:1 + | +LL | pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; + | ------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/mut_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) as *mut T + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unable to turn bytes into a pointer + | inside `std::ptr::mut_ptr::::offset` at $SRC_DIR/libcore/ptr/mut_ptr.rs:LL:COL + | inside `DANGLING` at $DIR/offset_ub.rs:17:42 + | + ::: $DIR/offset_ub.rs:17:1 + | +LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; + | --------------------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | inbounds test failed: 0x0 is not a valid pointer + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `NULL_OFFSET_ZERO` at $DIR/offset_ub.rs:20:50 + | + ::: $DIR/offset_ub.rs:20:1 + | +LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; + | ------------------------------------------------------------------------------- + +error: any use of this value will cause an error + --> $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | +LL | intrinsics::offset(self, count) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unable to turn bytes into a pointer + | inside `std::ptr::const_ptr::::offset` at $SRC_DIR/libcore/ptr/const_ptr.rs:LL:COL + | inside `UNDERFLOW_ABS` at $DIR/offset_ub.rs:23:47 + | + ::: $DIR/offset_ub.rs:23:1 + | +LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; + | --------------------------------------------------------------------------------------------- + +error: aborting due to 11 previous errors + 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.mut_refs.stderr b/src/test/ui/consts/projection_qualif.mut_refs.stderr index 0945a23f3b123..fad8f011f75f5 100644 --- a/src/test/ui/consts/projection_qualif.mut_refs.stderr +++ b/src/test/ui/consts/projection_qualif.mut_refs.stderr @@ -1,3 +1,9 @@ +error[E0764]: mutable references are not allowed in constants + --> $DIR/projection_qualif.rs:10:27 + | +LL | let b: *mut u32 = &mut a; + | ^^^^^^ `&mut` is only allowed in `const fn` + error[E0658]: dereferencing raw pointers in constants is unstable --> $DIR/projection_qualif.rs:11:18 | @@ -7,6 +13,7 @@ LL | unsafe { *b = 5; } = note: see issue #51911 for more information = help: add `#![feature(const_raw_ptr_deref)]` to the crate attributes to enable -error: aborting due to previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0658, E0764. +For more information about an error, try `rustc --explain E0658`. diff --git a/src/test/ui/consts/projection_qualif.rs b/src/test/ui/consts/projection_qualif.rs index cfe8e7f03d5e4..7db970cf1379f 100644 --- a/src/test/ui/consts/projection_qualif.rs +++ b/src/test/ui/consts/projection_qualif.rs @@ -7,7 +7,7 @@ use std::cell::Cell; const FOO: &u32 = { let mut a = 42; { - let b: *mut u32 = &mut a; //[stock]~ ERROR may only refer to immutable values + let b: *mut u32 = &mut a; //~ ERROR mutable references are not allowed in constants unsafe { *b = 5; } //~ ERROR dereferencing raw pointers in constants //[stock]~^ contains unimplemented expression } diff --git a/src/test/ui/consts/projection_qualif.stock.stderr b/src/test/ui/consts/projection_qualif.stock.stderr index 75625a4bd1bc3..212f12286455f 100644 --- a/src/test/ui/consts/projection_qualif.stock.stderr +++ b/src/test/ui/consts/projection_qualif.stock.stderr @@ -1,11 +1,8 @@ -error[E0658]: references in constants may only refer to immutable values +error[E0764]: mutable references are not allowed in constants --> $DIR/projection_qualif.rs:10:27 | LL | let b: *mut u32 = &mut a; - | ^^^^^^ constants require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error[E0658]: dereferencing raw pointers in constants is unstable --> $DIR/projection_qualif.rs:11:18 @@ -21,8 +18,10 @@ 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 -Some errors have detailed explanations: E0019, E0658. +Some errors have detailed explanations: E0019, E0658, E0764. For more information about an error, try `rustc --explain E0019`. 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..5faa983ab09f7 --- /dev/null +++ b/src/test/ui/consts/read_from_static_mut_ref.rs @@ -0,0 +1,9 @@ +// We are keeping this test in case we decide to allow mutable references in statics again +#![feature(const_mut_refs)] +#![allow(const_err)] + +static OH_NO: &mut i32 = &mut 42; +//~^ ERROR mutable references are not allowed in statics +fn main() { + assert_eq!(*OH_NO, 42); +} diff --git a/src/test/ui/consts/read_from_static_mut_ref.stderr b/src/test/ui/consts/read_from_static_mut_ref.stderr new file mode 100644 index 0000000000000..c936ac0b7d585 --- /dev/null +++ b/src/test/ui/consts/read_from_static_mut_ref.stderr @@ -0,0 +1,9 @@ +error[E0764]: mutable references are not allowed in statics + --> $DIR/read_from_static_mut_ref.rs:5:26 + | +LL | static OH_NO: &mut i32 = &mut 42; + | ^^^^^^^ `&mut` is only allowed in `const fn` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0764`. 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..9042c6f6be191 --- /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:1 + | +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..9042c6f6be191 --- /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:1 + | +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..36c280ca5c607 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 @@ -1,9 +1,9 @@ -error[E0080]: could not evaluate static initializer - --> $DIR/static_mut_containing_mut_ref2.rs:7:45 +error[E0764]: mutable references are not allowed in statics + --> $DIR/static_mut_containing_mut_ref2.rs:7:46 | 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 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `&mut` is only allowed in `const fn` error: aborting due to previous error -For more information about this error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0764`. diff --git a/src/test/ui/consts/static_mut_containing_mut_ref2.rs b/src/test/ui/consts/static_mut_containing_mut_ref2.rs index 74162fbd54b04..a6bbe8d6ec24c 100644 --- a/src/test/ui/consts/static_mut_containing_mut_ref2.rs +++ b/src/test/ui/consts/static_mut_containing_mut_ref2.rs @@ -5,8 +5,7 @@ static mut STDERR_BUFFER_SPACE: u8 = 0; pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; }; -//[mut_refs]~^ ERROR could not evaluate static initializer -//[stock]~^^ ERROR references in statics may only refer to immutable values +//~^ ERROR mutable references are not allowed in statics //[stock]~| ERROR static contains unimplemented expression type fn main() {} 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..57fb27e642e6f 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 @@ -1,19 +1,18 @@ -error[E0658]: references in statics may only refer to immutable values +error[E0764]: mutable references are not allowed in statics --> $DIR/static_mut_containing_mut_ref2.rs:7:46 | LL | pub static mut STDERR_BUFFER: () = unsafe { *(&mut STDERR_BUFFER_SPACE) = 42; }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ statics require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `&mut` is only allowed in `const fn` error[E0019]: static contains unimplemented expression type --> $DIR/static_mut_containing_mut_ref2.rs:7:45 | 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 -Some errors have detailed explanations: E0019, E0658. +Some errors have detailed explanations: E0019, E0764. For more information about an error, try `rustc --explain E0019`. 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 7a299169bc4e1..3ea5f88f07d1e 100644 --- a/src/test/ui/consts/too_generic_eval_ice.rs +++ b/src/test/ui/consts/too_generic_eval_ice.rs @@ -4,9 +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 + [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 8836de0023c9d..8b29c533bcc93 100644 --- a/src/test/ui/consts/too_generic_eval_ice.stderr +++ b/src/test/ui/consts/too_generic_eval_ice.stderr @@ -1,47 +1,19 @@ -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 +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 `A` - = note: to learn more, visit + = note: this may fail depending on what value the parameter takes -error[E0277]: the size for values of type `B` cannot be known at compilation time - --> $DIR/too_generic_eval_ice.rs:7:13 +error[E0369]: binary operation `==` cannot be applied to type `[{integer}; _]` + --> $DIR/too_generic_eval_ice.rs:7:30 | -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 + | -------------------- ^^ ------ [{integer}; 0] + | | + | [{integer}; _] -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0277, 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/trait_specialization.rs b/src/test/ui/consts/trait_specialization.rs index 8010d2fe1aee9..3adbbb5304634 100644 --- a/src/test/ui/consts/trait_specialization.rs +++ b/src/test/ui/consts/trait_specialization.rs @@ -5,7 +5,7 @@ // Tests that specialization does not cause optimizations running on polymorphic MIR to resolve // to a `default` implementation. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Marker {} diff --git a/src/test/ui/consts/trait_specialization.stderr b/src/test/ui/consts/trait_specialization.stderr new file mode 100644 index 0000000000000..03da7d512e592 --- /dev/null +++ b/src/test/ui/consts/trait_specialization.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/trait_specialization.rs:8:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + 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 296a55ef16076..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 @@ -21,7 +21,7 @@ LL | const ZST: &[u8] = unsafe { std::mem::transmute(1usize) }; | ^^^^^^^^^^^^^^^^^^^ | = note: source type: `usize` (word size) - = note: target type: `&'static [u8]` (2 * word size) + = note: target type: `&[u8]` (2 * word size) error: could not evaluate constant pattern --> $DIR/transmute-size-mismatch-before-typeck.rs:10:9 diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.rs b/src/test/ui/consts/uninhabited-const-issue-61744.rs index 15436f9c1b2cf..55f42d84f9cb0 100644 --- a/src/test/ui/consts/uninhabited-const-issue-61744.rs +++ b/src/test/ui/consts/uninhabited-const-issue-61744.rs @@ -1,11 +1,11 @@ // build-fail pub const unsafe fn fake_type() -> T { - hint_unreachable() + hint_unreachable() //~ ERROR evaluation of constant value failed } pub const unsafe fn hint_unreachable() -> ! { - fake_type() //~ ERROR evaluation of constant value failed + fake_type() } trait Const { diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.stderr b/src/test/ui/consts/uninhabited-const-issue-61744.stderr index 1f3e2cf5b2f58..fc908b2b2225f 100644 --- a/src/test/ui/consts/uninhabited-const-issue-61744.stderr +++ b/src/test/ui/consts/uninhabited-const-issue-61744.stderr @@ -1,79 +1,143 @@ error[E0080]: evaluation of constant value failed - --> $DIR/uninhabited-const-issue-61744.rs:8:5 + --> $DIR/uninhabited-const-issue-61744.rs:4:5 | 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 + | reached the configured maximum number of stack frames + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | inside `fake_type::` at $DIR/uninhabited-const-issue-61744.rs:4:5 + | 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/consts/unstable-const-fn-in-libcore.stderr b/src/test/ui/consts/unstable-const-fn-in-libcore.stderr index a8455cefd01cf..928605356a16e 100644 --- a/src/test/ui/consts/unstable-const-fn-in-libcore.stderr +++ b/src/test/ui/consts/unstable-const-fn-in-libcore.stderr @@ -9,12 +9,18 @@ error[E0493]: destructors cannot be evaluated at compile-time | LL | const fn unwrap_or_else T>(self, f: F) -> T { | ^ constant functions cannot evaluate destructors +... +LL | } + | - value is dropped here error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/unstable-const-fn-in-libcore.rs:19:47 | LL | const fn unwrap_or_else T>(self, f: F) -> T { | ^^^^ constant functions cannot evaluate destructors +... +LL | } + | - value is dropped here error: aborting due to 3 previous errors diff --git a/src/test/ui/copy-a-resource.stderr b/src/test/ui/copy-a-resource.stderr index c95e8d239d2b9..a5c961a061acb 100644 --- a/src/test/ui/copy-a-resource.stderr +++ b/src/test/ui/copy-a-resource.stderr @@ -6,6 +6,14 @@ LL | struct Foo { ... LL | let _y = x.clone(); | ^^^^^ method not found in `Foo` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `std::sync::Arc` here + | the method is available for `std::rc::Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: 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/cycle-projection-based-on-where-clause.stderr b/src/test/ui/cycle-projection-based-on-where-clause.stderr index 59815138e2e36..2c337cc6bf903 100644 --- a/src/test/ui/cycle-projection-based-on-where-clause.stderr +++ b/src/test/ui/cycle-projection-based-on-where-clause.stderr @@ -5,7 +5,7 @@ LL | T : Add | ^^^^^^^ | = note: ...which again requires computing the bounds for type parameter `T`, completing the cycle -note: cycle used when processing `A` +note: cycle used when computing explicit predicates of `A` --> $DIR/cycle-projection-based-on-where-clause.rs:17:19 | LL | T : Add diff --git a/src/test/ui/cycle-trait/cycle-trait-default-type-trait.stderr b/src/test/ui/cycle-trait/cycle-trait-default-type-trait.stderr index 6b38d85302e66..58c458709a839 100644 --- a/src/test/ui/cycle-trait/cycle-trait-default-type-trait.stderr +++ b/src/test/ui/cycle-trait/cycle-trait-default-type-trait.stderr @@ -1,23 +1,23 @@ -error[E0391]: cycle detected when processing `Foo::X` +error[E0391]: cycle detected when computing type of `Foo::X` --> $DIR/cycle-trait-default-type-trait.rs:4:23 | LL | trait Foo> { | ^^^ | - = note: ...which again requires processing `Foo::X`, completing the cycle + = note: ...which again requires computing type of `Foo::X`, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/cycle-trait-default-type-trait.rs:4:1 | LL | trait Foo> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0391]: cycle detected when processing `Foo::X` +error[E0391]: cycle detected when computing type of `Foo::X` --> $DIR/cycle-trait-default-type-trait.rs:4:23 | LL | trait Foo> { | ^^^ | - = note: ...which again requires processing `Foo::X`, completing the cycle + = note: ...which again requires computing type of `Foo::X`, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/cycle-trait-default-type-trait.rs:4:1 | 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/derived-errors/issue-31997-1.stderr b/src/test/ui/derived-errors/issue-31997-1.stderr index 6df748122a2eb..229c5c9e80ff8 100644 --- a/src/test/ui/derived-errors/issue-31997-1.stderr +++ b/src/test/ui/derived-errors/issue-31997-1.stderr @@ -2,7 +2,12 @@ error[E0433]: failed to resolve: use of undeclared type or module `HashMap` --> $DIR/issue-31997-1.rs:20:19 | LL | let mut map = HashMap::new(); - | ^^^^^^^ use of undeclared type or module `HashMap` + | ^^^^^^^ not found in this scope + | +help: consider importing this struct + | +LL | use std::collections::HashMap; + | error: aborting due to previous error 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 c4c85773fbc1d..be446feb847eb 100644 --- a/src/test/ui/derives/derive-assoc-type-not-impl.stderr +++ b/src/test/ui/derives/derive-assoc-type-not-impl.stderr @@ -12,6 +12,14 @@ LL | struct NotClone; ... LL | Bar:: { x: 1 }.clone(); | ^^^^^ method not found in `Bar` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `std::sync::Arc>` here + | the method is available for `std::rc::Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: `NotClone: std::clone::Clone` 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.rs b/src/test/ui/did_you_mean/bad-assoc-ty.rs index 00845a17b116b..e66b432ede20c 100644 --- a/src/test/ui/did_you_mean/bad-assoc-ty.rs +++ b/src/test/ui/did_you_mean/bad-assoc-ty.rs @@ -49,4 +49,36 @@ trait K {} fn foo>(x: X) {} //~^ ERROR the type placeholder `_` is not allowed within types on item signatures +fn bar(_: F) where F: Fn() -> _ {} +//~^ ERROR the type placeholder `_` is not allowed within types on item signatures + +fn baz _>(_: F) {} +//~^ ERROR the type placeholder `_` is not allowed within types on item signatures + +struct L(F) where F: Fn() -> _; +//~^ ERROR the type placeholder `_` is not allowed within types on item signatures +struct M where F: Fn() -> _ { +//~^ ERROR the type placeholder `_` is not allowed within types on item signatures + a: F, +} +enum N where F: Fn() -> _ { +//~^ ERROR the type placeholder `_` is not allowed within types on item signatures + Foo(F), +} + +union O where F: Fn() -> _ { +//~^ ERROR the type placeholder `_` is not allowed within types on item signatures +//~| ERROR unions with non-`Copy` fields are unstable + foo: F, +} + +trait P where F: Fn() -> _ { +//~^ ERROR the type placeholder `_` is not allowed within types on item signatures +} + +trait Q { + fn foo(_: F) where F: Fn() -> _ {} + //~^ ERROR the type placeholder `_` is not allowed within types on item signatures +} + fn main() {} 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 6d5f3d9f14348..c409ea9c6576d 100644 --- a/src/test/ui/did_you_mean/bad-assoc-ty.stderr +++ b/src/test/ui/did_you_mean/bad-assoc-ty.stderr @@ -57,6 +57,19 @@ LL | type J = ty!(u8); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +error[E0658]: unions with non-`Copy` fields are unstable + --> $DIR/bad-assoc-ty.rs:69:1 + | +LL | / union O where F: Fn() -> _ { +LL | | +LL | | +LL | | foo: F, +LL | | } + | |_^ + | + = note: see issue #55149 for more information + = help: add `#![feature(untagged_unions)]` to the crate attributes to enable + error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:1:10 | @@ -129,8 +142,101 @@ LL | fn foo>(x: X) {} | ^ ^ not allowed in type signatures | | | not allowed in type signatures + | +help: use type parameters instead + | +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 + | +LL | fn bar(_: F) where F: Fn() -> _ {} + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn bar(_: F) where F: Fn() -> T {} + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/bad-assoc-ty.rs:55:19 + | +LL | fn baz _>(_: F) {} + | ^ not allowed in type signatures + | +help: use type parameters instead + | +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 + | +LL | struct L(F) where F: Fn() -> _; + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | struct L(F) where F: Fn() -> T; + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/bad-assoc-ty.rs:60:30 + | +LL | struct M where F: Fn() -> _ { + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | struct M where F: Fn() -> T { + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/bad-assoc-ty.rs:64:28 + | +LL | enum N where F: Fn() -> _ { + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | enum N where F: Fn() -> T { + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/bad-assoc-ty.rs:69:29 + | +LL | union O where F: Fn() -> _ { + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | union O where F: Fn() -> T { + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/bad-assoc-ty.rs:75:29 + | +LL | trait P where F: Fn() -> _ { + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | trait P where F: Fn() -> T { + | ^^^ ^ + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/bad-assoc-ty.rs:80:38 + | +LL | fn foo(_: F) where F: Fn() -> _ {} + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL | fn foo(_: F) where F: Fn() -> T {} + | ^^^ ^ -error: aborting due to 20 previous errors +error: aborting due to 29 previous errors -Some errors have detailed explanations: E0121, E0223. +Some errors have detailed explanations: E0121, E0223, E0658. For more information about an error, try `rustc --explain E0121`. 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..c9a6d42b5cc22 100644 --- a/src/test/ui/did_you_mean/recursion_limit.stderr +++ b/src/test/ui/did_you_mean/recursion_limit.stderr @@ -1,13 +1,14 @@ -error[E0275]: overflow evaluating the requirement `J: std::marker::Send` +error[E0275]: overflow evaluating the requirement `K: 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::(); | ^^^^^^^^^^^^ | = help: consider adding a `#![recursion_limit="20"]` attribute to your crate (`recursion_limit`) + = note: required because it appears within the type `J` = note: required because it appears within the type `I` = note: required because it appears within the type `H` = note: required because it appears within the type `G` diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.stderr b/src/test/ui/did_you_mean/recursion_limit_deref.stderr index e8d11530b08aa..8339cc291cf30 100644 --- a/src/test/ui/did_you_mean/recursion_limit_deref.stderr +++ b/src/test/ui/did_you_mean/recursion_limit_deref.stderr @@ -1,4 +1,4 @@ -error[E0055]: reached the recursion limit while auto-dereferencing `I` +error[E0055]: reached the recursion limit while auto-dereferencing `J` --> $DIR/recursion_limit_deref.rs:50:22 | LL | let x: &Bottom = &t; diff --git a/src/test/ui/directory_ownership/macro-expanded-mod.rs b/src/test/ui/directory_ownership/macro-expanded-mod.rs index 376c1a9cd6627..9cb159603a8c5 100644 --- a/src/test/ui/directory_ownership/macro-expanded-mod.rs +++ b/src/test/ui/directory_ownership/macro-expanded-mod.rs @@ -1,7 +1,9 @@ // Test that macro-expanded non-inline modules behave correctly macro_rules! mod_decl { - ($i:ident) => { mod $i; } //~ ERROR Cannot declare a non-inline module inside a block + ($i:ident) => { + mod $i; //~ ERROR Cannot declare a non-inline module inside a block + }; } mod macro_expanded_mod_helper { diff --git a/src/test/ui/directory_ownership/macro-expanded-mod.stderr b/src/test/ui/directory_ownership/macro-expanded-mod.stderr index c7780c869d635..f90419247c92b 100644 --- a/src/test/ui/directory_ownership/macro-expanded-mod.stderr +++ b/src/test/ui/directory_ownership/macro-expanded-mod.stderr @@ -1,8 +1,8 @@ error: Cannot declare a non-inline module inside a block unless it has a path attribute - --> $DIR/macro-expanded-mod.rs:4:25 + --> $DIR/macro-expanded-mod.rs:5:9 | -LL | ($i:ident) => { mod $i; } - | ^^ +LL | mod $i; + | ^^^^^^^ ... LL | mod_decl!(foo); | --------------- in this macro invocation diff --git a/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr b/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr index 46acc7e66d8b8..d034942ca5d4c 100644 --- a/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr +++ b/src/test/ui/directory_ownership/non-inline-mod-restriction.stderr @@ -1,8 +1,8 @@ error: Cannot declare a non-inline module inside a block unless it has a path attribute - --> $DIR/non-inline-mod-restriction.rs:4:9 + --> $DIR/non-inline-mod-restriction.rs:4:5 | LL | mod foo; - | ^^^ + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr index dd0c438f421c6..5df69e4649df5 100644 --- a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr +++ b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr @@ -4,7 +4,7 @@ error[E0320]: overflow while adding drop-check rules for FingerTree LL | let ft = | ^^ | - = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> error[E0320]: overflow while adding drop-check rules for FingerTree --> $DIR/dropck_no_diverge_on_nonregular_1.rs:25:9 @@ -12,7 +12,7 @@ error[E0320]: overflow while adding drop-check rules for FingerTree LL | FingerTree::Single(1); | ^^^^^^^^^^^^^^^^^^^^^ | - = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> error: aborting due to 2 previous errors diff --git a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_2.stderr b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_2.stderr index 769d5aed664f3..d34097d401004 100644 --- a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_2.stderr +++ b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_2.stderr @@ -4,7 +4,7 @@ error[E0320]: overflow while adding drop-check rules for FingerTree LL | let ft = | ^^ | - = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> error[E0320]: overflow while adding drop-check rules for FingerTree --> $DIR/dropck_no_diverge_on_nonregular_2.rs:24:9 @@ -12,7 +12,7 @@ error[E0320]: overflow while adding drop-check rules for FingerTree LL | FingerTree::Single(1); | ^^^^^^^^^^^^^^^^^^^^^ | - = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> error: aborting due to 2 previous errors diff --git a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr index de8afdcc7cdab..1c810df242389 100644 --- a/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr +++ b/src/test/ui/dropck/dropck_no_diverge_on_nonregular_3.stderr @@ -4,7 +4,7 @@ error[E0320]: overflow while adding drop-check rules for std::option::Option>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> error[E0320]: overflow while adding drop-check rules for std::option::Option> --> $DIR/dropck_no_diverge_on_nonregular_3.rs:33:9 @@ -12,7 +12,7 @@ error[E0320]: overflow while adding drop-check rules for std::option::Option); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> error[E0320]: overflow while adding drop-check rules for Wrapper --> $DIR/dropck_no_diverge_on_nonregular_3.rs:33:14 @@ -20,7 +20,7 @@ error[E0320]: overflow while adding drop-check rules for Wrapper LL | Some(Wrapper::Simple::); | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + = note: overflowed on FingerTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> error: aborting due to 3 previous errors diff --git a/src/test/ui/dst/dst-sized-trait-param.stderr b/src/test/ui/dst/dst-sized-trait-param.stderr index 40dc9978f367b..006a334021b14 100644 --- a/src/test/ui/dst/dst-sized-trait-param.stderr +++ b/src/test/ui/dst/dst-sized-trait-param.stderr @@ -1,15 +1,25 @@ 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 | = help: the trait `std::marker::Sized` is not implemented for `[isize]` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | trait Foo : Sized { fn take(self, x: &T) { } } // Note: T is sized + | ^^^^^^^^ 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/editions/edition-keywords-2018-2015-parsing.rs b/src/test/ui/editions/edition-keywords-2018-2015-parsing.rs index dbc0465b08e77..d5ed9fb9a285e 100644 --- a/src/test/ui/editions/edition-keywords-2018-2015-parsing.rs +++ b/src/test/ui/editions/edition-keywords-2018-2015-parsing.rs @@ -1,9 +1,17 @@ // edition:2018 // aux-build:edition-kw-macro-2015.rs +#![feature(async_closure)] + +fn main() {} + #[macro_use] extern crate edition_kw_macro_2015; +mod module { + pub fn r#async() {} +} + pub fn check_async() { let mut async = 1; //~ ERROR expected identifier, found keyword `async` let mut r#async = 1; // OK @@ -17,4 +25,6 @@ pub fn check_async() { if passes_ident!(r#async) == 1 {} // OK module::async(); //~ ERROR expected identifier, found keyword `async` module::r#async(); // OK + + let _recovery_witness: () = 0; //~ ERROR mismatched types } diff --git a/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr b/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr index e12d1a48463d7..28663563c6ccd 100644 --- a/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr +++ b/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr @@ -1,5 +1,5 @@ error: expected identifier, found keyword `async` - --> $DIR/edition-keywords-2018-2015-parsing.rs:8:13 + --> $DIR/edition-keywords-2018-2015-parsing.rs:16:13 | LL | let mut async = 1; | ^^^^^ expected identifier, found keyword @@ -10,7 +10,7 @@ LL | let mut r#async = 1; | ^^^^^^^ error: expected identifier, found keyword `async` - --> $DIR/edition-keywords-2018-2015-parsing.rs:18:13 + --> $DIR/edition-keywords-2018-2015-parsing.rs:26:13 | LL | module::async(); | ^^^^^ expected identifier, found keyword @@ -21,13 +21,13 @@ LL | module::r#async(); | ^^^^^^^ error: no rules expected the token `r#async` - --> $DIR/edition-keywords-2018-2015-parsing.rs:12:31 + --> $DIR/edition-keywords-2018-2015-parsing.rs:20:31 | LL | r#async = consumes_async!(r#async); | ^^^^^^^ no rules expected this token in macro call error: no rules expected the token `async` - --> $DIR/edition-keywords-2018-2015-parsing.rs:13:35 + --> $DIR/edition-keywords-2018-2015-parsing.rs:21:35 | LL | r#async = consumes_async_raw!(async); | ^^^^^ no rules expected this token in macro call @@ -38,10 +38,19 @@ error: macro expansion ends with an incomplete expression: expected one of `move LL | ($i: ident) => ($i) | ^ expected one of `move`, `|`, or `||` | - ::: $DIR/edition-keywords-2018-2015-parsing.rs:16:8 + ::: $DIR/edition-keywords-2018-2015-parsing.rs:24:8 | LL | if passes_ident!(async) == 1 {} | -------------------- in this macro invocation -error: aborting due to 5 previous errors +error[E0308]: mismatched types + --> $DIR/edition-keywords-2018-2015-parsing.rs:29:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 6 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/editions/edition-keywords-2018-2018-parsing.rs b/src/test/ui/editions/edition-keywords-2018-2018-parsing.rs index 5aca0839f0f15..044ab249f2c26 100644 --- a/src/test/ui/editions/edition-keywords-2018-2018-parsing.rs +++ b/src/test/ui/editions/edition-keywords-2018-2018-parsing.rs @@ -1,9 +1,17 @@ // edition:2018 // aux-build:edition-kw-macro-2018.rs +#![feature(async_closure)] + +fn main() {} + #[macro_use] extern crate edition_kw_macro_2018; +mod module { + pub fn r#async() {} +} + pub fn check_async() { let mut async = 1; //~ ERROR expected identifier, found keyword `async` let mut r#async = 1; // OK @@ -17,4 +25,6 @@ pub fn check_async() { if passes_ident!(r#async) == 1 {} // OK module::async(); //~ ERROR expected identifier, found keyword `async` module::r#async(); // OK + + let _recovery_witness: () = 0; //~ ERROR mismatched types } diff --git a/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr b/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr index 110165fc077ca..cda7e65e437e8 100644 --- a/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr +++ b/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr @@ -1,5 +1,5 @@ error: expected identifier, found keyword `async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:8:13 + --> $DIR/edition-keywords-2018-2018-parsing.rs:16:13 | LL | let mut async = 1; | ^^^^^ expected identifier, found keyword @@ -10,7 +10,7 @@ LL | let mut r#async = 1; | ^^^^^^^ error: expected identifier, found keyword `async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:18:13 + --> $DIR/edition-keywords-2018-2018-parsing.rs:26:13 | LL | module::async(); | ^^^^^ expected identifier, found keyword @@ -21,13 +21,13 @@ LL | module::r#async(); | ^^^^^^^ error: no rules expected the token `r#async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:12:31 + --> $DIR/edition-keywords-2018-2018-parsing.rs:20:31 | LL | r#async = consumes_async!(r#async); | ^^^^^^^ no rules expected this token in macro call error: no rules expected the token `async` - --> $DIR/edition-keywords-2018-2018-parsing.rs:13:35 + --> $DIR/edition-keywords-2018-2018-parsing.rs:21:35 | LL | r#async = consumes_async_raw!(async); | ^^^^^ no rules expected this token in macro call @@ -38,10 +38,19 @@ error: macro expansion ends with an incomplete expression: expected one of `move LL | ($i: ident) => ($i) | ^ expected one of `move`, `|`, or `||` | - ::: $DIR/edition-keywords-2018-2018-parsing.rs:16:8 + ::: $DIR/edition-keywords-2018-2018-parsing.rs:24:8 | LL | if passes_ident!(async) == 1 {} | -------------------- in this macro invocation -error: aborting due to 5 previous errors +error[E0308]: mismatched types + --> $DIR/edition-keywords-2018-2018-parsing.rs:29:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 6 previous errors +For more information about this error, try `rustc --explain E0308`. 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..4e2cc89948a01 --- /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 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..eefbc44f585b2 --- /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 as i128 + 1, + SmallNegative = -1, + BigNegative = i128::MIN, + 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 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 as i128 + 1); + discr(Signed::SmallNegative, -1); + discr(Signed::BigNegative, i128::MIN); + discr(Signed::Next, i128::MIN + 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 as u128 + 1); + discr(Unsigned::Next, u64::MAX 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 new file mode 100644 index 0000000000000..f5243077384ac --- /dev/null +++ b/src/test/ui/eprint-on-tls-drop.rs @@ -0,0 +1,49 @@ +// run-pass +// ignore-emscripten no processes +// ignore-sgx no processes + +use std::cell::RefCell; +use std::env; +use std::process::Command; + +fn main() { + let name = "YOU_ARE_THE_TEST"; + if env::var(name).is_ok() { + std::thread::spawn(|| { + TLS.with(|f| f.borrow().ensure()); + }) + .join() + .unwrap(); + } else { + let me = env::current_exe().unwrap(); + let output = Command::new(&me).env(name, "1").output().unwrap(); + println!("{:?}", output); + assert!(output.status.success()); + let stderr = String::from_utf8(output.stderr).unwrap(); + assert!(stderr.contains("hello new\n")); + assert!(stderr.contains("hello drop\n")); + } +} + +struct Stuff { + _x: usize, +} + +impl Stuff { + fn new() -> Self { + eprintln!("hello new"); + Self { _x: 0 } + } + + fn ensure(&self) {} +} + +impl Drop for Stuff { + fn drop(&mut self) { + eprintln!("hello drop"); + } +} + +thread_local! { + static TLS: RefCell = RefCell::new(Stuff::new()); +} diff --git a/src/test/ui/error-codes/E0004-2.stderr b/src/test/ui/error-codes/E0004-2.stderr index f5b41cd1cc0bb..e48bc74d3579c 100644 --- a/src/test/ui/error-codes/E0004-2.stderr +++ b/src/test/ui/error-codes/E0004-2.stderr @@ -3,8 +3,17 @@ error[E0004]: non-exhaustive patterns: `None` and `Some(_)` not covered | LL | match x { } | ^ patterns `None` and `Some(_)` not covered + | + ::: $SRC_DIR/libcore/option.rs:LL:COL + | +LL | None, + | ---- not covered +... +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.stderr b/src/test/ui/error-codes/E0005.stderr index 577c6e886d523..68aff4638c889 100644 --- a/src/test/ui/error-codes/E0005.stderr +++ b/src/test/ui/error-codes/E0005.stderr @@ -3,9 +3,15 @@ error[E0005]: refutable pattern in local binding: `None` not covered | LL | let Some(y) = x; | ^^^^^^^ pattern `None` not covered + | + ::: $SRC_DIR/libcore/option.rs:LL:COL + | +LL | None, + | ---- not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `std::option::Option` 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.rs b/src/test/ui/error-codes/E0017.rs index 64be41170d0c8..818dec1207b96 100644 --- a/src/test/ui/error-codes/E0017.rs +++ b/src/test/ui/error-codes/E0017.rs @@ -2,10 +2,10 @@ static X: i32 = 1; const C: i32 = 2; static mut M: i32 = 3; -const CR: &'static mut i32 = &mut C; //~ ERROR E0658 -static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0658 +const CR: &'static mut i32 = &mut C; //~ ERROR E0764 +static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0764 //~| ERROR E0019 //~| ERROR cannot borrow -static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0658 -static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR E0658 +static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0764 +static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR E0764 fn main() {} diff --git a/src/test/ui/error-codes/E0017.stderr b/src/test/ui/error-codes/E0017.stderr index 2e687c18ed3ff..c1d96de1dca7c 100644 --- a/src/test/ui/error-codes/E0017.stderr +++ b/src/test/ui/error-codes/E0017.stderr @@ -1,26 +1,22 @@ -error[E0658]: references in constants may only refer to immutable values +error[E0764]: mutable references are not allowed in constants --> $DIR/E0017.rs:5:30 | LL | const CR: &'static mut i32 = &mut C; - | ^^^^^^ constants require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error[E0019]: static contains unimplemented expression type --> $DIR/E0017.rs:6:39 | 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 +error[E0764]: mutable references are not allowed in statics --> $DIR/E0017.rs:6:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; - | ^^^^^^ statics require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error[E0596]: cannot borrow immutable static item `X` as mutable --> $DIR/E0017.rs:6:39 @@ -28,25 +24,19 @@ error[E0596]: cannot borrow immutable static item `X` as mutable LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ cannot borrow as mutable -error[E0658]: references in statics may only refer to immutable values +error[E0764]: mutable references are not allowed in statics --> $DIR/E0017.rs:9:38 | LL | static CONST_REF: &'static mut i32 = &mut C; - | ^^^^^^ statics require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` -error[E0658]: references in statics may only refer to immutable values +error[E0764]: mutable references are not allowed in statics --> $DIR/E0017.rs:10:52 | LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; - | ^^^^^^ statics require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error: aborting due to 6 previous errors -Some errors have detailed explanations: E0019, E0596, E0658. +Some errors have detailed explanations: E0019, E0596, E0764. For more information about an error, try `rustc --explain E0019`. diff --git a/src/test/ui/error-codes/E0034.stderr b/src/test/ui/error-codes/E0034.stderr index 6db2ef5051d83..471512ca8f72c 100644 --- a/src/test/ui/error-codes/E0034.stderr +++ b/src/test/ui/error-codes/E0034.stderr @@ -1,8 +1,8 @@ error[E0034]: multiple applicable items in scope - --> $DIR/E0034.rs:20:5 + --> $DIR/E0034.rs:20:11 | LL | Test::foo() - | ^^^^^^^^^ multiple `foo` found + | ^^^ multiple `foo` found | note: candidate #1 is defined in an impl of the trait `Trait1` for the type `Test` --> $DIR/E0034.rs:12:5 @@ -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/E0040.stderr b/src/test/ui/error-codes/E0040.stderr index 966455902817d..69cf28b29704f 100644 --- a/src/test/ui/error-codes/E0040.stderr +++ b/src/test/ui/error-codes/E0040.stderr @@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method --> $DIR/E0040.rs:13:7 | LL | x.drop(); - | ^^^^ explicit destructor calls not allowed + | ^^^^ + | | + | explicit destructor calls not allowed + | help: consider using `drop` function: `drop(x)` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0055.rs b/src/test/ui/error-codes/E0055.rs index b525575d98d46..fd5804bbc2a59 100644 --- a/src/test/ui/error-codes/E0055.rs +++ b/src/test/ui/error-codes/E0055.rs @@ -1,4 +1,4 @@ -#![recursion_limit="5"] +#![recursion_limit="4"] struct Foo; impl Foo { diff --git a/src/test/ui/error-codes/E0055.stderr b/src/test/ui/error-codes/E0055.stderr index 01411e585abdd..1b8c5760e65bf 100644 --- a/src/test/ui/error-codes/E0055.stderr +++ b/src/test/ui/error-codes/E0055.stderr @@ -4,7 +4,7 @@ error[E0055]: reached the recursion limit while auto-dereferencing `Foo` LL | ref_foo.foo(); | ^^^ deref recursion limit reached | - = help: consider adding a `#![recursion_limit="10"]` attribute to your crate (`E0055`) + = help: consider adding a `#![recursion_limit="8"]` attribute to your crate (`E0055`) error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0121.stderr b/src/test/ui/error-codes/E0121.stderr index 5a5c6b40c5afe..ad854837ae5bd 100644 --- a/src/test/ui/error-codes/E0121.stderr +++ b/src/test/ui/error-codes/E0121.stderr @@ -14,7 +14,7 @@ LL | static BAR: _ = "test"; | ^ | | | not allowed in type signatures - | help: replace `_` with the correct type: `&'static str` + | help: replace `_` with the correct type: `&str` error: aborting due to 2 previous errors 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 8faa550a50935..0000000000000 --- a/src/test/ui/error-codes/E0192.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0192]: negative impls are only allowed for auto traits (e.g., `Send` and `Sync`) - --> $DIR/E0192.rs:9:1 - | -LL | impl !Trait for Foo { } - | ^^^^^^^^^^^^^^^^^^^^^^^ - -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..2692fe6945e09 100644 --- a/src/test/ui/error-codes/E0275.stderr +++ b/src/test/ui/error-codes/E0275.stderr @@ -1,13 +1,14 @@ -error[E0275]: overflow evaluating the requirement `Bar>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: Foo` +error[E0275]: overflow evaluating the requirement `Bar>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: Foo` --> $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 {} | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`E0275`) + = note: required because of the requirements on the impl of `Foo` for `Bar>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` = note: required because of the requirements on the impl of `Foo` for `Bar>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` = note: required because of the requirements on the impl of `Foo` for `Bar>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` = note: required because of the requirements on the impl of `Foo` for `Bar>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` 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.stderr b/src/test/ui/error-codes/E0297.stderr index f356a5b954d6d..b2d181b838fda 100644 --- a/src/test/ui/error-codes/E0297.stderr +++ b/src/test/ui/error-codes/E0297.stderr @@ -3,6 +3,13 @@ error[E0005]: refutable pattern in `for` loop binding: `None` not covered | LL | for Some(x) in xs {} | ^^^^^^^ pattern `None` not covered + | + ::: $SRC_DIR/libcore/option.rs:LL:COL + | +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.rs b/src/test/ui/error-codes/E0388.rs index 5954e3490b06c..13131017c2e07 100644 --- a/src/test/ui/error-codes/E0388.rs +++ b/src/test/ui/error-codes/E0388.rs @@ -1,10 +1,10 @@ static X: i32 = 1; const C: i32 = 2; -const CR: &'static mut i32 = &mut C; //~ ERROR E0658 -static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0658 +const CR: &'static mut i32 = &mut C; //~ ERROR E0764 +static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0019 //~| ERROR cannot borrow - //~| ERROR E0019 -static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0658 + //~| ERROR E0764 +static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0764 fn main() {} diff --git a/src/test/ui/error-codes/E0388.stderr b/src/test/ui/error-codes/E0388.stderr index 52822ebdd9e67..f09100bac43ce 100644 --- a/src/test/ui/error-codes/E0388.stderr +++ b/src/test/ui/error-codes/E0388.stderr @@ -1,26 +1,22 @@ -error[E0658]: references in constants may only refer to immutable values +error[E0764]: mutable references are not allowed in constants --> $DIR/E0388.rs:4:30 | LL | const CR: &'static mut i32 = &mut C; - | ^^^^^^ constants require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error[E0019]: static contains unimplemented expression type --> $DIR/E0388.rs:5:39 | 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 +error[E0764]: mutable references are not allowed in statics --> $DIR/E0388.rs:5:39 | LL | static STATIC_REF: &'static mut i32 = &mut X; - | ^^^^^^ statics require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error[E0596]: cannot borrow immutable static item `X` as mutable --> $DIR/E0388.rs:5:39 @@ -28,16 +24,13 @@ error[E0596]: cannot borrow immutable static item `X` as mutable LL | static STATIC_REF: &'static mut i32 = &mut X; | ^^^^^^ cannot borrow as mutable -error[E0658]: references in statics may only refer to immutable values +error[E0764]: mutable references are not allowed in statics --> $DIR/E0388.rs:8:38 | LL | static CONST_REF: &'static mut i32 = &mut C; - | ^^^^^^ statics require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error: aborting due to 5 previous errors -Some errors have detailed explanations: E0019, E0596, E0658. +Some errors have detailed explanations: E0019, E0596, E0764. For more information about an error, try `rustc --explain E0019`. diff --git a/src/test/ui/error-codes/E0395.rs b/src/test/ui/error-codes/E0395.rs index bbefff27d7f68..d2edd97efb232 100644 --- a/src/test/ui/error-codes/E0395.rs +++ b/src/test/ui/error-codes/E0395.rs @@ -1,10 +1,8 @@ -// gate-test-const_compare_raw_pointers - static FOO: i32 = 42; static BAR: i32 = 42; static BAZ: bool = unsafe { (&FOO as *const i32) == (&BAR as *const i32) }; -//~^ ERROR comparing raw pointers inside static +//~^ ERROR pointers cannot be reliably compared during const eval fn main() { } diff --git a/src/test/ui/error-codes/E0395.stderr b/src/test/ui/error-codes/E0395.stderr index 20c8622f33726..674cc69645029 100644 --- a/src/test/ui/error-codes/E0395.stderr +++ b/src/test/ui/error-codes/E0395.stderr @@ -1,12 +1,10 @@ -error[E0658]: comparing raw pointers inside static - --> $DIR/E0395.rs:6:29 +error: pointers cannot be reliably compared during const eval. + --> $DIR/E0395.rs:4:29 | LL | static BAZ: bool = unsafe { (&FOO as *const i32) == (&BAR as *const i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #53020 for more information - = help: add `#![feature(const_compare_raw_pointers)]` 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/error-codes/E0396-fixed.stderr b/src/test/ui/error-codes/E0396-fixed.stderr index 7222f87da248f..685055525627e 100644 --- a/src/test/ui/error-codes/E0396-fixed.stderr +++ b/src/test/ui/error-codes/E0396-fixed.stderr @@ -4,7 +4,7 @@ error: any use of this value will cause an error LL | const VALUE: u8 = unsafe { *REG_ADDR }; | ---------------------------^^^^^^^^^--- | | - | a memory access tried to interpret some bytes as a pointer + | unable to turn bytes into a pointer | = note: `#[deny(const_err)]` on by default diff --git a/src/test/ui/error-codes/E0423.stderr b/src/test/ui/error-codes/E0423.stderr index 09792845d162c..d4860394259b7 100644 --- a/src/test/ui/error-codes/E0423.stderr +++ b/src/test/ui/error-codes/E0423.stderr @@ -29,28 +29,28 @@ LL | for _ in (std::ops::Range { start: 0, end: 10 }) {} error[E0423]: expected function, tuple struct or tuple variant, found struct `Foo` --> $DIR/E0423.rs:4:13 | -LL | struct Foo { a: bool }; - | ---------------------- `Foo` defined here +LL | struct Foo { a: bool }; + | ---------------------- `Foo` defined here LL | -LL | let f = Foo(); - | ^^^ - | | - | did you mean `Foo { /* fields */ }`? - | help: a function with a similar name exists (notice the capitalization): `foo` +LL | let f = Foo(); + | ^^^ + | | + | did you mean `Foo { /* fields */ }`? + | help: a function with a similar name exists (notice the capitalization): `foo` ... -LL | / fn foo() { -LL | | for _ in std::ops::Range { start: 0, end: 10 } {} -LL | | -LL | | } - | |_- similarly named function `foo` defined here +LL | fn foo() { + | -------- similarly named function `foo` defined here 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/E0433.rs b/src/test/ui/error-codes/E0433.rs index 9b54ec8c5cfde..d555e6542632b 100644 --- a/src/test/ui/error-codes/E0433.rs +++ b/src/test/ui/error-codes/E0433.rs @@ -1,3 +1,3 @@ fn main () { - let map = HashMap::new(); //~ ERROR E0433 + let map = NonExistingMap::new(); //~ ERROR E0433 } diff --git a/src/test/ui/error-codes/E0433.stderr b/src/test/ui/error-codes/E0433.stderr index d852e18838442..d9555e1fcf7a8 100644 --- a/src/test/ui/error-codes/E0433.stderr +++ b/src/test/ui/error-codes/E0433.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared type or module `HashMap` +error[E0433]: failed to resolve: use of undeclared type or module `NonExistingMap` --> $DIR/E0433.rs:2:15 | -LL | let map = HashMap::new(); - | ^^^^^^^ use of undeclared type or module `HashMap` +LL | let map = NonExistingMap::new(); + | ^^^^^^^^^^^^^^ use of undeclared type or module `NonExistingMap` error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0451.stderr b/src/test/ui/error-codes/E0451.stderr index 655f3a98d7f09..bb92c23e0f600 100644 --- a/src/test/ui/error-codes/E0451.stderr +++ b/src/test/ui/error-codes/E0451.stderr @@ -2,13 +2,13 @@ error[E0451]: field `b` of struct `bar::Foo` is private --> $DIR/E0451.rs:14:21 | LL | let bar::Foo{a, b} = foo; - | ^ field `b` is private + | ^ private field error[E0451]: field `b` of struct `bar::Foo` is private --> $DIR/E0451.rs:18:29 | LL | let f = bar::Foo{ a: 0, b: 0 }; - | ^^^^ field `b` is private + | ^^^^ private field error: aborting due to 2 previous errors diff --git a/src/test/ui/error-codes/E0490.nll.stderr b/src/test/ui/error-codes/E0490.nll.stderr new file mode 100644 index 0000000000000..a1c33bbcd5f75 --- /dev/null +++ b/src/test/ui/error-codes/E0490.nll.stderr @@ -0,0 +1,28 @@ +error: lifetime may not live long enough + --> $DIR/E0490.rs:2:12 + | +LL | fn f<'a, 'b>(y: &'b ()) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | let x: &'a _ = &y; + | ^^^^^ type annotation requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error[E0597]: `y` does not live long enough + --> $DIR/E0490.rs:2:20 + | +LL | fn f<'a, 'b>(y: &'b ()) { + | -- lifetime `'a` defined here +LL | let x: &'a _ = &y; + | ----- ^^ borrowed value does not live long enough + | | + | type annotation requires that `y` is borrowed for `'a` +... +LL | } + | - `y` dropped here while still borrowed + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/error-codes/E0490.rs b/src/test/ui/error-codes/E0490.rs new file mode 100644 index 0000000000000..36bafa2bd868c --- /dev/null +++ b/src/test/ui/error-codes/E0490.rs @@ -0,0 +1,8 @@ +fn f<'a, 'b>(y: &'b ()) { + let x: &'a _ = &y; + //~^ E0490 + //~| E0495 + //~| E0495 +} + +fn main() {} diff --git a/src/test/ui/error-codes/E0490.stderr b/src/test/ui/error-codes/E0490.stderr new file mode 100644 index 0000000000000..9ba5bc330ea93 --- /dev/null +++ b/src/test/ui/error-codes/E0490.stderr @@ -0,0 +1,76 @@ +error[E0490]: a value of type `&'b ()` is borrowed for too long + --> $DIR/E0490.rs:2:20 + | +LL | let x: &'a _ = &y; + | ^^ + | +note: the type is valid for the lifetime `'a` as defined on the function body at 1:6 + --> $DIR/E0490.rs:1:6 + | +LL | fn f<'a, 'b>(y: &'b ()) { + | ^^ +note: but the borrow lasts for the lifetime `'b` as defined on the function body at 1:10 + --> $DIR/E0490.rs:1:10 + | +LL | fn f<'a, 'b>(y: &'b ()) { + | ^^ + +error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements + --> $DIR/E0490.rs:2:20 + | +LL | let x: &'a _ = &y; + | ^^ + | +note: first, the lifetime cannot outlive the lifetime `'b` as defined on the function body at 1:10... + --> $DIR/E0490.rs:1:10 + | +LL | fn f<'a, 'b>(y: &'b ()) { + | ^^ +note: ...so that the type `&'b ()` is not borrowed for too long + --> $DIR/E0490.rs:2:20 + | +LL | let x: &'a _ = &y; + | ^^ +note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 1:6... + --> $DIR/E0490.rs:1:6 + | +LL | fn f<'a, 'b>(y: &'b ()) { + | ^^ +note: ...so that reference does not outlive borrowed content + --> $DIR/E0490.rs:2:20 + | +LL | let x: &'a _ = &y; + | ^^ + +error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements + --> $DIR/E0490.rs:2:20 + | +LL | let x: &'a _ = &y; + | ^^ + | +note: first, the lifetime cannot outlive the lifetime `'b` as defined on the function body at 1:10... + --> $DIR/E0490.rs:1:10 + | +LL | fn f<'a, 'b>(y: &'b ()) { + | ^^ +note: ...so that the expression is assignable + --> $DIR/E0490.rs:2:20 + | +LL | let x: &'a _ = &y; + | ^^ + = note: expected `&'a &()` + found `&'a &'b ()` +note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 1:6... + --> $DIR/E0490.rs:1:6 + | +LL | fn f<'a, 'b>(y: &'b ()) { + | ^^ +note: ...so that the reference type `&'a &()` does not outlive the data it points at + --> $DIR/E0490.rs:2:12 + | +LL | let x: &'a _ = &y; + | ^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/error-codes/E0520.rs b/src/test/ui/error-codes/E0520.rs index b746ca63590ec..ead78b7ffa2c4 100644 --- a/src/test/ui/error-codes/E0520.rs +++ b/src/test/ui/error-codes/E0520.rs @@ -1,4 +1,5 @@ #![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete trait SpaceLlama { fn fly(&self); diff --git a/src/test/ui/error-codes/E0520.stderr b/src/test/ui/error-codes/E0520.stderr index 72fc85ab1e74b..1041ccee93704 100644 --- a/src/test/ui/error-codes/E0520.stderr +++ b/src/test/ui/error-codes/E0520.stderr @@ -1,5 +1,14 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/E0520.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0520]: `fly` specializes an item from a parent `impl`, but that item is not marked `default` - --> $DIR/E0520.rs:16:5 + --> $DIR/E0520.rs:17:5 | LL | / impl SpaceLlama for T { LL | | fn fly(&self) {} @@ -11,6 +20,6 @@ LL | default fn fly(&self) {} | = note: to specialize, `fly` in the parent `impl` must be marked `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 E0520`. diff --git a/src/test/ui/error-codes/E0583.stderr b/src/test/ui/error-codes/E0583.stderr index ef7a48bc8a48f..dbe700355957b 100644 --- a/src/test/ui/error-codes/E0583.stderr +++ b/src/test/ui/error-codes/E0583.stderr @@ -1,10 +1,10 @@ error[E0583]: file not found for module `module_that_doesnt_exist` - --> $DIR/E0583.rs:1:5 + --> $DIR/E0583.rs:1:1 | LL | mod module_that_doesnt_exist; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: name the file either module_that_doesnt_exist.rs or module_that_doesnt_exist/mod.rs inside the directory "$DIR" + = help: to create the module `module_that_doesnt_exist`, create file "$DIR/module_that_doesnt_exist.rs" error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0603.stderr b/src/test/ui/error-codes/E0603.stderr index 724d04954a3c7..ee902584f56da 100644 --- a/src/test/ui/error-codes/E0603.stderr +++ b/src/test/ui/error-codes/E0603.stderr @@ -2,7 +2,7 @@ error[E0603]: constant `PRIVATE` is private --> $DIR/E0603.rs:6:17 | LL | SomeModule::PRIVATE; - | ^^^^^^^ this constant is private + | ^^^^^^^ private constant | note: the constant `PRIVATE` is defined here --> $DIR/E0603.rs:2:5 diff --git a/src/test/ui/error-codes/E0604.stderr b/src/test/ui/error-codes/E0604.stderr index 5861bdcb7a953..18835310bd5e8 100644 --- a/src/test/ui/error-codes/E0604.stderr +++ b/src/test/ui/error-codes/E0604.stderr @@ -2,7 +2,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/E0604.rs:2:5 | LL | 1u32 as char; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ invalid cast error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0605.stderr b/src/test/ui/error-codes/E0605.stderr index 95e899db8b7e9..f23d2008e0b5f 100644 --- a/src/test/ui/error-codes/E0605.stderr +++ b/src/test/ui/error-codes/E0605.stderr @@ -2,17 +2,13 @@ error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` --> $DIR/E0605.rs:3:5 | LL | x as Vec; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/E0605.rs:6:5 | LL | v as &u8; - | ^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/error-codes/E0615.stderr b/src/test/ui/error-codes/E0615.stderr index 772058719ae04..1bc047dd356e0 100644 --- a/src/test/ui/error-codes/E0615.stderr +++ b/src/test/ui/error-codes/E0615.stderr @@ -2,7 +2,12 @@ error[E0615]: attempted to take value of method `method` on type `Foo` --> $DIR/E0615.rs:11:7 | LL | f.method; - | ^^^^^^ help: use parentheses to call the method: `method()` + | ^^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | f.method(); + | ^^ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0616.stderr b/src/test/ui/error-codes/E0616.stderr index 556e5db10a944..422bf687e7bd4 100644 --- a/src/test/ui/error-codes/E0616.stderr +++ b/src/test/ui/error-codes/E0616.stderr @@ -1,8 +1,8 @@ error[E0616]: field `x` of struct `a::Foo` is private - --> $DIR/E0616.rs:13:5 + --> $DIR/E0616.rs:13:7 | LL | f.x; - | ^^^ + | ^ private field error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.nll.stderr b/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.nll.stderr deleted file mode 100644 index 5140d1a9a7add..0000000000000 --- a/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.nll.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/E0621-does-not-trigger-for-closures.rs:15:45 - | -LL | invoke(&x, |a, b| if a > b { a } else { b }); - | -- ^ returning this value requires that `'1` must outlive `'2` - | || - | |return type of closure is &'2 i32 - | has type `&'1 i32` - -error: aborting due to previous error - diff --git a/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.rs b/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.rs index c58744d386ca5..44f174c0fb76f 100644 --- a/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.rs +++ b/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.rs @@ -1,9 +1,7 @@ -// Test that we give the generic E0495 when one of the free regions is +// Test that we give the generic error when one of the free regions is // bound in a closure (rather than suggesting a change to the signature // of the closure, which is not specified in `foo` but rather in `invoke`). -// FIXME - This might be better as a UI test, but the finer details -// of the error seem to vary on different machines. fn invoke<'a, F>(x: &'a i32, f: F) -> &'a i32 where F: FnOnce(&'a i32, &i32) -> &'a i32 { @@ -12,7 +10,7 @@ where F: FnOnce(&'a i32, &i32) -> &'a i32 } fn foo<'a>(x: &'a i32) { - invoke(&x, |a, b| if a > b { a } else { b }); //~ ERROR E0495 + invoke(&x, |a, b| if a > b { a } else { b }); //~ ERROR lifetime may not live long enough } fn main() {} diff --git a/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.stderr b/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.stderr index feca7f10b706b..b9edeb8346bdc 100644 --- a/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.stderr +++ b/src/test/ui/error-codes/E0621-does-not-trigger-for-closures.stderr @@ -1,30 +1,11 @@ -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements - --> $DIR/E0621-does-not-trigger-for-closures.rs:15:5 +error: lifetime may not live long enough + --> $DIR/E0621-does-not-trigger-for-closures.rs:13:45 | LL | invoke(&x, |a, b| if a > b { a } else { b }); - | ^^^^^^ - | -note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 15:16... - --> $DIR/E0621-does-not-trigger-for-closures.rs:15:16 - | -LL | invoke(&x, |a, b| if a > b { a } else { b }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...so that reference does not outlive borrowed content - --> $DIR/E0621-does-not-trigger-for-closures.rs:15:45 - | -LL | invoke(&x, |a, b| if a > b { a } else { b }); - | ^ -note: but, the lifetime must be valid for the call at 15:5... - --> $DIR/E0621-does-not-trigger-for-closures.rs:15:5 - | -LL | invoke(&x, |a, b| if a > b { a } else { b }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...so type `&i32` of expression is valid during the expression - --> $DIR/E0621-does-not-trigger-for-closures.rs:15:5 - | -LL | invoke(&x, |a, b| if a > b { a } else { b }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | -- ^ returning this value requires that `'1` must outlive `'2` + | || + | |return type of closure is &'2 i32 + | has type `&'1 i32` error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/error-codes/E0624.stderr b/src/test/ui/error-codes/E0624.stderr index 65256c8dd2dd6..1d3336fb181a0 100644 --- a/src/test/ui/error-codes/E0624.stderr +++ b/src/test/ui/error-codes/E0624.stderr @@ -2,7 +2,7 @@ error[E0624]: associated function `method` is private --> $DIR/E0624.rs:11:9 | LL | foo.method(); - | ^^^^^^ + | ^^^^^^ private associated function 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-codes/ex-E0611.stderr b/src/test/ui/error-codes/ex-E0611.stderr index 8bd00a392d4f8..2d22bb395140b 100644 --- a/src/test/ui/error-codes/ex-E0611.stderr +++ b/src/test/ui/error-codes/ex-E0611.stderr @@ -1,8 +1,8 @@ error[E0616]: field `0` of struct `a::Foo` is private - --> $DIR/ex-E0611.rs:11:4 + --> $DIR/ex-E0611.rs:11:6 | LL | y.0; - | ^^^ + | ^ private field error: aborting due to previous error diff --git a/src/test/ui/error-festival.stderr b/src/test/ui/error-festival.stderr index fb5290bf64eb4..905195d4ad963 100644 --- a/src/test/ui/error-festival.stderr +++ b/src/test/ui/error-festival.stderr @@ -8,7 +8,7 @@ error[E0603]: constant `FOO` is private --> $DIR/error-festival.rs:22:10 | LL | foo::FOO; - | ^^^ this constant is private + | ^^^ private constant | note: the constant `FOO` is defined here --> $DIR/error-festival.rs:7:5 @@ -42,15 +42,13 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/error-festival.rs:25:5 | LL | 0u32 as char; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `u8` as `std::vec::Vec` --> $DIR/error-festival.rs:29:5 | LL | x as Vec; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0054]: cannot cast as `bool` --> $DIR/error-festival.rs:33:24 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/explicit/explicit-call-to-dtor.stderr b/src/test/ui/explicit/explicit-call-to-dtor.stderr index cbbe967179ef3..5ebe4ee4b90f8 100644 --- a/src/test/ui/explicit/explicit-call-to-dtor.stderr +++ b/src/test/ui/explicit/explicit-call-to-dtor.stderr @@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method --> $DIR/explicit-call-to-dtor.rs:13:7 | LL | x.drop(); - | ^^^^ explicit destructor calls not allowed + | ^^^^ + | | + | explicit destructor calls not allowed + | help: consider using `drop` function: `drop(x)` error: aborting due to previous error diff --git a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr index 0b302e30b64f3..cd3fb3119a5cf 100644 --- a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr +++ b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr @@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method --> $DIR/explicit-call-to-supertrait-dtor.rs:17:14 | LL | self.drop(); - | ^^^^ explicit destructor calls not allowed + | ^^^^ + | | + | explicit destructor calls not allowed + | help: consider using `drop` function: `drop(self)` error: aborting due to previous error diff --git a/src/test/ui/explore-issue-38412.stderr b/src/test/ui/explore-issue-38412.stderr index 94a2cfe013d19..1855c0b14379d 100644 --- a/src/test/ui/explore-issue-38412.stderr +++ b/src/test/ui/explore-issue-38412.stderr @@ -17,22 +17,22 @@ LL | r.a_unstable_undeclared_pub; = help: add `#![feature(unstable_undeclared)]` to the crate attributes to enable error[E0616]: field `b_crate` of struct `pub_and_stability::Record` is private - --> $DIR/explore-issue-38412.rs:31:5 + --> $DIR/explore-issue-38412.rs:31:7 | LL | r.b_crate; - | ^^^^^^^^^ + | ^^^^^^^ private field error[E0616]: field `c_mod` of struct `pub_and_stability::Record` is private - --> $DIR/explore-issue-38412.rs:32:5 + --> $DIR/explore-issue-38412.rs:32:7 | LL | r.c_mod; - | ^^^^^^^ + | ^^^^^ private field error[E0616]: field `d_priv` of struct `pub_and_stability::Record` is private - --> $DIR/explore-issue-38412.rs:33:5 + --> $DIR/explore-issue-38412.rs:33:7 | LL | r.d_priv; - | ^^^^^^^^ + | ^^^^^^ private field error[E0658]: use of unstable library feature 'unstable_undeclared' --> $DIR/explore-issue-38412.rs:37:5 @@ -44,22 +44,22 @@ LL | t.2; = help: add `#![feature(unstable_undeclared)]` to the crate attributes to enable error[E0616]: field `3` of struct `pub_and_stability::Tuple` is private - --> $DIR/explore-issue-38412.rs:38:5 + --> $DIR/explore-issue-38412.rs:38:7 | LL | t.3; - | ^^^ + | ^ private field error[E0616]: field `4` of struct `pub_and_stability::Tuple` is private - --> $DIR/explore-issue-38412.rs:39:5 + --> $DIR/explore-issue-38412.rs:39:7 | LL | t.4; - | ^^^ + | ^ private field error[E0616]: field `5` of struct `pub_and_stability::Tuple` is private - --> $DIR/explore-issue-38412.rs:40:5 + --> $DIR/explore-issue-38412.rs:40:7 | LL | t.5; - | ^^^ + | ^ private field error[E0658]: use of unstable library feature 'unstable_undeclared' --> $DIR/explore-issue-38412.rs:44:7 @@ -83,19 +83,19 @@ error[E0624]: associated function `pub_crate` is private --> $DIR/explore-issue-38412.rs:50:7 | LL | r.pub_crate(); - | ^^^^^^^^^ + | ^^^^^^^^^ private associated function error[E0624]: associated function `pub_mod` is private --> $DIR/explore-issue-38412.rs:51:7 | LL | r.pub_mod(); - | ^^^^^^^ + | ^^^^^^^ private associated function error[E0624]: associated function `private` is private --> $DIR/explore-issue-38412.rs:52:7 | LL | r.private(); - | ^^^^^^^ + | ^^^^^^^ private associated function error[E0658]: use of unstable library feature 'unstable_undeclared' --> $DIR/explore-issue-38412.rs:57:7 @@ -119,19 +119,19 @@ error[E0624]: associated function `pub_crate` is private --> $DIR/explore-issue-38412.rs:63:7 | LL | t.pub_crate(); - | ^^^^^^^^^ + | ^^^^^^^^^ private associated function error[E0624]: associated function `pub_mod` is private --> $DIR/explore-issue-38412.rs:64:7 | LL | t.pub_mod(); - | ^^^^^^^ + | ^^^^^^^ private associated function error[E0624]: associated function `private` is private --> $DIR/explore-issue-38412.rs:65:7 | LL | t.private(); - | ^^^^^^^ + | ^^^^^^^ private associated function error: aborting due to 19 previous errors diff --git a/src/test/ui/export-import.stderr b/src/test/ui/export-import.stderr index 8160775ab589e..753424c7f88b8 100644 --- a/src/test/ui/export-import.stderr +++ b/src/test/ui/export-import.stderr @@ -2,7 +2,7 @@ error[E0603]: function `unexported` is private --> $DIR/export-import.rs:1:8 | LL | use m::unexported; - | ^^^^^^^^^^ this function is private + | ^^^^^^^^^^ private function | note: the function `unexported` is defined here --> $DIR/export-import.rs:7:5 diff --git a/src/test/ui/export-tag-variant.stderr b/src/test/ui/export-tag-variant.stderr index f4537a2fb6fae..f73bd454d3592 100644 --- a/src/test/ui/export-tag-variant.stderr +++ b/src/test/ui/export-tag-variant.stderr @@ -2,7 +2,7 @@ error[E0603]: enum `Y` is private --> $DIR/export-tag-variant.rs:7:26 | LL | fn main() { let z = foo::Y::Y1; } - | ^ this enum is private + | ^ private enum | note: the enum `Y` is defined here --> $DIR/export-tag-variant.rs:4:5 diff --git a/src/test/ui/export.stderr b/src/test/ui/export.stderr index 107f531c09a3a..23c29b31c6a8e 100644 --- a/src/test/ui/export.stderr +++ b/src/test/ui/export.stderr @@ -26,7 +26,7 @@ error[E0603]: function `z` is private --> $DIR/export.rs:10:18 | LL | fn main() { foo::z(10); } - | ^ this function is private + | ^ private function | note: the function `z` is defined here --> $DIR/export.rs:5:5 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/extenv/extenv-not-defined-custom.stderr b/src/test/ui/extenv/extenv-not-defined-custom.stderr index 523982dd0196b..56415fd1f0dd0 100644 --- a/src/test/ui/extenv/extenv-not-defined-custom.stderr +++ b/src/test/ui/extenv/extenv-not-defined-custom.stderr @@ -3,6 +3,8 @@ error: my error message | LL | fn main() { env!("__HOPEFULLY_NOT_DEFINED__", "my error message"); } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/extenv/extenv-not-defined-default.stderr b/src/test/ui/extenv/extenv-not-defined-default.stderr index 4bfe330f59235..1a9332c4f1c9f 100644 --- a/src/test/ui/extenv/extenv-not-defined-default.stderr +++ b/src/test/ui/extenv/extenv-not-defined-default.stderr @@ -3,6 +3,8 @@ error: environment variable `__HOPEFULLY_NOT_DEFINED__` not defined | LL | env!("__HOPEFULLY_NOT_DEFINED__"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/extenv/issue-55897.stderr b/src/test/ui/extenv/issue-55897.stderr index c57a467cdba56..b62f06e33e531 100644 --- a/src/test/ui/extenv/issue-55897.stderr +++ b/src/test/ui/extenv/issue-55897.stderr @@ -3,6 +3,8 @@ error: environment variable `NON_EXISTENT` not defined | LL | include!(concat!(env!("NON_EXISTENT"), "/data.rs")); | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: suffixes on a string literal are invalid --> $DIR/issue-55897.rs:16:22 diff --git a/src/test/ui/extern/extern-crate-visibility.stderr b/src/test/ui/extern/extern-crate-visibility.stderr index d0c073d67a4ee..9eeb83ae1a73f 100644 --- a/src/test/ui/extern/extern-crate-visibility.stderr +++ b/src/test/ui/extern/extern-crate-visibility.stderr @@ -2,7 +2,7 @@ error[E0603]: crate import `core` is private --> $DIR/extern-crate-visibility.rs:6:10 | LL | use foo::core::cell; - | ^^^^ this crate import is private + | ^^^^ private crate import | note: the crate import `core` is defined here --> $DIR/extern-crate-visibility.rs:2:5 @@ -14,7 +14,7 @@ error[E0603]: crate import `core` is private --> $DIR/extern-crate-visibility.rs:9:10 | LL | foo::core::cell::Cell::new(0); - | ^^^^ this crate import is private + | ^^^^ private crate import | note: the crate import `core` is defined here --> $DIR/extern-crate-visibility.rs:2:5 diff --git a/src/test/ui/extern/extern-methods.rs b/src/test/ui/extern/extern-methods.rs index b142ec59e8818..3c3e229104e0e 100644 --- a/src/test/ui/extern/extern-methods.rs +++ b/src/test/ui/extern/extern-methods.rs @@ -1,6 +1,7 @@ // run-pass // ignore-arm // ignore-aarch64 +// ignore-riscv64 fastcall isn't supported trait A { extern "fastcall" fn test1(i: i32); diff --git a/src/test/ui/extern/extern-thiscall.rs b/src/test/ui/extern/extern-thiscall.rs index e556c0512e9f9..c6ff8a43204de 100644 --- a/src/test/ui/extern/extern-thiscall.rs +++ b/src/test/ui/extern/extern-thiscall.rs @@ -1,6 +1,7 @@ // run-pass // ignore-arm // ignore-aarch64 +// ignore-riscv64 thiscall isn't supported #![feature(abi_thiscall)] 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 0c9165fd9585d..0c7995fde3273 100644 --- a/src/test/ui/extern/extern-types-unsized.stderr +++ b/src/test/ui/extern/extern-types-unsized.stderr @@ -2,21 +2,23 @@ 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() { } - | ------------ -- help: consider relaxing the implicit `Sized` restriction: `: ?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 | = help: the trait `std::marker::Sized` is not implemented for `A` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | fn assert_sized() { } + | ^^^^^^^^ error[E0277]: the size for values of type `A` cannot be known at compilation time --> $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 @@ -24,12 +26,16 @@ LL | assert_sized::(); = help: within `Foo`, the trait `std::marker::Sized` is not implemented for `A` = note: to learn more, visit = note: required because it appears within the type `Foo` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn assert_sized() { } + | ^^^^^^^^ error[E0277]: the size for values of type `A` cannot be known at compilation time --> $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 @@ -37,12 +43,16 @@ LL | assert_sized::>(); = help: within `Bar`, the trait `std::marker::Sized` is not implemented for `A` = note: to learn more, visit = note: required because it appears within the type `Bar` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn assert_sized() { } + | ^^^^^^^^ error[E0277]: the size for values of type `A` cannot be known at compilation time --> $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 @@ -51,6 +61,10 @@ LL | assert_sized::>>(); = note: to learn more, visit = note: required because it appears within the type `Bar` = note: required because it appears within the type `Bar>` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn assert_sized() { } + | ^^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/ui/extern/extern-vectorcall.rs b/src/test/ui/extern/extern-vectorcall.rs index 1427a8f55cb0c..da50c3fb927a4 100644 --- a/src/test/ui/extern/extern-vectorcall.rs +++ b/src/test/ui/extern/extern-vectorcall.rs @@ -1,6 +1,7 @@ // run-pass // ignore-arm // ignore-aarch64 +// ignore-riscv64 vectorcall isn't supported #![feature(abi_vectorcall)] 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/fat-ptr-cast.stderr b/src/test/ui/fat-ptr-cast.stderr index 93e1471838f72..56d5a26beb04e 100644 --- a/src/test/ui/fat-ptr-cast.stderr +++ b/src/test/ui/fat-ptr-cast.stderr @@ -34,9 +34,7 @@ error[E0605]: non-primitive cast: `std::boxed::Box<[i32]>` as `usize` --> $DIR/fat-ptr-cast.rs:14:5 | LL | b as usize; - | ^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0606]: casting `*const [i32]` as `usize` is invalid --> $DIR/fat-ptr-cast.rs:15:5 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-abi-avr-interrupt.rs b/src/test/ui/feature-gates/feature-gate-abi-avr-interrupt.rs new file mode 100644 index 0000000000000..0d7df8182c458 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-abi-avr-interrupt.rs @@ -0,0 +1,9 @@ +// Test that the AVR interrupt ABI cannot be used when avr_interrupt +// feature gate is not used. + +extern "avr-interrupt" fn foo() {} +//~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change + +fn main() { + foo(); +} diff --git a/src/test/ui/feature-gates/feature-gate-abi-avr-interrupt.stderr b/src/test/ui/feature-gates/feature-gate-abi-avr-interrupt.stderr new file mode 100644 index 0000000000000..be7040e1491fe --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-abi-avr-interrupt.stderr @@ -0,0 +1,12 @@ +error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change + --> $DIR/feature-gate-abi-avr-interrupt.rs:4:8 + | +LL | extern "avr-interrupt" fn foo() {} + | ^^^^^^^^^^^^^^^ + | + = note: see issue #69664 for more information + = help: add `#![feature(abi_avr_interrupt)]` 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-abi-msp430-interrupt.rs b/src/test/ui/feature-gates/feature-gate-abi-msp430-interrupt.rs index 37c39b6384dff..440570c54943a 100644 --- a/src/test/ui/feature-gates/feature-gate-abi-msp430-interrupt.rs +++ b/src/test/ui/feature-gates/feature-gate-abi-msp430-interrupt.rs @@ -1,6 +1,8 @@ // Test that the MSP430 interrupt ABI cannot be used when msp430_interrupt // feature gate is not used. +// ignore-riscv64 msp430 is not supported + extern "msp430-interrupt" fn foo() {} //~^ ERROR msp430-interrupt ABI is experimental and subject to change diff --git a/src/test/ui/feature-gates/feature-gate-abi-msp430-interrupt.stderr b/src/test/ui/feature-gates/feature-gate-abi-msp430-interrupt.stderr index 493b57f4303cb..554226bd2b962 100644 --- a/src/test/ui/feature-gates/feature-gate-abi-msp430-interrupt.stderr +++ b/src/test/ui/feature-gates/feature-gate-abi-msp430-interrupt.stderr @@ -1,5 +1,5 @@ error[E0658]: msp430-interrupt ABI is experimental and subject to change - --> $DIR/feature-gate-abi-msp430-interrupt.rs:4:8 + --> $DIR/feature-gate-abi-msp430-interrupt.rs:6:8 | LL | extern "msp430-interrupt" fn foo() {} | ^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/feature-gates/feature-gate-asm.rs b/src/test/ui/feature-gates/feature-gate-asm.rs index 1fce279c9ef3d..753e924f00495 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 prefer using the new asm! syntax instead } } diff --git a/src/test/ui/feature-gates/feature-gate-asm.stderr b/src/test/ui/feature-gates/feature-gate-asm.stderr index 265d38f83f540..d770565b0991c 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': prefer using the new asm! syntax 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..e9349acb64394 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 prefer using the new asm! syntax instead } } diff --git a/src/test/ui/feature-gates/feature-gate-asm2.stderr b/src/test/ui/feature-gates/feature-gate-asm2.stderr index 7ea7bdac44183..85278c98d77e9 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': prefer using the new asm! syntax 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-associated_type_bounds.rs b/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs index 0faa9090f4ebc..9bce274027ee0 100644 --- a/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs +++ b/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs @@ -1,3 +1,6 @@ +// compile-flags: -Zsave-analysis +// This is also a regression test for #69415 and the above flag is needed. + #![feature(untagged_unions)] trait Tr1 { type As1: Copy; } diff --git a/src/test/ui/feature-gates/feature-gate-associated_type_bounds.stderr b/src/test/ui/feature-gates/feature-gate-associated_type_bounds.stderr index bfa59d83c82fa..7f2704e1bc371 100644 --- a/src/test/ui/feature-gates/feature-gate-associated_type_bounds.stderr +++ b/src/test/ui/feature-gates/feature-gate-associated_type_bounds.stderr @@ -1,5 +1,5 @@ error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:12:22 + --> $DIR/feature-gate-associated_type_bounds.rs:15:22 | LL | type A: Iterator; | ^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | type A: Iterator; = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:15:22 + --> $DIR/feature-gate-associated_type_bounds.rs:18:22 | LL | type B: Iterator; | ^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | type B: Iterator; = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:19:20 + --> $DIR/feature-gate-associated_type_bounds.rs:22:20 | LL | struct _St1> { | ^^^^^^^^ @@ -26,7 +26,7 @@ LL | struct _St1> { = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:26:18 + --> $DIR/feature-gate-associated_type_bounds.rs:29:18 | LL | enum _En1> { | ^^^^^^^^ @@ -35,7 +35,7 @@ LL | enum _En1> { = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:33:19 + --> $DIR/feature-gate-associated_type_bounds.rs:36:19 | LL | union _Un1> { | ^^^^^^^^ @@ -44,7 +44,7 @@ LL | union _Un1> { = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:40:37 + --> $DIR/feature-gate-associated_type_bounds.rs:43:37 | LL | type _TaWhere1 where T: Iterator = T; | ^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | type _TaWhere1 where T: Iterator = T; = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:43:22 + --> $DIR/feature-gate-associated_type_bounds.rs:46:22 | LL | fn _apit(_: impl Tr1) {} | ^^^^^^^^^ @@ -62,7 +62,7 @@ LL | fn _apit(_: impl Tr1) {} = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:45:26 + --> $DIR/feature-gate-associated_type_bounds.rs:48:26 | LL | fn _apit_dyn(_: &dyn Tr1) {} | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | fn _apit_dyn(_: &dyn Tr1) {} = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:48:24 + --> $DIR/feature-gate-associated_type_bounds.rs:51:24 | LL | fn _rpit() -> impl Tr1 { S1 } | ^^^^^^^^^ @@ -80,7 +80,7 @@ LL | fn _rpit() -> impl Tr1 { S1 } = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:51:31 + --> $DIR/feature-gate-associated_type_bounds.rs:54:31 | LL | fn _rpit_dyn() -> Box> { Box::new(S1) } | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | fn _rpit_dyn() -> Box> { Box::new(S1) } = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:54:23 + --> $DIR/feature-gate-associated_type_bounds.rs:57:23 | LL | const _cdef: impl Tr1 = S1; | ^^^^^^^^^ @@ -98,7 +98,7 @@ LL | const _cdef: impl Tr1 = S1; = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:60:24 + --> $DIR/feature-gate-associated_type_bounds.rs:63:24 | LL | static _sdef: impl Tr1 = S1; | ^^^^^^^^^ @@ -107,7 +107,7 @@ LL | static _sdef: impl Tr1 = S1; = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0658]: associated type bounds are unstable - --> $DIR/feature-gate-associated_type_bounds.rs:67:21 + --> $DIR/feature-gate-associated_type_bounds.rs:70:21 | LL | let _: impl Tr1 = S1; | ^^^^^^^^^ @@ -116,7 +116,7 @@ LL | let _: impl Tr1 = S1; = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-associated_type_bounds.rs:54:14 + --> $DIR/feature-gate-associated_type_bounds.rs:57:14 | LL | const _cdef: impl Tr1 = S1; | ^^^^^^^^^^^^^^^^^^^ @@ -124,7 +124,7 @@ LL | const _cdef: impl Tr1 = S1; = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-associated_type_bounds.rs:60:15 + --> $DIR/feature-gate-associated_type_bounds.rs:63:15 | LL | static _sdef: impl Tr1 = S1; | ^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | static _sdef: impl Tr1 = S1; = help: add `#![feature(impl_trait_in_bindings)]` to the crate attributes to enable error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-associated_type_bounds.rs:67:12 + --> $DIR/feature-gate-associated_type_bounds.rs:70:12 | LL | let _: impl Tr1 = S1; | ^^^^^^^^^^^^^^^^^^^ 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_generics-ptr.rs b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.rs index 1ab11ce3b4423..dc602ba7e6f21 100644 --- a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.rs +++ b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.rs @@ -1,9 +1,9 @@ struct ConstFn; //~^ ERROR const generics are unstable -//~^^ ERROR using function pointers as const generic parameters is unstable +//~^^ ERROR using function pointers as const generic parameters is forbidden struct ConstPtr; //~^ ERROR const generics are unstable -//~^^ ERROR using raw pointers as const generic parameters is unstable +//~^^ ERROR using raw pointers as const generic parameters is forbidden fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr index dc7ef55e7ab99..b2c96d3810f98 100644 --- a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr +++ b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr @@ -16,23 +16,17 @@ LL | struct ConstPtr; = note: see issue #44580 for more information = help: add `#![feature(const_generics)]` to the crate attributes to enable -error[E0658]: using function pointers as const generic parameters is unstable +error: using function pointers as const generic parameters is forbidden --> $DIR/feature-gate-const_generics-ptr.rs:1:25 | LL | struct ConstFn; | ^^^^ - | - = note: see issue #53020 for more information - = help: add `#![feature(const_compare_raw_pointers)]` to the crate attributes to enable -error[E0658]: using raw pointers as const generic parameters is unstable +error: using raw pointers as const generic parameters is forbidden --> $DIR/feature-gate-const_generics-ptr.rs:5:26 | LL | struct ConstPtr; | ^^^^^^^^^^ - | - = note: see issue #53020 for more information - = help: add `#![feature(const_compare_raw_pointers)]` to the crate attributes to enable error: aborting due to 4 previous errors 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.stderr b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr index 08c36cece4cf9..823dad2c95e0e 100644 --- a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr +++ b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr @@ -3,9 +3,15 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered | LL | let Ok(_x) = foo(); | ^^^^^^ pattern `Err(_)` not covered + | + ::: $SRC_DIR/libcore/result.rs:LL:COL + | +LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), + | --- not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `std::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-generic_associated_types.rs b/src/test/ui/feature-gates/feature-gate-generic_associated_types.rs index 7ff348aca7cc1..17548d7b9e88c 100644 --- a/src/test/ui/feature-gates/feature-gate-generic_associated_types.rs +++ b/src/test/ui/feature-gates/feature-gate-generic_associated_types.rs @@ -3,11 +3,9 @@ use std::ops::Deref; trait PointerFamily { type Pointer: Deref; //~^ ERROR generic associated types are unstable - //~| ERROR type-generic associated types are not yet implemented type Pointer2: Deref where T: Clone, U: Clone; //~^ ERROR generic associated types are unstable //~| ERROR where clauses on associated types are unstable - //~| ERROR type-generic associated types are not yet implemented } struct Foo; diff --git a/src/test/ui/feature-gates/feature-gate-generic_associated_types.stderr b/src/test/ui/feature-gates/feature-gate-generic_associated_types.stderr index a0e06cb2b6795..8499b1ab70f5d 100644 --- a/src/test/ui/feature-gates/feature-gate-generic_associated_types.stderr +++ b/src/test/ui/feature-gates/feature-gate-generic_associated_types.stderr @@ -8,7 +8,7 @@ LL | type Pointer: Deref; = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable error[E0658]: generic associated types are unstable - --> $DIR/feature-gate-generic_associated_types.rs:7:5 + --> $DIR/feature-gate-generic_associated_types.rs:6:5 | LL | type Pointer2: Deref where T: Clone, U: Clone; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | type Pointer2: Deref where T: Clone, U: Clone; = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable error[E0658]: where clauses on associated types are unstable - --> $DIR/feature-gate-generic_associated_types.rs:7:5 + --> $DIR/feature-gate-generic_associated_types.rs:6:5 | LL | type Pointer2: Deref where T: Clone, U: Clone; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | type Pointer2: Deref where T: Clone, U: Clone; = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable error[E0658]: generic associated types are unstable - --> $DIR/feature-gate-generic_associated_types.rs:16:5 + --> $DIR/feature-gate-generic_associated_types.rs:14:5 | LL | type Pointer = Box; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | type Pointer = Box; = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable error[E0658]: generic associated types are unstable - --> $DIR/feature-gate-generic_associated_types.rs:18:5 + --> $DIR/feature-gate-generic_associated_types.rs:16:5 | LL | type Pointer2 = Box; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | type Pointer2 = Box; = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable error[E0658]: where clauses on associated types are unstable - --> $DIR/feature-gate-generic_associated_types.rs:23:5 + --> $DIR/feature-gate-generic_associated_types.rs:21:5 | LL | type Assoc where Self: Sized; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | type Assoc where Self: Sized; = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable error[E0658]: where clauses on associated types are unstable - --> $DIR/feature-gate-generic_associated_types.rs:28:5 + --> $DIR/feature-gate-generic_associated_types.rs:26:5 | LL | type Assoc where Self: Sized = Foo; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,22 +61,6 @@ LL | type Assoc where Self: Sized = Foo; = note: see issue #44265 for more information = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable -error: type-generic associated types are not yet implemented - --> $DIR/feature-gate-generic_associated_types.rs:4:5 - | -LL | type Pointer: Deref; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: type-generic associated types are not yet implemented - --> $DIR/feature-gate-generic_associated_types.rs:7:5 - | -LL | type Pointer2: Deref where T: Clone, U: Clone; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-infer_static_outlives_requirements.stderr b/src/test/ui/feature-gates/feature-gate-infer_static_outlives_requirements.stderr index fbc4e8abc42fd..2beeba8184a7d 100644 --- a/src/test/ui/feature-gates/feature-gate-infer_static_outlives_requirements.stderr +++ b/src/test/ui/feature-gates/feature-gate-infer_static_outlives_requirements.stderr @@ -4,13 +4,7 @@ error[E0310]: the parameter type `U` may not live long enough LL | struct Foo { | - help: consider adding an explicit lifetime bound...: `U: 'static` LL | bar: Bar - | ^^^^^^^^^^^ - | -note: ...so that the type `U` will meet its required lifetime bounds - --> $DIR/feature-gate-infer_static_outlives_requirements.rs:5:5 - | -LL | bar: Bar - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ ...so that the type `U` will meet its required lifetime bounds error: aborting due to previous error 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-type_alias_impl_trait.rs b/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.rs index 6088331cded77..3b6c9791722bb 100644 --- a/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.rs +++ b/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.rs @@ -9,10 +9,14 @@ trait Bar { impl Bar for () { type Baa = impl Debug; //~ ERROR `impl Trait` in type aliases is unstable - fn define() -> Self::Baa { 0 } + fn define() -> Self::Baa { + 0 + } } -fn define() -> Foo { 0 } +fn define() -> Foo { + 0 +} trait TraitWithDefault { type Assoc = impl Debug; @@ -26,20 +30,20 @@ type NestedFree = (Vec, impl Debug, impl Iterator //~| ERROR `impl Trait` in type aliases is unstable //~| ERROR `impl Trait` in type aliases is unstable //~| ERROR `impl Trait` in type aliases is unstable -//~| ERROR `impl Trait` not allowed outside of function -//~| ERROR `impl Trait` not allowed outside of function -//~| ERROR `impl Trait` not allowed outside of function + +fn define_multiple() -> NestedFree { + (vec![true], 0u8, 0i32..1) +} impl Bar for u8 { - type Baa = (Vec, impl Debug, impl Iterator); + type Baa = (Vec, impl Debug, impl Iterator + Debug); //~^ ERROR `impl Trait` in type aliases is unstable //~| ERROR `impl Trait` in type aliases is unstable //~| ERROR `impl Trait` in type aliases is unstable //~| ERROR `impl Trait` in type aliases is unstable - //~| ERROR `impl Trait` not allowed outside of function - //~| ERROR `impl Trait` not allowed outside of function - //~| ERROR `impl Trait` not allowed outside of function - fn define() -> Self::Baa { (vec![true], 0u8, 0i32..1) } + fn define() -> Self::Baa { + (vec![true], 0u8, 0i32..1) + } } fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr b/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr index 55cd2984ab665..8bab0d0c4a95c 100644 --- a/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr +++ b/src/test/ui/feature-gates/feature-gate-type_alias_impl_trait.stderr @@ -17,7 +17,7 @@ LL | type Baa = impl Debug; = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable error[E0658]: associated type defaults are unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:18:5 + --> $DIR/feature-gate-type_alias_impl_trait.rs:22:5 | LL | type Assoc = impl Debug; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | type Assoc = impl Debug; = help: add `#![feature(associated_type_defaults)]` to the crate attributes to enable error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:18:18 + --> $DIR/feature-gate-type_alias_impl_trait.rs:22:18 | LL | type Assoc = impl Debug; | ^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | type Assoc = impl Debug; = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:24:24 + --> $DIR/feature-gate-type_alias_impl_trait.rs:28:24 | LL | type NestedFree = (Vec, impl Debug, impl Iterator); | ^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | type NestedFree = (Vec, impl Debug, impl Iterator $DIR/feature-gate-type_alias_impl_trait.rs:24:37 + --> $DIR/feature-gate-type_alias_impl_trait.rs:28:37 | LL | type NestedFree = (Vec, impl Debug, impl Iterator); | ^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | type NestedFree = (Vec, impl Debug, impl Iterator $DIR/feature-gate-type_alias_impl_trait.rs:24:49 + --> $DIR/feature-gate-type_alias_impl_trait.rs:28:49 | LL | type NestedFree = (Vec, impl Debug, impl Iterator); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL | type NestedFree = (Vec, impl Debug, impl Iterator $DIR/feature-gate-type_alias_impl_trait.rs:24:70 + --> $DIR/feature-gate-type_alias_impl_trait.rs:28:70 | LL | type NestedFree = (Vec, impl Debug, impl Iterator); | ^^^^^^^^^^ @@ -71,84 +71,48 @@ LL | type NestedFree = (Vec, impl Debug, impl Iterator $DIR/feature-gate-type_alias_impl_trait.rs:34:21 + --> $DIR/feature-gate-type_alias_impl_trait.rs:39:21 | -LL | type Baa = (Vec, impl Debug, impl Iterator); +LL | type Baa = (Vec, impl Debug, impl Iterator + Debug); | ^^^^^^^^^^ | = note: see issue #63063 for more information = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:34:34 + --> $DIR/feature-gate-type_alias_impl_trait.rs:39:34 | -LL | type Baa = (Vec, impl Debug, impl Iterator); +LL | type Baa = (Vec, impl Debug, impl Iterator + Debug); | ^^^^^^^^^^ | = note: see issue #63063 for more information = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:34:46 + --> $DIR/feature-gate-type_alias_impl_trait.rs:39:46 | -LL | type Baa = (Vec, impl Debug, impl Iterator); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type Baa = (Vec, impl Debug, impl Iterator + Debug); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #63063 for more information = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable error[E0658]: `impl Trait` in type aliases is unstable - --> $DIR/feature-gate-type_alias_impl_trait.rs:34:67 + --> $DIR/feature-gate-type_alias_impl_trait.rs:39:67 | -LL | type Baa = (Vec, impl Debug, impl Iterator); +LL | type Baa = (Vec, impl Debug, impl Iterator + Debug); | ^^^^^^^^^^ | = note: see issue #63063 for more information = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-type_alias_impl_trait.rs:18:18 + --> $DIR/feature-gate-type_alias_impl_trait.rs:22:18 | LL | type Assoc = impl Debug; | ^^^^^^^^^^ -error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-type_alias_impl_trait.rs:24:24 - | -LL | type NestedFree = (Vec, impl Debug, impl Iterator); - | ^^^^^^^^^^ - -error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-type_alias_impl_trait.rs:24:37 - | -LL | type NestedFree = (Vec, impl Debug, impl Iterator); - | ^^^^^^^^^^ - -error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-type_alias_impl_trait.rs:24:49 - | -LL | type NestedFree = (Vec, impl Debug, impl Iterator); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-type_alias_impl_trait.rs:34:21 - | -LL | type Baa = (Vec, impl Debug, impl Iterator); - | ^^^^^^^^^^ - -error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-type_alias_impl_trait.rs:34:34 - | -LL | type Baa = (Vec, impl Debug, impl Iterator); - | ^^^^^^^^^^ - -error[E0562]: `impl Trait` not allowed outside of function and inherent method return types - --> $DIR/feature-gate-type_alias_impl_trait.rs:34:46 - | -LL | type Baa = (Vec, impl Debug, impl Iterator); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 19 previous errors +error: aborting due to 13 previous errors Some errors have detailed explanations: E0562, E0658. For more information about an error, try `rustc --explain E0562`. 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/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs new file mode 100644 index 0000000000000..61e512a12a18d --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.rs @@ -0,0 +1,6 @@ +#![deny(unsafe_op_in_unsafe_fn)] +//~^ ERROR the `unsafe_op_in_unsafe_fn` lint is unstable +//~| ERROR the `unsafe_op_in_unsafe_fn` lint is unstable +//~| ERROR the `unsafe_op_in_unsafe_fn` lint is unstable + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr new file mode 100644 index 0000000000000..c5cad4a98d9ca --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unsafe_block_in_unsafe_fn.stderr @@ -0,0 +1,30 @@ +error[E0658]: the `unsafe_op_in_unsafe_fn` lint is unstable + --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:1 + | +LL | #![deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #71668 for more information + = help: add `#![feature(unsafe_block_in_unsafe_fn)]` to the crate attributes to enable + +error[E0658]: the `unsafe_op_in_unsafe_fn` lint is unstable + --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:1 + | +LL | #![deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #71668 for more information + = help: add `#![feature(unsafe_block_in_unsafe_fn)]` to the crate attributes to enable + +error[E0658]: the `unsafe_op_in_unsafe_fn` lint is unstable + --> $DIR/feature-gate-unsafe_block_in_unsafe_fn.rs:1:1 + | +LL | #![deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #71668 for more information + = help: add `#![feature(unsafe_block_in_unsafe_fn)]` 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/ffi_returns_twice.stderr b/src/test/ui/ffi_returns_twice.stderr index 862892e27be98..2b7f5694f0206 100644 --- a/src/test/ui/ffi_returns_twice.stderr +++ b/src/test/ui/ffi_returns_twice.stderr @@ -6,3 +6,4 @@ LL | #[ffi_returns_twice] error: aborting due to previous error +For more information about this error, try `rustc --explain E0724`. 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-item-type.rs b/src/test/ui/fn/fn-item-type.rs index 68b75c18a43dc..abae40162a0fc 100644 --- a/src/test/ui/fn/fn-item-type.rs +++ b/src/test/ui/fn/fn-item-type.rs @@ -12,22 +12,44 @@ impl Foo for T { /* `foo` is still default here */ } fn main() { eq(foo::, bar::); //~^ ERROR mismatched types - //~| expected fn item `fn(_) -> _ {foo::}` - //~| found fn item `fn(_) -> _ {bar::}` - //~| expected fn item, found a different fn item + //~| expected fn item `fn(_) -> _ {foo::}` + //~| found fn item `fn(_) -> _ {bar::}` + //~| expected fn item, found a different fn item + //~| different `fn` items always have unique types, even if their signatures are the same + //~| change the expected type to be function pointer + //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer eq(foo::, foo::); //~^ ERROR mismatched types //~| expected `u8`, found `i8` + //~| different `fn` items always have unique types, even if their signatures are the same + //~| change the expected type to be function pointer + //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer eq(bar::, bar::>); //~^ ERROR mismatched types - //~| expected fn item `fn(_) -> _ {bar::}` - //~| found fn item `fn(_) -> _ {bar::>}` - //~| expected struct `std::string::String`, found struct `std::vec::Vec` + //~| expected fn item `fn(_) -> _ {bar::}` + //~| found fn item `fn(_) -> _ {bar::>}` + //~| expected struct `std::string::String`, found struct `std::vec::Vec` + //~| different `fn` items always have unique types, even if their signatures are the same + //~| change the expected type to be function pointer + //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer // Make sure we distinguish between trait methods correctly. eq(::foo, ::foo); //~^ ERROR mismatched types //~| expected `u8`, found `u16` + //~| different `fn` items always have unique types, even if their signatures are the same + //~| change the expected type to be function pointer + //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer + + eq(foo::, bar:: as fn(isize) -> isize); + //~^ ERROR mismatched types + //~| expected fn item `fn(_) -> _ {foo::}` + //~| found fn pointer `fn(_) -> _` + //~| expected fn item, found fn pointer + //~| change the expected type to be function pointer + //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer + + eq(foo:: as fn(isize) -> isize, bar::); // ok! } diff --git a/src/test/ui/fn/fn-item-type.stderr b/src/test/ui/fn/fn-item-type.stderr index 4cce25c43c485..bfa9efa219f4c 100644 --- a/src/test/ui/fn/fn-item-type.stderr +++ b/src/test/ui/fn/fn-item-type.stderr @@ -6,34 +6,57 @@ LL | eq(foo::, bar::); | = note: expected fn item `fn(_) -> _ {foo::}` found fn item `fn(_) -> _ {bar::}` + = note: different `fn` items always have unique types, even if their signatures are the same + = help: change the expected type to be function pointer `fn(isize) -> isize` + = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo:: as fn(isize) -> isize` error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:19:19 + --> $DIR/fn-item-type.rs:22:19 | LL | eq(foo::, foo::); | ^^^^^^^^^ expected `u8`, found `i8` | = note: expected fn item `fn(_) -> _ {foo::}` found fn item `fn(_) -> _ {foo::}` + = note: different `fn` items always have unique types, even if their signatures are the same + = help: change the expected type to be function pointer `fn(isize) -> isize` + = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo:: as fn(isize) -> isize` error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:23:23 + --> $DIR/fn-item-type.rs:29:23 | LL | eq(bar::, bar::>); | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found struct `std::vec::Vec` | = note: expected fn item `fn(_) -> _ {bar::}` found fn item `fn(_) -> _ {bar::>}` + = note: different `fn` items always have unique types, even if their signatures are the same + = help: change the expected type to be function pointer `fn(isize) -> isize` + = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `bar:: as fn(isize) -> isize` error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:30:26 + --> $DIR/fn-item-type.rs:39:26 | LL | eq(::foo, ::foo); | ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` | = note: expected fn item `fn() {::foo}` found fn item `fn() {::foo}` + = note: different `fn` items always have unique types, even if their signatures are the same + = help: change the expected type to be function pointer `fn()` + = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `::foo as fn()` -error: aborting due to 4 previous errors +error[E0308]: mismatched types + --> $DIR/fn-item-type.rs:46:19 + | +LL | eq(foo::, bar:: as fn(isize) -> isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer + | + = note: expected fn item `fn(_) -> _ {foo::}` + found fn pointer `fn(_) -> _` + = help: change the expected type to be function pointer `fn(isize) -> isize` + = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo:: as fn(isize) -> isize` + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0308`. 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..e32005e21a8e1 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 @@ -1,8 +1,10 @@ -error[E0005]: refutable pattern in `for` loop binding: `&std::i32::MIN..=0i32` and `&2i32..=std::i32::MAX` not covered +error[E0005]: refutable pattern in `for` loop binding: `&i32::MIN..=0i32` and `&2i32..=i32::MAX` not covered --> $DIR/for-loop-refutable-pattern-error-message.rs:2:9 | LL | for &1 in [1].iter() {} - | ^^ patterns `&std::i32::MIN..=0i32` and `&2i32..=std::i32::MAX` not covered + | ^^ patterns `&i32::MIN..=0i32` and `&2i32..=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/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 new file mode 100644 index 0000000000000..3d0930da42243 --- /dev/null +++ b/src/test/ui/generator/discriminant.rs @@ -0,0 +1,137 @@ +//! Tests that generator discriminant sizes and ranges are chosen optimally and that they are +//! reflected in the output of `mem::discriminant`. + +// run-pass + +#![feature(generators, generator_trait, core_intrinsics, discriminant_kind)] + +use std::intrinsics::discriminant_value; +use std::marker::{Unpin, DiscriminantKind}; +use std::mem::size_of_val; +use std::{cmp, ops::*}; + +macro_rules! yield25 { + ($e:expr) => { + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + + yield $e; + yield $e; + yield $e; + yield $e; + yield $e; + }; +} + +/// Yields 250 times. +macro_rules! yield250 { + () => { + yield250!(()) + }; + + ($e:expr) => { + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + yield25!($e); + }; +} + +fn cycle( + gen: impl Generator<()> + Unpin + DiscriminantKind, + expected_max_discr: i32 +) { + let mut gen = Box::pin(gen); + let mut max_discr = 0; + loop { + max_discr = cmp::max(max_discr, discriminant_value(gen.as_mut().get_mut())); + match gen.as_mut().resume(()) { + GeneratorState::Yielded(_) => {} + GeneratorState::Complete(_) => { + assert_eq!(max_discr, expected_max_discr); + return; + } + } + } +} + +fn main() { + // Has only one invalid discr. value. + let gen_u8_tiny_niche = || { + || { + // 3 reserved variants + + yield250!(); // 253 variants + + yield; // 254 + yield; // 255 + } + }; + + // Uses all values in the u8 discriminant. + let gen_u8_full = || { + || { + // 3 reserved variants + + yield250!(); // 253 variants + + yield; // 254 + yield; // 255 + yield; // 256 + } + }; + + // Barely needs a u16 discriminant. + let gen_u16 = || { + || { + // 3 reserved variants + + yield250!(); // 253 variants + + yield; // 254 + yield; // 255 + yield; // 256 + yield; // 257 + } + }; + + assert_eq!(size_of_val(&gen_u8_tiny_niche()), 1); + assert_eq!(size_of_val(&Some(gen_u8_tiny_niche())), 1); // uses niche + assert_eq!(size_of_val(&Some(Some(gen_u8_tiny_niche()))), 2); // cannot use niche anymore + assert_eq!(size_of_val(&gen_u8_full()), 1); + assert_eq!(size_of_val(&Some(gen_u8_full())), 2); // cannot use niche + assert_eq!(size_of_val(&gen_u16()), 2); + assert_eq!(size_of_val(&Some(gen_u16())), 2); // uses niche + + cycle(gen_u8_tiny_niche(), 254); + cycle(gen_u8_full(), 255); + cycle(gen_u16(), 256); +} 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.nll.stderr b/src/test/ui/generator/resume-arg-late-bound.nll.stderr new file mode 100644 index 0000000000000..7d71219192407 --- /dev/null +++ b/src/test/ui/generator/resume-arg-late-bound.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/resume-arg-late-bound.rs:15:5 + | +LL | test(gen); + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/generator/resume-arg-late-bound.rs b/src/test/ui/generator/resume-arg-late-bound.rs index 87b1f1a065bc8..a8f657eaabe47 100644 --- a/src/test/ui/generator/resume-arg-late-bound.rs +++ b/src/test/ui/generator/resume-arg-late-bound.rs @@ -13,5 +13,6 @@ fn main() { *arg = true; }; test(gen); - //~^ ERROR type mismatch in function arguments + //~^ ERROR mismatched types + //~| ERROR mismatched types } diff --git a/src/test/ui/generator/resume-arg-late-bound.stderr b/src/test/ui/generator/resume-arg-late-bound.stderr index 7719d5123f466..c379d9eae8ecd 100644 --- a/src/test/ui/generator/resume-arg-late-bound.stderr +++ b/src/test/ui/generator/resume-arg-late-bound.stderr @@ -1,15 +1,21 @@ -error[E0631]: type mismatch in function arguments - --> $DIR/resume-arg-late-bound.rs:15:10 +error[E0308]: mismatched types + --> $DIR/resume-arg-late-bound.rs:15:5 | -LL | fn test(a: impl for<'a> Generator<&'a mut bool>) {} - | ---- ------------------------------- required by this bound in `test` -... LL | test(gen); - | ^^^ - | | - | expected signature of `for<'a> fn(&'a mut bool) -> _` - | found signature of `fn(&mut bool) -> _` + | ^^^^ one type is more general than the other + | + = note: expected type `for<'a> std::ops::Generator<&'a mut bool>` + found type `std::ops::Generator<&mut bool>` + +error[E0308]: mismatched types + --> $DIR/resume-arg-late-bound.rs:15:5 + | +LL | test(gen); + | ^^^^ one type is more general than the other + | + = note: expected type `for<'a> std::ops::Generator<&'a mut bool>` + found type `std::ops::Generator<&mut bool>` -error: aborting due to previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0631`. +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/generator/resume-arg-size.rs b/src/test/ui/generator/resume-arg-size.rs index ffdc98d6f1984..b93dc54f7a97d 100644 --- a/src/test/ui/generator/resume-arg-size.rs +++ b/src/test/ui/generator/resume-arg-size.rs @@ -22,7 +22,7 @@ fn main() { }; // Neither of these generators have the resume arg live across the `yield`, so they should be - // 4 Bytes in size (only storing the discriminant) - assert_eq!(size_of_val(&gen_copy), 4); - assert_eq!(size_of_val(&gen_move), 4); + // 1 Byte in size (only storing the discriminant) + assert_eq!(size_of_val(&gen_copy), 1); + assert_eq!(size_of_val(&gen_move), 1); } 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/size-moved-locals.rs b/src/test/ui/generator/size-moved-locals.rs index 2864fbb2f3c8d..74c60d98154dd 100644 --- a/src/test/ui/generator/size-moved-locals.rs +++ b/src/test/ui/generator/size-moved-locals.rs @@ -58,7 +58,7 @@ fn overlap_move_points() -> impl Generator { } } -fn overlap_x_and_y() -> impl Generator{ +fn overlap_x_and_y() -> impl Generator { static || { let x = Foo([0; FOO_SIZE]); yield; @@ -70,8 +70,8 @@ fn overlap_x_and_y() -> impl Generator{ } fn main() { - assert_eq!(1028, std::mem::size_of_val(&move_before_yield())); - assert_eq!(1032, std::mem::size_of_val(&move_before_yield_with_noop())); - assert_eq!(2056, std::mem::size_of_val(&overlap_move_points())); - assert_eq!(1032, std::mem::size_of_val(&overlap_x_and_y())); + assert_eq!(1025, std::mem::size_of_val(&move_before_yield())); + assert_eq!(1026, std::mem::size_of_val(&move_before_yield_with_noop())); + assert_eq!(2051, std::mem::size_of_val(&overlap_move_points())); + assert_eq!(1026, std::mem::size_of_val(&overlap_x_and_y())); } 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/generator/too-many-parameters.stderr b/src/test/ui/generator/too-many-parameters.stderr index a297ee43de969..22d40db3f2678 100644 --- a/src/test/ui/generator/too-many-parameters.stderr +++ b/src/test/ui/generator/too-many-parameters.stderr @@ -6,3 +6,4 @@ LL | |(), ()| { error: aborting due to previous error +For more information about this error, try `rustc --explain E0628`. diff --git a/src/test/ui/generic-associated-types/collections-project-default.rs b/src/test/ui/generic-associated-types/collections-project-default.rs new file mode 100644 index 0000000000000..5fbae02573c62 --- /dev/null +++ b/src/test/ui/generic-associated-types/collections-project-default.rs @@ -0,0 +1,72 @@ +#![allow(incomplete_features)] +#![feature(generic_associated_types)] +#![feature(associated_type_defaults)] + +// A Collection trait and collection families. Based on +// http://smallcultfollowing.com/babysteps/blog/2016/11/03/ +// associated-type-constructors-part-2-family-traits/ + +// check that we don't normalize with trait defaults. + +trait Collection { + type Iter<'iter>: Iterator where T: 'iter; + type Family: CollectionFamily; + // Test associated type defaults with parameters + type Sibling: Collection = + <>::Family as CollectionFamily>::Member; + + fn empty() -> Self; + + fn add(&mut self, value: T); + + fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>; +} + +trait CollectionFamily { + type Member: Collection; +} + +struct VecFamily; + +impl CollectionFamily for VecFamily { + type Member = Vec; +} + +impl Collection for Vec { + type Iter<'iter> where T: 'iter = std::slice::Iter<'iter, T>; + type Family = VecFamily; + + fn empty() -> Self { + Vec::new() + } + + fn add(&mut self, value: T) { + self.push(value) + } + + fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> { + self.iter() + } +} + +fn floatify_sibling(ints: &C) -> >::Sibling +where + C: Collection, +{ + let mut res = ::Member::::empty(); + for &v in ints.iterate() { + res.add(v as f32); + } + res + //~^ ERROR mismatched types +} + +fn use_floatify() { + let a = vec![1i32, 2, 3]; + let c = floatify_sibling(&a); + assert_eq!(Some(&1.0), c.iterate().next()); +} + +fn main() { + use_floatify(); +} diff --git a/src/test/ui/generic-associated-types/collections-project-default.stderr b/src/test/ui/generic-associated-types/collections-project-default.stderr new file mode 100644 index 0000000000000..ca02b2603ba64 --- /dev/null +++ b/src/test/ui/generic-associated-types/collections-project-default.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/collections-project-default.rs:60:5 + | +LL | fn floatify_sibling(ints: &C) -> >::Sibling + | ------------------------------------ expected `>::Sibling` because of return type +... +LL | res + | ^^^ expected Collection::Sibling, found CollectionFamily::Member + | + = note: expected associated type `>::Sibling` + found associated type `<>::Family as CollectionFamily>::Member` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/generic-associated-types/collections.rs b/src/test/ui/generic-associated-types/collections.rs index 6f018f0401863..1b5b9c181fb61 100644 --- a/src/test/ui/generic-associated-types/collections.rs +++ b/src/test/ui/generic-associated-types/collections.rs @@ -6,13 +6,14 @@ // http://smallcultfollowing.com/babysteps/blog/2016/11/03/ // associated-type-constructors-part-2-family-traits/ +// run-pass + trait Collection { - type Iter<'iter>: Iterator; + type Iter<'iter>: Iterator where T: 'iter; type Family: CollectionFamily; // Test associated type defaults with parameters type Sibling: Collection = <>::Family as CollectionFamily>::Member; - //~^^ ERROR type-generic associated types are not yet implemented fn empty() -> Self; @@ -23,7 +24,6 @@ trait Collection { trait CollectionFamily { type Member: Collection; - //~^ ERROR type-generic associated types are not yet implemented } struct VecFamily; @@ -33,7 +33,7 @@ impl CollectionFamily for VecFamily { } impl Collection for Vec { - type Iter<'iter> = std::slice::Iter<'iter, T>; + type Iter<'iter> where T: 'iter = std::slice::Iter<'iter, T>; type Family = VecFamily; fn empty() -> Self { @@ -53,18 +53,7 @@ fn floatify(ints: &C) -> <>::Family as CollectionFamily> where C: Collection, { - let mut res = C::Family::Member::::empty(); - for &v in ints.iterate() { - res.add(v as f32); - } - res -} - -fn floatify_sibling(ints: &C) -> >::Sibling -where - C: Collection, -{ - let mut res = C::Family::Member::::empty(); + let mut res = ::Member::::empty(); for &v in ints.iterate() { res.add(v as f32); } @@ -72,11 +61,11 @@ where } fn use_floatify() { - let a = vec![1i32, 2, 3]; - let b = floatify(a); - println!("{}", b.iterate().next()); - let c = floatify_sibling(a); - println!("{}", c.iterate().next()); + let a = vec![1, 2, 3]; + let b = floatify(&a); + assert_eq!(Some(&1.0), b.iterate().next()); } -fn main() {} +fn main() { + use_floatify(); +} diff --git a/src/test/ui/generic-associated-types/collections.stderr b/src/test/ui/generic-associated-types/collections.stderr deleted file mode 100644 index fb06d5e49a391..0000000000000 --- a/src/test/ui/generic-associated-types/collections.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error: type-generic associated types are not yet implemented - --> $DIR/collections.rs:13:5 - | -LL | / type Sibling: Collection = -LL | | <>::Family as CollectionFamily>::Member; - | |_________________________________________________________________________^ - | - = note: for more information, see issue #44265 for more information - -error: type-generic associated types are not yet implemented - --> $DIR/collections.rs:25:5 - | -LL | type Member: Collection; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/generic-associated-types/construct_with_other_type.rs b/src/test/ui/generic-associated-types/construct_with_other_type.rs index 2198b99db25c1..ff9d61658f4eb 100644 --- a/src/test/ui/generic-associated-types/construct_with_other_type.rs +++ b/src/test/ui/generic-associated-types/construct_with_other_type.rs @@ -1,7 +1,7 @@ #![allow(incomplete_features)] #![feature(generic_associated_types)] -// FIXME(#30472) normalize enough to handle this. +// check-pass use std::ops::Deref; @@ -17,7 +17,6 @@ trait Baz { } impl Baz for T where T: Foo { -//~^ ERROR type mismatch resolving type Quux<'a> where T: 'a = T; type Baa<'a> where T: 'a = &'a ::Bar<'a, 'static>; 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 deleted file mode 100644 index bad746f7ef121..0000000000000 --- a/src/test/ui/generic-associated-types/construct_with_other_type.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0271]: type mismatch resolving `for<'a> <::Baa<'a> as std::ops::Deref>::Target == <::Quux<'a> as Foo>::Bar<'a, 'static>` - --> $DIR/construct_with_other_type.rs:19:9 - | -LL | impl Baz for T where T: Foo { - | ^^^ expected type parameter `T`, found associated type - | - = 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 - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/generic-associated-types/gat-dont-ice-on-absent-feature-2.rs b/src/test/ui/generic-associated-types/gat-dont-ice-on-absent-feature-2.rs index f88df6a608aa3..c1d68812e9356 100644 --- a/src/test/ui/generic-associated-types/gat-dont-ice-on-absent-feature-2.rs +++ b/src/test/ui/generic-associated-types/gat-dont-ice-on-absent-feature-2.rs @@ -6,7 +6,6 @@ struct Foo; trait MyTrait { type Item; //~^ ERROR generic associated types are unstable [E0658] - //~| ERROR type-generic associated types are not yet implemented } impl MyTrait for Foo { diff --git a/src/test/ui/generic-associated-types/gat-dont-ice-on-absent-feature-2.stderr b/src/test/ui/generic-associated-types/gat-dont-ice-on-absent-feature-2.stderr index f3da27775adcf..34f536dbe8f64 100644 --- a/src/test/ui/generic-associated-types/gat-dont-ice-on-absent-feature-2.stderr +++ b/src/test/ui/generic-associated-types/gat-dont-ice-on-absent-feature-2.stderr @@ -8,7 +8,7 @@ LL | type Item; = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable error[E0658]: generic associated types are unstable - --> $DIR/gat-dont-ice-on-absent-feature-2.rs:13:5 + --> $DIR/gat-dont-ice-on-absent-feature-2.rs:12:5 | LL | type Item = T; | ^^^^^^^^^^^^^^^^^ @@ -16,14 +16,6 @@ LL | type Item = T; = note: see issue #44265 for more information = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable -error: type-generic associated types are not yet implemented - --> $DIR/gat-dont-ice-on-absent-feature-2.rs:7:5 - | -LL | type Item; - | ^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/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/generic-associated-types-where.rs b/src/test/ui/generic-associated-types/generic-associated-types-where.rs index 589024e162166..1a94796535c14 100644 --- a/src/test/ui/generic-associated-types/generic-associated-types-where.rs +++ b/src/test/ui/generic-associated-types/generic-associated-types-where.rs @@ -9,11 +9,8 @@ use std::fmt::{Display, Debug}; trait Foo { type Assoc where Self: Sized; type Assoc2 where T: Display; - //~^ ERROR type-generic associated types are not yet implemented type Assoc3; - //~^ ERROR type-generic associated types are not yet implemented - type WithDefault<'a, T: Debug + 'a> = dyn Iterator; - //~^ ERROR type-generic associated types are not yet implemented + type WithDefault<'a, T: Debug + 'a>: ?Sized = dyn Iterator; type NoGenerics; } @@ -23,6 +20,7 @@ impl Foo for Bar { type Assoc = usize; type Assoc2 = Vec; type Assoc3 where T: Iterator = Vec; + //~^ impl has stricter requirements than trait type WithDefault<'a, T: Debug + 'a> = &'a dyn Iterator; type NoGenerics = ::std::cell::Cell; } diff --git a/src/test/ui/generic-associated-types/generic-associated-types-where.stderr b/src/test/ui/generic-associated-types/generic-associated-types-where.stderr index fb29b377a16e3..4d02f2c46a6d0 100644 --- a/src/test/ui/generic-associated-types/generic-associated-types-where.stderr +++ b/src/test/ui/generic-associated-types/generic-associated-types-where.stderr @@ -1,26 +1,12 @@ -error: type-generic associated types are not yet implemented - --> $DIR/generic-associated-types-where.rs:11:5 - | -LL | type Assoc2 where T: Display; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: type-generic associated types are not yet implemented - --> $DIR/generic-associated-types-where.rs:13:5 +error[E0276]: impl has stricter requirements than trait + --> $DIR/generic-associated-types-where.rs:22:5 | LL | type Assoc3; - | ^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: type-generic associated types are not yet implemented - --> $DIR/generic-associated-types-where.rs:15:5 - | -LL | type WithDefault<'a, T: Debug + 'a> = dyn Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information + | --------------- definition of `Assoc3` from trait +... +LL | type Assoc3 where T: Iterator = Vec; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: std::iter::Iterator` -error: aborting due to 3 previous errors +error: aborting due to previous error +For more information about this error, try `rustc --explain E0276`. diff --git a/src/test/ui/generic-associated-types/impl_bounds.stderr b/src/test/ui/generic-associated-types/impl_bounds.stderr index 486b538045e49..e06977ebbe3df 100644 --- a/src/test/ui/generic-associated-types/impl_bounds.stderr +++ b/src/test/ui/generic-associated-types/impl_bounds.stderr @@ -5,11 +5,7 @@ LL | type A<'a> where Self: 'static = (&'a ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `T: 'static`... -note: ...so that the type `Fooy` will meet its required lifetime bounds - --> $DIR/impl_bounds.rs:15:5 - | -LL | type A<'a> where Self: 'static = (&'a ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...so that the type `Fooy` will meet its required lifetime bounds error[E0478]: lifetime bound not satisfied --> $DIR/impl_bounds.rs:17:5 @@ -34,13 +30,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-47206-where-clause.rs b/src/test/ui/generic-associated-types/issue-47206-where-clause.rs index 53e350aacf88e..de2b978460f7f 100644 --- a/src/test/ui/generic-associated-types/issue-47206-where-clause.rs +++ b/src/test/ui/generic-associated-types/issue-47206-where-clause.rs @@ -5,13 +5,13 @@ trait Foo { type Assoc3; - //~^ type-generic associated types are not yet implemented } struct Bar; impl Foo for Bar { type Assoc3 where T: Iterator = Vec; + //~^ ERROR impl has stricter requirements than trait } fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-47206-where-clause.stderr b/src/test/ui/generic-associated-types/issue-47206-where-clause.stderr index c9c1a4753b0dd..bc5c40ff029f9 100644 --- a/src/test/ui/generic-associated-types/issue-47206-where-clause.stderr +++ b/src/test/ui/generic-associated-types/issue-47206-where-clause.stderr @@ -1,10 +1,12 @@ -error: type-generic associated types are not yet implemented - --> $DIR/issue-47206-where-clause.rs:7:5 +error[E0276]: impl has stricter requirements than trait + --> $DIR/issue-47206-where-clause.rs:13:5 | LL | type Assoc3; - | ^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information + | --------------- definition of `Assoc3` from trait +... +LL | type Assoc3 where T: Iterator = Vec; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `T: std::iter::Iterator` error: aborting due to previous error +For more information about this error, try `rustc --explain E0276`. diff --git a/src/test/ui/generic-associated-types/issue-62326-parameter-out-of-range.rs b/src/test/ui/generic-associated-types/issue-62326-parameter-out-of-range.rs index 1a79dbf2279a0..404be59a36d92 100644 --- a/src/test/ui/generic-associated-types/issue-62326-parameter-out-of-range.rs +++ b/src/test/ui/generic-associated-types/issue-62326-parameter-out-of-range.rs @@ -1,11 +1,14 @@ #![allow(incomplete_features)] #![feature(generic_associated_types)] -// FIXME(generic-associated-types) Investigate why this doesn't compile. +// check-pass trait Iterator { type Item<'a>: 'a; - //~^ ERROR the requirement `for<'a> ::Item<'a>: 'a` is not satisfied +} + +impl Iterator for () { + type Item<'a> = &'a (); } fn main() {} 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 deleted file mode 100644 index 687423962361b..0000000000000 --- a/src/test/ui/generic-associated-types/issue-62326-parameter-out-of-range.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error[E0280]: the requirement `for<'a> ::Item<'a>: 'a` is not satisfied - --> $DIR/issue-62326-parameter-out-of-range.rs:7:20 - | -LL | trait Iterator { - | -------------- required by `Iterator` -LL | type Item<'a>: 'a; - | ^^ - -error: aborting due to previous error - diff --git a/src/test/ui/generic-associated-types/issue-67424.rs b/src/test/ui/generic-associated-types/issue-67424.rs index 9b616b8abc2ee..fa35a3e8b04d1 100644 --- a/src/test/ui/generic-associated-types/issue-67424.rs +++ b/src/test/ui/generic-associated-types/issue-67424.rs @@ -7,7 +7,6 @@ trait Trait1 { trait Trait2 { type Type1: Trait1; //~^ ERROR: generic associated types are unstable - //~| ERROR: type-generic associated types are not yet implemented } fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-67424.stderr b/src/test/ui/generic-associated-types/issue-67424.stderr index 8b08c71759bb9..bbb7d56f5928e 100644 --- a/src/test/ui/generic-associated-types/issue-67424.stderr +++ b/src/test/ui/generic-associated-types/issue-67424.stderr @@ -7,14 +7,6 @@ LL | type Type1: Trait1; = note: see issue #44265 for more information = help: add `#![feature(generic_associated_types)]` to the crate attributes to enable -error: type-generic associated types are not yet implemented - --> $DIR/issue-67424.rs:8:5 - | -LL | type Type1: Trait1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.rs b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.rs new file mode 100644 index 0000000000000..71f9b2967dc58 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.rs @@ -0,0 +1,32 @@ +// Regression test for #68641 + +#![feature(generic_associated_types)] +//~^ WARNING the feature `generic_associated_types` is incomplete and may not + +trait UnsafeCopy { + type Item<'a>: Copy; + + fn copy<'a>(item: &Self::Item<'a>) -> Self::Item<'a> { + *item + } +} + +impl UnsafeCopy for T { + type Item<'a> = T; + //~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied +} + +fn main() { + let mut s = String::from("Hello world!"); + + let copy = String::copy(&s); + + // Do we indeed point to the samme memory? + assert!(s.as_ptr() == copy.as_ptr()); + + // Any use of `copy` is certeinly UB after this + drop(s); + + // UB UB UB UB UB!! + println!("{}", copy); +} diff --git a/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr new file mode 100644 index 0000000000000..834bc3b7878f2 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68641-check-gat-bounds.stderr @@ -0,0 +1,26 @@ +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-68641-check-gat-bounds.rs:3:12 + | +LL | #![feature(generic_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information + +error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied + --> $DIR/issue-68641-check-gat-bounds.rs:15:5 + | +LL | type Item<'a>: Copy; + | -------------------- required by `UnsafeCopy::Item` +... +LL | type Item<'a> = T; + | ^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | impl UnsafeCopy for T { + | ^^^^^^^^^^^^^^^^^^^ + +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/generic-associated-types/issue-68642-broken-llvm-ir.rs b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs new file mode 100644 index 0000000000000..c99073c13284d --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs @@ -0,0 +1,21 @@ +// Regression test for #68642 + +#![feature(generic_associated_types)] +//~^ WARNING the feature `generic_associated_types` is incomplete and may not + +trait Fun { + type F<'a>: Fn() -> u32; + + fn callme<'a>(f: Self::F<'a>) -> u32 { + f() + } +} + +impl Fun for T { + type F<'a> = Self; + //~^ ERROR expected a `std::ops::Fn<()>` closure, found `T` +} + +fn main() { + usize>::callme(|| 1); +} diff --git a/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr new file mode 100644 index 0000000000000..89cc5dfd06018 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr @@ -0,0 +1,28 @@ +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-68642-broken-llvm-ir.rs:3:12 + | +LL | #![feature(generic_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information + +error[E0277]: expected a `std::ops::Fn<()>` closure, found `T` + --> $DIR/issue-68642-broken-llvm-ir.rs:15:5 + | +LL | type F<'a>: Fn() -> u32; + | ------------------------ required by `Fun::F` +... +LL | type F<'a> = Self; + | ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T` + | + = help: the trait `std::ops::Fn<()>` is not implemented for `T` + = note: wrap the `T` in a closure with no arguments: `|| { /* code */ } +help: consider restricting type parameter `T` + | +LL | impl> Fun for T { + | ^^^^^^^^^^^^^^^^^^ + +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/generic-associated-types/issue-68643-broken-mir.rs b/src/test/ui/generic-associated-types/issue-68643-broken-mir.rs new file mode 100644 index 0000000000000..24133e75cccee --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68643-broken-mir.rs @@ -0,0 +1,21 @@ +// Regression test for #68643 + +#![feature(generic_associated_types)] +//~^ WARNING the feature `generic_associated_types` is incomplete and may not + +trait Fun { + type F<'a>: Fn() -> u32; + + fn callme<'a>(f: Self::F<'a>) -> u32 { + f() + } +} + +impl Fun for T { + type F<'a> = Self; + //~^ ERROR expected a `std::ops::Fn<()>` closure, found `T` +} + +pub fn main() { + ::callme(|| {}); +} diff --git a/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr b/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr new file mode 100644 index 0000000000000..efd3287853f03 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68643-broken-mir.stderr @@ -0,0 +1,28 @@ +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-68643-broken-mir.rs:3:12 + | +LL | #![feature(generic_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information + +error[E0277]: expected a `std::ops::Fn<()>` closure, found `T` + --> $DIR/issue-68643-broken-mir.rs:15:5 + | +LL | type F<'a>: Fn() -> u32; + | ------------------------ required by `Fun::F` +... +LL | type F<'a> = Self; + | ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T` + | + = help: the trait `std::ops::Fn<()>` is not implemented for `T` + = note: wrap the `T` in a closure with no arguments: `|| { /* code */ } +help: consider restricting type parameter `T` + | +LL | impl> Fun for T { + | ^^^^^^^^^^^^^^^^^^ + +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/generic-associated-types/issue-68644-codegen-selection.rs b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.rs new file mode 100644 index 0000000000000..22620c61b8390 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.rs @@ -0,0 +1,21 @@ +// Regression test for #68644 + +#![feature(generic_associated_types)] +//~^ WARNING the feature `generic_associated_types` is incomplete and may not + +trait Fun { + type F<'a>: Fn() -> u32; + + fn callme<'a>(f: Self::F<'a>) -> u32 { + f() + } +} + +impl Fun for T { + type F<'a> = Self; + //~^ ERROR expected a `std::ops::Fn<()>` closure, found `T` +} + +fn main() { + ::callme(0); +} diff --git a/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr new file mode 100644 index 0000000000000..5da924a512f00 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68644-codegen-selection.stderr @@ -0,0 +1,28 @@ +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-68644-codegen-selection.rs:3:12 + | +LL | #![feature(generic_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information + +error[E0277]: expected a `std::ops::Fn<()>` closure, found `T` + --> $DIR/issue-68644-codegen-selection.rs:15:5 + | +LL | type F<'a>: Fn() -> u32; + | ------------------------ required by `Fun::F` +... +LL | type F<'a> = Self; + | ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T` + | + = help: the trait `std::ops::Fn<()>` is not implemented for `T` + = note: wrap the `T` in a closure with no arguments: `|| { /* code */ } +help: consider restricting type parameter `T` + | +LL | impl> Fun for T { + | ^^^^^^^^^^^^^^^^^^ + +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/generic-associated-types/issue-68645-codegen-fulfillment.rs b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs new file mode 100644 index 0000000000000..423b80e8476f4 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs @@ -0,0 +1,21 @@ +// Regression test for #68645 + +#![feature(generic_associated_types)] +//~^ WARNING the feature `generic_associated_types` is incomplete and may not + +trait Fun { + type F<'a>: Fn() -> u32; + + fn callme<'a>(f: Self::F<'a>) -> u32 { + f() + } +} + +impl Fun for T { + type F<'a> = Self; + //~^ ERROR expected a `std::ops::Fn<()>` closure, found `T` +} + +fn main() { + <&dyn Iterator>::callme(&std::iter::once(1)); +} diff --git a/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr new file mode 100644 index 0000000000000..12d84ab6a369b --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr @@ -0,0 +1,28 @@ +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-68645-codegen-fulfillment.rs:3:12 + | +LL | #![feature(generic_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information + +error[E0277]: expected a `std::ops::Fn<()>` closure, found `T` + --> $DIR/issue-68645-codegen-fulfillment.rs:15:5 + | +LL | type F<'a>: Fn() -> u32; + | ------------------------ required by `Fun::F` +... +LL | type F<'a> = Self; + | ^^^^^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `T` + | + = help: the trait `std::ops::Fn<()>` is not implemented for `T` + = note: wrap the `T` in a closure with no arguments: `|| { /* code */ } +help: consider restricting type parameter `T` + | +LL | impl> Fun for T { + | ^^^^^^^^^^^^^^^^^^ + +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/generic-associated-types/issue-68656-unsized-values.rs b/src/test/ui/generic-associated-types/issue-68656-unsized-values.rs new file mode 100644 index 0000000000000..4ccd42ba6432d --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68656-unsized-values.rs @@ -0,0 +1,22 @@ +// Regression test for #68656 + +#![feature(generic_associated_types)] +//~^ WARNING the feature `generic_associated_types` is incomplete and may not + +trait UnsafeCopy { + type Item<'a>: std::ops::Deref; + + fn bug<'a>(item: &Self::Item<'a>) -> () { + let x: T = **item; + &x as *const _; + } +} + +impl UnsafeCopy for T { + type Item<'a> = T; + //~^ ERROR type mismatch resolving `::Target == T` +} + +fn main() { + <&'static str>::bug(&""); +} diff --git a/src/test/ui/generic-associated-types/issue-68656-unsized-values.stderr b/src/test/ui/generic-associated-types/issue-68656-unsized-values.stderr new file mode 100644 index 0000000000000..e1ceeac3196a8 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-68656-unsized-values.stderr @@ -0,0 +1,30 @@ +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-68656-unsized-values.rs:3:12 + | +LL | #![feature(generic_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #44265 for more information + +error[E0271]: type mismatch resolving `::Target == T` + --> $DIR/issue-68656-unsized-values.rs:16:5 + | +LL | type Item<'a>: std::ops::Deref; + | ------------------------------------------- required by `UnsafeCopy::Item` +... +LL | impl UnsafeCopy for T { + | - this type parameter +LL | type Item<'a> = T; + | ^^^^^^^^^^^^^^^^^^ expected type parameter `T`, found associated type + | + = note: expected type parameter `T` + found associated type `::Target` +help: consider further restricting this bound + | +LL | impl> UnsafeCopy for T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/generic-associated-types/iterable.rs b/src/test/ui/generic-associated-types/iterable.rs index 105ab4a8adc38..600a69006c1ea 100644 --- a/src/test/ui/generic-associated-types/iterable.rs +++ b/src/test/ui/generic-associated-types/iterable.rs @@ -1,7 +1,7 @@ #![allow(incomplete_features)] #![feature(generic_associated_types)] -// FIXME(#30472) normalize enough to handle this. +// run-pass trait Iterable { type Item<'a> where Self: 'a; @@ -13,39 +13,35 @@ trait Iterable { // Impl for struct type impl Iterable for Vec { type Item<'a> where T: 'a = as Iterator>::Item; - //~^ ERROR type mismatch resolving type Iter<'a> where T: 'a = std::slice::Iter<'a, T>; fn iter<'a>(&'a self) -> Self::Iter<'a> { - //~^ ERROR type mismatch resolving - self.iter() + self[..].iter() } } // Impl for a primitive type impl Iterable for [T] { type Item<'a> where T: 'a = as Iterator>::Item; - //~^ ERROR type mismatch resolving type Iter<'a> where T: 'a = std::slice::Iter<'a, T>; fn iter<'a>(&'a self) -> Self::Iter<'a> { - //~^ ERROR type mismatch resolving self.iter() } } -fn make_iter<'a, I: Iterable>(it: &'a I) -> I::Iter<'a> { +fn make_iter<'a, I: Iterable + ?Sized>(it: &'a I) -> I::Iter<'a> { it.iter() } -fn get_first<'a, I: Iterable>(it: &'a I) -> Option> { +fn get_first<'a, I: Iterable + ?Sized>(it: &'a I) -> Option> { it.iter().next() } fn main() { let v = vec![1, 2, 3]; - assert_eq!(v, make_iter(&v).copied().collect()); - assert_eq!(v, make_iter(&*v).copied().collect()); - assert_eq!(1, get_first(&v)); - assert_eq!(1, get_first(&*v)); + assert_eq!(v, make_iter(&v).copied().collect::>()); + assert_eq!(v, make_iter(&*v).copied().collect::>()); + assert_eq!(Some(&1), get_first(&v)); + assert_eq!(Some(&1), get_first(&*v)); } diff --git a/src/test/ui/generic-associated-types/iterable.stderr b/src/test/ui/generic-associated-types/iterable.stderr deleted file mode 100644 index ccb1c9bcc7f4e..0000000000000 --- a/src/test/ui/generic-associated-types/iterable.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error[E0271]: type mismatch resolving `for<'a> < as Iterable>::Iter<'a> as std::iter::Iterator>::Item == as Iterable>::Item<'a>` - --> $DIR/iterable.rs:15:5 - | -LL | impl Iterable for Vec { - | --------------------------- in this `impl` item -LL | type Item<'a> where T: 'a = as Iterator>::Item; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 `&_` - = 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:27:5 - | -LL | impl Iterable for [T] { - | ------------------------ in this `impl` item -LL | type Item<'a> where T: 'a = as Iterator>::Item; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 `&_` - = 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:19:30 - | -LL | trait Iterable { - | -------------- required by `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<'_>` - = 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:31:30 - | -LL | trait Iterable { - | -------------- required by `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<'_>` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0271`. 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..3ba7d043d0759 --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.fixed @@ -0,0 +1,45 @@ +// 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 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) + } +} + +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..962d2db9476bd --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.rs @@ -0,0 +1,45 @@ +// 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 B: Add { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) + } +} + +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..630ceac093ef2 --- /dev/null +++ b/src/test/ui/generic-associated-types/missing-bounds.stderr @@ -0,0 +1,49 @@ +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: aborting due to 3 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/generic-associated-types/parameter_number_and_kind.rs b/src/test/ui/generic-associated-types/parameter_number_and_kind.rs index 0edc5c48c01af..f4d09fc1539da 100644 --- a/src/test/ui/generic-associated-types/parameter_number_and_kind.rs +++ b/src/test/ui/generic-associated-types/parameter_number_and_kind.rs @@ -7,18 +7,14 @@ trait Foo { type B<'a, 'b>; type C; type D; - //~^ ERROR type-generic associated types are not yet implemented type E<'a, T>; - //~^ ERROR type-generic associated types are not yet implemented // Test parameters in default values type FOk = Self::E<'static, T>; - //~^ ERROR type-generic associated types are not yet implemented type FErr1 = Self::E<'static, 'static>; //~^ ERROR wrong number of lifetime arguments: expected 1, found 2 //~| ERROR wrong number of type arguments: expected 1, found 0 type FErr2 = Self::E<'static, T, u32>; - //~^ ERROR type-generic associated types are not yet implemented - //~| ERROR wrong number of type arguments: expected 1, found 2 + //~^ ERROR wrong number of type arguments: expected 1, found 2 } fn main() {} diff --git a/src/test/ui/generic-associated-types/parameter_number_and_kind.stderr b/src/test/ui/generic-associated-types/parameter_number_and_kind.stderr index 028ab72f48812..ed090e302cefa 100644 --- a/src/test/ui/generic-associated-types/parameter_number_and_kind.stderr +++ b/src/test/ui/generic-associated-types/parameter_number_and_kind.stderr @@ -1,53 +1,21 @@ -error: type-generic associated types are not yet implemented - --> $DIR/parameter_number_and_kind.rs:9:5 - | -LL | type D; - | ^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: type-generic associated types are not yet implemented - --> $DIR/parameter_number_and_kind.rs:11:5 - | -LL | type E<'a, T>; - | ^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: type-generic associated types are not yet implemented - --> $DIR/parameter_number_and_kind.rs:14:5 - | -LL | type FOk = Self::E<'static, T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: type-generic associated types are not yet implemented - --> $DIR/parameter_number_and_kind.rs:19:5 - | -LL | type FErr2 = Self::E<'static, T, u32>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - error[E0107]: wrong number of lifetime arguments: expected 1, found 2 - --> $DIR/parameter_number_and_kind.rs:16:35 + --> $DIR/parameter_number_and_kind.rs:13:35 | LL | type FErr1 = Self::E<'static, 'static>; | ^^^^^^^ unexpected lifetime argument error[E0107]: wrong number of type arguments: expected 1, found 0 - --> $DIR/parameter_number_and_kind.rs:16:18 + --> $DIR/parameter_number_and_kind.rs:13:18 | LL | type FErr1 = Self::E<'static, 'static>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 type argument error[E0107]: wrong number of type arguments: expected 1, found 2 - --> $DIR/parameter_number_and_kind.rs:19:41 + --> $DIR/parameter_number_and_kind.rs:16:41 | LL | type FErr2 = Self::E<'static, T, u32>; | ^^^ unexpected type argument -error: aborting due to 7 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/generic-associated-types/pointer_family.rs b/src/test/ui/generic-associated-types/pointer_family.rs index 1668759b4e39c..b322b752a1567 100644 --- a/src/test/ui/generic-associated-types/pointer_family.rs +++ b/src/test/ui/generic-associated-types/pointer_family.rs @@ -1,7 +1,7 @@ #![allow(incomplete_features)] #![feature(generic_associated_types)] -// FIXME(#44265): allow type-generic associated types. +// check-pass use std::rc::Rc; use std::sync::Arc; @@ -9,7 +9,6 @@ use std::ops::Deref; trait PointerFamily { type Pointer: Deref; - //~^ ERROR type-generic associated types are not yet implemented fn new(value: T) -> Self::Pointer; } diff --git a/src/test/ui/generic-associated-types/pointer_family.stderr b/src/test/ui/generic-associated-types/pointer_family.stderr deleted file mode 100644 index 83fe992fcb571..0000000000000 --- a/src/test/ui/generic-associated-types/pointer_family.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: type-generic associated types are not yet implemented - --> $DIR/pointer_family.rs:11:5 - | -LL | type Pointer: Deref; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: aborting due to previous error - diff --git a/src/test/ui/generic-associated-types/shadowing.rs b/src/test/ui/generic-associated-types/shadowing.rs index 5c308948bd3f8..44528ca1da36e 100644 --- a/src/test/ui/generic-associated-types/shadowing.rs +++ b/src/test/ui/generic-associated-types/shadowing.rs @@ -18,12 +18,10 @@ impl<'a> NoShadow<'a> for &'a u32 { trait ShadowT { type Bar; //~^ ERROR the name `T` is already used - //~| ERROR type-generic associated types are not yet implemented } trait NoShadowT { type Bar; // OK - //~^ ERROR type-generic associated types are not yet implemented } impl NoShadowT for Option { diff --git a/src/test/ui/generic-associated-types/shadowing.stderr b/src/test/ui/generic-associated-types/shadowing.stderr index 2d9a0d6fceb2d..d51c29080a0c9 100644 --- a/src/test/ui/generic-associated-types/shadowing.stderr +++ b/src/test/ui/generic-associated-types/shadowing.stderr @@ -7,7 +7,7 @@ LL | type Bar; | ^ already used error[E0403]: the name `T` is already used for a generic parameter in this item's generic parameters - --> $DIR/shadowing.rs:30:14 + --> $DIR/shadowing.rs:28:14 | LL | impl NoShadowT for Option { | - first use of `T` @@ -30,23 +30,7 @@ LL | impl<'a> NoShadow<'a> for &'a u32 { LL | type Bar<'a> = i32; | ^^ lifetime 'a already in scope -error: type-generic associated types are not yet implemented - --> $DIR/shadowing.rs:19:5 - | -LL | type Bar; - | ^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: type-generic associated types are not yet implemented - --> $DIR/shadowing.rs:25:5 - | -LL | type Bar; // OK - | ^^^^^^^^^^^^ - | - = note: for more information, see issue #44265 for more information - -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0403, E0496. For more information about an error, try `rustc --explain E0403`. diff --git a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs new file mode 100644 index 0000000000000..7510c58d57489 --- /dev/null +++ b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.rs @@ -0,0 +1,22 @@ +#![allow(incomplete_features)] +#![feature(generic_associated_types)] + +trait ATy { + type Item<'a>: 'a; +} + +impl<'b> ATy for &'b () { + type Item<'a> = &'b (); + //~^ ERROR does not fulfill the required lifetime +} + +trait StaticTy { + type Item<'a>: 'static; +} + +impl StaticTy for () { + type Item<'a> = &'a (); + //~^ ERROR does not fulfill the required lifetime +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr new file mode 100644 index 0000000000000..5d612284a2187 --- /dev/null +++ b/src/test/ui/generic-associated-types/unsatisfied-outlives-bound.stderr @@ -0,0 +1,23 @@ +error[E0477]: the type `&'b ()` does not fulfill the required lifetime + --> $DIR/unsatisfied-outlives-bound.rs:9:5 + | +LL | type Item<'a> = &'b (); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type must outlive the lifetime `'a` as defined on the associated item at 9:15 + --> $DIR/unsatisfied-outlives-bound.rs:9:15 + | +LL | type Item<'a> = &'b (); + | ^^ + +error[E0477]: the type `&'a ()` does not fulfill the required lifetime + --> $DIR/unsatisfied-outlives-bound.rs:18:5 + | +LL | type Item<'a> = &'a (); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: type must satisfy the static lifetime + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0477`. 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.rs b/src/test/ui/glob-resolve1.rs index 63c435cc20641..32660fdb41876 100644 --- a/src/test/ui/glob-resolve1.rs +++ b/src/test/ui/glob-resolve1.rs @@ -29,3 +29,7 @@ fn main() { foo::(); //~ ERROR: cannot find type `C` in this scope foo::(); //~ ERROR: cannot find type `D` in this scope } + +mod other { + pub fn import() {} +} diff --git a/src/test/ui/glob-resolve1.stderr b/src/test/ui/glob-resolve1.stderr index 3db24431586da..3c818f3ae48ea 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; | @@ -42,12 +42,17 @@ error[E0425]: cannot find function `import` in this scope | LL | import(); | ^^^^^^ not found in this scope + | +help: consider importing this function + | +LL | use other::import; + | error[E0412]: cannot find type `A` in this scope --> $DIR/glob-resolve1.rs:28:11 | LL | pub enum B { B1 } - | ----------------- similarly named enum `B` defined here + | ---------- similarly named enum `B` defined here ... LL | foo::(); | ^ @@ -56,7 +61,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; | @@ -65,7 +70,7 @@ error[E0412]: cannot find type `C` in this scope --> $DIR/glob-resolve1.rs:29:11 | LL | pub enum B { B1 } - | ----------------- similarly named enum `B` defined here + | ---------- similarly named enum `B` defined here ... LL | foo::(); | ^ @@ -74,7 +79,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; | @@ -83,7 +88,7 @@ error[E0412]: cannot find type `D` in this scope --> $DIR/glob-resolve1.rs:30:11 | LL | pub enum B { B1 } - | ----------------- similarly named enum `B` defined here + | ---------- similarly named enum `B` defined here ... LL | foo::(); | ^ @@ -92,7 +97,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..028bfb89312fc 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,22 +68,25 @@ 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 +error[E0004]: non-exhaustive patterns: `u8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:41:12 | LL | m!(0, ..core::u8::MAX); - | ^ pattern `std::u8::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `254u8..=u8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:42:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `254u8..=std::u8::MAX` not covered + | ^ pattern `254u8..=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,14 +95,16 @@ 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 +error[E0004]: non-exhaustive patterns: `u8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:44:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::u8::MAX` not covered + | ^ pattern `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,22 +122,25 @@ 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 +error[E0004]: non-exhaustive patterns: `u16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:54:12 | LL | m!(0, ..core::u16::MAX); - | ^ pattern `std::u16::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `65534u16..=u16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:55:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `65534u16..=std::u16::MAX` not covered + | ^ pattern `65534u16..=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,14 +149,16 @@ 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 +error[E0004]: non-exhaustive patterns: `u16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:57:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::u16::MAX` not covered + | ^ pattern `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,22 +176,25 @@ 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 +error[E0004]: non-exhaustive patterns: `u32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:67:12 | LL | m!(0, ..core::u32::MAX); - | ^ pattern `std::u32::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `4294967294u32..=u32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:68:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `4294967294u32..=std::u32::MAX` not covered + | ^ pattern `4294967294u32..=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,14 +203,16 @@ 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 +error[E0004]: non-exhaustive patterns: `u32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:70:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::u32::MAX` not covered + | ^ pattern `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,22 +230,25 @@ 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 +error[E0004]: non-exhaustive patterns: `u64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:80:12 | LL | m!(0, ..core::u64::MAX); - | ^ pattern `std::u64::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `18446744073709551614u64..=u64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:81:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `18446744073709551614u64..=std::u64::MAX` not covered + | ^ pattern `18446744073709551614u64..=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,14 +257,16 @@ 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 +error[E0004]: non-exhaustive patterns: `u64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:83:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::u64::MAX` not covered + | ^ pattern `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,22 +284,25 @@ 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 +error[E0004]: non-exhaustive patterns: `u128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:93:12 | LL | m!(0, ..core::u128::MAX); - | ^ pattern `std::u128::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `340282366920938463463374607431768211454u128..=u128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:94:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `340282366920938463463374607431768211454u128..=std::u128::MAX` not covered + | ^ pattern `340282366920938463463374607431768211454u128..=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,14 +311,16 @@ 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 +error[E0004]: non-exhaustive patterns: `u128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:96:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::u128::MAX` not covered + | ^ pattern `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,38 +338,43 @@ 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 +error[E0004]: non-exhaustive patterns: `i8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:109:12 | LL | m!(0, ..core::i8::MAX); - | ^ pattern `std::i8::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `126i8..=i8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:110:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `126i8..=std::i8::MAX` not covered + | ^ pattern `126i8..=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 +error[E0004]: non-exhaustive patterns: `i8::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:111:12 | LL | m!(0, ALMOST_MIN..); - | ^ pattern `std::i8::MIN` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `i8::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:112:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::i8::MAX` not covered + | ^ pattern `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,38 +392,43 @@ 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 +error[E0004]: non-exhaustive patterns: `i16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:122:12 | LL | m!(0, ..core::i16::MAX); - | ^ pattern `std::i16::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `32766i16..=i16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:123:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `32766i16..=std::i16::MAX` not covered + | ^ pattern `32766i16..=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 +error[E0004]: non-exhaustive patterns: `i16::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:124:12 | LL | m!(0, ALMOST_MIN..); - | ^ pattern `std::i16::MIN` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `i16::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:125:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::i16::MAX` not covered + | ^ pattern `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,38 +446,43 @@ 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 +error[E0004]: non-exhaustive patterns: `i32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:135:12 | LL | m!(0, ..core::i32::MAX); - | ^ pattern `std::i32::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `2147483646i32..=i32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:136:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `2147483646i32..=std::i32::MAX` not covered + | ^ pattern `2147483646i32..=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 +error[E0004]: non-exhaustive patterns: `i32::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:137:12 | LL | m!(0, ALMOST_MIN..); - | ^ pattern `std::i32::MIN` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `i32::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:138:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::i32::MAX` not covered + | ^ pattern `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,38 +500,43 @@ 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 +error[E0004]: non-exhaustive patterns: `i64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:148:12 | LL | m!(0, ..core::i64::MAX); - | ^ pattern `std::i64::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `9223372036854775806i64..=i64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:149:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `9223372036854775806i64..=std::i64::MAX` not covered + | ^ pattern `9223372036854775806i64..=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 +error[E0004]: non-exhaustive patterns: `i64::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:150:12 | LL | m!(0, ALMOST_MIN..); - | ^ pattern `std::i64::MIN` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `i64::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:151:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::i64::MAX` not covered + | ^ pattern `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,38 +554,43 @@ 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 +error[E0004]: non-exhaustive patterns: `i128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:161:12 | LL | m!(0, ..core::i128::MAX); - | ^ pattern `std::i128::MAX` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `170141183460469231731687303715884105726i128..=i128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:162:12 | LL | m!(0, ..ALMOST_MAX); - | ^ pattern `170141183460469231731687303715884105726i128..=std::i128::MAX` not covered + | ^ pattern `170141183460469231731687303715884105726i128..=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 +error[E0004]: non-exhaustive patterns: `i128::MIN` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:163:12 | LL | m!(0, ALMOST_MIN..); - | ^ pattern `std::i128::MIN` not covered + | ^ pattern `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 +error[E0004]: non-exhaustive patterns: `i128::MAX` not covered --> $DIR/half-open-range-pats-exhaustive-fail.rs:164:12 | LL | m!(0, ..=ALMOST_MAX); - | ^ pattern `std::i128::MAX` not covered + | ^ pattern `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/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.nll.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.nll.stderr new file mode 100644 index 0000000000000..d5343566633d5 --- /dev/null +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.nll.stderr @@ -0,0 +1,14 @@ +error: higher-ranked subtype error + --> $DIR/hr-subtype.rs:45:13 + | +LL | gimme::<$t1>(None::<$t2>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | / check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32, +LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) } + | |_____________________________________________- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr index b91798fa12379..92a85825030c2 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_ret_a_vs_bound_a_ret_a.stderr @@ -1,12 +1,12 @@ error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:39:26 + --> $DIR/hr-subtype.rs:45:26 | LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a + | ^^^^^^^^^^^ one type is more general than the other ... LL | / check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32, -LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) } - | |_________________________________________________________________________________________- in this macro invocation +LL | | for<'a> fn(&'a u32, &'a u32) -> &'a u32) } + | |_____________________________________________- in this macro invocation | = note: expected enum `std::option::Option fn(&'a u32, &'b u32) -> &'a u32>` found enum `std::option::Option fn(&'a u32, &'a u32) -> &'a u32>` diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr index 45f53d4fe99db..948375566104b 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_b_vs_bound_a.stderr @@ -1,17 +1,14 @@ -error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:39:26 +error: fatal error triggered by #[rustc_error] + --> $DIR/hr-subtype.rs:102:1 | -LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a -... -LL | / check! { bound_a_b_vs_bound_a: (for<'a,'b> fn(&'a u32, &'b u32), -LL | | for<'a> fn(&'a u32, &'a u32)) } - | |__________________________________________________________________- in this macro invocation - | - = note: expected enum `std::option::Option fn(&'a u32, &'b u32)>` - found enum `std::option::Option fn(&'a u32, &'a u32)>` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +LL | / fn main() { +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr index 6aba6466fada5..948375566104b 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_a.stderr @@ -1,11 +1,11 @@ error: fatal error triggered by #[rustc_error] - --> $DIR/hr-subtype.rs:100:1 + --> $DIR/hr-subtype.rs:102:1 | LL | / fn main() { LL | | LL | | LL | | -LL | | +... | LL | | LL | | } | |_^ diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr index 6aba6466fada5..948375566104b 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_bound_b.stderr @@ -1,11 +1,11 @@ error: fatal error triggered by #[rustc_error] - --> $DIR/hr-subtype.rs:100:1 + --> $DIR/hr-subtype.rs:102:1 | LL | / fn main() { LL | | LL | | LL | | -LL | | +... | LL | | LL | | } | |_^ diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.nll.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.nll.stderr new file mode 100644 index 0000000000000..f115609396756 --- /dev/null +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.nll.stderr @@ -0,0 +1,14 @@ +error: higher-ranked subtype error + --> $DIR/hr-subtype.rs:45:13 + | +LL | gimme::<$t1>(None::<$t2>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | / check! { bound_a_vs_free_x: (for<'a> fn(&'a u32), +LL | | fn(&'x u32)) } + | |______________- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr index c3e4f6d2ed0c1..98f5bff732762 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_a_vs_free_x.stderr @@ -1,12 +1,12 @@ error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:39:26 + --> $DIR/hr-subtype.rs:45:26 | LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a + | ^^^^^^^^^^^ one type is more general than the other ... LL | / check! { bound_a_vs_free_x: (for<'a> fn(&'a u32), -LL | | fn(&'x u32)) } - | |___________________________________________- in this macro invocation +LL | | fn(&'x u32)) } + | |______________- in this macro invocation | = note: expected enum `std::option::Option fn(&'a u32)>` found enum `std::option::Option` diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr index 4d7b86027f564..948375566104b 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_b_vs_bound_co_a.stderr @@ -1,17 +1,14 @@ -error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:39:26 +error: fatal error triggered by #[rustc_error] + --> $DIR/hr-subtype.rs:102:1 | -LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a -... -LL | / check! { bound_co_a_b_vs_bound_co_a: (for<'a,'b> fn(Co<'a>, Co<'b>), -LL | | for<'a> fn(Co<'a>, Co<'a>)) } - | |______________________________________________________________________- in this macro invocation - | - = note: expected enum `std::option::Option fn(Co<'a>, Co<'b>)>` - found enum `std::option::Option fn(Co<'a>, Co<'a>)>` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +LL | / fn main() { +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr index 7f0a4197dd7fe..948375566104b 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_co_b_ret_contra_a.stderr @@ -1,17 +1,14 @@ -error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:39:26 +error: fatal error triggered by #[rustc_error] + --> $DIR/hr-subtype.rs:102:1 | -LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a -... -LL | / check! { bound_co_a_co_b_ret_contra_a: (for<'a,'b> fn(Co<'a>, Co<'b>) -> Contra<'a>, -LL | | for<'a> fn(Co<'a>, Co<'a>) -> Contra<'a>) } - | |______________________________________________________________________________________- in this macro invocation - | - = note: expected enum `std::option::Option fn(Co<'a>, Co<'b>) -> Contra<'a>>` - found enum `std::option::Option fn(Co<'a>, Co<'a>) -> Contra<'a>>` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +LL | / fn main() { +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr index 6aba6466fada5..948375566104b 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_co_a_vs_bound_co_b.stderr @@ -1,11 +1,11 @@ error: fatal error triggered by #[rustc_error] - --> $DIR/hr-subtype.rs:100:1 + --> $DIR/hr-subtype.rs:102:1 | LL | / fn main() { LL | | LL | | LL | | -LL | | +... | LL | | LL | | } | |_^ diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr index c12e543a44e79..948375566104b 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_contra_a_contra_b_ret_co_a.stderr @@ -1,17 +1,14 @@ -error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:39:26 +error: fatal error triggered by #[rustc_error] + --> $DIR/hr-subtype.rs:102:1 | -LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a -... -LL | / check! { bound_contra_a_contra_b_ret_co_a: (for<'a,'b> fn(Contra<'a>, Contra<'b>) -> Co<'a>, -LL | | for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>) } - | |______________________________________________________________________________________________- in this macro invocation - | - = note: expected enum `std::option::Option fn(Contra<'a>, Contra<'b>) -> Co<'a>>` - found enum `std::option::Option fn(Contra<'a>, Contra<'a>) -> Co<'a>>` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +LL | / fn main() { +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.nll.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.nll.stderr new file mode 100644 index 0000000000000..4541c462ee056 --- /dev/null +++ b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.nll.stderr @@ -0,0 +1,26 @@ +error: higher-ranked subtype error + --> $DIR/hr-subtype.rs:45:13 + | +LL | gimme::<$t1>(None::<$t2>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>), +LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } + | |__________________________________- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: higher-ranked subtype error + --> $DIR/hr-subtype.rs:45:13 + | +LL | gimme::<$t1>(None::<$t2>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>), +LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } + | |__________________________________- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr index 460356856bd56..100ba6ac27e25 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_b_vs_bound_inv_a.stderr @@ -1,12 +1,12 @@ error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:39:26 + --> $DIR/hr-subtype.rs:45:26 | LL | gimme::<$t1>(None::<$t2>); - | ^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter 'a + | ^^^^^^^^^^^ one type is more general than the other ... LL | / check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>), -LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } - | |__________________________________________________________________________- in this macro invocation +LL | | for<'a> fn(Inv<'a>, Inv<'a>)) } + | |__________________________________- in this macro invocation | = note: expected enum `std::option::Option fn(Inv<'a>, Inv<'b>)>` found enum `std::option::Option fn(Inv<'a>, Inv<'a>)>` diff --git a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr index 6aba6466fada5..948375566104b 100644 --- a/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.bound_inv_a_vs_bound_inv_b.stderr @@ -1,11 +1,11 @@ error: fatal error triggered by #[rustc_error] - --> $DIR/hr-subtype.rs:100:1 + --> $DIR/hr-subtype.rs:102:1 | LL | / fn main() { LL | | LL | | LL | | -LL | | +... | LL | | LL | | } | |_^ diff --git a/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.nll.stderr b/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.nll.stderr index 6b5e7a5a6345a..af5cf41be0a48 100644 --- a/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.nll.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.nll.stderr @@ -1,33 +1,33 @@ error: lifetime may not live long enough - --> $DIR/hr-subtype.rs:33:13 + --> $DIR/hr-subtype.rs:39:13 | -LL | fn subtype<'x,'y:'x,'z:'y>() { - | -- -- lifetime `'y` defined here +LL | fn subtype<'x, 'y: 'x, 'z: 'y>() { + | -- -- lifetime `'y` defined here | | | lifetime `'x` defined here LL | gimme::<$t2>(None::<$t1>); | ^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'x` must outlive `'y` ... LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), -LL | | fn(Inv<'y>)) } - | |__________________________________________________- in this macro invocation +LL | | fn(Inv<'y>)) } + | |______________- in this macro invocation | = help: consider adding the following bound: `'x: 'y` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: lifetime may not live long enough - --> $DIR/hr-subtype.rs:39:13 + --> $DIR/hr-subtype.rs:45:13 | -LL | fn supertype<'x,'y:'x,'z:'y>() { - | -- -- lifetime `'y` defined here +LL | fn supertype<'x, 'y: 'x, 'z: 'y>() { + | -- -- lifetime `'y` defined here | | | lifetime `'x` defined here LL | gimme::<$t1>(None::<$t2>); | ^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'x` must outlive `'y` ... LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), -LL | | fn(Inv<'y>)) } - | |__________________________________________________- in this macro invocation +LL | | fn(Inv<'y>)) } + | |______________- in this macro invocation | = help: consider adding the following bound: `'x: 'y` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.stderr b/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.stderr index fc3643306e628..3c8af20e50cef 100644 --- a/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.stderr @@ -1,65 +1,65 @@ error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:33:26 + --> $DIR/hr-subtype.rs:39:26 | LL | gimme::<$t2>(None::<$t1>); | ^^^^^^^^^^^ lifetime mismatch ... LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), -LL | | fn(Inv<'y>)) } - | |__________________________________________________- in this macro invocation +LL | | fn(Inv<'y>)) } + | |______________- in this macro invocation | = note: expected enum `std::option::Option)>` found enum `std::option::Option)>` -note: the lifetime `'x` as defined on the function body at 32:20... - --> $DIR/hr-subtype.rs:32:20 +note: the lifetime `'x` as defined on the function body at 38:20... + --> $DIR/hr-subtype.rs:38:20 | -LL | fn subtype<'x,'y:'x,'z:'y>() { +LL | fn subtype<'x, 'y: 'x, 'z: 'y>() { | ^^ ... LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), -LL | | fn(Inv<'y>)) } - | |__________________________________________________- in this macro invocation -note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 32:23 - --> $DIR/hr-subtype.rs:32:23 +LL | | fn(Inv<'y>)) } + | |______________- in this macro invocation +note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 38:24 + --> $DIR/hr-subtype.rs:38:24 | -LL | fn subtype<'x,'y:'x,'z:'y>() { - | ^^ +LL | fn subtype<'x, 'y: 'x, 'z: 'y>() { + | ^^ ... LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), -LL | | fn(Inv<'y>)) } - | |__________________________________________________- in this macro invocation +LL | | fn(Inv<'y>)) } + | |______________- in this macro invocation = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:39:26 + --> $DIR/hr-subtype.rs:45:26 | LL | gimme::<$t1>(None::<$t2>); | ^^^^^^^^^^^ lifetime mismatch ... LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), -LL | | fn(Inv<'y>)) } - | |__________________________________________________- in this macro invocation +LL | | fn(Inv<'y>)) } + | |______________- in this macro invocation | = note: expected enum `std::option::Option)>` found enum `std::option::Option)>` -note: the lifetime `'x` as defined on the function body at 38:22... - --> $DIR/hr-subtype.rs:38:22 +note: the lifetime `'x` as defined on the function body at 44:22... + --> $DIR/hr-subtype.rs:44:22 | -LL | fn supertype<'x,'y:'x,'z:'y>() { +LL | fn supertype<'x, 'y: 'x, 'z: 'y>() { | ^^ ... LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), -LL | | fn(Inv<'y>)) } - | |__________________________________________________- in this macro invocation -note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 38:25 - --> $DIR/hr-subtype.rs:38:25 +LL | | fn(Inv<'y>)) } + | |______________- in this macro invocation +note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 44:26 + --> $DIR/hr-subtype.rs:44:26 | -LL | fn supertype<'x,'y:'x,'z:'y>() { - | ^^ +LL | fn supertype<'x, 'y: 'x, 'z: 'y>() { + | ^^ ... LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), -LL | | fn(Inv<'y>)) } - | |__________________________________________________- in this macro invocation +LL | | fn(Inv<'y>)) } + | |______________- in this macro invocation = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr index 6aba6466fada5..948375566104b 100644 --- a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_x.stderr @@ -1,11 +1,11 @@ error: fatal error triggered by #[rustc_error] - --> $DIR/hr-subtype.rs:100:1 + --> $DIR/hr-subtype.rs:102:1 | LL | / fn main() { LL | | LL | | LL | | -LL | | +... | LL | | LL | | } | |_^ diff --git a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.nll.stderr b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.nll.stderr index 7c0770924daaa..75d7e0e46b72a 100644 --- a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.nll.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.nll.stderr @@ -1,16 +1,16 @@ error: lifetime may not live long enough - --> $DIR/hr-subtype.rs:39:13 + --> $DIR/hr-subtype.rs:45:13 | -LL | fn supertype<'x,'y:'x,'z:'y>() { - | -- -- lifetime `'y` defined here +LL | fn supertype<'x, 'y: 'x, 'z: 'y>() { + | -- -- lifetime `'y` defined here | | | lifetime `'x` defined here LL | gimme::<$t1>(None::<$t2>); | ^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'x` must outlive `'y` ... LL | / check! { free_x_vs_free_y: (fn(&'x u32), -LL | | fn(&'y u32)) } - | |__________________________________________- in this macro invocation +LL | | fn(&'y u32)) } + | |______________- in this macro invocation | = help: consider adding the following bound: `'x: 'y` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.stderr b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.stderr index 0dde27788f629..7b4cdd4a419b4 100644 --- a/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.stderr +++ b/src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.stderr @@ -1,33 +1,33 @@ error[E0308]: mismatched types - --> $DIR/hr-subtype.rs:39:26 + --> $DIR/hr-subtype.rs:45:26 | LL | gimme::<$t1>(None::<$t2>); | ^^^^^^^^^^^ lifetime mismatch ... LL | / check! { free_x_vs_free_y: (fn(&'x u32), -LL | | fn(&'y u32)) } - | |__________________________________________- in this macro invocation +LL | | fn(&'y u32)) } + | |______________- in this macro invocation | = note: expected enum `std::option::Option` found enum `std::option::Option` -note: the lifetime `'x` as defined on the function body at 38:22... - --> $DIR/hr-subtype.rs:38:22 +note: the lifetime `'x` as defined on the function body at 44:22... + --> $DIR/hr-subtype.rs:44:22 | -LL | fn supertype<'x,'y:'x,'z:'y>() { +LL | fn supertype<'x, 'y: 'x, 'z: 'y>() { | ^^ ... LL | / check! { free_x_vs_free_y: (fn(&'x u32), -LL | | fn(&'y u32)) } - | |__________________________________________- in this macro invocation -note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 38:25 - --> $DIR/hr-subtype.rs:38:25 +LL | | fn(&'y u32)) } + | |______________- in this macro invocation +note: ...does not necessarily outlive the lifetime `'y` as defined on the function body at 44:26 + --> $DIR/hr-subtype.rs:44:26 | -LL | fn supertype<'x,'y:'x,'z:'y>() { - | ^^ +LL | fn supertype<'x, 'y: 'x, 'z: 'y>() { + | ^^ ... LL | / check! { free_x_vs_free_y: (fn(&'x u32), -LL | | fn(&'y u32)) } - | |__________________________________________- in this macro invocation +LL | | fn(&'y u32)) } + | |______________- in this macro invocation = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/hr-subtype/hr-subtype.rs b/src/test/ui/hr-subtype/hr-subtype.rs index b31f198bd97bf..ad9500eedca93 100644 --- a/src/test/ui/hr-subtype/hr-subtype.rs +++ b/src/test/ui/hr-subtype/hr-subtype.rs @@ -18,60 +18,62 @@ // revisions: bound_inv_a_b_vs_bound_inv_a // revisions: bound_a_b_ret_a_vs_bound_a_ret_a -fn gimme(_: Option) { } +fn gimme(_: Option) {} -struct Inv<'a> { x: *mut &'a u32 } +struct Inv<'a> { + x: *mut &'a u32, +} -struct Co<'a> { x: fn(&'a u32) } +struct Co<'a> { + x: fn(&'a u32), +} -struct Contra<'a> { x: &'a u32 } +struct Contra<'a> { + x: &'a u32, +} macro_rules! check { ($rev:ident: ($t1:ty, $t2:ty)) => { #[cfg($rev)] - fn subtype<'x,'y:'x,'z:'y>() { + fn subtype<'x, 'y: 'x, 'z: 'y>() { gimme::<$t2>(None::<$t1>); //[free_inv_x_vs_free_inv_y]~^ ERROR } #[cfg($rev)] - fn supertype<'x,'y:'x,'z:'y>() { + fn supertype<'x, 'y: 'x, 'z: 'y>() { gimme::<$t1>(None::<$t2>); //[bound_a_vs_free_x]~^ ERROR //[free_x_vs_free_y]~^^ ERROR //[bound_inv_a_b_vs_bound_inv_a]~^^^ ERROR //[bound_a_b_ret_a_vs_bound_a_ret_a]~^^^^ ERROR //[free_inv_x_vs_free_inv_y]~^^^^^ ERROR - //[bound_a_b_vs_bound_a]~^^^^^^ ERROR mismatched types - //[bound_co_a_co_b_ret_contra_a]~^^^^^^^ ERROR - //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^ ERROR - //[bound_co_a_b_vs_bound_co_a]~^^^^^^^^^ ERROR } - } + }; } // If both have bound regions, they are equivalent, regardless of // variant. check! { bound_a_vs_bound_a: (for<'a> fn(&'a u32), - for<'a> fn(&'a u32)) } +for<'a> fn(&'a u32)) } check! { bound_a_vs_bound_b: (for<'a> fn(&'a u32), - for<'b> fn(&'b u32)) } +for<'b> fn(&'b u32)) } check! { bound_inv_a_vs_bound_inv_b: (for<'a> fn(Inv<'a>), - for<'b> fn(Inv<'b>)) } +for<'b> fn(Inv<'b>)) } check! { bound_co_a_vs_bound_co_b: (for<'a> fn(Co<'a>), - for<'b> fn(Co<'b>)) } +for<'b> fn(Co<'b>)) } // Bound is a subtype of free. check! { bound_a_vs_free_x: (for<'a> fn(&'a u32), - fn(&'x u32)) } +fn(&'x u32)) } // Two free regions are relatable if subtyping holds. check! { free_x_vs_free_x: (fn(&'x u32), - fn(&'x u32)) } +fn(&'x u32)) } check! { free_x_vs_free_y: (fn(&'x u32), - fn(&'y u32)) } +fn(&'y u32)) } check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), - fn(Inv<'y>)) } +fn(Inv<'y>)) } // Somewhat surprisingly, a fn taking two distinct bound lifetimes and // a fn taking one bound lifetime can be interchangeable, but only if @@ -82,25 +84,29 @@ check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>), // intersection; // - if we are contravariant, then 'a can be inferred to 'static. check! { bound_a_b_vs_bound_a: (for<'a,'b> fn(&'a u32, &'b u32), - for<'a> fn(&'a u32, &'a u32)) } +for<'a> fn(&'a u32, &'a u32)) } check! { bound_co_a_b_vs_bound_co_a: (for<'a,'b> fn(Co<'a>, Co<'b>), - for<'a> fn(Co<'a>, Co<'a>)) } +for<'a> fn(Co<'a>, Co<'a>)) } check! { bound_contra_a_contra_b_ret_co_a: (for<'a,'b> fn(Contra<'a>, Contra<'b>) -> Co<'a>, - for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>) } +for<'a> fn(Contra<'a>, Contra<'a>) -> Co<'a>) } check! { bound_co_a_co_b_ret_contra_a: (for<'a,'b> fn(Co<'a>, Co<'b>) -> Contra<'a>, - for<'a> fn(Co<'a>, Co<'a>) -> Contra<'a>) } +for<'a> fn(Co<'a>, Co<'a>) -> Contra<'a>) } // If we make those lifetimes invariant, then the two types are not interchangeable. check! { bound_inv_a_b_vs_bound_inv_a: (for<'a,'b> fn(Inv<'a>, Inv<'b>), - for<'a> fn(Inv<'a>, Inv<'a>)) } +for<'a> fn(Inv<'a>, Inv<'a>)) } check! { bound_a_b_ret_a_vs_bound_a_ret_a: (for<'a,'b> fn(&'a u32, &'b u32) -> &'a u32, - for<'a> fn(&'a u32, &'a u32) -> &'a u32) } +for<'a> fn(&'a u32, &'a u32) -> &'a u32) } #[rustc_error] fn main() { -//[bound_a_vs_bound_a]~^ ERROR fatal error triggered by #[rustc_error] -//[bound_a_vs_bound_b]~^^ ERROR fatal error triggered by #[rustc_error] -//[bound_inv_a_vs_bound_inv_b]~^^^ ERROR fatal error triggered by #[rustc_error] -//[bound_co_a_vs_bound_co_b]~^^^^ ERROR fatal error triggered by #[rustc_error] -//[free_x_vs_free_x]~^^^^^ ERROR fatal error triggered by #[rustc_error] + //[bound_a_vs_bound_a]~^ ERROR fatal error triggered by #[rustc_error] + //[bound_a_vs_bound_b]~^^ ERROR fatal error triggered by #[rustc_error] + //[bound_inv_a_vs_bound_inv_b]~^^^ ERROR fatal error triggered by #[rustc_error] + //[bound_co_a_vs_bound_co_b]~^^^^ ERROR fatal error triggered by #[rustc_error] + //[free_x_vs_free_x]~^^^^^ ERROR fatal error triggered by #[rustc_error] + //[bound_co_a_b_vs_bound_co_a]~^^^^^^ ERROR + //[bound_co_a_co_b_ret_contra_a]~^^^^^^^ ERROR + //[bound_a_b_vs_bound_a]~^^^^^^^^ ERROR + //[bound_contra_a_contra_b_ret_co_a]~^^^^^^^^^ ERROR } diff --git a/src/test/ui/hr-subtype/return-static.rs b/src/test/ui/hr-subtype/return-static.rs new file mode 100644 index 0000000000000..6455854f34db8 --- /dev/null +++ b/src/test/ui/hr-subtype/return-static.rs @@ -0,0 +1,13 @@ +// check-pass + +fn make() -> T { + panic!() +} + +fn take(x: T) {} + +fn main() { + let x: for<'a> fn(&'a u32) -> _ = make(); + let y: &'static u32 = x(&22); + take:: fn(&'b u32) -> &'b u32>(x); +} 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.nll.stderr b/src/test/ui/hrtb/hrtb-conflate-regions.nll.stderr new file mode 100644 index 0000000000000..f290a93326f37 --- /dev/null +++ b/src/test/ui/hrtb/hrtb-conflate-regions.nll.stderr @@ -0,0 +1,14 @@ +error: higher-ranked subtype error + --> $DIR/hrtb-conflate-regions.rs:27:10 + | +LL | fn b() { want_foo2::(); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/hrtb-conflate-regions.rs:27:10 + | +LL | fn b() { want_foo2::(); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/hrtb/hrtb-conflate-regions.stderr b/src/test/ui/hrtb/hrtb-conflate-regions.stderr index 9822b48f4f48f..45573814d13c0 100644 --- a/src/test/ui/hrtb/hrtb-conflate-regions.stderr +++ b/src/test/ui/hrtb/hrtb-conflate-regions.stderr @@ -1,17 +1,16 @@ -error[E0277]: the trait bound `for<'a, 'b> SomeStruct: Foo<(&'a isize, &'b isize)>` is not satisfied - --> $DIR/hrtb-conflate-regions.rs:27:22 +error: implementation of `Foo` is not general enough + --> $DIR/hrtb-conflate-regions.rs:27:10 | -LL | fn want_foo2() - | --------- -LL | where T : for<'a,'b> Foo<(&'a isize, &'b isize)> - | -------------------------------------- required by this bound in `want_foo2` +LL | / trait Foo { +LL | | fn foo(&self, x: X) { } +LL | | } + | |_- trait `Foo` defined here ... -LL | fn b() { want_foo2::(); } - | ^^^^^^^^^^ the trait `for<'a, 'b> Foo<(&'a isize, &'b isize)>` is not implemented for `SomeStruct` +LL | fn b() { want_foo2::(); } + | ^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough | - = help: the following implementations were found: - > + = note: `SomeStruct` must implement `Foo<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... + = note: ...but `SomeStruct` actually implements `Foo<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` error: aborting due to previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/hrtb/hrtb-exists-forall-fn.nll.stderr b/src/test/ui/hrtb/hrtb-exists-forall-fn.nll.stderr new file mode 100644 index 0000000000000..11390d9e2d265 --- /dev/null +++ b/src/test/ui/hrtb/hrtb-exists-forall-fn.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/hrtb-exists-forall-fn.rs:17:12 + | +LL | let _: for<'b> fn(&'b u32) = foo(); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr b/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr index 328e98657effb..9914783d9767d 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr +++ b/src/test/ui/hrtb/hrtb-exists-forall-fn.stderr @@ -2,9 +2,7 @@ error[E0308]: mismatched types --> $DIR/hrtb-exists-forall-fn.rs:17:34 | LL | let _: for<'b> fn(&'b u32) = foo(); - | ------------------- ^^^^^ expected concrete lifetime, found bound lifetime parameter 'b - | | - | expected due to this + | ^^^^^ one type is more general than the other | = note: expected fn pointer `for<'b> fn(&'b u32)` found fn pointer `fn(&u32)` diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.nll.stderr b/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.nll.stderr new file mode 100644 index 0000000000000..a4c3ffd1f6c08 --- /dev/null +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/hrtb-exists-forall-trait-contravariant.rs:34:5 + | +LL | foo::<()>(); + | ^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.rs b/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.rs index 4c1d4d28a09b0..921061916fc95 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.rs +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.rs @@ -32,5 +32,5 @@ fn main() { // NB. *However*, the reinstated leak-check gives an error here. foo::<()>(); - //~^ ERROR not satisfied + //~^ ERROR implementation of `Trait` is not general enough } 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..fe8209d054c8a 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.stderr +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-contravariant.stderr @@ -1,18 +1,14 @@ -error[E0277]: the trait bound `(): Trait fn(&'b u32)>` is not satisfied - --> $DIR/hrtb-exists-forall-trait-contravariant.rs:34:11 +error: implementation of `Trait` is not general enough + --> $DIR/hrtb-exists-forall-trait-contravariant.rs:34:5 | -LL | fn foo() - | --- -LL | where -LL | T: Trait fn(&'b u32)>, - | -------------------------- required by this bound in `foo` +LL | trait Trait {} + | ----------------- trait `Trait` defined here ... LL | foo::<()>(); - | ^^ the trait `Trait fn(&'b u32)>` is not implemented for `()` + | ^^^^^^^^^ implementation of `Trait` is not general enough | - = help: the following implementations were found: - <() as Trait> + = note: `()` must implement `Trait fn(&'b u32)>` + = note: ...but `()` actually implements `Trait`, for some specific lifetime `'0` error: aborting due to previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.rs b/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.rs index 95b57d6c5bb5e..f95496a6c3cc0 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.rs +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.rs @@ -2,6 +2,8 @@ // // In particular, we test this pattern in trait solving, where it is not connected // to any part of the source code. +// +// check-pass trait Trait {} @@ -30,9 +32,6 @@ fn main() { // - `?b: ?a` -- solveable if `?b` is inferred to `'static` // - So the subtyping check succeeds, somewhat surprisingly. // This is because we can use `'static`. - // - // NB. *However*, the reinstated leak-check gives an error here. foo::<()>(); - //~^ ERROR not satisfied } diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.stderr b/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.stderr deleted file mode 100644 index dddc2bcce49e5..0000000000000 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-covariant.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0277]: the trait bound `(): Trait fn(fn(&'b u32))>` is not satisfied - --> $DIR/hrtb-exists-forall-trait-covariant.rs:36:11 - | -LL | fn foo() - | --- -LL | where -LL | T: Trait fn(fn(&'b u32))>, - | ------------------------------ required by this bound in `foo` -... -LL | foo::<()>(); - | ^^ the trait `Trait fn(fn(&'b u32))>` is not implemented for `()` - | - = help: the following implementations were found: - <() as Trait> - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.nll.stderr b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.nll.stderr new file mode 100644 index 0000000000000..e2a399b2faa9d --- /dev/null +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/hrtb-exists-forall-trait-invariant.rs:28:5 + | +LL | foo::<()>(); + | ^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.rs b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.rs index 827a68beee8bd..b1b7ec6bcf1a5 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.rs +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.rs @@ -25,5 +25,5 @@ fn main() { // yielding `fn(&!b u32)`, in a fresh universe U1 // - So we get `?a = !b` but the universe U0 assigned to `?a` cannot name `!b`. - foo::<()>(); //~ ERROR not satisfied + foo::<()>(); //~ ERROR implementation of `Trait` is not general enough } 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..720e2276d5343 100644 --- a/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr +++ b/src/test/ui/hrtb/hrtb-exists-forall-trait-invariant.stderr @@ -1,18 +1,14 @@ -error[E0277]: the trait bound `(): Trait fn(std::cell::Cell<&'b u32>)>` is not satisfied - --> $DIR/hrtb-exists-forall-trait-invariant.rs:28:11 +error: implementation of `Trait` is not general enough + --> $DIR/hrtb-exists-forall-trait-invariant.rs:28:5 | -LL | fn foo() - | --- -LL | where -LL | T: Trait fn(Cell<&'b u32>)>, - | -------------------------------- required by this bound in `foo` +LL | trait Trait {} + | ----------------- trait `Trait` defined here ... LL | foo::<()>(); - | ^^ the trait `Trait fn(std::cell::Cell<&'b u32>)>` is not implemented for `()` + | ^^^^^^^^^ implementation of `Trait` is not general enough | - = help: the following implementations were found: - <() as Trait)>> + = note: `()` must implement `Trait fn(std::cell::Cell<&'b u32>)>` + = note: ...but `()` actually implements `Trait)>`, for some specific lifetime `'0` error: aborting due to previous error -For more information about this error, try `rustc --explain E0277`. 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.nll.stderr b/src/test/ui/hrtb/hrtb-just-for-static.nll.stderr new file mode 100644 index 0000000000000..8901a1b46817d --- /dev/null +++ b/src/test/ui/hrtb/hrtb-just-for-static.nll.stderr @@ -0,0 +1,24 @@ +error: higher-ranked subtype error + --> $DIR/hrtb-just-for-static.rs:24:5 + | +LL | want_hrtb::() + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/hrtb-just-for-static.rs:30:5 + | +LL | fn give_some<'a>() { + | -- lifetime `'a` defined here +LL | want_hrtb::<&'a u32>() + | ^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + | + = help: consider replacing `'a` with `'static` + +error: higher-ranked subtype error + --> $DIR/hrtb-just-for-static.rs:30:5 + | +LL | want_hrtb::<&'a u32>() + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 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..5e3014317f5bc 100644 --- a/src/test/ui/hrtb/hrtb-just-for-static.stderr +++ b/src/test/ui/hrtb/hrtb-just-for-static.stderr @@ -1,31 +1,30 @@ -error[E0277]: the trait bound `for<'a> StaticInt: Foo<&'a isize>` is not satisfied - --> $DIR/hrtb-just-for-static.rs:24:17 +error: implementation of `Foo` is not general enough + --> $DIR/hrtb-just-for-static.rs:24:5 | -LL | fn want_hrtb() - | --------- -LL | where T : for<'a> Foo<&'a isize> - | ---------------------- required by this bound in `want_hrtb` +LL | / trait Foo { +LL | | fn foo(&self, x: X) { } +LL | | } + | |_- trait `Foo` defined here ... -LL | want_hrtb::() - | ^^^^^^^^^ the trait `for<'a> Foo<&'a isize>` is not implemented for `StaticInt` +LL | want_hrtb::() + | ^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough | - = help: the following implementations were found: - > + = note: `StaticInt` must implement `Foo<&'0 isize>`, for any lifetime `'0`... + = note: ...but `StaticInt` actually implements `Foo<&'1 isize>`, for some specific lifetime `'1` -error[E0277]: the trait bound `for<'a> &'a u32: Foo<&'a isize>` is not satisfied - --> $DIR/hrtb-just-for-static.rs:30:17 +error: implementation of `Foo` is not general enough + --> $DIR/hrtb-just-for-static.rs:30:5 | -LL | fn want_hrtb() - | --------- -LL | where T : for<'a> Foo<&'a isize> - | ---------------------- required by this bound in `want_hrtb` +LL | / trait Foo { +LL | | fn foo(&self, x: X) { } +LL | | } + | |_- trait `Foo` defined here ... -LL | want_hrtb::<&'a u32>() - | ^^^^^^^ the trait `for<'a> Foo<&'a isize>` is not implemented for `&'a u32` +LL | want_hrtb::<&'a u32>() + | ^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough | - = help: the following implementations were found: - <&'a u32 as Foo<&'a isize>> + = note: `Foo<&'0 isize>` would have to be implemented for the type `&'a u32`, for any lifetime `'0`... + = note: ...but `Foo<&'1 isize>` is actually implemented for the type `&'1 u32`, for some specific lifetime `'1` error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. 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-30786.migrate.stderr b/src/test/ui/hrtb/issue-30786.migrate.stderr index c0e3fd3cf4679..90a7cadca41b7 100644 --- a/src/test/ui/hrtb/issue-30786.migrate.stderr +++ b/src/test/ui/hrtb/issue-30786.migrate.stderr @@ -1,17 +1,43 @@ -error: implementation of `Stream` is not general enough - --> $DIR/issue-30786.rs:108:22 +error[E0599]: no method named `filterx` found for struct `Map` in the current scope + --> $DIR/issue-30786.rs:128:22 | -LL | / pub trait Stream { -LL | | type Item; -LL | | fn next(self) -> Option; -LL | | } - | |_- trait `Stream` defined here +LL | pub struct Map { + | -------------------- + | | + | method `filterx` not found for this + | doesn't satisfy `_: StreamExt` ... -LL | let map = source.map(|x: &_| x); - | ^^^ implementation of `Stream` is not general enough +LL | let filter = map.filterx(|x: &_| true); + | ^^^^^^^ method not found in `Map` | - = note: `Stream` would have to be implemented for the type `&'0 mut Map`, for any lifetime `'0`... - = note: ...but `Stream` is actually implemented for the type `&'1 mut Map`, for some specific lifetime `'1` + = note: the method `filterx` exists but the following trait bounds were not satisfied: + `&'a mut Map: Stream` + which is required by `Map: StreamExt` + `&'a mut &Map: Stream` + which is required by `&Map: StreamExt` + `&'a mut &mut Map: Stream` + which is required by `&mut Map: StreamExt` -error: aborting due to previous error +error[E0599]: no method named `countx` found for struct `Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>` in the current scope + --> $DIR/issue-30786.rs:141:24 + | +LL | pub struct Filter { + | ----------------------- + | | + | method `countx` not found for this + | doesn't satisfy `_: StreamExt` +... +LL | let count = filter.countx(); + | ^^^^^^ method not found in `Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>` + | + = note: the method `countx` exists but the following trait bounds were not satisfied: + `&'a mut Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream` + which is required by `Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: StreamExt` + `&'a mut &Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream` + which is required by `&Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: StreamExt` + `&'a mut &mut Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream` + which is required by `&mut Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: StreamExt` + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/hrtb/issue-30786.nll.stderr b/src/test/ui/hrtb/issue-30786.nll.stderr index c736c5479f848..90a7cadca41b7 100644 --- a/src/test/ui/hrtb/issue-30786.nll.stderr +++ b/src/test/ui/hrtb/issue-30786.nll.stderr @@ -1,56 +1,43 @@ -error: higher-ranked subtype error - --> $DIR/issue-30786.rs:108:15 - | -LL | let map = source.map(|x: &_| x); - | ^^^^^^^^^^^^^^^^^^^^^ - -error: higher-ranked subtype error - --> $DIR/issue-30786.rs:114:18 - | -LL | let filter = map.filter(|x: &_| true); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: higher-ranked subtype error - --> $DIR/issue-30786.rs:114:18 - | -LL | let filter = map.filter(|x: &_| true); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: higher-ranked subtype error - --> $DIR/issue-30786.rs:114:18 - | -LL | let filter = map.filter(|x: &_| true); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: higher-ranked subtype error - --> $DIR/issue-30786.rs:114:18 - | -LL | let filter = map.filter(|x: &_| true); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: higher-ranked subtype error - --> $DIR/issue-30786.rs:119:17 - | -LL | let count = filter.count(); // Assert that we still have a valid stream. - | ^^^^^^^^^^^^^^ - -error: higher-ranked subtype error - --> $DIR/issue-30786.rs:119:17 - | -LL | let count = filter.count(); // Assert that we still have a valid stream. - | ^^^^^^^^^^^^^^ - -error: higher-ranked subtype error - --> $DIR/issue-30786.rs:119:17 - | -LL | let count = filter.count(); // Assert that we still have a valid stream. - | ^^^^^^^^^^^^^^ - -error: higher-ranked subtype error - --> $DIR/issue-30786.rs:119:17 - | -LL | let count = filter.count(); // Assert that we still have a valid stream. - | ^^^^^^^^^^^^^^ - -error: aborting due to 9 previous errors - +error[E0599]: no method named `filterx` found for struct `Map` in the current scope + --> $DIR/issue-30786.rs:128:22 + | +LL | pub struct Map { + | -------------------- + | | + | method `filterx` not found for this + | doesn't satisfy `_: StreamExt` +... +LL | let filter = map.filterx(|x: &_| true); + | ^^^^^^^ method not found in `Map` + | + = note: the method `filterx` exists but the following trait bounds were not satisfied: + `&'a mut Map: Stream` + which is required by `Map: StreamExt` + `&'a mut &Map: Stream` + which is required by `&Map: StreamExt` + `&'a mut &mut Map: Stream` + which is required by `&mut Map: StreamExt` + +error[E0599]: no method named `countx` found for struct `Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>` in the current scope + --> $DIR/issue-30786.rs:141:24 + | +LL | pub struct Filter { + | ----------------------- + | | + | method `countx` not found for this + | doesn't satisfy `_: StreamExt` +... +LL | let count = filter.countx(); + | ^^^^^^ method not found in `Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>` + | + = note: the method `countx` exists but the following trait bounds were not satisfied: + `&'a mut Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream` + which is required by `Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: StreamExt` + `&'a mut &Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream` + which is required by `&Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: StreamExt` + `&'a mut &mut Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: Stream` + which is required by `&mut Filter fn(&'r u64) -> &'r u64 {identity::}>, [closure@$DIR/issue-30786.rs:140:30: 140:42]>: StreamExt` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/hrtb/issue-30786.rs b/src/test/ui/hrtb/issue-30786.rs index c656f84306536..8ce5c090b543e 100644 --- a/src/test/ui/hrtb/issue-30786.rs +++ b/src/test/ui/hrtb/issue-30786.rs @@ -16,7 +16,7 @@ //[nll]compile-flags: -Z borrowck=mir -pub trait Stream { //[migrate]~ NOTE trait `Stream` defined here +pub trait Stream { type Item; fn next(self) -> Option; } @@ -37,8 +37,9 @@ pub struct Map { } impl<'a, A, F, T> Stream for &'a mut Map -where &'a mut A: Stream, - F: FnMut(<&'a mut A as Stream>::Item) -> T, +where + &'a mut A: Stream, + F: FnMut(<&'a mut A as Stream>::Item) -> T, { type Item = T; fn next(self) -> Option { @@ -55,8 +56,9 @@ pub struct Filter { } impl<'a, A, F, T> Stream for &'a mut Filter -where for<'b> &'b mut A: Stream, // <---- BAD - F: FnMut(&T) -> bool, +where + for<'b> &'b mut A: Stream, // <---- BAD + F: FnMut(&T) -> bool, { type Item = <&'a mut A as Stream>::Item; fn next(self) -> Option { @@ -69,29 +71,29 @@ where for<'b> &'b mut A: Stream, // <---- BAD } } -pub trait StreamExt where for<'b> &'b mut Self: Stream { - fn map(self, func: F) -> Map - where Self: Sized, - for<'a> &'a mut Map: Stream, +pub trait StreamExt +where + for<'b> &'b mut Self: Stream, +{ + fn mapx(self, func: F) -> Map + where + Self: Sized, + for<'a> &'a mut Map: Stream, { - Map { - func: func, - stream: self, - } + Map { func: func, stream: self } } - fn filter(self, func: F) -> Filter - where Self: Sized, - for<'a> &'a mut Filter: Stream, + fn filterx(self, func: F) -> Filter + where + Self: Sized, + for<'a> &'a mut Filter: Stream, { - Filter { - func: func, - stream: self, - } + Filter { func: func, stream: self } } - fn count(mut self) -> usize - where Self: Sized, + fn countx(mut self) -> usize + where + Self: Sized, { let mut count = 0; while let Some(_) = self.next() { @@ -101,24 +103,44 @@ pub trait StreamExt where for<'b> &'b mut Self: Stream { } } -impl StreamExt for T where for<'a> &'a mut T: Stream { } +impl StreamExt for T where for<'a> &'a mut T: Stream {} -fn main() { +fn identity(x: &T) -> &T { + x +} + +fn variant1() { let source = Repeat(10); - let map = source.map(|x: &_| x); - //[nll]~^ ERROR higher-ranked subtype error - //[migrate]~^^ ERROR implementation of `Stream` is not general enough - //[migrate]~| NOTE `Stream` would have to be implemented for the type `&'0 mut Map - //[migrate]~| NOTE but `Stream` is actually implemented for the type `&'1 - //[migrate]~| NOTE implementation of `Stream` is not general enough - let filter = map.filter(|x: &_| true); - //[nll]~^ ERROR higher-ranked subtype error - //[nll]~| ERROR higher-ranked subtype error - //[nll]~| ERROR higher-ranked subtype error - //[nll]~| ERROR higher-ranked subtype error - let count = filter.count(); // Assert that we still have a valid stream. - //[nll]~^ ERROR higher-ranked subtype error - //[nll]~| ERROR higher-ranked subtype error - //[nll]~| ERROR higher-ranked subtype error - //[nll]~| ERROR higher-ranked subtype error + + // Here, the call to `mapx` returns a type `T` to which `StreamExt` + // is not applicable, because `for<'b> &'b mut T: Stream`) doesn't hold. + // + // More concretely, the type `T` is `Map`, and + // the where clause doesn't hold because the signature of the + // closure gets inferred to a signature like `|&'_ Stream| -> &'_` + // for some specific `'_`, rather than a more generic + // signature. + // + // Why *exactly* we opt for this signature is a bit unclear to me, + // we deduce it somehow from a reuqirement that `Map: Stream` I + // guess. + let map = source.mapx(|x: &_| x); + let filter = map.filterx(|x: &_| true); + //[migrate]~^ ERROR no method named `filterx` + //[nll]~^^ ERROR no method named `filterx` } + +fn variant2() { + let source = Repeat(10); + + // Here, we use a function, which is not subject to the vagaries + // of closure signature inference. In this case, we get the error + // on `countx` as, I think, the test originally expected. + let map = source.mapx(identity); + let filter = map.filterx(|x: &_| true); + let count = filter.countx(); + //[migrate]~^ ERROR no method named `countx` + //[nll]~^^ ERROR no method named `countx` +} + +fn main() {} diff --git a/src/test/ui/hrtb/issue-46989.nll.stderr b/src/test/ui/hrtb/issue-46989.nll.stderr new file mode 100644 index 0000000000000..6c127b92d97d1 --- /dev/null +++ b/src/test/ui/hrtb/issue-46989.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/issue-46989.rs:38:5 + | +LL | assert_foo::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/hrtb/issue-46989.rs b/src/test/ui/hrtb/issue-46989.rs index 2c85905545807..4a09f4be156e2 100644 --- a/src/test/ui/hrtb/issue-46989.rs +++ b/src/test/ui/hrtb/issue-46989.rs @@ -28,15 +28,13 @@ // // holds because 'a can be instantiated to 'empty. -trait Foo { +trait Foo {} -} - -impl Foo for fn(A) { } +impl Foo for fn(A) {} fn assert_foo() {} fn main() { assert_foo::(); - //~^ ERROR the trait bound `for<'r> fn(&'r i32): Foo` is not satisfied + //~^ ERROR implementation of `Foo` is not general enough } diff --git a/src/test/ui/hrtb/issue-46989.stderr b/src/test/ui/hrtb/issue-46989.stderr index c818041e59632..c85c37ff9239e 100644 --- a/src/test/ui/hrtb/issue-46989.stderr +++ b/src/test/ui/hrtb/issue-46989.stderr @@ -1,15 +1,14 @@ -error[E0277]: the trait bound `for<'r> fn(&'r i32): Foo` is not satisfied - --> $DIR/issue-46989.rs:40:18 +error: implementation of `Foo` is not general enough + --> $DIR/issue-46989.rs:38:5 | -LL | fn assert_foo() {} - | ---------- --- required by this bound in `assert_foo` +LL | trait Foo {} + | ------------ trait `Foo` defined here ... LL | assert_foo::(); - | ^^^^^^^^ the trait `Foo` is not implemented for `for<'r> fn(&'r i32)` + | ^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough | - = help: the following implementations were found: - + = note: `Foo` would have to be implemented for the type `for<'r> fn(&'r i32)` + = note: ...but `Foo` is actually implemented for the type `fn(&'0 i32)`, for some specific lifetime `'0` error: aborting due to previous error -For more information about this error, try `rustc --explain E0277`. 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/fields.stderr b/src/test/ui/hygiene/fields.stderr index 89deef492020d..6d784408016f5 100644 --- a/src/test/ui/hygiene/fields.stderr +++ b/src/test/ui/hygiene/fields.stderr @@ -2,7 +2,7 @@ error: type `foo::S` is private --> $DIR/fields.rs:15:17 | LL | let s = S { x: 0 }; - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type ... LL | let s = foo::m!(S, x); | ------------- in this macro invocation @@ -13,7 +13,7 @@ error: type `foo::S` is private --> $DIR/fields.rs:16:17 | LL | let _ = s.x; - | ^ + | ^ private type ... LL | let s = foo::m!(S, x); | ------------- in this macro invocation @@ -24,7 +24,7 @@ error: type `foo::T` is private --> $DIR/fields.rs:18:17 | LL | let t = T(0); - | ^^^^ + | ^^^^ private type ... LL | let s = foo::m!(S, x); | ------------- in this macro invocation @@ -35,7 +35,7 @@ error: type `foo::T` is private --> $DIR/fields.rs:19:17 | LL | let _ = t.0; - | ^ + | ^ private type ... LL | let s = foo::m!(S, x); | ------------- in this macro invocation 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..6dcbf055a8bb7 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,14 +23,10 @@ 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 this function | LL | use bar::g; | -LL | use foo::test2::test::g; - | -LL | use foo::test::g; - | error[E0425]: cannot find function `f` in this scope --> $DIR/globs.rs:61:12 @@ -41,7 +37,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 +50,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/impl_items.stderr b/src/test/ui/hygiene/impl_items.stderr index 85ee9f4cbf3e6..8ac59263cf21a 100644 --- a/src/test/ui/hygiene/impl_items.stderr +++ b/src/test/ui/hygiene/impl_items.stderr @@ -2,7 +2,7 @@ error: type `for<'r> fn(&'r foo::S) {foo::S::f}` is private --> $DIR/impl_items.rs:12:23 | LL | let _: () = S.f(); - | ^ + | ^ private type ... LL | foo::m!(); | ---------- in this macro invocation diff --git a/src/test/ui/hygiene/intercrate.stderr b/src/test/ui/hygiene/intercrate.stderr index 3912ca337fbec..c27ba74a263e0 100644 --- a/src/test/ui/hygiene/intercrate.stderr +++ b/src/test/ui/hygiene/intercrate.stderr @@ -2,7 +2,7 @@ error: type `fn() -> u32 {intercrate::foo::bar::f}` is private --> $DIR/intercrate.rs:10:16 | LL | assert_eq!(intercrate::foo::m!(), 1); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ private type | = 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/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/hygiene/macro-metavars-legacy.rs b/src/test/ui/hygiene/macro-metavars-legacy.rs new file mode 100644 index 0000000000000..09070f0f561a6 --- /dev/null +++ b/src/test/ui/hygiene/macro-metavars-legacy.rs @@ -0,0 +1,29 @@ +// Ensure macro metavariables are compared with legacy hygiene + +#![feature(rustc_attrs)] + +// run-pass + +macro_rules! make_mac { + ( $($dollar:tt $arg:ident),+ ) => { + macro_rules! mac { + ( $($dollar $arg : ident),+ ) => { + $( $dollar $arg )-+ + } + } + } +} + +macro_rules! show_hygiene { + ( $dollar:tt $arg:ident ) => { + make_mac!($dollar $arg, $dollar arg); + } +} + +show_hygiene!( $arg ); + +fn main() { + let x = 5; + let y = 3; + assert_eq!(2, mac!(x, y)); +} diff --git a/src/test/ui/hygiene/macro-metavars-transparent.rs b/src/test/ui/hygiene/macro-metavars-transparent.rs new file mode 100644 index 0000000000000..e475b5728a098 --- /dev/null +++ b/src/test/ui/hygiene/macro-metavars-transparent.rs @@ -0,0 +1,24 @@ +// Ensure macro metavariables are not compared without removing transparent +// marks. + +#![feature(rustc_attrs)] + +// run-pass + +#[rustc_macro_transparency = "transparent"] +macro_rules! k { + ($($s:tt)*) => { + macro_rules! m { + ($y:tt) => { + $($s)* + } + } + } +} + +k!(1 + $y); + +fn main() { + let x = 2; + assert_eq!(3, m!(x)); +} diff --git a/src/test/ui/hygiene/missing-self-diag.rs b/src/test/ui/hygiene/missing-self-diag.rs new file mode 100644 index 0000000000000..f934f793c7f27 --- /dev/null +++ b/src/test/ui/hygiene/missing-self-diag.rs @@ -0,0 +1,23 @@ +// Regression test for issue #66898 +// Tests that we don't emit a nonsensical error message +// when a macro invocation tries to access `self` from a function +// that has a 'self' parameter + +pub struct Foo; + +macro_rules! call_bar { + () => { + self.bar(); //~ ERROR expected value + } +} + +impl Foo { + pub fn foo(&self) { + call_bar!(); + } + + pub fn bar(&self) { + } +} + +fn main() {} diff --git a/src/test/ui/hygiene/missing-self-diag.stderr b/src/test/ui/hygiene/missing-self-diag.stderr new file mode 100644 index 0000000000000..075d6b76bb7b2 --- /dev/null +++ b/src/test/ui/hygiene/missing-self-diag.stderr @@ -0,0 +1,17 @@ +error[E0424]: expected value, found module `self` + --> $DIR/missing-self-diag.rs:10:9 + | +LL | self.bar(); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter +... +LL | / pub fn foo(&self) { +LL | | call_bar!(); + | | ------------ in this macro invocation +LL | | } + | |_____- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters + | + = 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 E0424`. diff --git a/src/test/ui/hygiene/nested_macro_privacy.stderr b/src/test/ui/hygiene/nested_macro_privacy.stderr index 6e78cb86d80f4..482957a326437 100644 --- a/src/test/ui/hygiene/nested_macro_privacy.stderr +++ b/src/test/ui/hygiene/nested_macro_privacy.stderr @@ -1,8 +1,8 @@ error[E0616]: field `i` of struct `foo::S` is private - --> $DIR/nested_macro_privacy.rs:15:5 + --> $DIR/nested_macro_privacy.rs:15:18 | LL | S::default().i; - | ^^^^^^^^^^^^^^ + | ^ private field error: aborting due to previous error diff --git a/src/test/ui/hygiene/no_implicit_prelude.stderr b/src/test/ui/hygiene/no_implicit_prelude.stderr index 986671c7810e7..990210ffb6b49 100644 --- a/src/test/ui/hygiene/no_implicit_prelude.stderr +++ b/src/test/ui/hygiene/no_implicit_prelude.stderr @@ -13,9 +13,13 @@ LL | fn f() { ::bar::m!(); } | ------------ in this macro invocation ... LL | Vec::new(); - | ^^^ use of undeclared type or module `Vec` + | ^^^ not found in this scope | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this struct + | +LL | use std::vec::Vec; + | error[E0599]: no method named `clone` found for unit type `()` in the current scope --> $DIR/no_implicit_prelude.rs:12:12 diff --git a/src/test/ui/hygiene/privacy.stderr b/src/test/ui/hygiene/privacy.stderr index 0649dc0ec5836..70a24304dda33 100644 --- a/src/test/ui/hygiene/privacy.stderr +++ b/src/test/ui/hygiene/privacy.stderr @@ -2,7 +2,7 @@ error[E0603]: function `f` is private --> $DIR/privacy.rs:16:14 | LL | foo::f() - | ^ this function is private + | ^ private function | note: the function `f` is defined here --> $DIR/privacy.rs:4:5 diff --git a/src/test/ui/hygiene/rustc-macro-transparency.stderr b/src/test/ui/hygiene/rustc-macro-transparency.stderr index 45a2efebbb8da..024ce8207601e 100644 --- a/src/test/ui/hygiene/rustc-macro-transparency.stderr +++ b/src/test/ui/hygiene/rustc-macro-transparency.stderr @@ -8,13 +8,23 @@ error[E0423]: expected value, found macro `semitransparent` --> $DIR/rustc-macro-transparency.rs:29:5 | LL | semitransparent; - | ^^^^^^^^^^^^^^^ help: use `!` to invoke the macro: `semitransparent!` + | ^^^^^^^^^^^^^^^ + | +help: use `!` to invoke the macro + | +LL | semitransparent!; + | ^ error[E0423]: expected value, found macro `opaque` --> $DIR/rustc-macro-transparency.rs:30:5 | LL | opaque; - | ^^^^^^ help: use `!` to invoke the macro: `opaque!` + | ^^^^^^ + | +help: use `!` to invoke the macro + | +LL | opaque!; + | ^ error: aborting due to 3 previous errors 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/illegal-ufcs-drop.stderr b/src/test/ui/illegal-ufcs-drop.stderr index d35d376962c17..57c99739afd24 100644 --- a/src/test/ui/illegal-ufcs-drop.stderr +++ b/src/test/ui/illegal-ufcs-drop.stderr @@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method --> $DIR/illegal-ufcs-drop.rs:8:5 | LL | Drop::drop(&mut Foo) - | ^^^^^^^^^^ explicit destructor calls not allowed + | ^^^^^^^^^^ + | | + | explicit destructor calls not allowed + | help: consider using `drop` function: `drop` 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-header-lifetime-elision/dyn-trait.stderr b/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr index 3300293bb36ca..268008c211129 100644 --- a/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr +++ b/src/test/ui/impl-header-lifetime-elision/dyn-trait.stderr @@ -14,16 +14,16 @@ note: ...so that the expression is assignable | LL | static_val(x); | ^ - = note: expected `std::boxed::Box` - found `std::boxed::Box<(dyn std::fmt::Debug + 'a)>` + = note: expected `std::boxed::Box` + found `std::boxed::Box<(dyn std::fmt::Debug + 'a)>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that the types are compatible --> $DIR/dyn-trait.rs:20:5 | LL | static_val(x); | ^^^^^^^^^^ - = note: expected `StaticTrait` - found `StaticTrait` + = note: expected `StaticTrait` + found `StaticTrait` error: aborting due to previous error diff --git a/src/test/ui/impl-trait-in-bindings-issue-73003.rs b/src/test/ui/impl-trait-in-bindings-issue-73003.rs new file mode 100644 index 0000000000000..fd8fe5f48dfa2 --- /dev/null +++ b/src/test/ui/impl-trait-in-bindings-issue-73003.rs @@ -0,0 +1,8 @@ +// check-pass + +#![feature(impl_trait_in_bindings)] +//~^ WARN the feature `impl_trait_in_bindings` is incomplete + +const _: impl Fn() = ||(); + +fn main() {} diff --git a/src/test/ui/impl-trait-in-bindings-issue-73003.stderr b/src/test/ui/impl-trait-in-bindings-issue-73003.stderr new file mode 100644 index 0000000000000..715671c8add83 --- /dev/null +++ b/src/test/ui/impl-trait-in-bindings-issue-73003.stderr @@ -0,0 +1,11 @@ +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-issue-73003.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-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.rs b/src/test/ui/impl-trait/auto-trait-leak.rs index a6012835f441e..087f4582b21c3 100644 --- a/src/test/ui/impl-trait/auto-trait-leak.rs +++ b/src/test/ui/impl-trait/auto-trait-leak.rs @@ -11,8 +11,6 @@ fn main() { // return type, which can't depend on the obligation. fn cycle1() -> impl Clone { //~^ ERROR cycle detected - //~| ERROR cycle detected - //~| ERROR cycle detected send(cycle2().clone()); //~^ ERROR cannot be sent between threads safely diff --git a/src/test/ui/impl-trait/auto-trait-leak.stderr b/src/test/ui/impl-trait/auto-trait-leak.stderr index 3d60cbff3203f..679b26efe5933 100644 --- a/src/test/ui/impl-trait/auto-trait-leak.stderr +++ b/src/test/ui/impl-trait/auto-trait-leak.stderr @@ -1,4 +1,4 @@ -error[E0391]: cycle detected when processing `cycle1::{{opaque}}#0` +error[E0391]: cycle detected when computing type of `cycle1::{{opaque}}#0` --> $DIR/auto-trait-leak.rs:12:16 | LL | fn cycle1() -> impl Clone { @@ -14,93 +14,7 @@ note: ...which requires processing `cycle1`... | LL | fn cycle1() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires unsafety-checking `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires building MIR for... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires type-checking `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`... -note: ...which requires processing `cycle2::{{opaque}}#0`... - --> $DIR/auto-trait-leak.rs:22:16 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^ -note: ...which requires borrow-checking `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires unsafety-checking `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires building MIR for... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires type-checking `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`... - = note: ...which again requires processing `cycle1::{{opaque}}#0`, completing the cycle -note: cycle used when checking item types in top-level module - --> $DIR/auto-trait-leak.rs:1:1 - | -LL | / use std::cell::Cell; -LL | | use std::rc::Rc; -LL | | -LL | | fn send(_: T) {} -... | -LL | | Rc::new(String::from("foo")) -LL | | } - | |_^ - -error[E0391]: cycle detected when processing `cycle1::{{opaque}}#0` - --> $DIR/auto-trait-leak.rs:12:16 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^ - | -note: ...which requires borrow-checking `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle1`... +note: ...which requires processing MIR for `cycle1`... --> $DIR/auto-trait-leak.rs:12:1 | LL | fn cycle1() -> impl Clone { @@ -110,7 +24,7 @@ note: ...which requires unsafety-checking `cycle1`... | LL | fn cycle1() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires building MIR for... +note: ...which requires building MIR for `cycle1`... --> $DIR/auto-trait-leak.rs:12:1 | LL | fn cycle1() -> impl Clone { @@ -121,127 +35,43 @@ note: ...which requires type-checking `cycle1`... LL | fn cycle1() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`... -note: ...which requires processing `cycle2::{{opaque}}#0`... - --> $DIR/auto-trait-leak.rs:22:16 +note: ...which requires computing type of `cycle2::{{opaque}}#0`... + --> $DIR/auto-trait-leak.rs:20:16 | LL | fn cycle2() -> impl Clone { | ^^^^^^^^^^ note: ...which requires borrow-checking `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 + --> $DIR/auto-trait-leak.rs:20:1 | LL | fn cycle2() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires processing `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 + --> $DIR/auto-trait-leak.rs:20:1 | LL | fn cycle2() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 +note: ...which requires processing MIR for `cycle2`... + --> $DIR/auto-trait-leak.rs:20:1 | LL | fn cycle2() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires unsafety-checking `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 + --> $DIR/auto-trait-leak.rs:20:1 | LL | fn cycle2() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires building MIR for... - --> $DIR/auto-trait-leak.rs:22:1 +note: ...which requires building MIR for `cycle2`... + --> $DIR/auto-trait-leak.rs:20:1 | LL | fn cycle2() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: ...which requires type-checking `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 + --> $DIR/auto-trait-leak.rs:20:1 | LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires processing `cycle1::{{opaque}}#0`, completing the cycle -note: cycle used when checking item types in top-level module - --> $DIR/auto-trait-leak.rs:1:1 - | -LL | / use std::cell::Cell; -LL | | use std::rc::Rc; -LL | | -LL | | fn send(_: T) {} -... | -LL | | Rc::new(String::from("foo")) -LL | | } - | |_^ - -error[E0391]: cycle detected when processing `cycle1::{{opaque}}#0` - --> $DIR/auto-trait-leak.rs:12:16 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^ - | -note: ...which requires borrow-checking `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires unsafety-checking `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires building MIR for... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires type-checking `cycle1`... - --> $DIR/auto-trait-leak.rs:12:1 - | -LL | fn cycle1() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`... -note: ...which requires processing `cycle2::{{opaque}}#0`... - --> $DIR/auto-trait-leak.rs:22:16 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^ -note: ...which requires borrow-checking `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires processing `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires unsafety-checking `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires building MIR for... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires type-checking `cycle2`... - --> $DIR/auto-trait-leak.rs:22:1 - | -LL | fn cycle2() -> impl Clone { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires processing `cycle1::{{opaque}}#0`, completing the cycle + = note: ...which again requires computing type of `cycle1::{{opaque}}#0`, completing the cycle note: cycle used when checking item types in top-level module --> $DIR/auto-trait-leak.rs:1:1 | @@ -255,10 +85,10 @@ LL | | } | |_^ error[E0277]: `std::rc::Rc` cannot be sent between threads safely - --> $DIR/auto-trait-leak.rs:16:5 + --> $DIR/auto-trait-leak.rs:14: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 @@ -269,7 +99,7 @@ LL | fn cycle2() -> impl Clone { = help: within `impl std::clone::Clone`, the trait `std::marker::Send` is not implemented for `std::rc::Rc` = note: required because it appears within the type `impl std::clone::Clone` -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0391. For more information about an error, try `rustc --explain E0277`. 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/auto-trait.rs b/src/test/ui/impl-trait/auto-trait.rs index c767578120883..cf2773f4ef59d 100644 --- a/src/test/ui/impl-trait/auto-trait.rs +++ b/src/test/ui/impl-trait/auto-trait.rs @@ -2,22 +2,24 @@ // the purposes of coherence checking #![feature(type_alias_impl_trait)] -trait OpaqueTrait { } -impl OpaqueTrait for T { } +trait OpaqueTrait {} +impl OpaqueTrait for T {} type OpaqueType = impl OpaqueTrait; -fn mk_opaque() -> OpaqueType { () } +fn mk_opaque() -> OpaqueType { + () +} #[derive(Debug)] struct D(T); -trait AnotherTrait { } -impl AnotherTrait for T { } +trait AnotherTrait {} +impl AnotherTrait for T {} // This is in error, because we cannot assume that `OpaqueType: !Send`. // (We treat opaque types as "foreign types" that could grow more impls // in the future.) impl AnotherTrait for D { - //~^ ERROR conflicting implementations of trait `AnotherTrait` for type `D` + //~^ ERROR conflicting implementations of trait `AnotherTrait` for type `D` } fn main() {} diff --git a/src/test/ui/impl-trait/auto-trait.stderr b/src/test/ui/impl-trait/auto-trait.stderr index 5e72ca7a47ba1..16fe1b56b50c6 100644 --- a/src/test/ui/impl-trait/auto-trait.stderr +++ b/src/test/ui/impl-trait/auto-trait.stderr @@ -1,11 +1,11 @@ -error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D`: - --> $DIR/auto-trait.rs:19:1 +error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D`: + --> $DIR/auto-trait.rs:21:1 | -LL | impl AnotherTrait for T { } +LL | impl AnotherTrait for T {} | -------------------------------- first implementation here ... LL | impl AnotherTrait for D { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D` error: aborting due to previous error diff --git a/src/test/ui/impl-trait/binding-without-value.rs b/src/test/ui/impl-trait/binding-without-value.rs new file mode 100644 index 0000000000000..6a97f28ff552b --- /dev/null +++ b/src/test/ui/impl-trait/binding-without-value.rs @@ -0,0 +1,9 @@ +#![allow(incomplete_features)] +#![feature(impl_trait_in_bindings)] + +fn foo() { + let _ : impl Copy; + //~^ ERROR cannot resolve opaque type +} + +fn main() {} diff --git a/src/test/ui/impl-trait/binding-without-value.stderr b/src/test/ui/impl-trait/binding-without-value.stderr new file mode 100644 index 0000000000000..0d2faeaf85d10 --- /dev/null +++ b/src/test/ui/impl-trait/binding-without-value.stderr @@ -0,0 +1,16 @@ +error[E0720]: cannot resolve opaque type + --> $DIR/binding-without-value.rs:5:13 + | +LL | let _ : impl Copy; + | - ^^^^^^^^^ cannot resolve opaque type + | | + | this binding might not have a concrete type + | +help: set the binding to a value for a concrete type to be resolved + | +LL | let _ : impl Copy = /* value */; + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0720`. 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..03aba10cc79b4 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,11 +15,13 @@ 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 +error[E0760]: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope --> $DIR/bound-normalization-fail.rs:43:41 | LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike { @@ -32,10 +35,13 @@ 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`. +Some errors have detailed explanations: E0271, E0760. +For more information about an 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/equality-rpass.rs b/src/test/ui/impl-trait/equality-rpass.rs index 05c9e4173b0e4..607b4a49661cc 100644 --- a/src/test/ui/impl-trait/equality-rpass.rs +++ b/src/test/ui/impl-trait/equality-rpass.rs @@ -1,6 +1,6 @@ // run-pass -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo: std::fmt::Debug + Eq {} diff --git a/src/test/ui/impl-trait/equality-rpass.stderr b/src/test/ui/impl-trait/equality-rpass.stderr new file mode 100644 index 0000000000000..1abf05dca8270 --- /dev/null +++ b/src/test/ui/impl-trait/equality-rpass.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/equality-rpass.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/impl-trait/equality.rs b/src/test/ui/impl-trait/equality.rs index 14b0eeb739ae5..828b5aac896be 100644 --- a/src/test/ui/impl-trait/equality.rs +++ b/src/test/ui/impl-trait/equality.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo: Copy + ToString {} diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index 9178358b60a9c..628dfb13d4ca8 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/equality.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0308]: mismatched types --> $DIR/equality.rs:15:5 | @@ -24,7 +33,7 @@ LL | n + sum_to(n - 1) | = help: the trait `std::ops::Add` is not implemented for `u32` -error: aborting due to 2 previous errors +error: aborting due to 2 previous errors; 1 warning emitted Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/impl-trait/equality2.rs b/src/test/ui/impl-trait/equality2.rs index abce8c8c204bd..2e325867da86e 100644 --- a/src/test/ui/impl-trait/equality2.rs +++ b/src/test/ui/impl-trait/equality2.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo: Copy + ToString {} diff --git a/src/test/ui/impl-trait/equality2.stderr b/src/test/ui/impl-trait/equality2.stderr index b882514f61609..1780931efc541 100644 --- a/src/test/ui/impl-trait/equality2.stderr +++ b/src/test/ui/impl-trait/equality2.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/equality2.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0308]: mismatched types --> $DIR/equality2.rs:25:18 | @@ -25,7 +34,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 @@ -58,6 +67,6 @@ LL | x.0); = note: expected opaque type `impl Foo` (`i32`) found opaque type `impl Foo` (`u32`) -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/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..5131509cdf03e 100644 --- a/src/test/ui/impl-trait/issue-55872-1.stderr +++ b/src/test/ui/impl-trait/issue-55872-1.stderr @@ -1,30 +1,28 @@ error[E0277]: the trait bound `S: std::marker::Copy` is not satisfied in `(S, T)` - --> $DIR/issue-55872-1.rs:12:5 + --> $DIR/issue-55872-1.rs:12:14 | LL | type E = impl Copy; - | ^^^^^^^^^^^^^^^^^^^ within `(S, T)`, the trait `std::marker::Copy` is not implemented for `S` + | ^^^^^^^^^ within `(S, T)`, the trait `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 + --> $DIR/issue-55872-1.rs:12:14 | LL | type E = impl Copy; - | ^^^^^^^^^^^^^^^^^^^ within `(S, T)`, the trait `std::marker::Copy` is not implemented for `T` + | ^^^^^^^^^ within `(S, T)`, the trait `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/issue-55872-2.stderr b/src/test/ui/impl-trait/issue-55872-2.stderr index 01371b4d5c61f..649109e4c9324 100644 --- a/src/test/ui/impl-trait/issue-55872-2.stderr +++ b/src/test/ui/impl-trait/issue-55872-2.stderr @@ -1,8 +1,8 @@ error[E0277]: the trait bound `impl std::future::Future: std::marker::Copy` is not satisfied - --> $DIR/issue-55872-2.rs:13:5 + --> $DIR/issue-55872-2.rs:13:14 | LL | type E = impl Copy; - | ^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `impl std::future::Future` + | ^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `impl std::future::Future` | = note: the return type of a function must have a statically known size diff --git a/src/test/ui/impl-trait/issue-56445.rs b/src/test/ui/impl-trait/issue-56445.rs new file mode 100644 index 0000000000000..a34d7bae3a6ca --- /dev/null +++ b/src/test/ui/impl-trait/issue-56445.rs @@ -0,0 +1,26 @@ +// Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-629426939 +// check-pass + +#![crate_type = "lib"] + +use std::marker::PhantomData; + +pub struct S<'a> +{ + pub m1: PhantomData<&'a u8>, + pub m2: [u8; S::size()], +} + +impl<'a> S<'a> +{ + pub const fn size() -> usize { 1 } + + pub fn new() -> Self + { + Self + { + m1: PhantomData, + m2: [0; Self::size()], + } + } +} diff --git a/src/test/ui/impl-trait/issue-60473.rs b/src/test/ui/impl-trait/issue-60473.rs index 50cf0c8c6d641..2ef86f03d340c 100644 --- a/src/test/ui/impl-trait/issue-60473.rs +++ b/src/test/ui/impl-trait/issue-60473.rs @@ -5,13 +5,11 @@ struct A<'a>(&'a ()); -trait Trait { -} +trait Trait {} -impl Trait for () { -} +impl Trait for () {} fn main() { - let x: impl Trait = (); // FIXME: The error doesn't seem correct. - //~^ ERROR: opaque type expands to a recursive type + let x: impl Trait = (); + //~^ ERROR: missing lifetime specifier } diff --git a/src/test/ui/impl-trait/issue-60473.stderr b/src/test/ui/impl-trait/issue-60473.stderr index 2d95be4e52c61..367b5db5d2dce 100644 --- a/src/test/ui/impl-trait/issue-60473.stderr +++ b/src/test/ui/impl-trait/issue-60473.stderr @@ -1,11 +1,15 @@ -error[E0720]: opaque type expands to a recursive type - --> $DIR/issue-60473.rs:15:12 +error[E0106]: missing lifetime specifier + --> $DIR/issue-60473.rs:13:23 | -LL | let x: impl Trait = (); // FIXME: The error doesn't seem correct. - | ^^^^^^^^^^^^^ expands to a recursive type +LL | let x: impl Trait = (); + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | fn main<'a>() { +LL | let x: impl Trait> = (); | - = note: type resolves to itself error: aborting due to previous error -For more information about this error, try `rustc --explain E0720`. +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/impl-trait/issue-67166.rs b/src/test/ui/impl-trait/issue-67166.rs index de7433a9bfc4c..efa67558bd7c1 100644 --- a/src/test/ui/impl-trait/issue-67166.rs +++ b/src/test/ui/impl-trait/issue-67166.rs @@ -4,8 +4,8 @@ #![allow(incomplete_features)] pub fn run() { - let _foo: Box = Box::new(()); // FIXME: The error doesn't much make sense. - //~^ ERROR: opaque type expands to a recursive type + let _foo: Box = Box::new(()); + //~^ ERROR: missing lifetime specifier } fn main() {} diff --git a/src/test/ui/impl-trait/issue-67166.stderr b/src/test/ui/impl-trait/issue-67166.stderr index 56cba3cff0b55..14c78684e3e2f 100644 --- a/src/test/ui/impl-trait/issue-67166.stderr +++ b/src/test/ui/impl-trait/issue-67166.stderr @@ -1,11 +1,15 @@ -error[E0720]: opaque type expands to a recursive type - --> $DIR/issue-67166.rs:7:19 +error[E0106]: missing lifetime specifier + --> $DIR/issue-67166.rs:7:31 | -LL | let _foo: Box = Box::new(()); // FIXME: The error doesn't much make sense. - | ^^^^^^^^^^^^^^ expands to a recursive type +LL | let _foo: Box = Box::new(()); + | ^^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | pub fn run<'a>() { +LL | let _foo: Box = Box::new(()); | - = note: type resolves to itself error: aborting due to previous error -For more information about this error, try `rustc --explain E0720`. +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/impl-trait/issue-68532.rs b/src/test/ui/impl-trait/issue-68532.rs new file mode 100644 index 0000000000000..01a7af0aee40e --- /dev/null +++ b/src/test/ui/impl-trait/issue-68532.rs @@ -0,0 +1,13 @@ +// check-pass + +pub struct A<'a>(&'a ()); + +impl<'a> A<'a> { + const N: usize = 68; + + pub fn foo(&self) { + let _b = [0; Self::N]; + } +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issue-69840.rs b/src/test/ui/impl-trait/issue-69840.rs new file mode 100644 index 0000000000000..b270f88b6886e --- /dev/null +++ b/src/test/ui/impl-trait/issue-69840.rs @@ -0,0 +1,16 @@ +// check-pass + +#![feature(impl_trait_in_bindings)] +#![allow(incomplete_features)] + +struct A<'a>(&'a ()); + +trait Trait {} + +impl Trait for () {} + +pub fn foo<'a>() { + let _x: impl Trait> = (); +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.rs b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.rs index 150a8015cbc75..451ddb3cce0e0 100644 --- a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.rs +++ b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.rs @@ -5,13 +5,13 @@ trait Quux {} -fn foo() -> impl Quux { //~ opaque type expands to a recursive type +fn foo() -> impl Quux { //~ ERROR cannot resolve opaque type struct Foo(T); impl Quux for Foo {} Foo(bar()) } -fn bar() -> impl Quux { //~ opaque type expands to a recursive type +fn bar() -> impl Quux { //~ ERROR cannot resolve opaque type struct Bar(T); impl Quux for Bar {} Bar(foo()) diff --git a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr index d10001e8a8e53..c538b77098a2d 100644 --- a/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr +++ b/src/test/ui/impl-trait/issues/infinite-impl-trait-issue-38064.stderr @@ -1,18 +1,26 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/infinite-impl-trait-issue-38064.rs:8:13 | LL | fn foo() -> impl Quux { - | ^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `foo::Foo>` + | ^^^^^^^^^ recursive opaque type +... +LL | Foo(bar()) + | ---------- returning here with type `foo::Foo` +... +LL | fn bar() -> impl Quux { + | --------- returning this opaque type `foo::Foo` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/infinite-impl-trait-issue-38064.rs:14:13 | +LL | fn foo() -> impl Quux { + | --------- returning this opaque type `bar::Bar` +... LL | fn bar() -> impl Quux { - | ^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `bar::Bar>` + | ^^^^^^^^^ recursive opaque type +... +LL | Bar(foo()) + | ---------- returning here with type `bar::Bar` error: aborting due to 2 previous errors 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-unrelated.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr index cd2d46ac18218..b42ff1486f0a8 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -4,17 +4,11 @@ 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 the scope of call-site for function at 23:1 - --> $DIR/ordinary-bounds-unrelated.rs:23:1 +note: hidden type `Ordinary<'_>` captures lifetime smaller than the function body + --> $DIR/ordinary-bounds-unrelated.rs:18:74 | -LL | / { -LL | | // Hidden type `Ordinary<'0>` with constraints: -LL | | // -LL | | // ``` -... | -LL | | if condition() { a } else { b } -LL | | } - | |_^ +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ 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/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr index 59ce93fa78b6b..254643c406cae 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -4,17 +4,11 @@ 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 the scope of call-site for function at 22:1 - --> $DIR/ordinary-bounds-unsuited.rs:22:1 +note: hidden type `Ordinary<'_>` captures lifetime smaller than the function body + --> $DIR/ordinary-bounds-unsuited.rs:20:62 | -LL | / { -LL | | // We return a value: -LL | | // -LL | | // ``` -... | -LL | | if condition() { a } else { b } -LL | | } - | |_^ +LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> + | ^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr index 1806d2607a3ac..3b339c5c3d7fc 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr @@ -26,7 +26,42 @@ LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } | ^^^^^^^^^^^^^^ error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:12:69 + --> $DIR/must_outlive_least_region_or_bound.rs:9:46 + | +LL | fn elided2(x: &i32) -> impl Copy + 'static { x } + | - ^ returning this value requires that `'1` must outlive `'static` + | | + | let's call the lifetime of this reference `'1` + | + = help: consider replacing `'1` with `'static` + +error: lifetime may not live long enough + --> $DIR/must_outlive_least_region_or_bound.rs:12:55 + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } + | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` + | + = help: consider replacing `'a` with `'static` + = help: consider replacing `'a` with `'static` + +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/must_outlive_least_region_or_bound.rs:15:41 + | +LL | fn foo<'a>(x: &i32) -> impl Copy + 'a { x } + | ---- ^ lifetime `'a` required + | | + | help: add explicit lifetime `'a` to the type of `x`: `&'a i32` + +error: lifetime may not live long enough + --> $DIR/must_outlive_least_region_or_bound.rs:30:24 + | +LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } + | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ opaque type requires that `'1` must outlive `'static` + | | + | let's call the lifetime of this reference `'1` + +error: lifetime may not live long enough + --> $DIR/must_outlive_least_region_or_bound.rs:37:69 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` @@ -35,7 +70,7 @@ LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } = help: consider replacing `'a` with `'static` error: lifetime may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:17:61 + --> $DIR/must_outlive_least_region_or_bound.rs:42:61 | LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { | -- -- lifetime `'b` defined here ^^^^^^^^^^^^^^^^ opaque type requires that `'b` must outlive `'a` @@ -45,13 +80,14 @@ LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32 = help: consider adding the following bound: `'b: 'a` error[E0310]: the parameter type `T` may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:22:51 + --> $DIR/must_outlive_least_region_or_bound.rs:47:51 | LL | fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { | ^^^^^^^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `T: 'static`... -error: aborting due to 5 previous errors +error: aborting due to 9 previous errors -For more information about this error, try `rustc --explain E0310`. +Some errors have detailed explanations: E0310, E0621. +For more information about an error, try `rustc --explain E0310`. diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs index 00f3490991b52..9bf86fa66cded 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.rs @@ -6,6 +6,31 @@ fn elided(x: &i32) -> impl Copy { x } fn explicit<'a>(x: &'a i32) -> impl Copy { x } //~^ ERROR cannot infer an appropriate lifetime +fn elided2(x: &i32) -> impl Copy + 'static { x } +//~^ ERROR cannot infer an appropriate lifetime + +fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } +//~^ ERROR cannot infer an appropriate lifetime + +fn foo<'a>(x: &i32) -> impl Copy + 'a { x } +//~^ ERROR explicit lifetime required in the type of `x` + +fn elided3(x: &i32) -> Box { Box::new(x) } +//~^ ERROR cannot infer an appropriate lifetime + +fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } +//~^ ERROR cannot infer an appropriate lifetime + +fn elided4(x: &i32) -> Box { Box::new(x) } +//~^ ERROR cannot infer an appropriate lifetime + +fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } +//~^ ERROR cannot infer an appropriate lifetime + +fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } +//~^ ERROR cannot infer an appropriate lifetime +//~| ERROR cannot infer an appropriate lifetime + trait LifetimeTrait<'a> {} impl<'a> LifetimeTrait<'a> for &'a i32 {} diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr index cffa5ee8f1461..ffadcaae08e05 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -1,59 +1,148 @@ -error: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:3:35 | LL | fn elided(x: &i32) -> impl Copy { x } - | --------- ^ ...but this borrow... - | | - | this return type evaluates to the `'static` lifetime... + | ---- ^ ...is captured here... + | | + | this data with an anonymous lifetime `'_`... | -note: ...can't outlive the anonymous lifetime #1 defined on the function body at 3:1 - --> $DIR/must_outlive_least_region_or_bound.rs:3:1 +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:3:23 | LL | fn elided(x: &i32) -> impl Copy { x } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: you can add a bound to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the function body at 3:1 + | ^^^^^^^^^ +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound | LL | fn elided(x: &i32) -> impl Copy + '_ { x } - | ^^^^^^^^^^^^^^ + | ^^^^ -error: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/must_outlive_least_region_or_bound.rs:6:44 | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } - | --------- ^ ...but this borrow... - | | - | this return type evaluates to the `'static` lifetime... + | ------- ^ ...is captured here... + | | + | this data with lifetime `'a`... | -note: ...can't outlive the lifetime `'a` as defined on the function body at 6:13 - --> $DIR/must_outlive_least_region_or_bound.rs:6:13 +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:6:32 | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } - | ^^ -help: you can add a bound to the return type to make it last less than `'static` and match the lifetime `'a` as defined on the function body at 6:13 + | ^^^^^^^^^ +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'a` lifetime bound | LL | fn explicit<'a>(x: &'a i32) -> impl Copy + 'a { x } - | ^^^^^^^^^^^^^^ + | ^^^^ -error: cannot infer an appropriate lifetime - --> $DIR/must_outlive_least_region_or_bound.rs:12:69 +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:9:46 + | +LL | fn elided2(x: &i32) -> impl Copy + 'static { x } + | ---- ^ ...is captured here... + | | + | this data with an anonymous lifetime `'_`... + | +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:9:24 + | +LL | fn elided2(x: &i32) -> impl Copy + 'static { x } + | ^^^^^^^^^^^^^^^^^^^ +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` + | +LL | fn elided2(x: &i32) -> impl Copy + '_ { x } + | ^^ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x } + | ^^^^^^^^^^^^ + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:12:55 + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } + | ------- ^ ...is captured here... + | | + | this data with lifetime `'a`... + | +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:12:33 + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } + | ^^^^^^^^^^^^^^^^^^^ +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } + | ^^ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x } + | ^^^^^^^^^^^^ + +error[E0621]: explicit lifetime required in the type of `x` + --> $DIR/must_outlive_least_region_or_bound.rs:15:24 + | +LL | fn foo<'a>(x: &i32) -> impl Copy + 'a { x } + | ---- ^^^^^^^^^^^^^^ lifetime `'a` required + | | + | help: add explicit lifetime `'a` to the type of `x`: `&'a i32` + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:30:65 + | +LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } + | ---- this data with an anonymous lifetime `'_`... ^ ...is captured here, requiring it to live as long as `'static` + | +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'_` lifetime bound + | +LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } + | ^^^^ +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound + | +LL | fn elided5(x: &i32) -> (Box, impl Debug + '_) { (Box::new(x), x) } + | ^^^^ + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:30:69 + | +LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } + | ---- this data with an anonymous lifetime `'_`... ^ ...is captured here... + | +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:30:41 + | +LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } + | ^^^^^^^^^^ +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'_` lifetime bound + | +LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } + | ^^^^ +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound + | +LL | fn elided5(x: &i32) -> (Box, impl Debug + '_) { (Box::new(x), x) } + | ^^^^ + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:37:69 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } - | -------------------------------- ^ ...but this borrow... - | | - | this return type evaluates to the `'static` lifetime... + | ------- this data with lifetime `'a`... ^ ...is captured here... | -note: ...can't outlive the lifetime `'a` as defined on the function body at 12:15 - --> $DIR/must_outlive_least_region_or_bound.rs:12:15 +note: ...and is required to live as long as `'static` here + --> $DIR/must_outlive_least_region_or_bound.rs:37:34 | LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } - | ^^ -help: you can add a bound to the return type to make it last less than `'static` and match the lifetime `'a` as defined on the function body at 12:15 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` | -LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static + 'a { x } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } + | ^^ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x } + | ^^^^^^^^^^^^ error[E0623]: lifetime mismatch - --> $DIR/must_outlive_least_region_or_bound.rs:17:61 + --> $DIR/must_outlive_least_region_or_bound.rs:42:61 | LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { | ------- ^^^^^^^^^^^^^^^^ @@ -62,20 +151,72 @@ LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32 | this parameter and the return type are declared with different lifetimes... error[E0310]: the parameter type `T` may not live long enough - --> $DIR/must_outlive_least_region_or_bound.rs:22:51 + --> $DIR/must_outlive_least_region_or_bound.rs:47:51 | LL | fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { - | -- ^^^^^^^^^^^^^^^^^^^^ + | -- ^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds | | | help: consider adding an explicit lifetime bound...: `T: 'static +` + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:18:50 | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/must_outlive_least_region_or_bound.rs:22:51 +LL | fn elided3(x: &i32) -> Box { Box::new(x) } + | ---- ^ ...is captured here, requiring it to live as long as `'static` + | | + | this data with an anonymous lifetime `'_`... | -LL | fn ty_param_wont_outlive_static(x: T) -> impl Debug + 'static { - | ^^^^^^^^^^^^^^^^^^^^ +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'_` lifetime bound + | +LL | fn elided3(x: &i32) -> Box { Box::new(x) } + | ^^^^ + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:21:59 + | +LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } + | ------- ^ ...is captured here, requiring it to live as long as `'static` + | | + | this data with lifetime `'a`... + | +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'a` lifetime bound + | +LL | fn explicit3<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^^^^ + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:24:60 + | +LL | fn elided4(x: &i32) -> Box { Box::new(x) } + | ---- ^ ...is captured here, requiring it to live as long as `'static` + | | + | this data with an anonymous lifetime `'_`... + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` + | +LL | fn elided4(x: &i32) -> Box { Box::new(x) } + | ^^ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn elided4(x: &'static i32) -> Box { Box::new(x) } + | ^^^^^^^^^^^^ + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/must_outlive_least_region_or_bound.rs:27:69 + | +LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } + | ------- this data with lifetime `'a`... ^ ...is captured here, requiring it to live as long as `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` + | +LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } + | ^^ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn explicit4<'a>(x: &'static i32) -> Box { Box::new(x) } + | ^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 14 previous errors -Some errors have detailed explanations: E0310, E0623. +Some errors have detailed explanations: E0310, E0621, E0623, E0759. For more information about an error, try `rustc --explain E0310`. diff --git a/src/test/ui/impl-trait/negative-reasoning.rs b/src/test/ui/impl-trait/negative-reasoning.rs index 4977f9bdbacd9..d173fe83fb791 100644 --- a/src/test/ui/impl-trait/negative-reasoning.rs +++ b/src/test/ui/impl-trait/negative-reasoning.rs @@ -2,21 +2,22 @@ // other trait #![feature(type_alias_impl_trait)] -trait OpaqueTrait { } -impl OpaqueTrait for T { } +trait OpaqueTrait {} +impl OpaqueTrait for T {} type OpaqueType = impl OpaqueTrait; -fn mk_opaque() -> OpaqueType { () } +fn mk_opaque() -> OpaqueType { + () +} #[derive(Debug)] struct D(T); -trait AnotherTrait { } -impl AnotherTrait for T { } - +trait AnotherTrait {} +impl AnotherTrait for T {} // This is in error, because we cannot assume that `OpaqueType: !Debug` impl AnotherTrait for D { - //~^ ERROR conflicting implementations of trait `AnotherTrait` for type `D` + //~^ ERROR conflicting implementations of trait `AnotherTrait` for type `D` } fn main() {} diff --git a/src/test/ui/impl-trait/negative-reasoning.stderr b/src/test/ui/impl-trait/negative-reasoning.stderr index 526a664726ac2..e43d8c857b257 100644 --- a/src/test/ui/impl-trait/negative-reasoning.stderr +++ b/src/test/ui/impl-trait/negative-reasoning.stderr @@ -1,13 +1,13 @@ -error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D`: - --> $DIR/negative-reasoning.rs:18:1 +error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D`: + --> $DIR/negative-reasoning.rs:19:1 | -LL | impl AnotherTrait for T { } +LL | impl AnotherTrait for T {} | ------------------------------------------- first implementation here ... LL | impl AnotherTrait for D { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D` | - = note: upstream crates may add a new impl of trait `std::fmt::Debug` for type `OpaqueType` in future versions + = note: upstream crates may add a new impl of trait `std::fmt::Debug` for type `impl OpaqueTrait` in future versions error: aborting due to previous error diff --git a/src/test/ui/impl-trait/no-method-suggested-traits.stderr b/src/test/ui/impl-trait/no-method-suggested-traits.stderr index c0ca341385df5..3cd4d0dd391af 100644 --- a/src/test/ui/impl-trait/no-method-suggested-traits.stderr +++ b/src/test/ui/impl-trait/no-method-suggested-traits.stderr @@ -49,14 +49,6 @@ LL | use foo::Bar; error[E0599]: no method named `method` found for struct `std::rc::Rc<&mut std::boxed::Box<&char>>` in the current scope --> $DIR/no-method-suggested-traits.rs:32:43 | -LL | fn method(&self) {} - | ------ - | | - | the method is available for `std::boxed::Box>>` here - | the method is available for `std::pin::Pin>>` here - | the method is available for `std::sync::Arc>>` here - | the method is available for `std::rc::Rc>>` here -... LL | std::rc::Rc::new(&mut Box::new(&'a')).method(); | ^^^^^^ method not found in `std::rc::Rc<&mut std::boxed::Box<&char>>` | diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr index 5a95e2969d1b0..5a3027ec751a9 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-direct.stderr @@ -1,10 +1,11 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-direct.rs:5:14 | LL | fn test() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^^ recursive opaque type +LL | +LL | test() + | ------ returning here with type `impl Sized` error: aborting due to previous error diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr index 6573b00870c5b..75ff9e078cc2c 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-indirect.stderr @@ -1,114 +1,147 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:7:22 | LL | fn option(i: i32) -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `std::option::Option<(impl Sized, i32)>` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | if i < 0 { None } else { Some((option(i - 1), i)) } + | ---- ------------------------ returning here with type `std::option::Option<(impl Sized, i32)>` + | | + | returning here with type `std::option::Option<(impl Sized, i32)>` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:12:15 | LL | fn tuple() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `(impl Sized,)` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | (tuple(),) + | ---------- returning here with type `(impl Sized,)` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:17:15 | LL | fn array() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[impl Sized; 1]` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | [array()] + | --------- returning here with type `[impl Sized; 1]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:22:13 | LL | fn ptr() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `*const impl Sized` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | &ptr() as *const _ + | ------------------ returning here with type `*const impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:27:16 | LL | fn fn_ptr() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `fn() -> impl Sized` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | fn_ptr as fn() -> _ + | ------------------- returning here with type `fn() -> impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:32:25 | -LL | fn closure_capture() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 37:6 x:impl Sized]` +LL | fn closure_capture() -> impl Sized { + | ^^^^^^^^^^ recursive opaque type +... +LL | / move || { +LL | | x; +LL | | } + | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 37:6 x:impl Sized]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:40:29 | -LL | fn closure_ref_capture() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 45:6 x:impl Sized]` +LL | fn closure_ref_capture() -> impl Sized { + | ^^^^^^^^^^ recursive opaque type +... +LL | / move || { +LL | | &x; +LL | | } + | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 45:6 x:impl Sized]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:48:21 | LL | fn closure_sig() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:21]` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | || closure_sig() + | ---------------- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:21]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:53:23 | LL | fn generator_sig() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[closure@$DIR/recursive-impl-trait-type-indirect.rs:55:5: 55:23]` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | || generator_sig() + | ------------------ returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:55:5: 55:23]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:58:27 | -LL | fn generator_capture() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[generator@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 64:6 x:impl Sized {()}]` +LL | fn generator_capture() -> impl Sized { + | ^^^^^^^^^^ recursive opaque type +... +LL | / move || { +LL | | yield; +LL | | x; +LL | | } + | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 64:6 x:impl Sized {()}]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:67:35 | LL | fn substs_change() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `(impl Sized,)` + | ^^^^^^^^^^ recursive opaque type +LL | +LL | (substs_change::<&T>(),) + | ------------------------ returning here with type `(impl Sized,)` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:72:24 | -LL | fn generator_hold() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `[generator@$DIR/recursive-impl-trait-type-indirect.rs:74:5: 78:6 {impl Sized, ()}]` +LL | fn generator_hold() -> impl Sized { + | ^^^^^^^^^^ recursive opaque type +LL | +LL | / move || { +LL | | let x = generator_hold(); +LL | | yield; +LL | | x; +LL | | } + | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:74:5: 78:6 {impl Sized, ()}]` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:86:26 | LL | fn mutual_recursion() -> impl Sync { - | ^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^ recursive opaque type +LL | +LL | mutual_recursion_b() + | -------------------- returning here with type `impl Sized` +... +LL | fn mutual_recursion_b() -> impl Sized { + | ---------- returning this opaque type `impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-indirect.rs:91:28 | +LL | fn mutual_recursion() -> impl Sync { + | --------- returning this opaque type `impl std::marker::Sync` +... LL | fn mutual_recursion_b() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^^ recursive opaque type +LL | +LL | mutual_recursion() + | ------------------ returning here with type `impl std::marker::Sync` error: aborting due to 14 previous errors diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.rs b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.rs index cfd9c0ec5b45b..818e40365394d 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.rs +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.rs @@ -4,21 +4,21 @@ fn id(t: T) -> impl Sized { t } -fn recursive_id() -> impl Sized { //~ ERROR opaque type expands to a recursive type +fn recursive_id() -> impl Sized { //~ ERROR cannot resolve opaque type id(recursive_id2()) } -fn recursive_id2() -> impl Sized { //~ ERROR opaque type expands to a recursive type +fn recursive_id2() -> impl Sized { //~ ERROR cannot resolve opaque type id(recursive_id()) } fn wrap(t: T) -> impl Sized { (t,) } -fn recursive_wrap() -> impl Sized { //~ ERROR opaque type expands to a recursive type +fn recursive_wrap() -> impl Sized { //~ ERROR cannot resolve opaque type wrap(recursive_wrap2()) } -fn recursive_wrap2() -> impl Sized { //~ ERROR opaque type expands to a recursive type +fn recursive_wrap2() -> impl Sized { //~ ERROR cannot resolve opaque type wrap(recursive_wrap()) } diff --git a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr index 73c12f6137d24..fbc58837a8e94 100644 --- a/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr +++ b/src/test/ui/impl-trait/recursive-impl-trait-type-through-non-recursive.stderr @@ -1,34 +1,46 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:7:22 | +LL | fn id(t: T) -> impl Sized { t } + | ---------- returning this opaque type `impl Sized` +LL | LL | fn recursive_id() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^^ recursive opaque type +LL | id(recursive_id2()) + | ------------------- returning here with type `impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:11:23 | +LL | fn id(t: T) -> impl Sized { t } + | ---------- returning this opaque type `impl Sized` +... LL | fn recursive_id2() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: type resolves to itself + | ^^^^^^^^^^ recursive opaque type +LL | id(recursive_id()) + | ------------------ returning here with type `impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:17:24 | +LL | fn wrap(t: T) -> impl Sized { (t,) } + | ---------- returning this opaque type `impl Sized` +LL | LL | fn recursive_wrap() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `((impl Sized,),)` + | ^^^^^^^^^^ recursive opaque type +LL | wrap(recursive_wrap2()) + | ----------------------- returning here with type `impl Sized` -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/recursive-impl-trait-type-through-non-recursive.rs:21:25 | +LL | fn wrap(t: T) -> impl Sized { (t,) } + | ---------- returning this opaque type `impl Sized` +... LL | fn recursive_wrap2() -> impl Sized { - | ^^^^^^^^^^ expands to a recursive type - | - = note: expanded type is `((impl Sized,),)` + | ^^^^^^^^^^ recursive opaque type +LL | wrap(recursive_wrap()) + | ---------------------- returning here with type `impl Sized` error: aborting due to 4 previous errors diff --git a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr index e550be1917474..df0db6e4fc6df 100644 --- a/src/test/ui/impl-trait/static-return-lifetime-infered.stderr +++ b/src/test/ui/impl-trait/static-return-lifetime-infered.stderr @@ -1,44 +1,43 @@ -error: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:7:16 | LL | fn iter_values_anon(&self) -> impl Iterator { - | ----------------------- this return type evaluates to the `'static` lifetime... + | ----- this data with an anonymous lifetime `'_`... LL | self.x.iter().map(|a| a.0) | ------ ^^^^ | | - | ...but this borrow... + | ...is captured here... | -note: ...can't outlive the anonymous lifetime #1 defined on the method body at 6:5 - --> $DIR/static-return-lifetime-infered.rs:6:5 +note: ...and is required to live as long as `'static` here + --> $DIR/static-return-lifetime-infered.rs:6:35 | -LL | / fn iter_values_anon(&self) -> impl Iterator { -LL | | self.x.iter().map(|a| a.0) -LL | | } - | |_____^ -help: you can add a bound to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the method body at 6:5 +LL | fn iter_values_anon(&self) -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound | LL | fn iter_values_anon(&self) -> impl Iterator + '_ { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ -error: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:11:16 | LL | fn iter_values<'a>(&'a self) -> impl Iterator { - | ----------------------- this return type evaluates to the `'static` lifetime... + | -------- this data with lifetime `'a`... LL | self.x.iter().map(|a| a.0) | ------ ^^^^ | | - | ...but this borrow... + | ...is captured here... | -note: ...can't outlive the lifetime `'a` as defined on the method body at 10:20 - --> $DIR/static-return-lifetime-infered.rs:10:20 +note: ...and is required to live as long as `'static` here + --> $DIR/static-return-lifetime-infered.rs:10:37 | LL | fn iter_values<'a>(&'a self) -> impl Iterator { - | ^^ -help: you can add a bound to the return type to make it last less than `'static` and match the lifetime `'a` as defined on the method body at 10:20 + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'a` lifetime bound | LL | fn iter_values<'a>(&'a self) -> impl Iterator + 'a { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/impl-trait/type_parameters_captured.stderr b/src/test/ui/impl-trait/type_parameters_captured.stderr index 34f0f7f1d731c..40e50b9922f8d 100644 --- a/src/test/ui/impl-trait/type_parameters_captured.stderr +++ b/src/test/ui/impl-trait/type_parameters_captured.stderr @@ -2,15 +2,9 @@ error[E0310]: the parameter type `T` may not live long enough --> $DIR/type_parameters_captured.rs:7:20 | LL | fn foo(x: T) -> impl Any + 'static { - | - ^^^^^^^^^^^^^^^^^^ + | - ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds | | | help: consider adding an explicit lifetime bound...: `T: 'static` - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/type_parameters_captured.rs:7:20 - | -LL | fn foo(x: T) -> impl Any + 'static { - | ^^^^^^^^^^^^^^^^^^ 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/impl-trait/where-allowed-2.rs b/src/test/ui/impl-trait/where-allowed-2.rs index f7744ef1b3eae..462508f306ef3 100644 --- a/src/test/ui/impl-trait/where-allowed-2.rs +++ b/src/test/ui/impl-trait/where-allowed-2.rs @@ -3,7 +3,6 @@ use std::fmt::Debug; // Disallowed -fn in_adt_in_return() -> Vec { panic!() } -//~^ ERROR opaque type expands to a recursive type +fn in_adt_in_return() -> Vec { panic!() } //~ ERROR cannot resolve opaque type fn main() {} diff --git a/src/test/ui/impl-trait/where-allowed-2.stderr b/src/test/ui/impl-trait/where-allowed-2.stderr index 1de15014c1f8d..b8e06725cbcdd 100644 --- a/src/test/ui/impl-trait/where-allowed-2.stderr +++ b/src/test/ui/impl-trait/where-allowed-2.stderr @@ -1,10 +1,12 @@ -error[E0720]: opaque type expands to a recursive type +error[E0720]: cannot resolve opaque type --> $DIR/where-allowed-2.rs:6:30 | LL | fn in_adt_in_return() -> Vec { panic!() } - | ^^^^^^^^^^ expands to a recursive type + | ^^^^^^^^^^ -------- this returned value is of `!` type + | | + | cannot resolve opaque type | - = note: type resolves to itself + = help: this error will resolve once the item's body returns a concrete type error: aborting due to previous error diff --git a/src/test/ui/impl-trait/where-allowed.stderr b/src/test/ui/impl-trait/where-allowed.stderr index 5d9ae6a03018f..7addc006e1900 100644 --- a/src/test/ui/impl-trait/where-allowed.stderr +++ b/src/test/ui/impl-trait/where-allowed.stderr @@ -256,16 +256,16 @@ LL | let _in_return_in_local_variable = || -> impl Fn() { || {} }; | ^^^^^^^^^ error: could not find defining uses - --> $DIR/where-allowed.rs:155:1 + --> $DIR/where-allowed.rs:119:16 | -LL | type InTypeAlias = impl Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type Out = impl Debug; + | ^^^^^^^^^^ error: could not find defining uses - --> $DIR/where-allowed.rs:119:5 + --> $DIR/where-allowed.rs:155:23 | -LL | type Out = impl Debug; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | type InTypeAlias = impl Debug; + | ^^^^^^^^^^ error: aborting due to 42 previous errors diff --git a/src/test/ui/implicit-method-bind.stderr b/src/test/ui/implicit-method-bind.stderr index 968272d4d2c19..c6af47805ea5b 100644 --- a/src/test/ui/implicit-method-bind.stderr +++ b/src/test/ui/implicit-method-bind.stderr @@ -2,7 +2,12 @@ error[E0615]: attempted to take value of method `abs` on type `i32` --> $DIR/implicit-method-bind.rs:2:20 | LL | let _f = 10i32.abs; - | ^^^ help: use parentheses to call the method: `abs()` + | ^^^ method, not a field + | +help: use parentheses to call the method + | +LL | let _f = 10i32.abs(); + | ^^ error: aborting due to previous error diff --git a/src/test/ui/import.stderr b/src/test/ui/import.stderr index 5219ffacd15c0..797712e2db99e 100644 --- a/src/test/ui/import.stderr +++ b/src/test/ui/import.stderr @@ -17,7 +17,7 @@ error[E0603]: unresolved item import `foo` is private --> $DIR/import.rs:15:10 | LL | zed::foo(); - | ^^^ this unresolved item import is private + | ^^^ private unresolved item import | note: the unresolved item import `foo` is defined here --> $DIR/import.rs:10:9 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/issue-55884-2.stderr b/src/test/ui/imports/issue-55884-2.stderr index f16d2adb3656e..5adbc4b66d133 100644 --- a/src/test/ui/imports/issue-55884-2.stderr +++ b/src/test/ui/imports/issue-55884-2.stderr @@ -2,13 +2,28 @@ error[E0603]: struct import `ParseOptions` is private --> $DIR/issue-55884-2.rs:12:17 | LL | pub use parser::ParseOptions; - | ^^^^^^^^^^^^ this struct import is private + | ^^^^^^^^^^^^ private struct import | -note: the struct import `ParseOptions` is defined here +note: the struct import `ParseOptions` is defined here... --> $DIR/issue-55884-2.rs:9:9 | LL | use ParseOptions; | ^^^^^^^^^^^^ +note: ...and refers to the struct import `ParseOptions` which is defined here... + --> $DIR/issue-55884-2.rs:12:9 + | +LL | pub use parser::ParseOptions; + | ^^^^^^^^^^^^^^^^^^^^ consider importing it directly +note: ...and refers to the struct import `ParseOptions` which is defined here... + --> $DIR/issue-55884-2.rs:6:13 + | +LL | pub use options::*; + | ^^^^^^^^^^ consider importing it directly +note: ...and refers to the struct `ParseOptions` which is defined here + --> $DIR/issue-55884-2.rs:2:5 + | +LL | pub struct ParseOptions {} + | ^^^^^^^^^^^^^^^^^^^^^^^ consider importing it directly error: aborting due to previous error diff --git a/src/test/ui/imports/issue-62767.rs b/src/test/ui/imports/issue-62767.rs new file mode 100644 index 0000000000000..984d3f0ca92f4 --- /dev/null +++ b/src/test/ui/imports/issue-62767.rs @@ -0,0 +1,15 @@ +// check-pass + +mod m { + pub enum Same { + Same, + } +} + +use m::*; + +// The variant `Same` introduced by this import is not considered when resolving the prefix +// `Same::` during import validation (issue #62767). +use Same::Same; + +fn main() {} diff --git a/src/test/ui/imports/reexports.stderr b/src/test/ui/imports/reexports.stderr index 7b0d63574ec8e..8cbff0ac73dee 100644 --- a/src/test/ui/imports/reexports.stderr +++ b/src/test/ui/imports/reexports.stderr @@ -14,25 +14,35 @@ error[E0603]: module import `foo` is private --> $DIR/reexports.rs:33:15 | LL | use b::a::foo::S; - | ^^^ this module import is private + | ^^^ private module import | -note: the module import `foo` is defined here +note: the module import `foo` is defined here... --> $DIR/reexports.rs:21:17 | LL | pub use super::foo; // This is OK since the value `foo` is visible enough. | ^^^^^^^^^^ +note: ...and refers to the module `foo` which is defined here + --> $DIR/reexports.rs:16:5 + | +LL | mod foo { + | ^^^^^^^ error[E0603]: module import `foo` is private --> $DIR/reexports.rs:34:15 | LL | use b::b::foo::S as T; - | ^^^ this module import is private + | ^^^ private module import | -note: the module import `foo` is defined here +note: the module import `foo` is defined here... --> $DIR/reexports.rs:26:17 | LL | pub use super::*; // This is also OK since the value `foo` is visible enough. | ^^^^^^^^ +note: ...and refers to the module `foo` which is defined here + --> $DIR/reexports.rs:16:5 + | +LL | mod foo { + | ^^^^^^^ warning: glob import doesn't reexport anything because no candidate is public enough --> $DIR/reexports.rs:9:17 @@ -46,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/imports/unresolved-imports-used.stderr b/src/test/ui/imports/unresolved-imports-used.stderr index 69765b9227d6e..ddf3608933909 100644 --- a/src/test/ui/imports/unresolved-imports-used.stderr +++ b/src/test/ui/imports/unresolved-imports-used.stderr @@ -38,7 +38,7 @@ error[E0603]: function `quz` is private --> $DIR/unresolved-imports-used.rs:9:10 | LL | use qux::quz; - | ^^^ this function is private + | ^^^ private function | note: the function `quz` is defined here --> $DIR/unresolved-imports-used.rs:5:4 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..b93d98ca39f47 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,16 +1,18 @@ 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 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&Struct) -> &dyn Trait` | ::: $SRC_DIR/libcore/ops/deref.rs:LL:COL | LL | fn deref(&self) -> &Self::Target; - | --------------------------------- expected fn(&Struct) -> &(dyn Trait + 'static) + | --------------------------------- expected `fn(&Struct) -> &(dyn Trait + 'static)` | = note: expected `fn(&Struct) -> &(dyn Trait + 'static)` found `fn(&Struct) -> &dyn Trait` + = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr index c245d78ae828f..149c2aeb958c0 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.nll.stderr @@ -2,13 +2,15 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/mismatched_trait_impl.rs:9:5 | LL | fn foo(&self, x: &'a u32, y: &u32) -> &'a u32; - | ---------------------------------------------- expected fn(&i32, &'a u32, &u32) -> &'a u32 + | ---------------------------------------------- expected `fn(&i32, &'a u32, &u32) -> &'a u32` ... LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &u32, &u32) -> &u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&i32, &u32, &u32) -> &u32` | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` found `fn(&i32, &u32, &u32) -> &u32` + = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr index 15891c9e7a62d..9a0bd827850cf 100644 --- a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr @@ -2,13 +2,15 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/mismatched_trait_impl.rs:9:5 | LL | fn foo(&self, x: &'a u32, y: &u32) -> &'a u32; - | ---------------------------------------------- expected fn(&i32, &'a u32, &u32) -> &'a u32 + | ---------------------------------------------- expected `fn(&i32, &'a u32, &u32) -> &'a u32` ... LL | fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &u32, &u32) -> &u32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&i32, &u32, &u32) -> &u32` | = note: expected `fn(&i32, &'a u32, &u32) -> &'a u32` found `fn(&i32, &u32, &u32) -> &u32` + = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error[E0623]: lifetime mismatch --> $DIR/mismatched_trait_impl.rs:10:9 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/infinite/infinite-instantiation.rs b/src/test/ui/infinite/infinite-instantiation.rs index 6f53680f7c81d..9fee01c1ba623 100644 --- a/src/test/ui/infinite/infinite-instantiation.rs +++ b/src/test/ui/infinite/infinite-instantiation.rs @@ -1,9 +1,3 @@ -// -// We get an error message at the top of file (dummy span). -// This is not helpful, but also kind of annoying to prevent, -// so for now just live with it. -// This test case was originally for issue #2258. - // build-fail trait ToOpt: Sized { @@ -23,11 +17,9 @@ impl ToOpt for Option { } fn function(counter: usize, t: T) { -//~^ ERROR reached the recursion limit while instantiating `function:: 0 { function(counter - 1, t.to_option()); - // FIXME(#4287) Error message should be here. It should be - // a type error to instantiate `test` at a type other than T. + //~^ ERROR reached the recursion limit while instantiating `function::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - --> $DIR/infinite-instantiation.rs:25:1 + --> $DIR/infinite-instantiation.rs:21:9 + | +LL | function(counter - 1, t.to_option()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `function` defined here + --> $DIR/infinite-instantiation.rs:19:1 | LL | / fn function(counter: usize, t: T) { -LL | | LL | | if counter > 0 { LL | | function(counter - 1, t.to_option()); -... | +LL | | LL | | } LL | | } | |_^ diff --git a/src/test/ui/infinite/infinite-recursion-const-fn.stderr b/src/test/ui/infinite/infinite-recursion-const-fn.stderr index 6bd5e035f5743..de0c579f63089 100644 --- a/src/test/ui/infinite/infinite-recursion-const-fn.stderr +++ b/src/test/ui/infinite/infinite-recursion-const-fn.stderr @@ -1,14 +1,14 @@ error[E0391]: cycle detected when const-evaluating `a` - --> $DIR/infinite-recursion-const-fn.rs:3:25 + --> $DIR/infinite-recursion-const-fn.rs:3:1 | LL | const fn a() -> usize { b() } - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | note: ...which requires const-evaluating `b`... - --> $DIR/infinite-recursion-const-fn.rs:4:25 + --> $DIR/infinite-recursion-const-fn.rs:4:1 | LL | const fn b() -> usize { a() } - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^ = note: ...which again requires const-evaluating `a`, completing the cycle note: cycle used when const-evaluating `ARR::{{constant}}#0` --> $DIR/infinite-recursion-const-fn.rs:5:18 diff --git a/src/test/ui/infinite/infinite-tag-type-recursion.rs b/src/test/ui/infinite/infinite-tag-type-recursion.rs index bbfaaa62f0d32..8578c5545bc90 100644 --- a/src/test/ui/infinite/infinite-tag-type-recursion.rs +++ b/src/test/ui/infinite/infinite-tag-type-recursion.rs @@ -1,5 +1,5 @@ enum MList { Cons(isize, MList), Nil } //~^ ERROR recursive type `MList` has infinite size -//~| ERROR cycle detected when processing `MList` +//~| ERROR cycle detected when computing drop-check constraints for `MList` fn main() { let a = MList::Cons(10, MList::Cons(11, MList::Nil)); } diff --git a/src/test/ui/infinite/infinite-tag-type-recursion.stderr b/src/test/ui/infinite/infinite-tag-type-recursion.stderr index 8f6529db0bec5..6d1df4fda2eb0 100644 --- a/src/test/ui/infinite/infinite-tag-type-recursion.stderr +++ b/src/test/ui/infinite/infinite-tag-type-recursion.stderr @@ -6,15 +6,18 @@ LL | enum MList { Cons(isize, MList), Nil } | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `MList` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `MList` representable + | +LL | enum MList { Cons(isize, Box), Nil } + | ^^^^ ^ -error[E0391]: cycle detected when processing `MList` +error[E0391]: cycle detected when computing drop-check constraints for `MList` --> $DIR/infinite-tag-type-recursion.rs:1:1 | LL | enum MList { Cons(isize, MList), Nil } | ^^^^^^^^^^ | - = note: ...which again requires processing `MList`, completing the cycle + = note: ...which again requires computing drop-check constraints for `MList`, completing the cycle = note: cycle used when computing dropck types for `Canonical { max_universe: U0, variables: [], value: ParamEnvAnd { param_env: ParamEnv { caller_bounds: [], reveal: UserFacing, def_id: None }, value: MList } }` error: aborting due to 2 previous errors diff --git a/src/test/ui/infinite/infinite-vec-type-recursion.stderr b/src/test/ui/infinite/infinite-vec-type-recursion.stderr index be0db56f03449..77adefeb124e3 100644 --- a/src/test/ui/infinite/infinite-vec-type-recursion.stderr +++ b/src/test/ui/infinite/infinite-vec-type-recursion.stderr @@ -1,10 +1,10 @@ -error[E0391]: cycle detected when processing `X` +error[E0391]: cycle detected when computing type of `X` --> $DIR/infinite-vec-type-recursion.rs:1:14 | LL | type X = Vec; | ^ | - = note: ...which again requires processing `X`, completing the cycle + = note: ...which again requires computing type of `X`, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/infinite-vec-type-recursion.rs:1:1 | diff --git a/src/test/ui/init-large-type.rs b/src/test/ui/init-large-type.rs index a304fc9356b51..ce905572f2a8c 100644 --- a/src/test/ui/init-large-type.rs +++ b/src/test/ui/init-large-type.rs @@ -1,3 +1,4 @@ +// compile-flags: -O // run-pass #![allow(unused_must_use)] @@ -10,17 +11,13 @@ #![feature(intrinsics)] -use std::thread; - -extern "rust-intrinsic" { - pub fn init() -> T; -} +use std::{mem, thread}; const SIZE: usize = 1024 * 1024; fn main() { // do the test in a new thread to avoid (spurious?) stack overflows thread::spawn(|| { - let _memory: [u8; SIZE] = unsafe { init() }; + let _memory: [u8; SIZE] = unsafe { mem::zeroed() }; }).join(); } diff --git a/src/test/ui/init-unsafe.rs b/src/test/ui/init-unsafe.rs deleted file mode 100644 index 3d65cfc234092..0000000000000 --- a/src/test/ui/init-unsafe.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![allow(deprecated)] -#![feature(core_intrinsics)] - -use std::intrinsics::{init}; - -// Test that the `init` intrinsic is really unsafe -pub fn main() { - let stuff = init::(); //~ ERROR call to unsafe function is unsafe -} diff --git a/src/test/ui/init-unsafe.stderr b/src/test/ui/init-unsafe.stderr deleted file mode 100644 index e1126316af34e..0000000000000 --- a/src/test/ui/init-unsafe.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0133]: call to unsafe function is unsafe and requires unsafe function or block - --> $DIR/init-unsafe.rs:8:17 - | -LL | let stuff = init::(); - | ^^^^^^^^^^^^^^^ call to unsafe function - | - = note: consult the function's documentation for information on how to avoid undefined behavior - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0133`. 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/intrinsic-move-val.rs b/src/test/ui/intrinsics/intrinsic-move-val.rs index 75b4ec365fe03..b672f1ed26e8d 100644 --- a/src/test/ui/intrinsics/intrinsic-move-val.rs +++ b/src/test/ui/intrinsics/intrinsic-move-val.rs @@ -5,7 +5,6 @@ mod rusti { extern "rust-intrinsic" { - pub fn init() -> T; pub fn move_val_init(dst: *mut T, src: T); } } @@ -15,17 +14,17 @@ pub fn main() { // sanity check check_drops_state(0, None); - let mut x: Box = box D(1); - assert_eq!(x.0, 1); + let mut x: Option> = Some(box D(1)); + assert_eq!(x.as_ref().unwrap().0, 1); // A normal overwrite, to demonstrate `check_drops_state`. - x = box D(2); + x = Some(box D(2)); // At this point, one destructor has run, because the // overwrite of `x` drops its initial value. check_drops_state(1, Some(1)); - let mut y: Box = rusti::init(); + let mut y: Option> = std::mem::zeroed(); // An initial binding does not overwrite anything. check_drops_state(1, Some(1)); @@ -51,9 +50,9 @@ pub fn main() { // during such a destructor call. We do so after the end of // this scope. - assert_eq!(y.0, 2); - y.0 = 3; - assert_eq!(y.0, 3); + assert_eq!(y.as_ref().unwrap().0, 2); + y.as_mut().unwrap().0 = 3; + assert_eq!(y.as_ref().unwrap().0, 3); check_drops_state(1, Some(1)); } diff --git a/src/test/ui/intrinsics/intrinsic-uninit.rs b/src/test/ui/intrinsics/intrinsic-uninit.rs deleted file mode 100644 index 9555efb639b50..0000000000000 --- a/src/test/ui/intrinsics/intrinsic-uninit.rs +++ /dev/null @@ -1,13 +0,0 @@ -// run-pass -// pretty-expanded FIXME #23616 - -#![feature(intrinsics)] - -mod rusti { - extern "rust-intrinsic" { - pub fn uninit() -> T; - } -} -pub fn main() { - let _a : isize = unsafe {rusti::uninit()}; -} 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-module-declaration/invalid-module-declaration.stderr b/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr index c95df5b4534c1..52296042eb4a7 100644 --- a/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr +++ b/src/test/ui/invalid-module-declaration/invalid-module-declaration.stderr @@ -1,10 +1,10 @@ error[E0583]: file not found for module `baz` - --> $DIR/auxiliary/foo/bar.rs:1:9 + --> $DIR/auxiliary/foo/bar.rs:1:1 | LL | pub mod baz; - | ^^^ + | ^^^^^^^^^^^^ | - = help: name the file either bar/baz.rs or bar/baz/mod.rs inside the directory "$DIR/auxiliary/foo" + = help: to create the module `baz`, create file "$DIR/auxiliary/foo/bar/baz.rs" error: aborting due to previous error 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..d7a4bf4f21f18 100644 --- a/src/test/ui/issues/issue-10412.stderr +++ b/src/test/ui/issues/issue-10412.stderr @@ -49,11 +49,18 @@ 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 | = help: the trait `std::marker::Sized` is not implemented for `str` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | trait Serializable<'self, T: ?Sized> { + | ^^^^^^^^ error: aborting due to 9 previous errors diff --git a/src/test/ui/issues/issue-10545.stderr b/src/test/ui/issues/issue-10545.stderr index 4ed7028c0a06b..f1da33eaba1c3 100644 --- a/src/test/ui/issues/issue-10545.stderr +++ b/src/test/ui/issues/issue-10545.stderr @@ -2,7 +2,7 @@ error[E0603]: struct `S` is private --> $DIR/issue-10545.rs:6:14 | LL | fn foo(_: a::S) { - | ^ this struct is private + | ^ private struct | note: the struct `S` is defined here --> $DIR/issue-10545.rs:2:5 diff --git a/src/test/ui/issues/issue-10656.rs b/src/test/ui/issues/issue-10656.rs index 8918dadb47a35..250c4bc442f98 100644 --- a/src/test/ui/issues/issue-10656.rs +++ b/src/test/ui/issues/issue-10656.rs @@ -1,3 +1,3 @@ #![deny(missing_docs)] #![crate_type="lib"] -//~^^ ERROR missing documentation for crate +//~^^ ERROR missing documentation for the crate diff --git a/src/test/ui/issues/issue-10656.stderr b/src/test/ui/issues/issue-10656.stderr index 2e91a598dce40..2e4365f1ed76b 100644 --- a/src/test/ui/issues/issue-10656.stderr +++ b/src/test/ui/issues/issue-10656.stderr @@ -1,4 +1,4 @@ -error: missing documentation for crate +error: missing documentation for the crate --> $DIR/issue-10656.rs:1:1 | LL | / #![deny(missing_docs)] diff --git a/src/test/ui/issues/issue-10991.stderr b/src/test/ui/issues/issue-10991.stderr index f12539b47cf44..5b8a182338693 100644 --- a/src/test/ui/issues/issue-10991.stderr +++ b/src/test/ui/issues/issue-10991.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `()` as `usize` --> $DIR/issue-10991.rs:3:14 | LL | let _t = nil as usize; - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/issues/issue-11593.stderr b/src/test/ui/issues/issue-11593.stderr index bfb4d31323b13..aa9768b1885a2 100644 --- a/src/test/ui/issues/issue-11593.stderr +++ b/src/test/ui/issues/issue-11593.stderr @@ -2,7 +2,7 @@ error[E0603]: trait `Foo` is private --> $DIR/issue-11593.rs:7:24 | LL | impl private_trait_xc::Foo for Bar {} - | ^^^ this trait is private + | ^^^ private trait | note: the trait `Foo` is defined here --> $DIR/auxiliary/private-trait-xc.rs:1:1 diff --git a/src/test/ui/issues/issue-11680.stderr b/src/test/ui/issues/issue-11680.stderr index 898ac10f7d9a9..ea224af8ed7e2 100644 --- a/src/test/ui/issues/issue-11680.stderr +++ b/src/test/ui/issues/issue-11680.stderr @@ -2,7 +2,7 @@ error[E0603]: enum `Foo` is private --> $DIR/issue-11680.rs:6:21 | LL | let _b = other::Foo::Bar(1); - | ^^^ this enum is private + | ^^^ private enum | note: the enum `Foo` is defined here --> $DIR/auxiliary/issue-11680.rs:1:1 @@ -14,7 +14,7 @@ error[E0603]: enum `Foo` is private --> $DIR/issue-11680.rs:9:27 | LL | let _b = other::test::Foo::Bar(1); - | ^^^ this enum is private + | ^^^ private enum | note: the enum `Foo` is defined here --> $DIR/auxiliary/issue-11680.rs:6:5 diff --git a/src/test/ui/issues/issue-11958.rs b/src/test/ui/issues/issue-11958.rs index 8fe8a8c606189..a7af01e25b4e2 100644 --- a/src/test/ui/issues/issue-11958.rs +++ b/src/test/ui/issues/issue-11958.rs @@ -1,5 +1,4 @@ // run-pass -#![forbid(warnings)] // We shouldn't need to rebind a moved upvar as mut if it's already // marked as mut @@ -7,4 +6,6 @@ pub fn main() { let mut x = 1; let _thunk = Box::new(move|| { x = 2; }); + //~^ WARN value assigned to `x` is never read + //~| WARN unused variable: `x` } diff --git a/src/test/ui/issues/issue-11958.stderr b/src/test/ui/issues/issue-11958.stderr new file mode 100644 index 0000000000000..25de6ff4c118c --- /dev/null +++ b/src/test/ui/issues/issue-11958.stderr @@ -0,0 +1,20 @@ +warning: value assigned to `x` is never read + --> $DIR/issue-11958.rs:8:36 + | +LL | let _thunk = Box::new(move|| { x = 2; }); + | ^ + | + = note: `#[warn(unused_assignments)]` on by default + = help: maybe it is overwritten before being read? + +warning: unused variable: `x` + --> $DIR/issue-11958.rs:8:36 + | +LL | let _thunk = Box::new(move|| { x = 2; }); + | ^ + | + = note: `#[warn(unused_variables)]` on by default + = help: did you mean to capture by reference instead? + +warning: 2 warnings emitted + 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-13407.stderr b/src/test/ui/issues/issue-13407.stderr index f211d623ab12b..f30b6cdeaf073 100644 --- a/src/test/ui/issues/issue-13407.stderr +++ b/src/test/ui/issues/issue-13407.stderr @@ -2,7 +2,7 @@ error[E0603]: unit struct `C` is private --> $DIR/issue-13407.rs:6:8 | LL | A::C = 1; - | ^ this unit struct is private + | ^ private unit struct | note: the unit struct `C` is defined here --> $DIR/issue-13407.rs:2:5 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-13641.stderr b/src/test/ui/issues/issue-13641.stderr index f90cb18b6fc9d..cdd0772d39fb0 100644 --- a/src/test/ui/issues/issue-13641.stderr +++ b/src/test/ui/issues/issue-13641.stderr @@ -2,7 +2,7 @@ error[E0603]: struct `Foo` is private --> $DIR/issue-13641.rs:9:8 | LL | a::Foo::new(); - | ^^^ this struct is private + | ^^^ private struct | note: the struct `Foo` is defined here --> $DIR/issue-13641.rs:2:5 @@ -14,7 +14,7 @@ error[E0603]: enum `Bar` is private --> $DIR/issue-13641.rs:11:8 | LL | a::Bar::new(); - | ^^^ this enum is private + | ^^^ private enum | note: the enum `Bar` is defined here --> $DIR/issue-13641.rs:4:5 diff --git a/src/test/ui/issues/issue-13853-2.stderr b/src/test/ui/issues/issue-13853-2.stderr index ea3b38940cf01..49b946b354e53 100644 --- a/src/test/ui/issues/issue-13853-2.stderr +++ b/src/test/ui/issues/issue-13853-2.stderr @@ -2,7 +2,12 @@ error[E0615]: attempted to take value of method `get` on type `std::boxed::Box<( --> $DIR/issue-13853-2.rs:5:43 | LL | fn foo(res : Box) { res.get } - | ^^^ help: use parentheses to call the method: `get()` + | ^^^ method, not a field + | +help: use parentheses to call the method + | +LL | fn foo(res : Box) { res.get() } + | ^^ 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-16048.rs b/src/test/ui/issues/issue-16048.rs index 7d24f3a40a742..eaf6acff26bf3 100644 --- a/src/test/ui/issues/issue-16048.rs +++ b/src/test/ui/issues/issue-16048.rs @@ -18,12 +18,12 @@ impl<'a> Test<'a> for Foo<'a> { } impl<'a> NoLifetime for Foo<'a> { - fn get<'p, T : Test<'a>>(&self) -> T { + fn get<'p, T: Test<'a> + From>>(&self) -> T { //~^ ERROR E0195 //~| NOTE lifetimes do not match method in trait return *self as T; //~^ ERROR non-primitive cast: `Foo<'a>` as `T` - //~| NOTE an `as` expression can only be used to convert between primitive types. + //~| NOTE an `as` expression can only be used to convert between primitive types } } diff --git a/src/test/ui/issues/issue-16048.stderr b/src/test/ui/issues/issue-16048.stderr index a137bcdf1915e..73610942d7a7e 100644 --- a/src/test/ui/issues/issue-16048.stderr +++ b/src/test/ui/issues/issue-16048.stderr @@ -4,16 +4,16 @@ error[E0195]: lifetime parameters or bounds on method `get` do not match the tra LL | fn get<'p, T : Test<'p>>(&self) -> T; | ------------------ lifetimes in impl do not match this method in trait ... -LL | fn get<'p, T : Test<'a>>(&self) -> T { - | ^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait +LL | fn get<'p, T: Test<'a> + From>>(&self) -> T { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetimes do not match method in trait error[E0605]: non-primitive cast: `Foo<'a>` as `T` --> $DIR/issue-16048.rs:24:16 | LL | return *self as T; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: consider using the `From` trait instead: `T::from(*self)` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-16441.rs b/src/test/ui/issues/issue-16441.rs index bae3813f9da95..bafa204e06b25 100644 --- a/src/test/ui/issues/issue-16441.rs +++ b/src/test/ui/issues/issue-16441.rs @@ -5,6 +5,7 @@ struct Empty; // This used to cause an ICE +#[allow(improper_ctypes_definitions)] extern "C" fn ice(_a: Empty) {} fn main() { 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-16683.stderr b/src/test/ui/issues/issue-16683.stderr index 99700f2084e4a..4f65833075814 100644 --- a/src/test/ui/issues/issue-16683.stderr +++ b/src/test/ui/issues/issue-16683.stderr @@ -26,8 +26,8 @@ note: ...so that the types are compatible | LL | self.a(); | ^ - = note: expected `&'a Self` - found `&Self` + = note: expected `&'a Self` + found `&Self` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-16725.stderr b/src/test/ui/issues/issue-16725.stderr index e0a1ca8a5ac31..84359803bbae7 100644 --- a/src/test/ui/issues/issue-16725.stderr +++ b/src/test/ui/issues/issue-16725.stderr @@ -2,7 +2,7 @@ error[E0603]: function `bar` is private --> $DIR/issue-16725.rs:6:19 | LL | unsafe { foo::bar(); } - | ^^^ this function is private + | ^^^ private function | note: the function `bar` is defined here --> $DIR/auxiliary/issue-16725.rs:2:5 diff --git a/src/test/ui/issues/issue-16922.nll.stderr b/src/test/ui/issues/issue-16922.nll.stderr new file mode 100644 index 0000000000000..7f4f5b22eb302 --- /dev/null +++ b/src/test/ui/issues/issue-16922.nll.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/issue-16922.rs:4:5 + | +LL | fn foo(value: &T) -> Box { + | - let's call the lifetime of this reference `'1` +LL | Box::new(value) as Box + | ^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-16922.rs b/src/test/ui/issues/issue-16922.rs index 10a5cccbceef0..827163ef83cf7 100644 --- a/src/test/ui/issues/issue-16922.rs +++ b/src/test/ui/issues/issue-16922.rs @@ -2,7 +2,7 @@ use std::any::Any; fn foo(value: &T) -> Box { Box::new(value) as Box - //~^ ERROR explicit lifetime required in the type of `value` [E0621] + //~^ ERROR cannot infer an appropriate lifetime } fn main() { diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index 4e3d3ecb9c03a..919594fc9af4b 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -1,11 +1,16 @@ -error[E0621]: explicit lifetime required in the type of `value` - --> $DIR/issue-16922.rs:4:5 +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/issue-16922.rs:4:14 | LL | fn foo(value: &T) -> Box { - | -- help: add explicit lifetime `'static` to the type of `value`: `&'static T` + | -- this data with an anonymous lifetime `'_`... LL | Box::new(value) as Box - | ^^^^^^^^^^^^^^^ lifetime `'static` required + | ^^^^^ ...is captured here, requiring it to live as long as `'static` + | +help: to declare that the trait object captures data from argument `value`, you can add an explicit `'_` lifetime bound + | +LL | fn foo(value: &T) -> Box { + | ^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0621`. +For more information about this error, try `rustc --explain E0759`. 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-17431-1.stderr b/src/test/ui/issues/issue-17431-1.stderr index eb5a1366e8953..58d087ca1998b 100644 --- a/src/test/ui/issues/issue-17431-1.stderr +++ b/src/test/ui/issues/issue-17431-1.stderr @@ -2,11 +2,14 @@ error[E0072]: recursive type `Foo` has infinite size --> $DIR/issue-17431-1.rs:1:1 | LL | struct Foo { foo: Option> } - | ^^^^^^^^^^ ------------------------ recursive without indirection + | ^^^^^^^^^^ ------------------- recursive without indirection | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable + | +LL | struct Foo { foo: Box>> } + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17431-2.stderr b/src/test/ui/issues/issue-17431-2.stderr index 3a7b0e9ce7997..eba4bf6d1d5ea 100644 --- a/src/test/ui/issues/issue-17431-2.stderr +++ b/src/test/ui/issues/issue-17431-2.stderr @@ -2,21 +2,27 @@ error[E0072]: recursive type `Baz` has infinite size --> $DIR/issue-17431-2.rs:1:1 | LL | struct Baz { q: Option } - | ^^^^^^^^^^ -------------- recursive without indirection + | ^^^^^^^^^^ ----------- recursive without indirection | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Baz` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Baz` representable + | +LL | struct Baz { q: Box> } + | ^^^^ ^ error[E0072]: recursive type `Foo` has infinite size --> $DIR/issue-17431-2.rs:4:1 | LL | struct Foo { q: Option } - | ^^^^^^^^^^ -------------- recursive without indirection + | ^^^^^^^^^^ ----------- recursive without indirection | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable + | +LL | struct Foo { q: Box> } + | ^^^^ ^ error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-17431-3.stderr b/src/test/ui/issues/issue-17431-3.stderr index 675a2e2714209..f6b15d0528ae8 100644 --- a/src/test/ui/issues/issue-17431-3.stderr +++ b/src/test/ui/issues/issue-17431-3.stderr @@ -2,11 +2,14 @@ error[E0072]: recursive type `Foo` has infinite size --> $DIR/issue-17431-3.rs:3:1 | LL | struct Foo { foo: Mutex> } - | ^^^^^^^^^^ ----------------------- recursive without indirection + | ^^^^^^^^^^ ------------------ recursive without indirection | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable + | +LL | struct Foo { foo: Box>> } + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17431-4.stderr b/src/test/ui/issues/issue-17431-4.stderr index aff9071095ca0..aa709e1ad5183 100644 --- a/src/test/ui/issues/issue-17431-4.stderr +++ b/src/test/ui/issues/issue-17431-4.stderr @@ -2,11 +2,14 @@ error[E0072]: recursive type `Foo` has infinite size --> $DIR/issue-17431-4.rs:3:1 | LL | struct Foo { foo: Option>>, marker: marker::PhantomData } - | ^^^^^^^^^^^^^ --------------------------- recursive without indirection + | ^^^^^^^^^^^^^ ---------------------- recursive without indirection | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable + | +LL | struct Foo { foo: Box>>>, marker: marker::PhantomData } + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17431-5.stderr b/src/test/ui/issues/issue-17431-5.stderr index 537f9f34f55ca..1558cffb036b3 100644 --- a/src/test/ui/issues/issue-17431-5.stderr +++ b/src/test/ui/issues/issue-17431-5.stderr @@ -2,11 +2,14 @@ error[E0072]: recursive type `Bar` has infinite size --> $DIR/issue-17431-5.rs:5:1 | LL | struct Bar { x: Bar , marker: marker::PhantomData } - | ^^^^^^^^^^^^^ ----------- recursive without indirection + | ^^^^^^^^^^^^^ -------- recursive without indirection | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Bar` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Bar` representable + | +LL | struct Bar { x: Box> , marker: marker::PhantomData } + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17431-6.stderr b/src/test/ui/issues/issue-17431-6.stderr index cb2dab9501488..f2aa2a79c8200 100644 --- a/src/test/ui/issues/issue-17431-6.stderr +++ b/src/test/ui/issues/issue-17431-6.stderr @@ -6,7 +6,10 @@ LL | enum Foo { X(Mutex>) } | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable + | +LL | enum Foo { X(Box>>) } + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17431-7.stderr b/src/test/ui/issues/issue-17431-7.stderr index de70851da4b5f..684c3089e85ec 100644 --- a/src/test/ui/issues/issue-17431-7.stderr +++ b/src/test/ui/issues/issue-17431-7.stderr @@ -6,7 +6,10 @@ LL | enum Foo { Voo(Option>) } | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable + | +LL | enum Foo { Voo(Box>>) } + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17441.stderr b/src/test/ui/issues/issue-17441.stderr index 0ab035515a051..b63a3995d255d 100644 --- a/src/test/ui/issues/issue-17441.stderr +++ b/src/test/ui/issues/issue-17441.stderr @@ -16,7 +16,7 @@ error[E0620]: cast to unsized type: `std::boxed::Box` as `dyn std::fmt::D LL | let _bar = Box::new(1_usize) as dyn std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^^------------------- | | - | help: try casting to a `Box` instead: `Box` + | help: you can cast to a `Box` instead: `Box` error[E0620]: cast to unsized type: `usize` as `dyn std::fmt::Debug` --> $DIR/issue-17441.rs:8:16 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..95939cf6b3840 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,43 +19,41 @@ 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; | LL | use std::io::Result; | -LL | use std::prelude::v1::Result; - | LL | use std::result::Result; | - and 1 other candidate +LL | use std::thread::Result; + | 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; | LL | use std::io::Result; | -LL | use std::prelude::v1::Result; - | LL | use std::result::Result; | - and 1 other candidate +LL | use std::thread::Result; + | 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-bad-values.rs b/src/test/ui/issues/issue-17718-const-bad-values.rs index 9355c8ab15256..49023f18ddbfb 100644 --- a/src/test/ui/issues/issue-17718-const-bad-values.rs +++ b/src/test/ui/issues/issue-17718-const-bad-values.rs @@ -1,10 +1,10 @@ const C1: &'static mut [usize] = &mut []; -//~^ ERROR: references in constants may only refer to immutable values +//~^ ERROR: mutable references are not allowed in constants static mut S: usize = 3; const C2: &'static mut usize = unsafe { &mut S }; //~^ ERROR: constants cannot refer to statics //~| ERROR: constants cannot refer to statics -//~| ERROR: references in constants may only refer to immutable values +//~| ERROR: mutable references are not allowed in constants fn main() {} diff --git a/src/test/ui/issues/issue-17718-const-bad-values.stderr b/src/test/ui/issues/issue-17718-const-bad-values.stderr index 688efcdd022eb..7c50978d4ebb8 100644 --- a/src/test/ui/issues/issue-17718-const-bad-values.stderr +++ b/src/test/ui/issues/issue-17718-const-bad-values.stderr @@ -1,11 +1,8 @@ -error[E0658]: references in constants may only refer to immutable values +error[E0764]: mutable references are not allowed in constants --> $DIR/issue-17718-const-bad-values.rs:1:34 | LL | const C1: &'static mut [usize] = &mut []; - | ^^^^^^^ constants require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^^ `&mut` is only allowed in `const fn` error[E0013]: constants cannot refer to statics --> $DIR/issue-17718-const-bad-values.rs:5:46 @@ -23,16 +20,13 @@ LL | const C2: &'static mut usize = unsafe { &mut S }; | = help: consider extracting the value of the `static` to a `const`, and referring to that -error[E0658]: references in constants may only refer to immutable values +error[E0764]: mutable references are not allowed in constants --> $DIR/issue-17718-const-bad-values.rs:5:41 | LL | const C2: &'static mut usize = unsafe { &mut S }; - | ^^^^^^ constants require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^ `&mut` is only allowed in `const fn` error: aborting due to 4 previous errors -Some errors have detailed explanations: E0013, E0658. +Some errors have detailed explanations: E0013, E0764. For more information about an error, try `rustc --explain E0013`. 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-const-privacy.stderr b/src/test/ui/issues/issue-17718-const-privacy.stderr index 07d825ba9cb3b..d4595be749034 100644 --- a/src/test/ui/issues/issue-17718-const-privacy.stderr +++ b/src/test/ui/issues/issue-17718-const-privacy.stderr @@ -2,7 +2,7 @@ error[E0603]: constant `B` is private --> $DIR/issue-17718-const-privacy.rs:5:8 | LL | use a::B; - | ^ this constant is private + | ^ private constant | note: the constant `B` is defined here --> $DIR/issue-17718-const-privacy.rs:13:5 @@ -14,7 +14,7 @@ error[E0603]: constant `BAR` is private --> $DIR/issue-17718-const-privacy.rs:8:5 | LL | BAR, - | ^^^ this constant is private + | ^^^ private constant | note: the constant `BAR` is defined here --> $DIR/auxiliary/issue-17718-const-privacy.rs:4:1 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-17758.stderr b/src/test/ui/issues/issue-17758.stderr index adfc3f5085826..31788cfa61c4c 100644 --- a/src/test/ui/issues/issue-17758.stderr +++ b/src/test/ui/issues/issue-17758.stderr @@ -27,8 +27,8 @@ note: ...so that the types are compatible | LL | self.foo(); | ^^^ - = note: expected `&'a Self` - found `&Self` + = note: expected `&'a Self` + found `&Self` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-17999.stderr b/src/test/ui/issues/issue-17999.stderr index 448208ef033f7..4a1ef7350b8cb 100644 --- a/src/test/ui/issues/issue-17999.stderr +++ b/src/test/ui/issues/issue-17999.stderr @@ -2,7 +2,7 @@ error: unused variable: `x` --> $DIR/issue-17999.rs:5:13 | LL | let x = (); - | ^ help: consider prefixing with an underscore: `_x` + | ^ help: if this is intentional, prefix it with an underscore: `_x` | note: the lint level is defined here --> $DIR/issue-17999.rs:1:9 @@ -14,7 +14,7 @@ error: unused variable: `a` --> $DIR/issue-17999.rs:7:13 | LL | a => {} - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` error: aborting due to 2 previous errors 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-18400.stderr b/src/test/ui/issues/issue-18400.stderr index 57067ad51759a..ed9137ce396cf 100644 --- a/src/test/ui/issues/issue-18400.stderr +++ b/src/test/ui/issues/issue-18400.stderr @@ -133,6 +133,7 @@ LL | 0.contains(bits); = note: required because of the requirements on the impl of `Set<&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[_]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]>` for `{integer}` = note: required because of the requirements on the impl of `Set<&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[_]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]>` for `{integer}` = note: required because of the requirements on the impl of `Set<&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[_]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]>` for `{integer}` + = note: required because of the requirements on the impl of `Set<&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[&[_]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]>` for `{integer}` error: aborting due to previous error 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-1866.rs b/src/test/ui/issues/issue-1866.rs index e4fe26800eff3..668baefa5e4ad 100644 --- a/src/test/ui/issues/issue-1866.rs +++ b/src/test/ui/issues/issue-1866.rs @@ -1,6 +1,7 @@ // build-pass #![allow(dead_code)] #![allow(non_camel_case_types)] +#![warn(clashing_extern_decl)] // pretty-expanded FIXME #23616 @@ -20,6 +21,7 @@ mod b { use super::rust_task; extern { pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool; + //~^ WARN `rust_task_is_unwinding` redeclared with a different signature } } } diff --git a/src/test/ui/issues/issue-1866.stderr b/src/test/ui/issues/issue-1866.stderr new file mode 100644 index 0000000000000..13c08ebd373ed --- /dev/null +++ b/src/test/ui/issues/issue-1866.stderr @@ -0,0 +1,19 @@ +warning: `rust_task_is_unwinding` redeclared with a different signature + --> $DIR/issue-1866.rs:23:13 + | +LL | pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool; + | ------------------------------------------------------------ `rust_task_is_unwinding` previously declared here +... +LL | pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | +note: the lint level is defined here + --> $DIR/issue-1866.rs:4:9 + | +LL | #![warn(clashing_extern_decl)] + | ^^^^^^^^^^^^^^^^^^^^ + = note: expected `unsafe extern "C" fn(*const usize) -> bool` + found `unsafe extern "C" fn(*const bool) -> bool` + +warning: 1 warning emitted + 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..383cdd4979ad9 100644 --- a/src/test/ui/issues/issue-18919.stderr +++ b/src/test/ui/issues/issue-18919.stderr @@ -3,10 +3,19 @@ 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` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/issue-18919.rs:7:13 + | +LL | enum Option { + | ^ this could be changed to `T: ?Sized`... +LL | Some(T), + | - ...if indirection was used here: `Box` 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..a3eb4fec70f32 100644 --- a/src/test/ui/issues/issue-20413.stderr +++ b/src/test/ui/issues/issue-20413.stderr @@ -6,16 +6,17 @@ LL | struct NoData; | = help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData` -error[E0275]: overflow evaluating the requirement `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: Foo` +error[E0275]: overflow evaluating the requirement `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: Foo` --> $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 { | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`) + = note: required because of the requirements on the impl of `Foo` for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` = note: required because of the requirements on the impl of `Foo` for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` = note: required because of the requirements on the impl of `Foo` for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` = note: required because of the requirements on the impl of `Foo` for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` @@ -144,16 +145,17 @@ LL | impl Foo for T where NoData: Foo { = note: required because of the requirements on the impl of `Foo` for `NoData>` = note: required because of the requirements on the impl of `Foo` for `NoData` -error[E0275]: overflow evaluating the requirement `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: Foo` +error[E0275]: overflow evaluating the requirement `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>: Foo` --> $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 { | ^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_20413`) + = note: required because of the requirements on the impl of `Foo` for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` = note: required because of the requirements on the impl of `Foo` for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` = note: required because of the requirements on the impl of `Foo` for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` = note: required because of the requirements on the impl of `Foo` for `NoData>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` 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-20605.stderr b/src/test/ui/issues/issue-20605.stderr index 89df58dd2dc1b..5e050f27ac546 100644 --- a/src/test/ui/issues/issue-20605.stderr +++ b/src/test/ui/issues/issue-20605.stderr @@ -1,10 +1,10 @@ -error[E0277]: the size for values of type `dyn std::iter::Iterator` cannot be known at compilation time +error[E0277]: the size for values of type `dyn std::iter::Iterator` cannot be known at compilation time --> $DIR/issue-20605.rs:2:17 | LL | for item in *things { *item = 0 } | ^^^^^^^ doesn't have a size known at compile-time | - = help: the trait `std::marker::Sized` is not implemented for `dyn std::iter::Iterator` + = help: the trait `std::marker::Sized` is not implemented for `dyn std::iter::Iterator` = note: to learn more, visit = note: required by `std::iter::IntoIterator::into_iter` diff --git a/src/test/ui/issues/issue-20831-debruijn.stderr b/src/test/ui/issues/issue-20831-debruijn.stderr index a785a956ca9f5..e7c1dcc5d698c 100644 --- a/src/test/ui/issues/issue-20831-debruijn.stderr +++ b/src/test/ui/issues/issue-20831-debruijn.stderr @@ -87,8 +87,8 @@ note: ...so that the types are compatible | LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `Publisher<'_>` - found `Publisher<'_>` + = note: expected `Publisher<'_>` + found `Publisher<'_>` error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> $DIR/issue-20831-debruijn.rs:28:33 @@ -117,8 +117,8 @@ note: ...so that the types are compatible | LL | fn subscribe(&mut self, t : Box::Output> + 'a>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `Publisher<'_>` - found `Publisher<'_>` + = note: expected `Publisher<'_>` + found `Publisher<'_>` error: aborting due to 4 previous errors 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-21174.stderr b/src/test/ui/issues/issue-21174.stderr index 5ac5a8665bc69..09402c3d81410 100644 --- a/src/test/ui/issues/issue-21174.stderr +++ b/src/test/ui/issues/issue-21174.stderr @@ -4,8 +4,8 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | let new: T::B = unsafe { std::mem::transmute(value) }; | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `>::A` (size can vary because of ::A) - = note: target type: `>::B` (size can vary because of ::B) + = note: source type: `::A` (this type does not have a fixed size) + = note: target type: `::B` (this type does not have a fixed size) error: aborting due to previous error diff --git a/src/test/ui/issues/issue-21177.stderr b/src/test/ui/issues/issue-21177.stderr index 00d9a3c46a723..59cc6550a8bd6 100644 --- a/src/test/ui/issues/issue-21177.stderr +++ b/src/test/ui/issues/issue-21177.stderr @@ -5,7 +5,7 @@ LL | fn foo>() { } | ^^^^ | = note: ...which again requires computing the bounds for type parameter `T`, completing the cycle -note: cycle used when processing `foo` +note: cycle used when computing explicit predicates of `foo` --> $DIR/issue-21177.rs:6:21 | LL | fn foo>() { } diff --git a/src/test/ui/issues/issue-21202.stderr b/src/test/ui/issues/issue-21202.stderr index 18669add20551..9b3b7a72e049e 100644 --- a/src/test/ui/issues/issue-21202.stderr +++ b/src/test/ui/issues/issue-21202.stderr @@ -1,8 +1,8 @@ error[E0624]: associated function `foo` is private - --> $DIR/issue-21202.rs:10:9 + --> $DIR/issue-21202.rs:10:14 | LL | Foo::foo(&f); - | ^^^^^^^^ + | ^^^ private associated function error: aborting due to previous error 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-22289.stderr b/src/test/ui/issues/issue-22289.stderr index cc7ace30cabef..4c35deb1fbe4e 100644 --- a/src/test/ui/issues/issue-22289.stderr +++ b/src/test/ui/issues/issue-22289.stderr @@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `i32` as `&(dyn std::any::Any + 'static)` --> $DIR/issue-22289.rs:2:5 | LL | 0 as &dyn std::any::Any; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait +help: borrow the value for the cast to be valid + | +LL | &0 as &dyn std::any::Any; + | ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-22312.rs b/src/test/ui/issues/issue-22312.rs index 250fec2588702..4e359b3412a71 100644 --- a/src/test/ui/issues/issue-22312.rs +++ b/src/test/ui/issues/issue-22312.rs @@ -1,6 +1,6 @@ use std::ops::Index; -pub trait Array2D: Index { +pub trait Array2D: Index + Sized { fn rows(&self) -> usize; fn columns(&self) -> usize; fn get<'a>(&'a self, y: usize, x: usize) -> Option<&'a >::Output> { diff --git a/src/test/ui/issues/issue-22312.stderr b/src/test/ui/issues/issue-22312.stderr index fc32fd376b75a..28564b074633b 100644 --- a/src/test/ui/issues/issue-22312.stderr +++ b/src/test/ui/issues/issue-22312.stderr @@ -2,9 +2,12 @@ error[E0605]: non-primitive cast: `Self` as `&dyn std::ops::Index $DIR/issue-22312.rs:11:24 | LL | let indexer = &(*self as &dyn Index>::Output>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait +help: borrow the value for the cast to be valid + | +LL | let indexer = &(&*self as &dyn Index>::Output>); + | ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-22599.stderr b/src/test/ui/issues/issue-22599.stderr index 9c3b2cbe6c796..2b34830d08427 100644 --- a/src/test/ui/issues/issue-22599.stderr +++ b/src/test/ui/issues/issue-22599.stderr @@ -2,7 +2,7 @@ error: unused variable: `a` --> $DIR/issue-22599.rs:8:19 | LL | v = match 0 { a => 0 }; - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: the lint level is defined here --> $DIR/issue-22599.rs:1:9 diff --git a/src/test/ui/issues/issue-22638.stderr b/src/test/ui/issues/issue-22638.stderr index 83dd93b853dad..41965d6b35536 100644 --- a/src/test/ui/issues/issue-22638.stderr +++ b/src/test/ui/issues/issue-22638.stderr @@ -8,7 +8,7 @@ LL | | a.matches(f) LL | | } | |_____^ | - = note: consider adding a `#![type_length_limit="26214380"]` attribute to your crate + = note: consider adding a `#![type_length_limit="30408681"]` attribute to your crate 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-23046.rs b/src/test/ui/issues/issue-23046.rs index a68369616d8b6..75be4a11efd22 100644 --- a/src/test/ui/issues/issue-23046.rs +++ b/src/test/ui/issues/issue-23046.rs @@ -14,7 +14,7 @@ pub fn let_<'var, VAR, F: for<'v> Fn(Expr<'v, VAR>) -> Expr<'v, VAR>> } fn main() { - let ex = |x| { //~ ERROR type annotations needed - let_(add(x,x), |y| { + let ex = |x| { + let_(add(x,x), |y| { //~ ERROR type annotations needed let_(add(x, x), |x|x)})}; } diff --git a/src/test/ui/issues/issue-23046.stderr b/src/test/ui/issues/issue-23046.stderr index 12b2eb48e7eaa..77555fce7c460 100644 --- a/src/test/ui/issues/issue-23046.stderr +++ b/src/test/ui/issues/issue-23046.stderr @@ -1,8 +1,13 @@ -error[E0282]: type annotations needed for `Expr<'_, VAR>` - --> $DIR/issue-23046.rs:17:15 +error[E0282]: type annotations needed for the closure `fn(Expr<'_, _>) -> Expr<'_, _>` + --> $DIR/issue-23046.rs:18:9 | -LL | let ex = |x| { - | ^ consider giving this closure parameter the explicit type `Expr<'_, VAR>`, where the type parameter `VAR` is specified +LL | let_(add(x,x), |y| { + | ^^^^ cannot infer type for type parameter `VAR` declared on the function `let_` + | +help: give this closure an explicit return type without `_` placeholders + | +LL | let_(add(x, x), |x|-> Expr<'_, _> { x })})}; + | ^^^^^^^^^^^^^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-23122-2.stderr b/src/test/ui/issues/issue-23122-2.stderr index 7625e30498ac3..c4032b27edcbd 100644 --- a/src/test/ui/issues/issue-23122-2.stderr +++ b/src/test/ui/issues/issue-23122-2.stderr @@ -1,20 +1,20 @@ -error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: std::marker::Sized` +error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: std::marker::Sized` --> $DIR/issue-23122-2.rs:7:15 | LL | impl Next for GetNext { | ^^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_23122_2`) - = note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` + = note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` -error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: std::marker::Sized` +error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: std::marker::Sized` --> $DIR/issue-23122-2.rs:9:5 | LL | type Next = as Next>::Next; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`issue_23122_2`) - = note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` + = note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>` error: aborting due to 2 previous errors 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..cffa52361696c 100644 --- a/src/test/ui/issues/issue-23281.stderr +++ b/src/test/ui/issues/issue-23281.stderr @@ -3,10 +3,19 @@ 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` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/issue-23281.rs:8:12 + | +LL | struct Vec { + | ^ this could be changed to `T: ?Sized`... +LL | t: T, + | - ...if indirection was used here: `Box` 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..a6500b9bb4c24 100644 --- a/src/test/ui/issues/issue-23458.stderr +++ b/src/test/ui/issues/issue-23458.stderr @@ -1,17 +1,20 @@ error: invalid operand in inline asm: 'int $3' --> $DIR/issue-23458.rs:8:9 | -LL | asm!("int $3"); - | ^^^^^^^^^^^^^^^ - -error: :1:2: error: too few operands for instruction - int - ^ +LL | llvm_asm!("int $3"); + | ^ +error: too few operands for instruction --> $DIR/issue-23458.rs:8:9 | -LL | asm!("int $3"); - | ^^^^^^^^^^^^^^^ +LL | llvm_asm!("int $3"); + | ^ + | +note: instantiated into assembly here + --> :1:2 + | +LL | int + | ^ 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-25386.rs b/src/test/ui/issues/issue-25386.rs index 607c9fceab84f..45775e0e4ae36 100644 --- a/src/test/ui/issues/issue-25386.rs +++ b/src/test/ui/issues/issue-25386.rs @@ -17,12 +17,12 @@ mod stuff { macro_rules! check_ptr_exist { ($var:expr, $member:ident) => ( (*$var.c_object).$member.is_some() - //~^ ERROR field `name` of struct `stuff::CObj` is private - //~^^ ERROR field `c_object` of struct `stuff::Item` is private + //~^ ERROR field `c_object` of struct `stuff::Item` is private ); } fn main() { let item = stuff::Item::new(); println!("{}", check_ptr_exist!(item, name)); + //~^ ERROR field `name` of struct `stuff::CObj` is private } diff --git a/src/test/ui/issues/issue-25386.stderr b/src/test/ui/issues/issue-25386.stderr index 76a4a5a493f59..6419e7a557194 100644 --- a/src/test/ui/issues/issue-25386.stderr +++ b/src/test/ui/issues/issue-25386.stderr @@ -1,8 +1,8 @@ error[E0616]: field `c_object` of struct `stuff::Item` is private - --> $DIR/issue-25386.rs:19:11 + --> $DIR/issue-25386.rs:19:16 | LL | (*$var.c_object).$member.is_some() - | ^^^^^^^^^^^^^ + | ^^^^^^^^ private field ... LL | println!("{}", check_ptr_exist!(item, name)); | ---------------------------- in this macro invocation @@ -10,15 +10,10 @@ LL | println!("{}", check_ptr_exist!(item, name)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0616]: field `name` of struct `stuff::CObj` is private - --> $DIR/issue-25386.rs:19:9 + --> $DIR/issue-25386.rs:26:43 | -LL | (*$var.c_object).$member.is_some() - | ^^^^^^^^^^^^^^^^^^^^^^^^ -... LL | println!("{}", check_ptr_exist!(item, name)); - | ---------------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^ private field error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-25826.rs b/src/test/ui/issues/issue-25826.rs index 36a69cf4c22ff..d1093c205798a 100644 --- a/src/test/ui/issues/issue-25826.rs +++ b/src/test/ui/issues/issue-25826.rs @@ -1,6 +1,6 @@ fn id(t: T) -> T { t } fn main() { const A: bool = unsafe { id:: as *const () < id:: as *const () }; - //~^ ERROR comparing raw pointers inside constant + //~^ ERROR pointers cannot be reliably compared during const eval println!("{}", A); } diff --git a/src/test/ui/issues/issue-25826.stderr b/src/test/ui/issues/issue-25826.stderr index 3a5a6b509ba9d..67d1b3ab9bed6 100644 --- a/src/test/ui/issues/issue-25826.stderr +++ b/src/test/ui/issues/issue-25826.stderr @@ -1,12 +1,10 @@ -error[E0658]: comparing raw pointers inside constant +error: pointers cannot be reliably compared during const eval. --> $DIR/issue-25826.rs:3:30 | LL | const A: bool = unsafe { id:: as *const () < id:: as *const () }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #53020 for more information - = help: add `#![feature(const_compare_raw_pointers)]` 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/issues/issue-26472.stderr b/src/test/ui/issues/issue-26472.stderr index 245ebeaf972ed..f7df5b6232bda 100644 --- a/src/test/ui/issues/issue-26472.stderr +++ b/src/test/ui/issues/issue-26472.stderr @@ -1,16 +1,19 @@ error[E0616]: field `len` of struct `sub::S` is private - --> $DIR/issue-26472.rs:11:13 + --> $DIR/issue-26472.rs:11:15 | LL | let v = s.len; - | ^^--- - | | - | help: a method `len` also exists, call it with parentheses: `len()` + | ^^^ private field + | +help: a method `len` also exists, call it with parentheses + | +LL | let v = s.len(); + | ^^ error[E0616]: field `len` of struct `sub::S` is private - --> $DIR/issue-26472.rs:12:5 + --> $DIR/issue-26472.rs:12:7 | LL | s.len = v; - | ^^^^^ + | ^^^ private field error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-26545.rs b/src/test/ui/issues/issue-26545.rs new file mode 100644 index 0000000000000..5652ee7470605 --- /dev/null +++ b/src/test/ui/issues/issue-26545.rs @@ -0,0 +1,12 @@ +mod foo { + pub struct B(pub ()); +} + +mod baz { + fn foo() { + B(()); + //~^ ERROR cannot find function, tuple struct or tuple variant `B` in this scope [E0425] + } +} + +fn main() {} diff --git a/src/test/ui/issues/issue-26545.stderr b/src/test/ui/issues/issue-26545.stderr new file mode 100644 index 0000000000000..d3c86692501d6 --- /dev/null +++ b/src/test/ui/issues/issue-26545.stderr @@ -0,0 +1,14 @@ +error[E0425]: cannot find function, tuple struct or tuple variant `B` in this scope + --> $DIR/issue-26545.rs:7:9 + | +LL | B(()); + | ^ not found in this scope + | +help: consider importing this tuple struct + | +LL | use foo::B; + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. 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-26997.rs b/src/test/ui/issues/issue-26997.rs index f6d349a38f523..fcabd1d84557c 100644 --- a/src/test/ui/issues/issue-26997.rs +++ b/src/test/ui/issues/issue-26997.rs @@ -6,6 +6,7 @@ pub struct Foo { } impl Foo { + #[allow(improper_ctypes_definitions)] pub extern fn foo_new() -> Foo { Foo { x: 21, y: 33 } } 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-27060-rpass.rs b/src/test/ui/issues/issue-27060-rpass.rs index b6ffc3ecb5133..b20d614b3036b 100644 --- a/src/test/ui/issues/issue-27060-rpass.rs +++ b/src/test/ui/issues/issue-27060-rpass.rs @@ -7,19 +7,10 @@ pub struct Good { aligned: [u8; 32], } -#[repr(packed)] -pub struct JustArray { - array: [u32] -} - // kill this test when that turns to a hard error #[allow(safe_packed_borrows)] fn main() { - let good = Good { - data: &0, - data2: [&0, &0], - aligned: [0; 32] - }; + let good = Good { data: &0, data2: [&0, &0], aligned: [0; 32] }; unsafe { let _ = &good.data; // ok diff --git a/src/test/ui/issues/issue-27060.rs b/src/test/ui/issues/issue-27060.rs index 4caad03a36151..78f2022ed38df 100644 --- a/src/test/ui/issues/issue-27060.rs +++ b/src/test/ui/issues/issue-27060.rs @@ -5,11 +5,6 @@ pub struct Good { aligned: [u8; 32], } -#[repr(packed)] -pub struct JustArray { - array: [u32] -} - #[deny(safe_packed_borrows)] fn main() { let good = Good { diff --git a/src/test/ui/issues/issue-27060.stderr b/src/test/ui/issues/issue-27060.stderr index 6bf6348631a70..d14ae4d41d5c5 100644 --- a/src/test/ui/issues/issue-27060.stderr +++ b/src/test/ui/issues/issue-27060.stderr @@ -1,11 +1,11 @@ error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) - --> $DIR/issue-27060.rs:26:13 + --> $DIR/issue-27060.rs:21:13 | LL | let _ = &good.data; | ^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/issue-27060.rs:13:8 + --> $DIR/issue-27060.rs:8:8 | LL | #[deny(safe_packed_borrows)] | ^^^^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL | #[deny(safe_packed_borrows)] = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) - --> $DIR/issue-27060.rs:28:13 + --> $DIR/issue-27060.rs:23:13 | LL | let _ = &good.data2[0]; | ^^^^^^^^^^^^^^ 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-2718-a.stderr b/src/test/ui/issues/issue-2718-a.stderr index 0f52c79192843..d152ffde4e57d 100644 --- a/src/test/ui/issues/issue-2718-a.stderr +++ b/src/test/ui/issues/issue-2718-a.stderr @@ -7,7 +7,10 @@ LL | pub struct Pong(SendPacket); | | recursive without indirection | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `pingpong::Pong` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `pingpong::Pong` representable + | +LL | pub struct Pong(Box>); + | ^^^^ ^ 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.stderr b/src/test/ui/issues/issue-2823.stderr index aa720fd45895a..0cdc501d56811 100644 --- a/src/test/ui/issues/issue-2823.stderr +++ b/src/test/ui/issues/issue-2823.stderr @@ -6,6 +6,14 @@ LL | struct C { ... LL | let _d = c.clone(); | ^^^^^ method not found in `C` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `std::sync::Arc` here + | the method is available for `std::rc::Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: diff --git a/src/test/ui/issues/issue-28388-2.stderr b/src/test/ui/issues/issue-28388-2.stderr index 58bd775f295fc..1afaf622be7da 100644 --- a/src/test/ui/issues/issue-28388-2.stderr +++ b/src/test/ui/issues/issue-28388-2.stderr @@ -2,7 +2,7 @@ error[E0603]: module `n` is private --> $DIR/issue-28388-2.rs:7:8 | LL | use m::n::{}; - | ^ this module is private + | ^ private module | note: the module `n` is defined here --> $DIR/issue-28388-2.rs:4:5 diff --git a/src/test/ui/issues/issue-28600.rs b/src/test/ui/issues/issue-28600.rs index 3bbe4ae29bdd6..297519b9a79e2 100644 --- a/src/test/ui/issues/issue-28600.rs +++ b/src/test/ui/issues/issue-28600.rs @@ -6,6 +6,7 @@ struct Test; impl Test { #[allow(dead_code)] #[allow(unused_variables)] + #[allow(improper_ctypes_definitions)] pub extern fn test(val: &str) { } 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-29161.stderr b/src/test/ui/issues/issue-29161.stderr index 1bfa211ef7962..7ce95e4b0df8b 100644 --- a/src/test/ui/issues/issue-29161.stderr +++ b/src/test/ui/issues/issue-29161.stderr @@ -8,7 +8,7 @@ error[E0603]: struct `A` is private --> $DIR/issue-29161.rs:13:8 | LL | a::A::default(); - | ^ this struct is private + | ^ private struct | note: the struct `A` is defined here --> $DIR/issue-29161.rs:2:5 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-2995.stderr b/src/test/ui/issues/issue-2995.stderr index c316780d5f6a5..9f5968399a37d 100644 --- a/src/test/ui/issues/issue-2995.stderr +++ b/src/test/ui/issues/issue-2995.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `*const isize` as `&isize` --> $DIR/issue-2995.rs:2:22 | LL | let _q: &isize = p as &isize; - | ^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error 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-3008-1.stderr b/src/test/ui/issues/issue-3008-1.stderr index f12274134ee05..87ee36df21696 100644 --- a/src/test/ui/issues/issue-3008-1.stderr +++ b/src/test/ui/issues/issue-3008-1.stderr @@ -7,7 +7,10 @@ LL | enum Bar { LL | BarSome(Bar) | --- recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Bar` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Bar` representable + | +LL | BarSome(Box) + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-3008-2.stderr b/src/test/ui/issues/issue-3008-2.stderr index acc15f4b57c73..369a19d37e6f6 100644 --- a/src/test/ui/issues/issue-3008-2.stderr +++ b/src/test/ui/issues/issue-3008-2.stderr @@ -2,11 +2,14 @@ error[E0072]: recursive type `Bar` has infinite size --> $DIR/issue-3008-2.rs:2:1 | LL | struct Bar { x: Bar } - | ^^^^^^^^^^ ------ recursive without indirection + | ^^^^^^^^^^ --- recursive without indirection | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Bar` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Bar` representable + | +LL | struct Bar { x: Box } + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-3008-3.stderr b/src/test/ui/issues/issue-3008-3.stderr index d08a3d9708db3..0b162eff94a7c 100644 --- a/src/test/ui/issues/issue-3008-3.stderr +++ b/src/test/ui/issues/issue-3008-3.stderr @@ -6,7 +6,10 @@ LL | enum E2 { V2(E2, marker::PhantomData), } | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `E2` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `E2` representable + | +LL | enum E2 { V2(Box>, marker::PhantomData), } + | ^^^^ ^ error: aborting due to previous error 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-31845.stderr b/src/test/ui/issues/issue-31845.stderr index 75d8859961a04..fe51fa0699fb4 100644 --- a/src/test/ui/issues/issue-31845.stderr +++ b/src/test/ui/issues/issue-31845.stderr @@ -1,11 +1,10 @@ error[E0425]: cannot find function `g` in this scope --> $DIR/issue-31845.rs:7:12 | -LL | / fn h() { -LL | | g(); - | | ^ help: a function with a similar name exists: `h` -LL | | } - | |_________- similarly named function `h` defined here +LL | fn h() { + | ------ similarly named function `h` defined here +LL | g(); + | ^ help: a function with a similar name exists: `h` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-31910.stderr b/src/test/ui/issues/issue-31910.stderr index c5c988cdaa75a..2603c944207d0 100644 --- a/src/test/ui/issues/issue-31910.stderr +++ b/src/test/ui/issues/issue-31910.stderr @@ -3,11 +3,6 @@ error[E0308]: mismatched types | LL | X = Trait::Number, | ^^^^^^^^^^^^^ expected `isize`, found `i32` - | -help: you can convert an `i32` to `isize` and panic if the converted value wouldn't fit - | -LL | X = Trait::Number.try_into().unwrap(), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error 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-32326.stderr b/src/test/ui/issues/issue-32326.stderr index 5967627e51a4b..0f3d3690b732e 100644 --- a/src/test/ui/issues/issue-32326.stderr +++ b/src/test/ui/issues/issue-32326.stderr @@ -8,7 +8,10 @@ LL | Plus(Expr, Expr), | | | recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Expr` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Expr` representable + | +LL | Plus(Box, Box), + | ^^^^ ^ ^^^^ ^ 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-34373.stderr b/src/test/ui/issues/issue-34373.stderr index f260a8477431b..e8c1e8f966973 100644 --- a/src/test/ui/issues/issue-34373.stderr +++ b/src/test/ui/issues/issue-34373.stderr @@ -1,15 +1,15 @@ -error[E0391]: cycle detected when processing `Foo::T` +error[E0391]: cycle detected when computing type of `Foo::T` --> $DIR/issue-34373.rs:7:30 | LL | pub struct Foo>>; | ^^^^^^^^^^ | -note: ...which requires processing `DefaultFoo`... +note: ...which requires computing type of `DefaultFoo`... --> $DIR/issue-34373.rs:8:19 | LL | type DefaultFoo = Foo; | ^^^ - = note: ...which again requires processing `Foo::T`, completing the cycle + = note: ...which again requires computing type of `Foo::T`, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/issue-34373.rs:1:1 | 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-35241.stderr b/src/test/ui/issues/issue-35241.stderr index 4a52a292ef30a..b6045c993a958 100644 --- a/src/test/ui/issues/issue-35241.stderr +++ b/src/test/ui/issues/issue-35241.stderr @@ -5,14 +5,16 @@ LL | struct Foo(u32); | ---------------- fn(u32) -> Foo {Foo} defined here LL | LL | fn test() -> Foo { Foo } - | --- ^^^ - | | | - | | expected struct `Foo`, found fn item - | | help: use parentheses to instantiate this tuple struct: `Foo(_)` + | --- ^^^ expected struct `Foo`, found fn item + | | | expected `Foo` because of return type | = note: expected struct `Foo` found fn item `fn(u32) -> Foo {Foo}` +help: use parentheses to instantiate this tuple struct + | +LL | fn test() -> Foo { Foo(_) } + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-35376.rs b/src/test/ui/issues/issue-35376.rs index eb139ec4d7f43..cc35213b93d68 100644 --- a/src/test/ui/issues/issue-35376.rs +++ b/src/test/ui/issues/issue-35376.rs @@ -1,5 +1,6 @@ // check-pass #![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete fn main() {} diff --git a/src/test/ui/issues/issue-35376.stderr b/src/test/ui/issues/issue-35376.stderr new file mode 100644 index 0000000000000..06c31f3bae062 --- /dev/null +++ b/src/test/ui/issues/issue-35376.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-35376.rs:2:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/issues/issue-35675.rs b/src/test/ui/issues/issue-35675.rs index 7876811a9ac39..683761667d40a 100644 --- a/src/test/ui/issues/issue-35675.rs +++ b/src/test/ui/issues/issue-35675.rs @@ -33,7 +33,7 @@ fn qux() -> Some { fn main() {} mod x { - enum Enum { + pub enum Enum { Variant1, Variant2(), Variant3(usize), 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-3763.stderr b/src/test/ui/issues/issue-3763.stderr index d548477a88ff8..b63967bb9dce3 100644 --- a/src/test/ui/issues/issue-3763.stderr +++ b/src/test/ui/issues/issue-3763.stderr @@ -1,32 +1,32 @@ error[E0616]: field `priv_field` of struct `my_mod::MyStruct` is private - --> $DIR/issue-3763.rs:18:19 + --> $DIR/issue-3763.rs:18:32 | LL | let _woohoo = (&my_struct).priv_field; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ private field error[E0616]: field `priv_field` of struct `my_mod::MyStruct` is private - --> $DIR/issue-3763.rs:21:19 + --> $DIR/issue-3763.rs:21:41 | LL | let _woohoo = (Box::new(my_struct)).priv_field; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ private field error[E0624]: associated function `happyfun` is private --> $DIR/issue-3763.rs:24:18 | LL | (&my_struct).happyfun(); - | ^^^^^^^^ + | ^^^^^^^^ private associated function error[E0624]: associated function `happyfun` is private --> $DIR/issue-3763.rs:26:27 | LL | (Box::new(my_struct)).happyfun(); - | ^^^^^^^^ + | ^^^^^^^^ private associated function error[E0616]: field `priv_field` of struct `my_mod::MyStruct` is private - --> $DIR/issue-3763.rs:27:16 + --> $DIR/issue-3763.rs:27:26 | LL | let nope = my_struct.priv_field; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ private field error: aborting due to 5 previous errors diff --git a/src/test/ui/issues/issue-3779.stderr b/src/test/ui/issues/issue-3779.stderr index ba1e842c610ba..7b17e91421660 100644 --- a/src/test/ui/issues/issue-3779.stderr +++ b/src/test/ui/issues/issue-3779.stderr @@ -5,9 +5,12 @@ LL | struct S { | ^^^^^^^^ recursive type has infinite size LL | LL | element: Option - | ------------------ recursive without indirection + | --------- recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `S` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `S` representable + | +LL | element: Box> + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-38091.rs b/src/test/ui/issues/issue-38091.rs index 00aa810f8308c..a84391b94d1de 100644 --- a/src/test/ui/issues/issue-38091.rs +++ b/src/test/ui/issues/issue-38091.rs @@ -1,5 +1,5 @@ -// run-pass #![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete trait Iterate<'a> { type Ty: Valid; @@ -7,6 +7,7 @@ trait Iterate<'a> { } impl<'a, T> Iterate<'a> for T where T: Check { default type Ty = (); + //~^ ERROR the trait bound `(): Valid` is not satisfied default fn iterate(self) {} } diff --git a/src/test/ui/issues/issue-38091.stderr b/src/test/ui/issues/issue-38091.stderr new file mode 100644 index 0000000000000..81beec8026314 --- /dev/null +++ b/src/test/ui/issues/issue-38091.stderr @@ -0,0 +1,21 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-38091.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +error[E0277]: the trait bound `(): Valid` is not satisfied + --> $DIR/issue-38091.rs:9:5 + | +LL | type Ty: Valid; + | --------------- required by `Iterate::Ty` +... +LL | default type Ty = (); + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Valid` is not implemented for `()` + +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/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-38763.rs b/src/test/ui/issues/issue-38763.rs index 6e6de09225f57..a966cf217e165 100644 --- a/src/test/ui/issues/issue-38763.rs +++ b/src/test/ui/issues/issue-38763.rs @@ -5,6 +5,7 @@ pub struct Foo(i128); #[no_mangle] +#[allow(improper_ctypes_definitions)] pub extern "C" fn foo(x: Foo) -> Foo { x } fn main() { 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 ba0f1336ff09d..ed700ff95e5b5 100644 --- a/src/test/ui/issues/issue-38857.stderr +++ b/src/test/ui/issues/issue-38857.stderr @@ -1,14 +1,14 @@ 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!() }; - | ^^^ this module is private + | ^^^ private module | note: the module `sys` is defined here --> $SRC_DIR/libstd/lib.rs:LL:COL diff --git a/src/test/ui/issues/issue-38940.rs b/src/test/ui/issues/issue-38940.rs index 1c785949547e5..3f10fc017a73f 100644 --- a/src/test/ui/issues/issue-38940.rs +++ b/src/test/ui/issues/issue-38940.rs @@ -42,5 +42,5 @@ fn main() { let t = Top::new(); let x: &Bottom = &t; //~^ ERROR mismatched types - //~| ERROR reached the recursion limit while auto-dereferencing `I` + //~| ERROR reached the recursion limit while auto-dereferencing `J` } diff --git a/src/test/ui/issues/issue-38940.stderr b/src/test/ui/issues/issue-38940.stderr index 36117278fd814..0671cede73bbe 100644 --- a/src/test/ui/issues/issue-38940.stderr +++ b/src/test/ui/issues/issue-38940.stderr @@ -1,4 +1,4 @@ -error[E0055]: reached the recursion limit while auto-dereferencing `I` +error[E0055]: reached the recursion limit while auto-dereferencing `J` --> $DIR/issue-38940.rs:43:22 | LL | let x: &Bottom = &t; 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-3993.stderr b/src/test/ui/issues/issue-3993.stderr index 3fa8ed4af28fa..deecf7a9d752c 100644 --- a/src/test/ui/issues/issue-3993.stderr +++ b/src/test/ui/issues/issue-3993.stderr @@ -2,7 +2,7 @@ error[E0603]: function `fly` is private --> $DIR/issue-3993.rs:1:10 | LL | use zoo::fly; - | ^^^ this function is private + | ^^^ private function | note: the function `fly` is defined here --> $DIR/issue-3993.rs:4:5 diff --git a/src/test/ui/issues/issue-40000.nll.stderr b/src/test/ui/issues/issue-40000.nll.stderr new file mode 100644 index 0000000000000..f673fbae8b79c --- /dev/null +++ b/src/test/ui/issues/issue-40000.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/issue-40000.rs:6:9 + | +LL | foo(bar); + | ^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-40000.stderr b/src/test/ui/issues/issue-40000.stderr index 983fdb13083a1..3eb3482ac910e 100644 --- a/src/test/ui/issues/issue-40000.stderr +++ b/src/test/ui/issues/issue-40000.stderr @@ -2,10 +2,10 @@ error[E0308]: mismatched types --> $DIR/issue-40000.rs:6:9 | LL | foo(bar); - | ^^^ expected concrete lifetime, found bound lifetime parameter + | ^^^ one type is more general than the other | - = note: expected struct `std::boxed::Box<(dyn for<'r> std::ops::Fn(&'r i32) + 'static)>` - found struct `std::boxed::Box` + = note: expected trait object `dyn for<'r> std::ops::Fn(&'r i32)` + found trait object `dyn std::ops::Fn(&i32)` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-40510-1.stderr b/src/test/ui/issues/issue-40510-1.stderr index f4fda0abc2049..54df40b6e3d05 100644 --- a/src/test/ui/issues/issue-40510-1.stderr +++ b/src/test/ui/issues/issue-40510-1.stderr @@ -1,10 +1,16 @@ error: captured variable cannot escape `FnMut` closure body --> $DIR/issue-40510-1.rs:7:9 | +LL | let mut x: Box<()> = Box::new(()); + | ----- variable defined here +LL | LL | || { | - inferred to be a `FnMut` closure LL | &mut x - | ^^^^^^ returns a reference to a captured variable which escapes the closure body + | ^^^^^- + | | | + | | variable captured here + | returns a reference to a captured variable which escapes the closure body | = note: `FnMut` closures only have access to their captured variables while they are executing... = note: ...therefore, they cannot allow references to captured variables to escape diff --git a/src/test/ui/issues/issue-40510-3.stderr b/src/test/ui/issues/issue-40510-3.stderr index 4bc7d0f5deac5..cb885ec7d952a 100644 --- a/src/test/ui/issues/issue-40510-3.stderr +++ b/src/test/ui/issues/issue-40510-3.stderr @@ -1,10 +1,14 @@ error: captured variable cannot escape `FnMut` closure body --> $DIR/issue-40510-3.rs:7:9 | +LL | let mut x: Vec<()> = Vec::new(); + | ----- variable defined here +LL | LL | || { | - inferred to be a `FnMut` closure LL | / || { LL | | x.push(()) + | | - variable captured here LL | | } | |_________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body | 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-40883.rs b/src/test/ui/issues/issue-40883.rs index 37e61b1b0e603..8a4aef46dd514 100644 --- a/src/test/ui/issues/issue-40883.rs +++ b/src/test/ui/issues/issue-40883.rs @@ -71,15 +71,16 @@ pub fn supersize_me(out: &mut Vec) { #[inline(never)] fn verify_stack_usage(before_ptr: *mut Vec) { - // to check stack usage, create locals before and after + // To check stack usage, create locals before and after // and check the difference in addresses between them. let mut stack_var: Vec = vec![]; test::black_box(&mut stack_var); let stack_usage = isize::abs( (&mut stack_var as *mut _ as isize) - (before_ptr as isize)) as usize; - // give space for 2 copies of `Big` + 128 "misc" bytes. - if stack_usage > mem::size_of::() * 2 + 128 { + // Give space for 2 copies of `Big` + 272 "misc" bytes + // (value observed on x86_64-pc-windows-gnu). + if stack_usage > mem::size_of::() * 2 + 272 { panic!("used {} bytes of stack, but `struct Big` is only {} bytes", stack_usage, mem::size_of::()); } 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.rs b/src/test/ui/issues/issue-42944.rs index cc365dc4c938e..a088f91554dfb 100644 --- a/src/test/ui/issues/issue-42944.rs +++ b/src/test/ui/issues/issue-42944.rs @@ -1,20 +1,20 @@ mod foo { - pub struct B(()); + pub struct Bx(()); } mod bar { - use foo::B; + use foo::Bx; fn foo() { - B(()); - //~^ ERROR expected function, tuple struct or tuple variant, found struct `B` [E0423] + Bx(()); + //~^ ERROR expected function, tuple struct or tuple variant, found struct `Bx` [E0423] } } mod baz { fn foo() { - B(()); - //~^ ERROR cannot find function, tuple struct or tuple variant `B` in this scope [E0425] + Bx(()); + //~^ ERROR cannot find function, tuple struct or tuple variant `Bx` in this scope [E0425] } } diff --git a/src/test/ui/issues/issue-42944.stderr b/src/test/ui/issues/issue-42944.stderr index c71194f41c114..9fad43757ba62 100644 --- a/src/test/ui/issues/issue-42944.stderr +++ b/src/test/ui/issues/issue-42944.stderr @@ -1,18 +1,18 @@ -error[E0423]: expected function, tuple struct or tuple variant, found struct `B` +error[E0423]: expected function, tuple struct or tuple variant, found struct `Bx` --> $DIR/issue-42944.rs:9:9 | -LL | B(()); - | ^ constructor is not visible here due to private fields +LL | Bx(()); + | ^^ constructor is not visible here due to private fields -error[E0425]: cannot find function, tuple struct or tuple variant `B` in this scope +error[E0425]: cannot find function, tuple struct or tuple variant `Bx` in this scope --> $DIR/issue-42944.rs:16:9 | -LL | B(()); - | ^ not found in this scope +LL | Bx(()); + | ^^ 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; +LL | use foo::Bx; | error: aborting due to 2 previous errors 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.rs b/src/test/ui/issues/issue-43623.rs index b259e9e269d06..99cae46fd9cf2 100644 --- a/src/test/ui/issues/issue-43623.rs +++ b/src/test/ui/issues/issue-43623.rs @@ -9,11 +9,12 @@ impl<'a> Trait<'a> for Type { } pub fn break_me(f: F) -where T: for<'b> Trait<'b>, - F: for<'b> FnMut(>::Assoc) { +where + T: for<'b> Trait<'b>, + F: for<'b> FnMut(>::Assoc), +{ break_me::; //~^ ERROR: type mismatch in function arguments - //~| ERROR: type mismatch resolving } fn main() {} diff --git a/src/test/ui/issues/issue-43623.stderr b/src/test/ui/issues/issue-43623.stderr index d90eb53f9006f..80aca482b3d29 100644 --- a/src/test/ui/issues/issue-43623.stderr +++ b/src/test/ui/issues/issue-43623.stderr @@ -1,29 +1,18 @@ error[E0631]: type mismatch in function arguments - --> $DIR/issue-43623.rs:14:5 + --> $DIR/issue-43623.rs:16:5 | LL | pub fn break_me(f: F) - | -------- -LL | where T: for<'b> Trait<'b>, -LL | F: for<'b> FnMut(>::Assoc) { - | -------------------------------------- required by this bound in `break_me` + | -------- required by a bound in this +... +LL | F: for<'b> FnMut(>::Assoc), + | ------------------------------ required by this bound in `break_me` +LL | { LL | break_me::; | ^^^^^^^^^^^^^^^^^^^^^^^ | | - | expected signature of `for<'b> fn(>::Assoc) -> _` - | found signature of `fn(_) -> _` + | expected signature of `fn(>::Assoc) -> _` + | found signature of `fn(()) -> _` -error[E0271]: type mismatch resolving `for<'b> >::Assoc,)>>::Output == ()` - --> $DIR/issue-43623.rs:14:5 - | -LL | pub fn break_me(f: F) - | -------- -LL | where T: for<'b> Trait<'b>, -LL | F: for<'b> FnMut(>::Assoc) { - | ------------------------------ required by this bound in `break_me` -LL | break_me::; - | ^^^^^^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'b, found concrete lifetime - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0271, E0631. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0631`. diff --git a/src/test/ui/issues/issue-4366-2.stderr b/src/test/ui/issues/issue-4366-2.stderr index 60a1155c614f6..a86ec7fabea4b 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,12 +15,10 @@ 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 this function instead | LL | use foo::foo; | -LL | use m1::foo; - | error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-4366.stderr b/src/test/ui/issues/issue-4366.stderr index d931d51911756..469ea93e90468 100644 --- a/src/test/ui/issues/issue-4366.stderr +++ b/src/test/ui/issues/issue-4366.stderr @@ -4,12 +4,10 @@ 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 this function | LL | use foo::foo; | -LL | use m1::foo; - | error: aborting due to previous error 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/ui/issues/issue-44078.stderr b/src/test/ui/issues/issue-44078.stderr index 43b49e463128f..daf67219f4d0a 100644 --- a/src/test/ui/issues/issue-44078.stderr +++ b/src/test/ui/issues/issue-44078.stderr @@ -1,4 +1,4 @@ -error: unterminated double quote string +error[E0765]: unterminated double quote string --> $DIR/issue-44078.rs:2:8 | LL | "😊""; @@ -8,3 +8,4 @@ LL | | } error: aborting due to previous error +For more information about this error, try `rustc --explain E0765`. diff --git a/src/test/ui/issues/issue-44216-add-instant.rs b/src/test/ui/issues/issue-44216-add-instant.rs new file mode 100644 index 0000000000000..78cfecf2f32f0 --- /dev/null +++ b/src/test/ui/issues/issue-44216-add-instant.rs @@ -0,0 +1,10 @@ +// run-fail +// error-pattern:overflow +// ignore-emscripten no processes + +use std::time::{Instant, Duration}; + +fn main() { + let now = Instant::now(); + let _ = now + Duration::from_secs(u64::MAX); +} diff --git a/src/test/ui/issues/issue-44216-add-system-time.rs b/src/test/ui/issues/issue-44216-add-system-time.rs new file mode 100644 index 0000000000000..7e9a3f802ec89 --- /dev/null +++ b/src/test/ui/issues/issue-44216-add-system-time.rs @@ -0,0 +1,10 @@ +// run-fail +// error-pattern:overflow +// ignore-emscripten no processes + +use std::time::{Duration, SystemTime}; + +fn main() { + let now = SystemTime::now(); + let _ = now + Duration::from_secs(u64::MAX); +} diff --git a/src/test/ui/issues/issue-44216-sub-instant.rs b/src/test/ui/issues/issue-44216-sub-instant.rs new file mode 100644 index 0000000000000..e40f80d449d96 --- /dev/null +++ b/src/test/ui/issues/issue-44216-sub-instant.rs @@ -0,0 +1,10 @@ +// run-fail +// error-pattern:overflow +// ignore-emscripten no processes + +use std::time::{Instant, Duration}; + +fn main() { + let now = Instant::now(); + let _ = now - Duration::from_secs(u64::MAX); +} diff --git a/src/test/ui/issues/issue-44216-sub-system-time.rs b/src/test/ui/issues/issue-44216-sub-system-time.rs new file mode 100644 index 0000000000000..2c5a000fab692 --- /dev/null +++ b/src/test/ui/issues/issue-44216-sub-system-time.rs @@ -0,0 +1,10 @@ +// run-fail +// error-pattern:overflow +// ignore-emscripten no processes + +use std::time::{Duration, SystemTime}; + +fn main() { + let now = SystemTime::now(); + let _ = now - Duration::from_secs(u64::MAX); +} diff --git a/src/test/ui/issues/issue-45730.stderr b/src/test/ui/issues/issue-45730.stderr index d4ddba52df14a..d00f3d91b49da 100644 --- a/src/test/ui/issues/issue-45730.stderr +++ b/src/test/ui/issues/issue-45730.stderr @@ -1,30 +1,24 @@ error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:3:23 + --> $DIR/issue-45730.rs:3:28 | LL | let x: *const _ = 0 as _; - | ^^^^^- - | | - | help: consider giving more type information + | ^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:5:23 + --> $DIR/issue-45730.rs:5:28 | LL | let x: *const _ = 0 as *const _; - | ^^^^^-------- - | | - | help: consider giving more type information + | ^^^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/issue-45730.rs:8:13 + --> $DIR/issue-45730.rs:8:44 | LL | let x = 0 as *const i32 as *const _ as *mut _; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------ - | | - | help: consider giving more type information + | ^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid 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-46332.stderr b/src/test/ui/issues/issue-46332.stderr index 5d8a859a7379c..890ef8014b489 100644 --- a/src/test/ui/issues/issue-46332.stderr +++ b/src/test/ui/issues/issue-46332.stderr @@ -2,7 +2,7 @@ error[E0422]: cannot find struct, variant or union type `TyUInt` in this scope --> $DIR/issue-46332.rs:9:5 | LL | struct TyUint {} - | ---------------- similarly named struct `TyUint` defined here + | ------------- similarly named struct `TyUint` defined here ... LL | TyUInt {}; | ^^^^^^ help: a struct with a similar name exists (notice the capitalization): `TyUint` diff --git a/src/test/ui/issues/issue-46604.rs b/src/test/ui/issues/issue-46604.rs index e1967eb765542..273187a5a13be 100644 --- a/src/test/ui/issues/issue-46604.rs +++ b/src/test/ui/issues/issue-46604.rs @@ -1,4 +1,4 @@ -static buf: &mut [u8] = &mut [1u8,2,3,4,5,7]; //~ ERROR E0658 +static buf: &mut [u8] = &mut [1u8,2,3,4,5,7]; //~ ERROR E0764 fn write>(buffer: T) { } fn main() { diff --git a/src/test/ui/issues/issue-46604.stderr b/src/test/ui/issues/issue-46604.stderr index 771e368a35d93..5421721dec2e3 100644 --- a/src/test/ui/issues/issue-46604.stderr +++ b/src/test/ui/issues/issue-46604.stderr @@ -1,11 +1,8 @@ -error[E0658]: references in statics may only refer to immutable values +error[E0764]: mutable references are not allowed in statics --> $DIR/issue-46604.rs:1:25 | LL | static buf: &mut [u8] = &mut [1u8,2,3,4,5,7]; - | ^^^^^^^^^^^^^^^^^^^^ statics require immutable values - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + | ^^^^^^^^^^^^^^^^^^^^ `&mut` is only allowed in `const fn` error[E0594]: cannot assign to `buf[_]`, as `buf` is an immutable static item --> $DIR/issue-46604.rs:6:5 @@ -15,5 +12,5 @@ LL | buf[0]=2; error: aborting due to 2 previous errors -Some errors have detailed explanations: E0594, E0658. +Some errors have detailed explanations: E0594, E0764. For more information about an error, try `rustc --explain E0594`. 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-48508.rs b/src/test/ui/issues/issue-48508.rs index 87965c204ada7..8dc9351260ebc 100644 --- a/src/test/ui/issues/issue-48508.rs +++ b/src/test/ui/issues/issue-48508.rs @@ -11,7 +11,7 @@ // ignore-asmjs wasm2js does not support source maps yet #![feature(non_ascii_idents)] -#[allow(uncommon_codepoints)] +#![allow(uncommon_codepoints)] #[path = "issue-48508-aux.rs"] mod other_file; diff --git a/src/test/ui/issues/issue-49824.stderr b/src/test/ui/issues/issue-49824.stderr index 6b486aafcdf40..2fec482543d7b 100644 --- a/src/test/ui/issues/issue-49824.stderr +++ b/src/test/ui/issues/issue-49824.stderr @@ -1,11 +1,14 @@ error: captured variable cannot escape `FnMut` closure body --> $DIR/issue-49824.rs:4:9 | +LL | let mut x = 0; + | ----- variable defined here LL | || { | - inferred to be a `FnMut` closure LL | / || { LL | | LL | | let _y = &mut x; + | | - variable captured here LL | | } | |_________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body | diff --git a/src/test/ui/issues/issue-49851/compiler-builtins-error.rs b/src/test/ui/issues/issue-49851/compiler-builtins-error.rs index 3484ff3b87432..9449376513fd5 100644 --- a/src/test/ui/issues/issue-49851/compiler-builtins-error.rs +++ b/src/test/ui/issues/issue-49851/compiler-builtins-error.rs @@ -1,6 +1,4 @@ //~ ERROR 1:1: 1:1: can't find crate for `core` [E0463] -// http://rust-lang.org/COPYRIGHT. -// // compile-flags: --target thumbv7em-none-eabihf #![deny(unsafe_code)] 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-50687-ice-on-borrow.rs b/src/test/ui/issues/issue-50687-ice-on-borrow.rs new file mode 100644 index 0000000000000..7a8a12c2a93af --- /dev/null +++ b/src/test/ui/issues/issue-50687-ice-on-borrow.rs @@ -0,0 +1,41 @@ +// This previously caused an ICE at: +// librustc/traits/structural_impls.rs:180: impossible case reached + +#![no_main] + +use std::borrow::Borrow; +use std::io; +use std::io::Write; + +trait Constraint {} + +struct Container { + t: T, +} + +struct Borrowed; +struct Owned; + +impl<'a, T> Write for &'a Container +where + T: Constraint, + &'a T: Write, +{ + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Borrow for Owned { + fn borrow(&self) -> &Borrowed { + &Borrowed + } +} + +fn func(owned: Owned) { + let _: () = Borrow::borrow(&owned); //~ ERROR mismatched types +} diff --git a/src/test/ui/issues/issue-50687-ice-on-borrow.stderr b/src/test/ui/issues/issue-50687-ice-on-borrow.stderr new file mode 100644 index 0000000000000..f6adfc87dad33 --- /dev/null +++ b/src/test/ui/issues/issue-50687-ice-on-borrow.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/issue-50687-ice-on-borrow.rs:40:17 + | +LL | let _: () = Borrow::borrow(&owned); + | -- ^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | expected `()`, found reference + | | help: consider dereferencing the borrow: `*Borrow::borrow(&owned)` + | expected due to this + | + = note: expected unit type `()` + found reference `&_` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. 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-51907.rs b/src/test/ui/issues/issue-51907.rs index 3691fe1911774..52d26d0954af8 100644 --- a/src/test/ui/issues/issue-51907.rs +++ b/src/test/ui/issues/issue-51907.rs @@ -6,7 +6,9 @@ trait Foo { struct Bar; impl Foo for Bar { + #[allow(improper_ctypes_definitions)] extern fn borrow(&self) {} + #[allow(improper_ctypes_definitions)] extern fn take(self: Box) {} } diff --git a/src/test/ui/issues/issue-52213.stderr b/src/test/ui/issues/issue-52213.stderr index a8960f7756367..7463af9332a76 100644 --- a/src/test/ui/issues/issue-52213.stderr +++ b/src/test/ui/issues/issue-52213.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | match (&t,) { | ^^^^^ - = note: expected `(&&(T,),)` - found `(&&'a (T,),)` + = note: expected `(&&(T,),)` + found `(&&'a (T,),)` note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 1:27... --> $DIR/issue-52213.rs:1:27 | diff --git a/src/test/ui/issues/issue-53275.rs b/src/test/ui/issues/issue-53275.rs new file mode 100644 index 0000000000000..5ae6fb2d47249 --- /dev/null +++ b/src/test/ui/issues/issue-53275.rs @@ -0,0 +1,9 @@ +// build-pass + +#![crate_type = "lib"] +#![allow(unconditional_panic)] +struct S(u8); + +pub fn ice() { + S([][0]); +} diff --git a/src/test/ui/issues/issue-53498.stderr b/src/test/ui/issues/issue-53498.stderr index 042848c27bbdc..3c0f7f2b55026 100644 --- a/src/test/ui/issues/issue-53498.stderr +++ b/src/test/ui/issues/issue-53498.stderr @@ -1,8 +1,8 @@ error[E0624]: associated function `foo` is private - --> $DIR/issue-53498.rs:16:5 + --> $DIR/issue-53498.rs:16:27 | LL | test::Foo::::foo(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ private associated function error: aborting due to previous error 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-54062.stderr b/src/test/ui/issues/issue-54062.stderr index 5222e3ee95d59..f9aef08c353bb 100644 --- a/src/test/ui/issues/issue-54062.stderr +++ b/src/test/ui/issues/issue-54062.stderr @@ -1,8 +1,8 @@ error[E0616]: field `inner` of struct `std::sync::Mutex` is private - --> $DIR/issue-54062.rs:10:13 + --> $DIR/issue-54062.rs:10:24 | LL | let _ = test.comps.inner.lock().unwrap(); - | ^^^^^^^^^^^^^^^^ + | ^^^^^ private field error[E0599]: no method named `unwrap` found for struct `std::sys_common::mutex::MutexGuard<'_>` in the current scope --> $DIR/issue-54062.rs:10:37 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-55380.rs b/src/test/ui/issues/issue-55380.rs index 862218e219279..f7cb296d3b8bb 100644 --- a/src/test/ui/issues/issue-55380.rs +++ b/src/test/ui/issues/issue-55380.rs @@ -1,6 +1,6 @@ // run-pass - #![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete pub trait Foo { fn abc() -> u32; diff --git a/src/test/ui/issues/issue-55380.stderr b/src/test/ui/issues/issue-55380.stderr new file mode 100644 index 0000000000000..451beebd1061e --- /dev/null +++ b/src/test/ui/issues/issue-55380.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-55380.rs:2:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + 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-55796.stderr b/src/test/ui/issues/issue-55796.stderr index b8cafdc5c14b5..6bfb7af54446d 100644 --- a/src/test/ui/issues/issue-55796.stderr +++ b/src/test/ui/issues/issue-55796.stderr @@ -20,8 +20,8 @@ note: ...so that the expression is assignable | LL | Box::new(self.out_edges(u).map(|e| e.target())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` - found `std::boxed::Box>::Node>>` + = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` + found `std::boxed::Box>::Node>>` error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> $DIR/issue-55796.rs:21:9 @@ -45,8 +45,8 @@ note: ...so that the expression is assignable | LL | Box::new(self.in_edges(u).map(|e| e.target())) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` - found `std::boxed::Box>::Node>>` + = note: expected `std::boxed::Box<(dyn std::iter::Iterator>::Node> + 'static)>` + found `std::boxed::Box>::Node>>` error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-56685.stderr b/src/test/ui/issues/issue-56685.stderr index 2cef3126b9ee0..eccb71095acc7 100644 --- a/src/test/ui/issues/issue-56685.stderr +++ b/src/test/ui/issues/issue-56685.stderr @@ -9,7 +9,7 @@ note: the lint level is defined here | LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ -help: consider prefixing with an underscore +help: if this is intentional, prefix it with an underscore | LL | E::A(_x) | E::B(_x) => {} | ^^ ^^ @@ -20,7 +20,7 @@ error: unused variable: `x` LL | F::A(x, y) | F::B(x, y) => { y }, | ^ ^ | -help: consider prefixing with an underscore +help: if this is intentional, prefix it with an underscore | LL | F::A(_x, y) | F::B(_x, y) => { y }, | ^^ ^^ @@ -29,13 +29,13 @@ error: unused variable: `a` --> $DIR/issue-56685.rs:27:14 | LL | F::C(a, b) => { 3 } - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` --> $DIR/issue-56685.rs:27:17 | LL | F::C(a, b) => { 3 } - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `x` --> $DIR/issue-56685.rs:32:25 @@ -43,7 +43,7 @@ error: unused variable: `x` LL | let _ = if let F::A(x, y) | F::B(x, y) = F::A(1, 2) { | ^ ^ | -help: consider prefixing with an underscore +help: if this is intentional, prefix it with an underscore | LL | let _ = if let F::A(_x, y) | F::B(_x, y) = F::A(1, 2) { | ^^ ^^ @@ -54,7 +54,7 @@ error: unused variable: `x` LL | while let F::A(x, y) | F::B(x, y) = F::A(1, 2) { | ^ ^ | -help: consider prefixing with an underscore +help: if this is intentional, prefix it with an underscore | LL | while let F::A(_x, y) | F::B(_x, y) = F::A(1, 2) { | ^^ ^^ diff --git a/src/test/ui/issues/issue-57271.stderr b/src/test/ui/issues/issue-57271.stderr index 4f164624f7a53..b7c799e163cee 100644 --- a/src/test/ui/issues/issue-57271.stderr +++ b/src/test/ui/issues/issue-57271.stderr @@ -7,7 +7,10 @@ LL | Class(ClassTypeSignature), LL | Array(TypeSignature), | ------------- recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `ObjectType` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `ObjectType` representable + | +LL | Array(Box), + | ^^^^ ^ error[E0072]: recursive type `TypeSignature` has infinite size --> $DIR/issue-57271.rs:19:1 @@ -18,7 +21,10 @@ LL | Base(BaseType), LL | Object(ObjectType), | ---------- recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `TypeSignature` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `TypeSignature` representable + | +LL | Object(Box), + | ^^^^ ^ error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-57362-2.stderr b/src/test/ui/issues/issue-57362-2.stderr index 2edc009746455..47cc64ec470a5 100644 --- a/src/test/ui/issues/issue-57362-2.stderr +++ b/src/test/ui/issues/issue-57362-2.stderr @@ -4,6 +4,8 @@ error[E0599]: no function or associated item named `make_g` found for fn pointer LL | let x = ::make_g(); | ^^^^^^ function or associated item not found in `for<'r> fn(&'r ())` | + = note: the method `make_g` exists but the following trait bounds were not satisfied: + `for<'r> fn(&'r ()): X` = help: items from traits can only be used if the trait is implemented and in scope note: `X` defines an item `make_g`, perhaps you need to implement it --> $DIR/issue-57362-2.rs:8:1 diff --git a/src/test/ui/issues/issue-5791.rs b/src/test/ui/issues/issue-5791.rs index 2f8bf1e936905..fda72a1b20e4a 100644 --- a/src/test/ui/issues/issue-5791.rs +++ b/src/test/ui/issues/issue-5791.rs @@ -1,11 +1,13 @@ // run-pass #![allow(dead_code)] +#![warn(clashing_extern_decl)] // pretty-expanded FIXME #23616 extern { #[link_name = "malloc"] fn malloc1(len: i32) -> *const u8; #[link_name = "malloc"] + //~^ WARN `malloc2` redeclares `malloc` with a different signature fn malloc2(len: i32, foo: i32) -> *const u8; } diff --git a/src/test/ui/issues/issue-5791.stderr b/src/test/ui/issues/issue-5791.stderr new file mode 100644 index 0000000000000..7ae83c43f1339 --- /dev/null +++ b/src/test/ui/issues/issue-5791.stderr @@ -0,0 +1,21 @@ +warning: `malloc2` redeclares `malloc` with a different signature + --> $DIR/issue-5791.rs:9:5 + | +LL | / #[link_name = "malloc"] +LL | | fn malloc1(len: i32) -> *const u8; + | |______________________________________- `malloc` previously declared here +LL | / #[link_name = "malloc"] +LL | | +LL | | fn malloc2(len: i32, foo: i32) -> *const u8; + | |________________________________________________^ this signature doesn't match the previous declaration + | +note: the lint level is defined here + --> $DIR/issue-5791.rs:3:9 + | +LL | #![warn(clashing_extern_decl)] + | ^^^^^^^^^^^^^^^^^^^^ + = note: expected `unsafe extern "C" fn(i32) -> *const u8` + found `unsafe extern "C" fn(i32, i32) -> *const u8` + +warning: 1 warning emitted + 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-58319.rs b/src/test/ui/issues/issue-58319.rs new file mode 100644 index 0000000000000..757307d944f18 --- /dev/null +++ b/src/test/ui/issues/issue-58319.rs @@ -0,0 +1,621 @@ +// run-pass +fn main() {} +#[derive(Clone)] +pub struct Little; +#[derive(Clone)] +pub struct Big( + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, + Little, +); 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.rs b/src/test/ui/issues/issue-60283.rs index e5a9caa32fae7..9c2b2dc9f4dae 100644 --- a/src/test/ui/issues/issue-60283.rs +++ b/src/test/ui/issues/issue-60283.rs @@ -7,11 +7,13 @@ impl<'a> Trait<'a> for () { } pub fn foo(_: T, _: F) -where T: for<'a> Trait<'a>, - F: for<'a> FnMut(>::Item) {} +where + T: for<'a> Trait<'a>, + F: for<'a> FnMut(>::Item), +{ +} fn main() { foo((), drop) //~^ ERROR type mismatch in function arguments - //~| ERROR type mismatch resolving } diff --git a/src/test/ui/issues/issue-60283.stderr b/src/test/ui/issues/issue-60283.stderr index d13dcd54a479a..ad679bfa22063 100644 --- a/src/test/ui/issues/issue-60283.stderr +++ b/src/test/ui/issues/issue-60283.stderr @@ -1,31 +1,18 @@ error[E0631]: type mismatch in function arguments - --> $DIR/issue-60283.rs:14:13 + --> $DIR/issue-60283.rs:17:13 | LL | pub fn foo(_: T, _: F) - | --- -LL | where T: for<'a> Trait<'a>, -LL | F: for<'a> FnMut(>::Item) {} - | ------------------------------------- required by this bound in `foo` + | --- required by a bound in this +... +LL | F: for<'a> FnMut(>::Item), + | ----------------------------- required by this bound in `foo` ... LL | foo((), drop) | ^^^^ | | - | expected signature of `for<'a> fn(<() as Trait<'a>>::Item) -> _` - | found signature of `fn(_) -> _` - -error[E0271]: type mismatch resolving `for<'a> } as std::ops::FnOnce<(<() as Trait<'a>>::Item,)>>::Output == ()` - --> $DIR/issue-60283.rs:14:5 - | -LL | pub fn foo(_: T, _: F) - | --- -LL | where T: for<'a> Trait<'a>, -LL | F: for<'a> FnMut(>::Item) {} - | ----------------------------- required by this bound in `foo` -... -LL | foo((), drop) - | ^^^ expected bound lifetime parameter 'a, found concrete lifetime + | expected signature of `fn(<() as Trait<'a>>::Item) -> _` + | found signature of `fn(()) -> _` -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0271, E0631. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0631`. diff --git a/src/test/ui/issues/issue-60662.stdout b/src/test/ui/issues/issue-60662.stdout index 5a4b49cfa1e76..cebe834824a61 100644 --- a/src/test/ui/issues/issue-60662.stdout +++ b/src/test/ui/issues/issue-60662.stdout @@ -10,5 +10,5 @@ extern crate std; trait Animal { } fn main() { - pub type ServeFut = impl Animal; + pub type ServeFut = /*impl Trait*/; } 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-65131.rs b/src/test/ui/issues/issue-65131.rs new file mode 100644 index 0000000000000..8b5345da900aa --- /dev/null +++ b/src/test/ui/issues/issue-65131.rs @@ -0,0 +1,18 @@ +fn get_pair(_a: &mut u32, _b: &mut u32) {} + +macro_rules! x10 { + ($($t:tt)*) => { + $($t)* $($t)* $($t)* $($t)* $($t)* + $($t)* $($t)* $($t)* $($t)* $($t)* + } +} + +#[allow(unused_assignments)] +fn main() { + let mut x = 1; + + get_pair(&mut x, &mut x); + //~^ ERROR: cannot borrow `x` as mutable more than once at a time + + x10! { x10!{ x10!{ if x > 0 { x += 2 } else { x += 1 } } } } +} diff --git a/src/test/ui/issues/issue-65131.stderr b/src/test/ui/issues/issue-65131.stderr new file mode 100644 index 0000000000000..e234e6da552a8 --- /dev/null +++ b/src/test/ui/issues/issue-65131.stderr @@ -0,0 +1,12 @@ +error[E0499]: cannot borrow `x` as mutable more than once at a time + --> $DIR/issue-65131.rs:14:22 + | +LL | get_pair(&mut x, &mut x); + | -------- ------ ^^^^^^ second mutable borrow occurs here + | | | + | | first mutable borrow occurs here + | first borrow later used by call + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0499`. 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-67552.rs b/src/test/ui/issues/issue-67552.rs new file mode 100644 index 0000000000000..b0fcb74764b98 --- /dev/null +++ b/src/test/ui/issues/issue-67552.rs @@ -0,0 +1,30 @@ +// build-fail + +fn main() { + rec(Empty); +} + +struct Empty; + +impl Iterator for Empty { + type Item = (); + fn next<'a>(&'a mut self) -> core::option::Option<()> { + None + } +} + +fn identity(x: T) -> T { + x +} + +fn rec(mut it: T) +where + T: Iterator, +{ + if () == () { + T::count(it); + } else { + rec(identity(&mut it)) + //~^ ERROR reached the recursion limit while instantiating + } +} diff --git a/src/test/ui/issues/issue-67552.stderr b/src/test/ui/issues/issue-67552.stderr new file mode 100644 index 0000000000000..3bb2016f07d24 --- /dev/null +++ b/src/test/ui/issues/issue-67552.stderr @@ -0,0 +1,20 @@ +error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut &mut Empty>` + --> $DIR/issue-67552.rs:27:9 + | +LL | rec(identity(&mut it)) + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: `rec` defined here + --> $DIR/issue-67552.rs:20:1 + | +LL | / fn rec(mut it: T) +LL | | where +LL | | T: Iterator, +LL | | { +... | +LL | | } +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-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-69602-type-err-during-codegen-ice.rs b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs index d060f26fb2a08..2c5257ce063cb 100644 --- a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs +++ b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs @@ -19,5 +19,4 @@ impl TraitB for B { //~ ERROR not all trait items implemented, missing: `MyA` fn main() { let _ = [0; B::VALUE]; - //~^ ERROR array lengths can't depend on generic parameters } diff --git a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr index c6b2b4d27a208..8ae0f8b804c93 100644 --- a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr +++ b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr @@ -13,13 +13,7 @@ LL | type MyA: TraitA; LL | impl TraitB for B { | ^^^^^^^^^^^^^^^^^ missing `MyA` in implementation -error: array lengths can't depend on generic parameters - --> $DIR/issue-69602-type-err-during-codegen-ice.rs:21:17 - | -LL | let _ = [0; B::VALUE]; - | ^^^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0046, E0437. For more information about an error, try `rustc --explain E0046`. 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.stderr b/src/test/ui/issues/issue-69725.stderr index 667383e072a54..d9d61fe66f78e 100644 --- a/src/test/ui/issues/issue-69725.stderr +++ b/src/test/ui/issues/issue-69725.stderr @@ -8,6 +8,14 @@ LL | let _ = Struct::::new().clone(); | LL | pub struct Struct(A); | ------------------------ doesn't satisfy `issue_69725::Struct: std::clone::Clone` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `std::sync::Arc>` here + | the method is available for `std::rc::Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: `A: std::clone::Clone` 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.rs b/src/test/ui/issues/issue-70041.rs new file mode 100644 index 0000000000000..22e42295eedf3 --- /dev/null +++ b/src/test/ui/issues/issue-70041.rs @@ -0,0 +1,13 @@ +// compile-flags: --edition=2018 +// run-pass + +macro_rules! regex { + //~^ WARN unused macro definition + () => {}; +} + +#[allow(dead_code)] +use regex; +//~^ WARN unused import + +fn main() {} diff --git a/src/test/ui/issues/issue-70041.stderr b/src/test/ui/issues/issue-70041.stderr new file mode 100644 index 0000000000000..ecd618eae8b07 --- /dev/null +++ b/src/test/ui/issues/issue-70041.stderr @@ -0,0 +1,21 @@ +warning: unused macro definition + --> $DIR/issue-70041.rs:4:1 + | +LL | / macro_rules! regex { +LL | | +LL | | () => {}; +LL | | } + | |_^ + | + = note: `#[warn(unused_macros)]` on by default + +warning: unused import: `regex` + --> $DIR/issue-70041.rs:10: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-72076.rs b/src/test/ui/issues/issue-72076.rs new file mode 100644 index 0000000000000..1659044a64fe1 --- /dev/null +++ b/src/test/ui/issues/issue-72076.rs @@ -0,0 +1,6 @@ +trait X { + type S; + fn f() -> Self::S {} //~ ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/issues/issue-72076.stderr b/src/test/ui/issues/issue-72076.stderr new file mode 100644 index 0000000000000..b942cf75b06a7 --- /dev/null +++ b/src/test/ui/issues/issue-72076.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/issue-72076.rs:3:23 + | +LL | fn f() -> Self::S {} + | ^^ expected associated type, found `()` + | + = note: expected associated type `::S` + found unit type `()` + = help: consider constraining the associated type `::S` to `()` or calling a method that returns `::S` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/issues/issue-72253.rs b/src/test/ui/issues/issue-72253.rs new file mode 100644 index 0000000000000..6f9af73b039e4 --- /dev/null +++ b/src/test/ui/issues/issue-72253.rs @@ -0,0 +1,6 @@ +fn main() { + let a = std::process::Command::new("echo") + .arg("1") + ,arg("2") //~ ERROR expected one of `.`, `;`, `?`, or an operator, found `,` + .output(); +} diff --git a/src/test/ui/issues/issue-72253.stderr b/src/test/ui/issues/issue-72253.stderr new file mode 100644 index 0000000000000..3819fd92a9e21 --- /dev/null +++ b/src/test/ui/issues/issue-72253.stderr @@ -0,0 +1,10 @@ +error: expected one of `.`, `;`, `?`, or an operator, found `,` + --> $DIR/issue-72253.rs:4:9 + | +LL | .arg("1") + | - expected one of `.`, `;`, `?`, or an operator +LL | ,arg("2") + | ^ unexpected token + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-72278.rs b/src/test/ui/issues/issue-72278.rs new file mode 100644 index 0000000000000..92fd1f73a937f --- /dev/null +++ b/src/test/ui/issues/issue-72278.rs @@ -0,0 +1,19 @@ +// run-pass + +#![allow(unused)] + +struct S; + +impl S { + fn func<'a, U>(&'a self) -> U { + todo!() + } +} + +fn dont_crash<'a, U>() -> U { + S.func::<'a, U>() + //~^ WARN cannot specify lifetime arguments explicitly + //~| WARN this was previously accepted +} + +fn main() {} diff --git a/src/test/ui/issues/issue-72278.stderr b/src/test/ui/issues/issue-72278.stderr new file mode 100644 index 0000000000000..41dff686bc4ae --- /dev/null +++ b/src/test/ui/issues/issue-72278.stderr @@ -0,0 +1,15 @@ +warning: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/issue-72278.rs:14:14 + | +LL | fn func<'a, U>(&'a self) -> U { + | -- the late bound lifetime parameter is introduced here +... +LL | S.func::<'a, U>() + | ^^ + | + = note: `#[warn(late_bound_lifetime_arguments)]` 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 #42868 + +warning: 1 warning emitted + diff --git a/src/test/ui/issues/issue-72373.rs b/src/test/ui/issues/issue-72373.rs new file mode 100644 index 0000000000000..4da6061c27fe8 --- /dev/null +++ b/src/test/ui/issues/issue-72373.rs @@ -0,0 +1,9 @@ +fn foo(c: &[u32], n: u32) -> u32 { + match *c { + [h, ..] if h > n => 0, + [h, ..] if h == n => 1, + [h, ref ts..] => foo(c, n - h) + foo(ts, n), + //~^ ERROR expected one of `,`, `@`, `]`, or `|`, found `..` + [] => 0, + } +} diff --git a/src/test/ui/issues/issue-72373.stderr b/src/test/ui/issues/issue-72373.stderr new file mode 100644 index 0000000000000..dfde8624814f8 --- /dev/null +++ b/src/test/ui/issues/issue-72373.stderr @@ -0,0 +1,13 @@ +error: expected one of `,`, `@`, `]`, or `|`, found `..` + --> $DIR/issue-72373.rs:5:19 + | +LL | [h, ref ts..] => foo(c, n - h) + foo(ts, n), + | ^^ expected one of `,`, `@`, `]`, or `|` + | +help: if you meant to bind the contents of the rest of the array pattern into `ts`, use `@` + | +LL | [h, ref ts @ ..] => foo(c, n - h) + foo(ts, n), + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-72455.rs b/src/test/ui/issues/issue-72455.rs new file mode 100644 index 0000000000000..b6c3bb222876d --- /dev/null +++ b/src/test/ui/issues/issue-72455.rs @@ -0,0 +1,27 @@ +// check-pass + +pub trait ResultExt { + type Ok; + fn err_eprint_and_ignore(self) -> Option; +} + +impl ResultExt for std::result::Result +where + E: std::error::Error, +{ + type Ok = O; + fn err_eprint_and_ignore(self) -> Option + where + Self: , + { + match self { + Err(e) => { + eprintln!("{}", e); + None + } + Ok(o) => Some(o), + } + } +} + +fn main() {} diff --git a/src/test/ui/issues/issue-72554.rs b/src/test/ui/issues/issue-72554.rs new file mode 100644 index 0000000000000..47aca05d7786f --- /dev/null +++ b/src/test/ui/issues/issue-72554.rs @@ -0,0 +1,20 @@ +use std::collections::BTreeSet; + +#[derive(Hash)] +pub enum ElemDerived { //~ ERROR recursive type `ElemDerived` has infinite size + A(ElemDerived) +} + +pub enum Elem { + Derived(ElemDerived) +} + +pub struct Set(BTreeSet); + +impl Set { + pub fn into_iter(self) -> impl Iterator { + self.0.into_iter() + } +} + +fn main() {} diff --git a/src/test/ui/issues/issue-72554.stderr b/src/test/ui/issues/issue-72554.stderr new file mode 100644 index 0000000000000..9de94c393a711 --- /dev/null +++ b/src/test/ui/issues/issue-72554.stderr @@ -0,0 +1,16 @@ +error[E0072]: recursive type `ElemDerived` has infinite size + --> $DIR/issue-72554.rs:4:1 + | +LL | pub enum ElemDerived { + | ^^^^^^^^^^^^^^^^^^^^ recursive type has infinite size +LL | A(ElemDerived) + | ----------- recursive without indirection + | +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `ElemDerived` representable + | +LL | A(Box) + | ^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0072`. diff --git a/src/test/ui/issues/issue-72574-1.rs b/src/test/ui/issues/issue-72574-1.rs new file mode 100644 index 0000000000000..efbb0bfb1508f --- /dev/null +++ b/src/test/ui/issues/issue-72574-1.rs @@ -0,0 +1,8 @@ +fn main() { + let x = (1, 2, 3); + match x { + (_a, _x @ ..) => {} + _ => {} + } +} +//~^^^^ ERROR `_x @` is not allowed in a tuple diff --git a/src/test/ui/issues/issue-72574-1.stderr b/src/test/ui/issues/issue-72574-1.stderr new file mode 100644 index 0000000000000..329f7d008d498 --- /dev/null +++ b/src/test/ui/issues/issue-72574-1.stderr @@ -0,0 +1,14 @@ +error: `_x @` is not allowed in a tuple + --> $DIR/issue-72574-1.rs:4:14 + | +LL | (_a, _x @ ..) => {} + | ^^^^^^^ this is only allowed in slice patterns + | + = help: remove this and bind each tuple field independently +help: if you don't need to use the contents of _x, discard the tuple's remaining fields + | +LL | (_a, ..) => {} + | ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-72574-2.rs b/src/test/ui/issues/issue-72574-2.rs new file mode 100644 index 0000000000000..0c8f6fcc50889 --- /dev/null +++ b/src/test/ui/issues/issue-72574-2.rs @@ -0,0 +1,10 @@ +struct Binder(i32, i32, i32); + +fn main() { + let x = Binder(1, 2, 3); + match x { + Binder(_a, _x @ ..) => {} + _ => {} + } +} +//~^^^^ ERROR `_x @` is not allowed in a tuple struct diff --git a/src/test/ui/issues/issue-72574-2.stderr b/src/test/ui/issues/issue-72574-2.stderr new file mode 100644 index 0000000000000..6faa57bcca6b1 --- /dev/null +++ b/src/test/ui/issues/issue-72574-2.stderr @@ -0,0 +1,14 @@ +error: `_x @` is not allowed in a tuple struct + --> $DIR/issue-72574-2.rs:6:20 + | +LL | Binder(_a, _x @ ..) => {} + | ^^^^^^^ this is only allowed in slice patterns + | + = help: remove this and bind each tuple field independently +help: if you don't need to use the contents of _x, discard the tuple's remaining fields + | +LL | Binder(_a, ..) => {} + | ^^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-72690.rs b/src/test/ui/issues/issue-72690.rs new file mode 100644 index 0000000000000..4edbd9ca15de7 --- /dev/null +++ b/src/test/ui/issues/issue-72690.rs @@ -0,0 +1,62 @@ +fn no_err() { + |x: String| x; + let _ = String::from("x"); +} + +fn err() { + String::from("x".as_ref()); //~ ERROR type annotations needed +} + +fn arg_pat_closure_err() { + |x| String::from("x".as_ref()); //~ ERROR type annotations needed +} + +fn local_pat_closure_err() { + let _ = "x".as_ref(); //~ ERROR type annotations needed +} + +fn err_first_arg_pat() { + String::from("x".as_ref()); //~ ERROR type annotations needed + |x: String| x; +} + +fn err_second_arg_pat() { + |x: String| x; + String::from("x".as_ref()); //~ ERROR type annotations needed +} + +fn err_mid_arg_pat() { + |x: String| x; + |x: String| x; + |x: String| x; + |x: String| x; + String::from("x".as_ref()); //~ ERROR type annotations needed + |x: String| x; + |x: String| x; + |x: String| x; + |x: String| x; +} + +fn err_first_local_pat() { + String::from("x".as_ref()); //~ ERROR type annotations needed + let _ = String::from("x"); +} + +fn err_second_local_pat() { + let _ = String::from("x"); + String::from("x".as_ref()); //~ ERROR type annotations needed +} + +fn err_mid_local_pat() { + let _ = String::from("x"); + let _ = String::from("x"); + let _ = String::from("x"); + let _ = String::from("x"); + String::from("x".as_ref()); //~ ERROR type annotations needed + let _ = String::from("x"); + let _ = String::from("x"); + let _ = String::from("x"); + let _ = String::from("x"); +} + +fn main() {} diff --git a/src/test/ui/issues/issue-72690.stderr b/src/test/ui/issues/issue-72690.stderr new file mode 100644 index 0000000000000..64e78ddf60474 --- /dev/null +++ b/src/test/ui/issues/issue-72690.stderr @@ -0,0 +1,88 @@ +error[E0283]: type annotations needed + --> $DIR/issue-72690.rs:7:5 + | +LL | String::from("x".as_ref()); + | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | + = note: cannot satisfy `std::string::String: std::convert::From<&_>` + = note: required by `std::convert::From::from` + +error[E0282]: type annotations needed + --> $DIR/issue-72690.rs:11:6 + | +LL | |x| String::from("x".as_ref()); + | ^ consider giving this closure parameter a type + +error[E0283]: type annotations needed + --> $DIR/issue-72690.rs:15:17 + | +LL | let _ = "x".as_ref(); + | ^^^^^^ cannot infer type for type `str` + | + = note: cannot satisfy `str: std::convert::AsRef<_>` + +error[E0283]: type annotations needed + --> $DIR/issue-72690.rs:19:5 + | +LL | String::from("x".as_ref()); + | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | + = note: cannot satisfy `std::string::String: std::convert::From<&_>` + = note: required by `std::convert::From::from` + +error[E0283]: type annotations needed + --> $DIR/issue-72690.rs:25:5 + | +LL | String::from("x".as_ref()); + | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | + = note: cannot satisfy `std::string::String: std::convert::From<&_>` + = note: required by `std::convert::From::from` + +error[E0283]: type annotations needed + --> $DIR/issue-72690.rs:33:5 + | +LL | String::from("x".as_ref()); + | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | + = note: cannot satisfy `std::string::String: std::convert::From<&_>` + = note: required by `std::convert::From::from` + +error[E0283]: type annotations needed for `std::string::String` + --> $DIR/issue-72690.rs:41:5 + | +LL | String::from("x".as_ref()); + | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` +LL | let _ = String::from("x"); + | - consider giving this pattern a type + | + = note: cannot satisfy `std::string::String: std::convert::From<&_>` + = note: required by `std::convert::From::from` + +error[E0283]: type annotations needed for `std::string::String` + --> $DIR/issue-72690.rs:47:5 + | +LL | let _ = String::from("x"); + | - consider giving this pattern a type +LL | String::from("x".as_ref()); + | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | + = note: cannot satisfy `std::string::String: std::convert::From<&_>` + = note: required by `std::convert::From::from` + +error[E0283]: type annotations needed for `std::string::String` + --> $DIR/issue-72690.rs:55:5 + | +LL | let _ = String::from("x"); + | - consider giving this pattern a type +... +LL | String::from("x".as_ref()); + | ^^^^^^^^^^^^ cannot infer type for struct `std::string::String` + | + = note: cannot satisfy `std::string::String: std::convert::From<&_>` + = note: required by `std::convert::From::from` + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0282, E0283. +For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/issues/issue-72839-error-overflow.rs b/src/test/ui/issues/issue-72839-error-overflow.rs new file mode 100644 index 0000000000000..6562d228409f3 --- /dev/null +++ b/src/test/ui/issues/issue-72839-error-overflow.rs @@ -0,0 +1,19 @@ +// Regression test for issue #72839 +// Tests that we do not overflow during trait selection after +// a type error occurs +use std::ops::Rem; +trait Foo {} +struct MyStruct(T); + +impl Rem> for MyStruct where MyStruct: Rem> { + type Output = u8; + fn rem(self, _: MyStruct) -> Self::Output { + panic!() + } +} + +fn main() {} + +fn foo() { + if missing_var % 8 == 0 {} //~ ERROR cannot find +} diff --git a/src/test/ui/issues/issue-72839-error-overflow.stderr b/src/test/ui/issues/issue-72839-error-overflow.stderr new file mode 100644 index 0000000000000..c4b6f90ca69a3 --- /dev/null +++ b/src/test/ui/issues/issue-72839-error-overflow.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `missing_var` in this scope + --> $DIR/issue-72839-error-overflow.rs:18:8 + | +LL | if missing_var % 8 == 0 {} + | ^^^^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/issues/issue-72933-match-stack-overflow.rs b/src/test/ui/issues/issue-72933-match-stack-overflow.rs new file mode 100644 index 0000000000000..aa796bcf5a0f3 --- /dev/null +++ b/src/test/ui/issues/issue-72933-match-stack-overflow.rs @@ -0,0 +1,5208 @@ +// build-pass +// ignore-tidy-filelength +#![crate_type="rlib"] + +fn banana(v: &str) -> u32 { + match v { + "0" => 0, + "1" => 1, + "2" => 2, + "3" => 3, + "4" => 4, + "5" => 5, + "6" => 6, + "7" => 7, + "8" => 8, + "9" => 9, + "10" => 10, + "11" => 11, + "12" => 12, + "13" => 13, + "14" => 14, + "15" => 15, + "16" => 16, + "17" => 17, + "18" => 18, + "19" => 19, + "20" => 20, + "21" => 21, + "22" => 22, + "23" => 23, + "24" => 24, + "25" => 25, + "26" => 26, + "27" => 27, + "28" => 28, + "29" => 29, + "30" => 30, + "31" => 31, + "32" => 32, + "33" => 33, + "34" => 34, + "35" => 35, + "36" => 36, + "37" => 37, + "38" => 38, + "39" => 39, + "40" => 40, + "41" => 41, + "42" => 42, + "43" => 43, + "44" => 44, + "45" => 45, + "46" => 46, + "47" => 47, + "48" => 48, + "49" => 49, + "50" => 50, + "51" => 51, + "52" => 52, + "53" => 53, + "54" => 54, + "55" => 55, + "56" => 56, + "57" => 57, + "58" => 58, + "59" => 59, + "60" => 60, + "61" => 61, + "62" => 62, + "63" => 63, + "64" => 64, + "65" => 65, + "66" => 66, + "67" => 67, + "68" => 68, + "69" => 69, + "70" => 70, + "71" => 71, + "72" => 72, + "73" => 73, + "74" => 74, + "75" => 75, + "76" => 76, + "77" => 77, + "78" => 78, + "79" => 79, + "80" => 80, + "81" => 81, + "82" => 82, + "83" => 83, + "84" => 84, + "85" => 85, + "86" => 86, + "87" => 87, + "88" => 88, + "89" => 89, + "90" => 90, + "91" => 91, + "92" => 92, + "93" => 93, + "94" => 94, + "95" => 95, + "96" => 96, + "97" => 97, + "98" => 98, + "99" => 99, + "100" => 100, + "101" => 101, + "102" => 102, + "103" => 103, + "104" => 104, + "105" => 105, + "106" => 106, + "107" => 107, + "108" => 108, + "109" => 109, + "110" => 110, + "111" => 111, + "112" => 112, + "113" => 113, + "114" => 114, + "115" => 115, + "116" => 116, + "117" => 117, + "118" => 118, + "119" => 119, + "120" => 120, + "121" => 121, + "122" => 122, + "123" => 123, + "124" => 124, + "125" => 125, + "126" => 126, + "127" => 127, + "128" => 128, + "129" => 129, + "130" => 130, + "131" => 131, + "132" => 132, + "133" => 133, + "134" => 134, + "135" => 135, + "136" => 136, + "137" => 137, + "138" => 138, + "139" => 139, + "140" => 140, + "141" => 141, + "142" => 142, + "143" => 143, + "144" => 144, + "145" => 145, + "146" => 146, + "147" => 147, + "148" => 148, + "149" => 149, + "150" => 150, + "151" => 151, + "152" => 152, + "153" => 153, + "154" => 154, + "155" => 155, + "156" => 156, + "157" => 157, + "158" => 158, + "159" => 159, + "160" => 160, + "161" => 161, + "162" => 162, + "163" => 163, + "164" => 164, + "165" => 165, + "166" => 166, + "167" => 167, + "168" => 168, + "169" => 169, + "170" => 170, + "171" => 171, + "172" => 172, + "173" => 173, + "174" => 174, + "175" => 175, + "176" => 176, + "177" => 177, + "178" => 178, + "179" => 179, + "180" => 180, + "181" => 181, + "182" => 182, + "183" => 183, + "184" => 184, + "185" => 185, + "186" => 186, + "187" => 187, + "188" => 188, + "189" => 189, + "190" => 190, + "191" => 191, + "192" => 192, + "193" => 193, + "194" => 194, + "195" => 195, + "196" => 196, + "197" => 197, + "198" => 198, + "199" => 199, + "200" => 200, + "201" => 201, + "202" => 202, + "203" => 203, + "204" => 204, + "205" => 205, + "206" => 206, + "207" => 207, + "208" => 208, + "209" => 209, + "210" => 210, + "211" => 211, + "212" => 212, + "213" => 213, + "214" => 214, + "215" => 215, + "216" => 216, + "217" => 217, + "218" => 218, + "219" => 219, + "220" => 220, + "221" => 221, + "222" => 222, + "223" => 223, + "224" => 224, + "225" => 225, + "226" => 226, + "227" => 227, + "228" => 228, + "229" => 229, + "230" => 230, + "231" => 231, + "232" => 232, + "233" => 233, + "234" => 234, + "235" => 235, + "236" => 236, + "237" => 237, + "238" => 238, + "239" => 239, + "240" => 240, + "241" => 241, + "242" => 242, + "243" => 243, + "244" => 244, + "245" => 245, + "246" => 246, + "247" => 247, + "248" => 248, + "249" => 249, + "250" => 250, + "251" => 251, + "252" => 252, + "253" => 253, + "254" => 254, + "255" => 255, + "256" => 256, + "257" => 257, + "258" => 258, + "259" => 259, + "260" => 260, + "261" => 261, + "262" => 262, + "263" => 263, + "264" => 264, + "265" => 265, + "266" => 266, + "267" => 267, + "268" => 268, + "269" => 269, + "270" => 270, + "271" => 271, + "272" => 272, + "273" => 273, + "274" => 274, + "275" => 275, + "276" => 276, + "277" => 277, + "278" => 278, + "279" => 279, + "280" => 280, + "281" => 281, + "282" => 282, + "283" => 283, + "284" => 284, + "285" => 285, + "286" => 286, + "287" => 287, + "288" => 288, + "289" => 289, + "290" => 290, + "291" => 291, + "292" => 292, + "293" => 293, + "294" => 294, + "295" => 295, + "296" => 296, + "297" => 297, + "298" => 298, + "299" => 299, + "300" => 300, + "301" => 301, + "302" => 302, + "303" => 303, + "304" => 304, + "305" => 305, + "306" => 306, + "307" => 307, + "308" => 308, + "309" => 309, + "310" => 310, + "311" => 311, + "312" => 312, + "313" => 313, + "314" => 314, + "315" => 315, + "316" => 316, + "317" => 317, + "318" => 318, + "319" => 319, + "320" => 320, + "321" => 321, + "322" => 322, + "323" => 323, + "324" => 324, + "325" => 325, + "326" => 326, + "327" => 327, + "328" => 328, + "329" => 329, + "330" => 330, + "331" => 331, + "332" => 332, + "333" => 333, + "334" => 334, + "335" => 335, + "336" => 336, + "337" => 337, + "338" => 338, + "339" => 339, + "340" => 340, + "341" => 341, + "342" => 342, + "343" => 343, + "344" => 344, + "345" => 345, + "346" => 346, + "347" => 347, + "348" => 348, + "349" => 349, + "350" => 350, + "351" => 351, + "352" => 352, + "353" => 353, + "354" => 354, + "355" => 355, + "356" => 356, + "357" => 357, + "358" => 358, + "359" => 359, + "360" => 360, + "361" => 361, + "362" => 362, + "363" => 363, + "364" => 364, + "365" => 365, + "366" => 366, + "367" => 367, + "368" => 368, + "369" => 369, + "370" => 370, + "371" => 371, + "372" => 372, + "373" => 373, + "374" => 374, + "375" => 375, + "376" => 376, + "377" => 377, + "378" => 378, + "379" => 379, + "380" => 380, + "381" => 381, + "382" => 382, + "383" => 383, + "384" => 384, + "385" => 385, + "386" => 386, + "387" => 387, + "388" => 388, + "389" => 389, + "390" => 390, + "391" => 391, + "392" => 392, + "393" => 393, + "394" => 394, + "395" => 395, + "396" => 396, + "397" => 397, + "398" => 398, + "399" => 399, + "400" => 400, + "401" => 401, + "402" => 402, + "403" => 403, + "404" => 404, + "405" => 405, + "406" => 406, + "407" => 407, + "408" => 408, + "409" => 409, + "410" => 410, + "411" => 411, + "412" => 412, + "413" => 413, + "414" => 414, + "415" => 415, + "416" => 416, + "417" => 417, + "418" => 418, + "419" => 419, + "420" => 420, + "421" => 421, + "422" => 422, + "423" => 423, + "424" => 424, + "425" => 425, + "426" => 426, + "427" => 427, + "428" => 428, + "429" => 429, + "430" => 430, + "431" => 431, + "432" => 432, + "433" => 433, + "434" => 434, + "435" => 435, + "436" => 436, + "437" => 437, + "438" => 438, + "439" => 439, + "440" => 440, + "441" => 441, + "442" => 442, + "443" => 443, + "444" => 444, + "445" => 445, + "446" => 446, + "447" => 447, + "448" => 448, + "449" => 449, + "450" => 450, + "451" => 451, + "452" => 452, + "453" => 453, + "454" => 454, + "455" => 455, + "456" => 456, + "457" => 457, + "458" => 458, + "459" => 459, + "460" => 460, + "461" => 461, + "462" => 462, + "463" => 463, + "464" => 464, + "465" => 465, + "466" => 466, + "467" => 467, + "468" => 468, + "469" => 469, + "470" => 470, + "471" => 471, + "472" => 472, + "473" => 473, + "474" => 474, + "475" => 475, + "476" => 476, + "477" => 477, + "478" => 478, + "479" => 479, + "480" => 480, + "481" => 481, + "482" => 482, + "483" => 483, + "484" => 484, + "485" => 485, + "486" => 486, + "487" => 487, + "488" => 488, + "489" => 489, + "490" => 490, + "491" => 491, + "492" => 492, + "493" => 493, + "494" => 494, + "495" => 495, + "496" => 496, + "497" => 497, + "498" => 498, + "499" => 499, + "500" => 500, + "501" => 501, + "502" => 502, + "503" => 503, + "504" => 504, + "505" => 505, + "506" => 506, + "507" => 507, + "508" => 508, + "509" => 509, + "510" => 510, + "511" => 511, + "512" => 512, + "513" => 513, + "514" => 514, + "515" => 515, + "516" => 516, + "517" => 517, + "518" => 518, + "519" => 519, + "520" => 520, + "521" => 521, + "522" => 522, + "523" => 523, + "524" => 524, + "525" => 525, + "526" => 526, + "527" => 527, + "528" => 528, + "529" => 529, + "530" => 530, + "531" => 531, + "532" => 532, + "533" => 533, + "534" => 534, + "535" => 535, + "536" => 536, + "537" => 537, + "538" => 538, + "539" => 539, + "540" => 540, + "541" => 541, + "542" => 542, + "543" => 543, + "544" => 544, + "545" => 545, + "546" => 546, + "547" => 547, + "548" => 548, + "549" => 549, + "550" => 550, + "551" => 551, + "552" => 552, + "553" => 553, + "554" => 554, + "555" => 555, + "556" => 556, + "557" => 557, + "558" => 558, + "559" => 559, + "560" => 560, + "561" => 561, + "562" => 562, + "563" => 563, + "564" => 564, + "565" => 565, + "566" => 566, + "567" => 567, + "568" => 568, + "569" => 569, + "570" => 570, + "571" => 571, + "572" => 572, + "573" => 573, + "574" => 574, + "575" => 575, + "576" => 576, + "577" => 577, + "578" => 578, + "579" => 579, + "580" => 580, + "581" => 581, + "582" => 582, + "583" => 583, + "584" => 584, + "585" => 585, + "586" => 586, + "587" => 587, + "588" => 588, + "589" => 589, + "590" => 590, + "591" => 591, + "592" => 592, + "593" => 593, + "594" => 594, + "595" => 595, + "596" => 596, + "597" => 597, + "598" => 598, + "599" => 599, + "600" => 600, + "601" => 601, + "602" => 602, + "603" => 603, + "604" => 604, + "605" => 605, + "606" => 606, + "607" => 607, + "608" => 608, + "609" => 609, + "610" => 610, + "611" => 611, + "612" => 612, + "613" => 613, + "614" => 614, + "615" => 615, + "616" => 616, + "617" => 617, + "618" => 618, + "619" => 619, + "620" => 620, + "621" => 621, + "622" => 622, + "623" => 623, + "624" => 624, + "625" => 625, + "626" => 626, + "627" => 627, + "628" => 628, + "629" => 629, + "630" => 630, + "631" => 631, + "632" => 632, + "633" => 633, + "634" => 634, + "635" => 635, + "636" => 636, + "637" => 637, + "638" => 638, + "639" => 639, + "640" => 640, + "641" => 641, + "642" => 642, + "643" => 643, + "644" => 644, + "645" => 645, + "646" => 646, + "647" => 647, + "648" => 648, + "649" => 649, + "650" => 650, + "651" => 651, + "652" => 652, + "653" => 653, + "654" => 654, + "655" => 655, + "656" => 656, + "657" => 657, + "658" => 658, + "659" => 659, + "660" => 660, + "661" => 661, + "662" => 662, + "663" => 663, + "664" => 664, + "665" => 665, + "666" => 666, + "667" => 667, + "668" => 668, + "669" => 669, + "670" => 670, + "671" => 671, + "672" => 672, + "673" => 673, + "674" => 674, + "675" => 675, + "676" => 676, + "677" => 677, + "678" => 678, + "679" => 679, + "680" => 680, + "681" => 681, + "682" => 682, + "683" => 683, + "684" => 684, + "685" => 685, + "686" => 686, + "687" => 687, + "688" => 688, + "689" => 689, + "690" => 690, + "691" => 691, + "692" => 692, + "693" => 693, + "694" => 694, + "695" => 695, + "696" => 696, + "697" => 697, + "698" => 698, + "699" => 699, + "700" => 700, + "701" => 701, + "702" => 702, + "703" => 703, + "704" => 704, + "705" => 705, + "706" => 706, + "707" => 707, + "708" => 708, + "709" => 709, + "710" => 710, + "711" => 711, + "712" => 712, + "713" => 713, + "714" => 714, + "715" => 715, + "716" => 716, + "717" => 717, + "718" => 718, + "719" => 719, + "720" => 720, + "721" => 721, + "722" => 722, + "723" => 723, + "724" => 724, + "725" => 725, + "726" => 726, + "727" => 727, + "728" => 728, + "729" => 729, + "730" => 730, + "731" => 731, + "732" => 732, + "733" => 733, + "734" => 734, + "735" => 735, + "736" => 736, + "737" => 737, + "738" => 738, + "739" => 739, + "740" => 740, + "741" => 741, + "742" => 742, + "743" => 743, + "744" => 744, + "745" => 745, + "746" => 746, + "747" => 747, + "748" => 748, + "749" => 749, + "750" => 750, + "751" => 751, + "752" => 752, + "753" => 753, + "754" => 754, + "755" => 755, + "756" => 756, + "757" => 757, + "758" => 758, + "759" => 759, + "760" => 760, + "761" => 761, + "762" => 762, + "763" => 763, + "764" => 764, + "765" => 765, + "766" => 766, + "767" => 767, + "768" => 768, + "769" => 769, + "770" => 770, + "771" => 771, + "772" => 772, + "773" => 773, + "774" => 774, + "775" => 775, + "776" => 776, + "777" => 777, + "778" => 778, + "779" => 779, + "780" => 780, + "781" => 781, + "782" => 782, + "783" => 783, + "784" => 784, + "785" => 785, + "786" => 786, + "787" => 787, + "788" => 788, + "789" => 789, + "790" => 790, + "791" => 791, + "792" => 792, + "793" => 793, + "794" => 794, + "795" => 795, + "796" => 796, + "797" => 797, + "798" => 798, + "799" => 799, + "800" => 800, + "801" => 801, + "802" => 802, + "803" => 803, + "804" => 804, + "805" => 805, + "806" => 806, + "807" => 807, + "808" => 808, + "809" => 809, + "810" => 810, + "811" => 811, + "812" => 812, + "813" => 813, + "814" => 814, + "815" => 815, + "816" => 816, + "817" => 817, + "818" => 818, + "819" => 819, + "820" => 820, + "821" => 821, + "822" => 822, + "823" => 823, + "824" => 824, + "825" => 825, + "826" => 826, + "827" => 827, + "828" => 828, + "829" => 829, + "830" => 830, + "831" => 831, + "832" => 832, + "833" => 833, + "834" => 834, + "835" => 835, + "836" => 836, + "837" => 837, + "838" => 838, + "839" => 839, + "840" => 840, + "841" => 841, + "842" => 842, + "843" => 843, + "844" => 844, + "845" => 845, + "846" => 846, + "847" => 847, + "848" => 848, + "849" => 849, + "850" => 850, + "851" => 851, + "852" => 852, + "853" => 853, + "854" => 854, + "855" => 855, + "856" => 856, + "857" => 857, + "858" => 858, + "859" => 859, + "860" => 860, + "861" => 861, + "862" => 862, + "863" => 863, + "864" => 864, + "865" => 865, + "866" => 866, + "867" => 867, + "868" => 868, + "869" => 869, + "870" => 870, + "871" => 871, + "872" => 872, + "873" => 873, + "874" => 874, + "875" => 875, + "876" => 876, + "877" => 877, + "878" => 878, + "879" => 879, + "880" => 880, + "881" => 881, + "882" => 882, + "883" => 883, + "884" => 884, + "885" => 885, + "886" => 886, + "887" => 887, + "888" => 888, + "889" => 889, + "890" => 890, + "891" => 891, + "892" => 892, + "893" => 893, + "894" => 894, + "895" => 895, + "896" => 896, + "897" => 897, + "898" => 898, + "899" => 899, + "900" => 900, + "901" => 901, + "902" => 902, + "903" => 903, + "904" => 904, + "905" => 905, + "906" => 906, + "907" => 907, + "908" => 908, + "909" => 909, + "910" => 910, + "911" => 911, + "912" => 912, + "913" => 913, + "914" => 914, + "915" => 915, + "916" => 916, + "917" => 917, + "918" => 918, + "919" => 919, + "920" => 920, + "921" => 921, + "922" => 922, + "923" => 923, + "924" => 924, + "925" => 925, + "926" => 926, + "927" => 927, + "928" => 928, + "929" => 929, + "930" => 930, + "931" => 931, + "932" => 932, + "933" => 933, + "934" => 934, + "935" => 935, + "936" => 936, + "937" => 937, + "938" => 938, + "939" => 939, + "940" => 940, + "941" => 941, + "942" => 942, + "943" => 943, + "944" => 944, + "945" => 945, + "946" => 946, + "947" => 947, + "948" => 948, + "949" => 949, + "950" => 950, + "951" => 951, + "952" => 952, + "953" => 953, + "954" => 954, + "955" => 955, + "956" => 956, + "957" => 957, + "958" => 958, + "959" => 959, + "960" => 960, + "961" => 961, + "962" => 962, + "963" => 963, + "964" => 964, + "965" => 965, + "966" => 966, + "967" => 967, + "968" => 968, + "969" => 969, + "970" => 970, + "971" => 971, + "972" => 972, + "973" => 973, + "974" => 974, + "975" => 975, + "976" => 976, + "977" => 977, + "978" => 978, + "979" => 979, + "980" => 980, + "981" => 981, + "982" => 982, + "983" => 983, + "984" => 984, + "985" => 985, + "986" => 986, + "987" => 987, + "988" => 988, + "989" => 989, + "990" => 990, + "991" => 991, + "992" => 992, + "993" => 993, + "994" => 994, + "995" => 995, + "996" => 996, + "997" => 997, + "998" => 998, + "999" => 999, + "1000" => 1000, + "1001" => 1001, + "1002" => 1002, + "1003" => 1003, + "1004" => 1004, + "1005" => 1005, + "1006" => 1006, + "1007" => 1007, + "1008" => 1008, + "1009" => 1009, + "1010" => 1010, + "1011" => 1011, + "1012" => 1012, + "1013" => 1013, + "1014" => 1014, + "1015" => 1015, + "1016" => 1016, + "1017" => 1017, + "1018" => 1018, + "1019" => 1019, + "1020" => 1020, + "1021" => 1021, + "1022" => 1022, + "1023" => 1023, + "1024" => 1024, + "1025" => 1025, + "1026" => 1026, + "1027" => 1027, + "1028" => 1028, + "1029" => 1029, + "1030" => 1030, + "1031" => 1031, + "1032" => 1032, + "1033" => 1033, + "1034" => 1034, + "1035" => 1035, + "1036" => 1036, + "1037" => 1037, + "1038" => 1038, + "1039" => 1039, + "1040" => 1040, + "1041" => 1041, + "1042" => 1042, + "1043" => 1043, + "1044" => 1044, + "1045" => 1045, + "1046" => 1046, + "1047" => 1047, + "1048" => 1048, + "1049" => 1049, + "1050" => 1050, + "1051" => 1051, + "1052" => 1052, + "1053" => 1053, + "1054" => 1054, + "1055" => 1055, + "1056" => 1056, + "1057" => 1057, + "1058" => 1058, + "1059" => 1059, + "1060" => 1060, + "1061" => 1061, + "1062" => 1062, + "1063" => 1063, + "1064" => 1064, + "1065" => 1065, + "1066" => 1066, + "1067" => 1067, + "1068" => 1068, + "1069" => 1069, + "1070" => 1070, + "1071" => 1071, + "1072" => 1072, + "1073" => 1073, + "1074" => 1074, + "1075" => 1075, + "1076" => 1076, + "1077" => 1077, + "1078" => 1078, + "1079" => 1079, + "1080" => 1080, + "1081" => 1081, + "1082" => 1082, + "1083" => 1083, + "1084" => 1084, + "1085" => 1085, + "1086" => 1086, + "1087" => 1087, + "1088" => 1088, + "1089" => 1089, + "1090" => 1090, + "1091" => 1091, + "1092" => 1092, + "1093" => 1093, + "1094" => 1094, + "1095" => 1095, + "1096" => 1096, + "1097" => 1097, + "1098" => 1098, + "1099" => 1099, + "1100" => 1100, + "1101" => 1101, + "1102" => 1102, + "1103" => 1103, + "1104" => 1104, + "1105" => 1105, + "1106" => 1106, + "1107" => 1107, + "1108" => 1108, + "1109" => 1109, + "1110" => 1110, + "1111" => 1111, + "1112" => 1112, + "1113" => 1113, + "1114" => 1114, + "1115" => 1115, + "1116" => 1116, + "1117" => 1117, + "1118" => 1118, + "1119" => 1119, + "1120" => 1120, + "1121" => 1121, + "1122" => 1122, + "1123" => 1123, + "1124" => 1124, + "1125" => 1125, + "1126" => 1126, + "1127" => 1127, + "1128" => 1128, + "1129" => 1129, + "1130" => 1130, + "1131" => 1131, + "1132" => 1132, + "1133" => 1133, + "1134" => 1134, + "1135" => 1135, + "1136" => 1136, + "1137" => 1137, + "1138" => 1138, + "1139" => 1139, + "1140" => 1140, + "1141" => 1141, + "1142" => 1142, + "1143" => 1143, + "1144" => 1144, + "1145" => 1145, + "1146" => 1146, + "1147" => 1147, + "1148" => 1148, + "1149" => 1149, + "1150" => 1150, + "1151" => 1151, + "1152" => 1152, + "1153" => 1153, + "1154" => 1154, + "1155" => 1155, + "1156" => 1156, + "1157" => 1157, + "1158" => 1158, + "1159" => 1159, + "1160" => 1160, + "1161" => 1161, + "1162" => 1162, + "1163" => 1163, + "1164" => 1164, + "1165" => 1165, + "1166" => 1166, + "1167" => 1167, + "1168" => 1168, + "1169" => 1169, + "1170" => 1170, + "1171" => 1171, + "1172" => 1172, + "1173" => 1173, + "1174" => 1174, + "1175" => 1175, + "1176" => 1176, + "1177" => 1177, + "1178" => 1178, + "1179" => 1179, + "1180" => 1180, + "1181" => 1181, + "1182" => 1182, + "1183" => 1183, + "1184" => 1184, + "1185" => 1185, + "1186" => 1186, + "1187" => 1187, + "1188" => 1188, + "1189" => 1189, + "1190" => 1190, + "1191" => 1191, + "1192" => 1192, + "1193" => 1193, + "1194" => 1194, + "1195" => 1195, + "1196" => 1196, + "1197" => 1197, + "1198" => 1198, + "1199" => 1199, + "1200" => 1200, + "1201" => 1201, + "1202" => 1202, + "1203" => 1203, + "1204" => 1204, + "1205" => 1205, + "1206" => 1206, + "1207" => 1207, + "1208" => 1208, + "1209" => 1209, + "1210" => 1210, + "1211" => 1211, + "1212" => 1212, + "1213" => 1213, + "1214" => 1214, + "1215" => 1215, + "1216" => 1216, + "1217" => 1217, + "1218" => 1218, + "1219" => 1219, + "1220" => 1220, + "1221" => 1221, + "1222" => 1222, + "1223" => 1223, + "1224" => 1224, + "1225" => 1225, + "1226" => 1226, + "1227" => 1227, + "1228" => 1228, + "1229" => 1229, + "1230" => 1230, + "1231" => 1231, + "1232" => 1232, + "1233" => 1233, + "1234" => 1234, + "1235" => 1235, + "1236" => 1236, + "1237" => 1237, + "1238" => 1238, + "1239" => 1239, + "1240" => 1240, + "1241" => 1241, + "1242" => 1242, + "1243" => 1243, + "1244" => 1244, + "1245" => 1245, + "1246" => 1246, + "1247" => 1247, + "1248" => 1248, + "1249" => 1249, + "1250" => 1250, + "1251" => 1251, + "1252" => 1252, + "1253" => 1253, + "1254" => 1254, + "1255" => 1255, + "1256" => 1256, + "1257" => 1257, + "1258" => 1258, + "1259" => 1259, + "1260" => 1260, + "1261" => 1261, + "1262" => 1262, + "1263" => 1263, + "1264" => 1264, + "1265" => 1265, + "1266" => 1266, + "1267" => 1267, + "1268" => 1268, + "1269" => 1269, + "1270" => 1270, + "1271" => 1271, + "1272" => 1272, + "1273" => 1273, + "1274" => 1274, + "1275" => 1275, + "1276" => 1276, + "1277" => 1277, + "1278" => 1278, + "1279" => 1279, + "1280" => 1280, + "1281" => 1281, + "1282" => 1282, + "1283" => 1283, + "1284" => 1284, + "1285" => 1285, + "1286" => 1286, + "1287" => 1287, + "1288" => 1288, + "1289" => 1289, + "1290" => 1290, + "1291" => 1291, + "1292" => 1292, + "1293" => 1293, + "1294" => 1294, + "1295" => 1295, + "1296" => 1296, + "1297" => 1297, + "1298" => 1298, + "1299" => 1299, + "1300" => 1300, + "1301" => 1301, + "1302" => 1302, + "1303" => 1303, + "1304" => 1304, + "1305" => 1305, + "1306" => 1306, + "1307" => 1307, + "1308" => 1308, + "1309" => 1309, + "1310" => 1310, + "1311" => 1311, + "1312" => 1312, + "1313" => 1313, + "1314" => 1314, + "1315" => 1315, + "1316" => 1316, + "1317" => 1317, + "1318" => 1318, + "1319" => 1319, + "1320" => 1320, + "1321" => 1321, + "1322" => 1322, + "1323" => 1323, + "1324" => 1324, + "1325" => 1325, + "1326" => 1326, + "1327" => 1327, + "1328" => 1328, + "1329" => 1329, + "1330" => 1330, + "1331" => 1331, + "1332" => 1332, + "1333" => 1333, + "1334" => 1334, + "1335" => 1335, + "1336" => 1336, + "1337" => 1337, + "1338" => 1338, + "1339" => 1339, + "1340" => 1340, + "1341" => 1341, + "1342" => 1342, + "1343" => 1343, + "1344" => 1344, + "1345" => 1345, + "1346" => 1346, + "1347" => 1347, + "1348" => 1348, + "1349" => 1349, + "1350" => 1350, + "1351" => 1351, + "1352" => 1352, + "1353" => 1353, + "1354" => 1354, + "1355" => 1355, + "1356" => 1356, + "1357" => 1357, + "1358" => 1358, + "1359" => 1359, + "1360" => 1360, + "1361" => 1361, + "1362" => 1362, + "1363" => 1363, + "1364" => 1364, + "1365" => 1365, + "1366" => 1366, + "1367" => 1367, + "1368" => 1368, + "1369" => 1369, + "1370" => 1370, + "1371" => 1371, + "1372" => 1372, + "1373" => 1373, + "1374" => 1374, + "1375" => 1375, + "1376" => 1376, + "1377" => 1377, + "1378" => 1378, + "1379" => 1379, + "1380" => 1380, + "1381" => 1381, + "1382" => 1382, + "1383" => 1383, + "1384" => 1384, + "1385" => 1385, + "1386" => 1386, + "1387" => 1387, + "1388" => 1388, + "1389" => 1389, + "1390" => 1390, + "1391" => 1391, + "1392" => 1392, + "1393" => 1393, + "1394" => 1394, + "1395" => 1395, + "1396" => 1396, + "1397" => 1397, + "1398" => 1398, + "1399" => 1399, + "1400" => 1400, + "1401" => 1401, + "1402" => 1402, + "1403" => 1403, + "1404" => 1404, + "1405" => 1405, + "1406" => 1406, + "1407" => 1407, + "1408" => 1408, + "1409" => 1409, + "1410" => 1410, + "1411" => 1411, + "1412" => 1412, + "1413" => 1413, + "1414" => 1414, + "1415" => 1415, + "1416" => 1416, + "1417" => 1417, + "1418" => 1418, + "1419" => 1419, + "1420" => 1420, + "1421" => 1421, + "1422" => 1422, + "1423" => 1423, + "1424" => 1424, + "1425" => 1425, + "1426" => 1426, + "1427" => 1427, + "1428" => 1428, + "1429" => 1429, + "1430" => 1430, + "1431" => 1431, + "1432" => 1432, + "1433" => 1433, + "1434" => 1434, + "1435" => 1435, + "1436" => 1436, + "1437" => 1437, + "1438" => 1438, + "1439" => 1439, + "1440" => 1440, + "1441" => 1441, + "1442" => 1442, + "1443" => 1443, + "1444" => 1444, + "1445" => 1445, + "1446" => 1446, + "1447" => 1447, + "1448" => 1448, + "1449" => 1449, + "1450" => 1450, + "1451" => 1451, + "1452" => 1452, + "1453" => 1453, + "1454" => 1454, + "1455" => 1455, + "1456" => 1456, + "1457" => 1457, + "1458" => 1458, + "1459" => 1459, + "1460" => 1460, + "1461" => 1461, + "1462" => 1462, + "1463" => 1463, + "1464" => 1464, + "1465" => 1465, + "1466" => 1466, + "1467" => 1467, + "1468" => 1468, + "1469" => 1469, + "1470" => 1470, + "1471" => 1471, + "1472" => 1472, + "1473" => 1473, + "1474" => 1474, + "1475" => 1475, + "1476" => 1476, + "1477" => 1477, + "1478" => 1478, + "1479" => 1479, + "1480" => 1480, + "1481" => 1481, + "1482" => 1482, + "1483" => 1483, + "1484" => 1484, + "1485" => 1485, + "1486" => 1486, + "1487" => 1487, + "1488" => 1488, + "1489" => 1489, + "1490" => 1490, + "1491" => 1491, + "1492" => 1492, + "1493" => 1493, + "1494" => 1494, + "1495" => 1495, + "1496" => 1496, + "1497" => 1497, + "1498" => 1498, + "1499" => 1499, + "1500" => 1500, + "1501" => 1501, + "1502" => 1502, + "1503" => 1503, + "1504" => 1504, + "1505" => 1505, + "1506" => 1506, + "1507" => 1507, + "1508" => 1508, + "1509" => 1509, + "1510" => 1510, + "1511" => 1511, + "1512" => 1512, + "1513" => 1513, + "1514" => 1514, + "1515" => 1515, + "1516" => 1516, + "1517" => 1517, + "1518" => 1518, + "1519" => 1519, + "1520" => 1520, + "1521" => 1521, + "1522" => 1522, + "1523" => 1523, + "1524" => 1524, + "1525" => 1525, + "1526" => 1526, + "1527" => 1527, + "1528" => 1528, + "1529" => 1529, + "1530" => 1530, + "1531" => 1531, + "1532" => 1532, + "1533" => 1533, + "1534" => 1534, + "1535" => 1535, + "1536" => 1536, + "1537" => 1537, + "1538" => 1538, + "1539" => 1539, + "1540" => 1540, + "1541" => 1541, + "1542" => 1542, + "1543" => 1543, + "1544" => 1544, + "1545" => 1545, + "1546" => 1546, + "1547" => 1547, + "1548" => 1548, + "1549" => 1549, + "1550" => 1550, + "1551" => 1551, + "1552" => 1552, + "1553" => 1553, + "1554" => 1554, + "1555" => 1555, + "1556" => 1556, + "1557" => 1557, + "1558" => 1558, + "1559" => 1559, + "1560" => 1560, + "1561" => 1561, + "1562" => 1562, + "1563" => 1563, + "1564" => 1564, + "1565" => 1565, + "1566" => 1566, + "1567" => 1567, + "1568" => 1568, + "1569" => 1569, + "1570" => 1570, + "1571" => 1571, + "1572" => 1572, + "1573" => 1573, + "1574" => 1574, + "1575" => 1575, + "1576" => 1576, + "1577" => 1577, + "1578" => 1578, + "1579" => 1579, + "1580" => 1580, + "1581" => 1581, + "1582" => 1582, + "1583" => 1583, + "1584" => 1584, + "1585" => 1585, + "1586" => 1586, + "1587" => 1587, + "1588" => 1588, + "1589" => 1589, + "1590" => 1590, + "1591" => 1591, + "1592" => 1592, + "1593" => 1593, + "1594" => 1594, + "1595" => 1595, + "1596" => 1596, + "1597" => 1597, + "1598" => 1598, + "1599" => 1599, + "1600" => 1600, + "1601" => 1601, + "1602" => 1602, + "1603" => 1603, + "1604" => 1604, + "1605" => 1605, + "1606" => 1606, + "1607" => 1607, + "1608" => 1608, + "1609" => 1609, + "1610" => 1610, + "1611" => 1611, + "1612" => 1612, + "1613" => 1613, + "1614" => 1614, + "1615" => 1615, + "1616" => 1616, + "1617" => 1617, + "1618" => 1618, + "1619" => 1619, + "1620" => 1620, + "1621" => 1621, + "1622" => 1622, + "1623" => 1623, + "1624" => 1624, + "1625" => 1625, + "1626" => 1626, + "1627" => 1627, + "1628" => 1628, + "1629" => 1629, + "1630" => 1630, + "1631" => 1631, + "1632" => 1632, + "1633" => 1633, + "1634" => 1634, + "1635" => 1635, + "1636" => 1636, + "1637" => 1637, + "1638" => 1638, + "1639" => 1639, + "1640" => 1640, + "1641" => 1641, + "1642" => 1642, + "1643" => 1643, + "1644" => 1644, + "1645" => 1645, + "1646" => 1646, + "1647" => 1647, + "1648" => 1648, + "1649" => 1649, + "1650" => 1650, + "1651" => 1651, + "1652" => 1652, + "1653" => 1653, + "1654" => 1654, + "1655" => 1655, + "1656" => 1656, + "1657" => 1657, + "1658" => 1658, + "1659" => 1659, + "1660" => 1660, + "1661" => 1661, + "1662" => 1662, + "1663" => 1663, + "1664" => 1664, + "1665" => 1665, + "1666" => 1666, + "1667" => 1667, + "1668" => 1668, + "1669" => 1669, + "1670" => 1670, + "1671" => 1671, + "1672" => 1672, + "1673" => 1673, + "1674" => 1674, + "1675" => 1675, + "1676" => 1676, + "1677" => 1677, + "1678" => 1678, + "1679" => 1679, + "1680" => 1680, + "1681" => 1681, + "1682" => 1682, + "1683" => 1683, + "1684" => 1684, + "1685" => 1685, + "1686" => 1686, + "1687" => 1687, + "1688" => 1688, + "1689" => 1689, + "1690" => 1690, + "1691" => 1691, + "1692" => 1692, + "1693" => 1693, + "1694" => 1694, + "1695" => 1695, + "1696" => 1696, + "1697" => 1697, + "1698" => 1698, + "1699" => 1699, + "1700" => 1700, + "1701" => 1701, + "1702" => 1702, + "1703" => 1703, + "1704" => 1704, + "1705" => 1705, + "1706" => 1706, + "1707" => 1707, + "1708" => 1708, + "1709" => 1709, + "1710" => 1710, + "1711" => 1711, + "1712" => 1712, + "1713" => 1713, + "1714" => 1714, + "1715" => 1715, + "1716" => 1716, + "1717" => 1717, + "1718" => 1718, + "1719" => 1719, + "1720" => 1720, + "1721" => 1721, + "1722" => 1722, + "1723" => 1723, + "1724" => 1724, + "1725" => 1725, + "1726" => 1726, + "1727" => 1727, + "1728" => 1728, + "1729" => 1729, + "1730" => 1730, + "1731" => 1731, + "1732" => 1732, + "1733" => 1733, + "1734" => 1734, + "1735" => 1735, + "1736" => 1736, + "1737" => 1737, + "1738" => 1738, + "1739" => 1739, + "1740" => 1740, + "1741" => 1741, + "1742" => 1742, + "1743" => 1743, + "1744" => 1744, + "1745" => 1745, + "1746" => 1746, + "1747" => 1747, + "1748" => 1748, + "1749" => 1749, + "1750" => 1750, + "1751" => 1751, + "1752" => 1752, + "1753" => 1753, + "1754" => 1754, + "1755" => 1755, + "1756" => 1756, + "1757" => 1757, + "1758" => 1758, + "1759" => 1759, + "1760" => 1760, + "1761" => 1761, + "1762" => 1762, + "1763" => 1763, + "1764" => 1764, + "1765" => 1765, + "1766" => 1766, + "1767" => 1767, + "1768" => 1768, + "1769" => 1769, + "1770" => 1770, + "1771" => 1771, + "1772" => 1772, + "1773" => 1773, + "1774" => 1774, + "1775" => 1775, + "1776" => 1776, + "1777" => 1777, + "1778" => 1778, + "1779" => 1779, + "1780" => 1780, + "1781" => 1781, + "1782" => 1782, + "1783" => 1783, + "1784" => 1784, + "1785" => 1785, + "1786" => 1786, + "1787" => 1787, + "1788" => 1788, + "1789" => 1789, + "1790" => 1790, + "1791" => 1791, + "1792" => 1792, + "1793" => 1793, + "1794" => 1794, + "1795" => 1795, + "1796" => 1796, + "1797" => 1797, + "1798" => 1798, + "1799" => 1799, + "1800" => 1800, + "1801" => 1801, + "1802" => 1802, + "1803" => 1803, + "1804" => 1804, + "1805" => 1805, + "1806" => 1806, + "1807" => 1807, + "1808" => 1808, + "1809" => 1809, + "1810" => 1810, + "1811" => 1811, + "1812" => 1812, + "1813" => 1813, + "1814" => 1814, + "1815" => 1815, + "1816" => 1816, + "1817" => 1817, + "1818" => 1818, + "1819" => 1819, + "1820" => 1820, + "1821" => 1821, + "1822" => 1822, + "1823" => 1823, + "1824" => 1824, + "1825" => 1825, + "1826" => 1826, + "1827" => 1827, + "1828" => 1828, + "1829" => 1829, + "1830" => 1830, + "1831" => 1831, + "1832" => 1832, + "1833" => 1833, + "1834" => 1834, + "1835" => 1835, + "1836" => 1836, + "1837" => 1837, + "1838" => 1838, + "1839" => 1839, + "1840" => 1840, + "1841" => 1841, + "1842" => 1842, + "1843" => 1843, + "1844" => 1844, + "1845" => 1845, + "1846" => 1846, + "1847" => 1847, + "1848" => 1848, + "1849" => 1849, + "1850" => 1850, + "1851" => 1851, + "1852" => 1852, + "1853" => 1853, + "1854" => 1854, + "1855" => 1855, + "1856" => 1856, + "1857" => 1857, + "1858" => 1858, + "1859" => 1859, + "1860" => 1860, + "1861" => 1861, + "1862" => 1862, + "1863" => 1863, + "1864" => 1864, + "1865" => 1865, + "1866" => 1866, + "1867" => 1867, + "1868" => 1868, + "1869" => 1869, + "1870" => 1870, + "1871" => 1871, + "1872" => 1872, + "1873" => 1873, + "1874" => 1874, + "1875" => 1875, + "1876" => 1876, + "1877" => 1877, + "1878" => 1878, + "1879" => 1879, + "1880" => 1880, + "1881" => 1881, + "1882" => 1882, + "1883" => 1883, + "1884" => 1884, + "1885" => 1885, + "1886" => 1886, + "1887" => 1887, + "1888" => 1888, + "1889" => 1889, + "1890" => 1890, + "1891" => 1891, + "1892" => 1892, + "1893" => 1893, + "1894" => 1894, + "1895" => 1895, + "1896" => 1896, + "1897" => 1897, + "1898" => 1898, + "1899" => 1899, + "1900" => 1900, + "1901" => 1901, + "1902" => 1902, + "1903" => 1903, + "1904" => 1904, + "1905" => 1905, + "1906" => 1906, + "1907" => 1907, + "1908" => 1908, + "1909" => 1909, + "1910" => 1910, + "1911" => 1911, + "1912" => 1912, + "1913" => 1913, + "1914" => 1914, + "1915" => 1915, + "1916" => 1916, + "1917" => 1917, + "1918" => 1918, + "1919" => 1919, + "1920" => 1920, + "1921" => 1921, + "1922" => 1922, + "1923" => 1923, + "1924" => 1924, + "1925" => 1925, + "1926" => 1926, + "1927" => 1927, + "1928" => 1928, + "1929" => 1929, + "1930" => 1930, + "1931" => 1931, + "1932" => 1932, + "1933" => 1933, + "1934" => 1934, + "1935" => 1935, + "1936" => 1936, + "1937" => 1937, + "1938" => 1938, + "1939" => 1939, + "1940" => 1940, + "1941" => 1941, + "1942" => 1942, + "1943" => 1943, + "1944" => 1944, + "1945" => 1945, + "1946" => 1946, + "1947" => 1947, + "1948" => 1948, + "1949" => 1949, + "1950" => 1950, + "1951" => 1951, + "1952" => 1952, + "1953" => 1953, + "1954" => 1954, + "1955" => 1955, + "1956" => 1956, + "1957" => 1957, + "1958" => 1958, + "1959" => 1959, + "1960" => 1960, + "1961" => 1961, + "1962" => 1962, + "1963" => 1963, + "1964" => 1964, + "1965" => 1965, + "1966" => 1966, + "1967" => 1967, + "1968" => 1968, + "1969" => 1969, + "1970" => 1970, + "1971" => 1971, + "1972" => 1972, + "1973" => 1973, + "1974" => 1974, + "1975" => 1975, + "1976" => 1976, + "1977" => 1977, + "1978" => 1978, + "1979" => 1979, + "1980" => 1980, + "1981" => 1981, + "1982" => 1982, + "1983" => 1983, + "1984" => 1984, + "1985" => 1985, + "1986" => 1986, + "1987" => 1987, + "1988" => 1988, + "1989" => 1989, + "1990" => 1990, + "1991" => 1991, + "1992" => 1992, + "1993" => 1993, + "1994" => 1994, + "1995" => 1995, + "1996" => 1996, + "1997" => 1997, + "1998" => 1998, + "1999" => 1999, + "2000" => 2000, + "2001" => 2001, + "2002" => 2002, + "2003" => 2003, + "2004" => 2004, + "2005" => 2005, + "2006" => 2006, + "2007" => 2007, + "2008" => 2008, + "2009" => 2009, + "2010" => 2010, + "2011" => 2011, + "2012" => 2012, + "2013" => 2013, + "2014" => 2014, + "2015" => 2015, + "2016" => 2016, + "2017" => 2017, + "2018" => 2018, + "2019" => 2019, + "2020" => 2020, + "2021" => 2021, + "2022" => 2022, + "2023" => 2023, + "2024" => 2024, + "2025" => 2025, + "2026" => 2026, + "2027" => 2027, + "2028" => 2028, + "2029" => 2029, + "2030" => 2030, + "2031" => 2031, + "2032" => 2032, + "2033" => 2033, + "2034" => 2034, + "2035" => 2035, + "2036" => 2036, + "2037" => 2037, + "2038" => 2038, + "2039" => 2039, + "2040" => 2040, + "2041" => 2041, + "2042" => 2042, + "2043" => 2043, + "2044" => 2044, + "2045" => 2045, + "2046" => 2046, + "2047" => 2047, + "2048" => 2048, + "2049" => 2049, + "2050" => 2050, + "2051" => 2051, + "2052" => 2052, + "2053" => 2053, + "2054" => 2054, + "2055" => 2055, + "2056" => 2056, + "2057" => 2057, + "2058" => 2058, + "2059" => 2059, + "2060" => 2060, + "2061" => 2061, + "2062" => 2062, + "2063" => 2063, + "2064" => 2064, + "2065" => 2065, + "2066" => 2066, + "2067" => 2067, + "2068" => 2068, + "2069" => 2069, + "2070" => 2070, + "2071" => 2071, + "2072" => 2072, + "2073" => 2073, + "2074" => 2074, + "2075" => 2075, + "2076" => 2076, + "2077" => 2077, + "2078" => 2078, + "2079" => 2079, + "2080" => 2080, + "2081" => 2081, + "2082" => 2082, + "2083" => 2083, + "2084" => 2084, + "2085" => 2085, + "2086" => 2086, + "2087" => 2087, + "2088" => 2088, + "2089" => 2089, + "2090" => 2090, + "2091" => 2091, + "2092" => 2092, + "2093" => 2093, + "2094" => 2094, + "2095" => 2095, + "2096" => 2096, + "2097" => 2097, + "2098" => 2098, + "2099" => 2099, + "2100" => 2100, + "2101" => 2101, + "2102" => 2102, + "2103" => 2103, + "2104" => 2104, + "2105" => 2105, + "2106" => 2106, + "2107" => 2107, + "2108" => 2108, + "2109" => 2109, + "2110" => 2110, + "2111" => 2111, + "2112" => 2112, + "2113" => 2113, + "2114" => 2114, + "2115" => 2115, + "2116" => 2116, + "2117" => 2117, + "2118" => 2118, + "2119" => 2119, + "2120" => 2120, + "2121" => 2121, + "2122" => 2122, + "2123" => 2123, + "2124" => 2124, + "2125" => 2125, + "2126" => 2126, + "2127" => 2127, + "2128" => 2128, + "2129" => 2129, + "2130" => 2130, + "2131" => 2131, + "2132" => 2132, + "2133" => 2133, + "2134" => 2134, + "2135" => 2135, + "2136" => 2136, + "2137" => 2137, + "2138" => 2138, + "2139" => 2139, + "2140" => 2140, + "2141" => 2141, + "2142" => 2142, + "2143" => 2143, + "2144" => 2144, + "2145" => 2145, + "2146" => 2146, + "2147" => 2147, + "2148" => 2148, + "2149" => 2149, + "2150" => 2150, + "2151" => 2151, + "2152" => 2152, + "2153" => 2153, + "2154" => 2154, + "2155" => 2155, + "2156" => 2156, + "2157" => 2157, + "2158" => 2158, + "2159" => 2159, + "2160" => 2160, + "2161" => 2161, + "2162" => 2162, + "2163" => 2163, + "2164" => 2164, + "2165" => 2165, + "2166" => 2166, + "2167" => 2167, + "2168" => 2168, + "2169" => 2169, + "2170" => 2170, + "2171" => 2171, + "2172" => 2172, + "2173" => 2173, + "2174" => 2174, + "2175" => 2175, + "2176" => 2176, + "2177" => 2177, + "2178" => 2178, + "2179" => 2179, + "2180" => 2180, + "2181" => 2181, + "2182" => 2182, + "2183" => 2183, + "2184" => 2184, + "2185" => 2185, + "2186" => 2186, + "2187" => 2187, + "2188" => 2188, + "2189" => 2189, + "2190" => 2190, + "2191" => 2191, + "2192" => 2192, + "2193" => 2193, + "2194" => 2194, + "2195" => 2195, + "2196" => 2196, + "2197" => 2197, + "2198" => 2198, + "2199" => 2199, + "2200" => 2200, + "2201" => 2201, + "2202" => 2202, + "2203" => 2203, + "2204" => 2204, + "2205" => 2205, + "2206" => 2206, + "2207" => 2207, + "2208" => 2208, + "2209" => 2209, + "2210" => 2210, + "2211" => 2211, + "2212" => 2212, + "2213" => 2213, + "2214" => 2214, + "2215" => 2215, + "2216" => 2216, + "2217" => 2217, + "2218" => 2218, + "2219" => 2219, + "2220" => 2220, + "2221" => 2221, + "2222" => 2222, + "2223" => 2223, + "2224" => 2224, + "2225" => 2225, + "2226" => 2226, + "2227" => 2227, + "2228" => 2228, + "2229" => 2229, + "2230" => 2230, + "2231" => 2231, + "2232" => 2232, + "2233" => 2233, + "2234" => 2234, + "2235" => 2235, + "2236" => 2236, + "2237" => 2237, + "2238" => 2238, + "2239" => 2239, + "2240" => 2240, + "2241" => 2241, + "2242" => 2242, + "2243" => 2243, + "2244" => 2244, + "2245" => 2245, + "2246" => 2246, + "2247" => 2247, + "2248" => 2248, + "2249" => 2249, + "2250" => 2250, + "2251" => 2251, + "2252" => 2252, + "2253" => 2253, + "2254" => 2254, + "2255" => 2255, + "2256" => 2256, + "2257" => 2257, + "2258" => 2258, + "2259" => 2259, + "2260" => 2260, + "2261" => 2261, + "2262" => 2262, + "2263" => 2263, + "2264" => 2264, + "2265" => 2265, + "2266" => 2266, + "2267" => 2267, + "2268" => 2268, + "2269" => 2269, + "2270" => 2270, + "2271" => 2271, + "2272" => 2272, + "2273" => 2273, + "2274" => 2274, + "2275" => 2275, + "2276" => 2276, + "2277" => 2277, + "2278" => 2278, + "2279" => 2279, + "2280" => 2280, + "2281" => 2281, + "2282" => 2282, + "2283" => 2283, + "2284" => 2284, + "2285" => 2285, + "2286" => 2286, + "2287" => 2287, + "2288" => 2288, + "2289" => 2289, + "2290" => 2290, + "2291" => 2291, + "2292" => 2292, + "2293" => 2293, + "2294" => 2294, + "2295" => 2295, + "2296" => 2296, + "2297" => 2297, + "2298" => 2298, + "2299" => 2299, + "2300" => 2300, + "2301" => 2301, + "2302" => 2302, + "2303" => 2303, + "2304" => 2304, + "2305" => 2305, + "2306" => 2306, + "2307" => 2307, + "2308" => 2308, + "2309" => 2309, + "2310" => 2310, + "2311" => 2311, + "2312" => 2312, + "2313" => 2313, + "2314" => 2314, + "2315" => 2315, + "2316" => 2316, + "2317" => 2317, + "2318" => 2318, + "2319" => 2319, + "2320" => 2320, + "2321" => 2321, + "2322" => 2322, + "2323" => 2323, + "2324" => 2324, + "2325" => 2325, + "2326" => 2326, + "2327" => 2327, + "2328" => 2328, + "2329" => 2329, + "2330" => 2330, + "2331" => 2331, + "2332" => 2332, + "2333" => 2333, + "2334" => 2334, + "2335" => 2335, + "2336" => 2336, + "2337" => 2337, + "2338" => 2338, + "2339" => 2339, + "2340" => 2340, + "2341" => 2341, + "2342" => 2342, + "2343" => 2343, + "2344" => 2344, + "2345" => 2345, + "2346" => 2346, + "2347" => 2347, + "2348" => 2348, + "2349" => 2349, + "2350" => 2350, + "2351" => 2351, + "2352" => 2352, + "2353" => 2353, + "2354" => 2354, + "2355" => 2355, + "2356" => 2356, + "2357" => 2357, + "2358" => 2358, + "2359" => 2359, + "2360" => 2360, + "2361" => 2361, + "2362" => 2362, + "2363" => 2363, + "2364" => 2364, + "2365" => 2365, + "2366" => 2366, + "2367" => 2367, + "2368" => 2368, + "2369" => 2369, + "2370" => 2370, + "2371" => 2371, + "2372" => 2372, + "2373" => 2373, + "2374" => 2374, + "2375" => 2375, + "2376" => 2376, + "2377" => 2377, + "2378" => 2378, + "2379" => 2379, + "2380" => 2380, + "2381" => 2381, + "2382" => 2382, + "2383" => 2383, + "2384" => 2384, + "2385" => 2385, + "2386" => 2386, + "2387" => 2387, + "2388" => 2388, + "2389" => 2389, + "2390" => 2390, + "2391" => 2391, + "2392" => 2392, + "2393" => 2393, + "2394" => 2394, + "2395" => 2395, + "2396" => 2396, + "2397" => 2397, + "2398" => 2398, + "2399" => 2399, + "2400" => 2400, + "2401" => 2401, + "2402" => 2402, + "2403" => 2403, + "2404" => 2404, + "2405" => 2405, + "2406" => 2406, + "2407" => 2407, + "2408" => 2408, + "2409" => 2409, + "2410" => 2410, + "2411" => 2411, + "2412" => 2412, + "2413" => 2413, + "2414" => 2414, + "2415" => 2415, + "2416" => 2416, + "2417" => 2417, + "2418" => 2418, + "2419" => 2419, + "2420" => 2420, + "2421" => 2421, + "2422" => 2422, + "2423" => 2423, + "2424" => 2424, + "2425" => 2425, + "2426" => 2426, + "2427" => 2427, + "2428" => 2428, + "2429" => 2429, + "2430" => 2430, + "2431" => 2431, + "2432" => 2432, + "2433" => 2433, + "2434" => 2434, + "2435" => 2435, + "2436" => 2436, + "2437" => 2437, + "2438" => 2438, + "2439" => 2439, + "2440" => 2440, + "2441" => 2441, + "2442" => 2442, + "2443" => 2443, + "2444" => 2444, + "2445" => 2445, + "2446" => 2446, + "2447" => 2447, + "2448" => 2448, + "2449" => 2449, + "2450" => 2450, + "2451" => 2451, + "2452" => 2452, + "2453" => 2453, + "2454" => 2454, + "2455" => 2455, + "2456" => 2456, + "2457" => 2457, + "2458" => 2458, + "2459" => 2459, + "2460" => 2460, + "2461" => 2461, + "2462" => 2462, + "2463" => 2463, + "2464" => 2464, + "2465" => 2465, + "2466" => 2466, + "2467" => 2467, + "2468" => 2468, + "2469" => 2469, + "2470" => 2470, + "2471" => 2471, + "2472" => 2472, + "2473" => 2473, + "2474" => 2474, + "2475" => 2475, + "2476" => 2476, + "2477" => 2477, + "2478" => 2478, + "2479" => 2479, + "2480" => 2480, + "2481" => 2481, + "2482" => 2482, + "2483" => 2483, + "2484" => 2484, + "2485" => 2485, + "2486" => 2486, + "2487" => 2487, + "2488" => 2488, + "2489" => 2489, + "2490" => 2490, + "2491" => 2491, + "2492" => 2492, + "2493" => 2493, + "2494" => 2494, + "2495" => 2495, + "2496" => 2496, + "2497" => 2497, + "2498" => 2498, + "2499" => 2499, + "2500" => 2500, + "2501" => 2501, + "2502" => 2502, + "2503" => 2503, + "2504" => 2504, + "2505" => 2505, + "2506" => 2506, + "2507" => 2507, + "2508" => 2508, + "2509" => 2509, + "2510" => 2510, + "2511" => 2511, + "2512" => 2512, + "2513" => 2513, + "2514" => 2514, + "2515" => 2515, + "2516" => 2516, + "2517" => 2517, + "2518" => 2518, + "2519" => 2519, + "2520" => 2520, + "2521" => 2521, + "2522" => 2522, + "2523" => 2523, + "2524" => 2524, + "2525" => 2525, + "2526" => 2526, + "2527" => 2527, + "2528" => 2528, + "2529" => 2529, + "2530" => 2530, + "2531" => 2531, + "2532" => 2532, + "2533" => 2533, + "2534" => 2534, + "2535" => 2535, + "2536" => 2536, + "2537" => 2537, + "2538" => 2538, + "2539" => 2539, + "2540" => 2540, + "2541" => 2541, + "2542" => 2542, + "2543" => 2543, + "2544" => 2544, + "2545" => 2545, + "2546" => 2546, + "2547" => 2547, + "2548" => 2548, + "2549" => 2549, + "2550" => 2550, + "2551" => 2551, + "2552" => 2552, + "2553" => 2553, + "2554" => 2554, + "2555" => 2555, + "2556" => 2556, + "2557" => 2557, + "2558" => 2558, + "2559" => 2559, + "2560" => 2560, + "2561" => 2561, + "2562" => 2562, + "2563" => 2563, + "2564" => 2564, + "2565" => 2565, + "2566" => 2566, + "2567" => 2567, + "2568" => 2568, + "2569" => 2569, + "2570" => 2570, + "2571" => 2571, + "2572" => 2572, + "2573" => 2573, + "2574" => 2574, + "2575" => 2575, + "2576" => 2576, + "2577" => 2577, + "2578" => 2578, + "2579" => 2579, + "2580" => 2580, + "2581" => 2581, + "2582" => 2582, + "2583" => 2583, + "2584" => 2584, + "2585" => 2585, + "2586" => 2586, + "2587" => 2587, + "2588" => 2588, + "2589" => 2589, + "2590" => 2590, + "2591" => 2591, + "2592" => 2592, + "2593" => 2593, + "2594" => 2594, + "2595" => 2595, + "2596" => 2596, + "2597" => 2597, + "2598" => 2598, + "2599" => 2599, + "2600" => 2600, + "2601" => 2601, + "2602" => 2602, + "2603" => 2603, + "2604" => 2604, + "2605" => 2605, + "2606" => 2606, + "2607" => 2607, + "2608" => 2608, + "2609" => 2609, + "2610" => 2610, + "2611" => 2611, + "2612" => 2612, + "2613" => 2613, + "2614" => 2614, + "2615" => 2615, + "2616" => 2616, + "2617" => 2617, + "2618" => 2618, + "2619" => 2619, + "2620" => 2620, + "2621" => 2621, + "2622" => 2622, + "2623" => 2623, + "2624" => 2624, + "2625" => 2625, + "2626" => 2626, + "2627" => 2627, + "2628" => 2628, + "2629" => 2629, + "2630" => 2630, + "2631" => 2631, + "2632" => 2632, + "2633" => 2633, + "2634" => 2634, + "2635" => 2635, + "2636" => 2636, + "2637" => 2637, + "2638" => 2638, + "2639" => 2639, + "2640" => 2640, + "2641" => 2641, + "2642" => 2642, + "2643" => 2643, + "2644" => 2644, + "2645" => 2645, + "2646" => 2646, + "2647" => 2647, + "2648" => 2648, + "2649" => 2649, + "2650" => 2650, + "2651" => 2651, + "2652" => 2652, + "2653" => 2653, + "2654" => 2654, + "2655" => 2655, + "2656" => 2656, + "2657" => 2657, + "2658" => 2658, + "2659" => 2659, + "2660" => 2660, + "2661" => 2661, + "2662" => 2662, + "2663" => 2663, + "2664" => 2664, + "2665" => 2665, + "2666" => 2666, + "2667" => 2667, + "2668" => 2668, + "2669" => 2669, + "2670" => 2670, + "2671" => 2671, + "2672" => 2672, + "2673" => 2673, + "2674" => 2674, + "2675" => 2675, + "2676" => 2676, + "2677" => 2677, + "2678" => 2678, + "2679" => 2679, + "2680" => 2680, + "2681" => 2681, + "2682" => 2682, + "2683" => 2683, + "2684" => 2684, + "2685" => 2685, + "2686" => 2686, + "2687" => 2687, + "2688" => 2688, + "2689" => 2689, + "2690" => 2690, + "2691" => 2691, + "2692" => 2692, + "2693" => 2693, + "2694" => 2694, + "2695" => 2695, + "2696" => 2696, + "2697" => 2697, + "2698" => 2698, + "2699" => 2699, + "2700" => 2700, + "2701" => 2701, + "2702" => 2702, + "2703" => 2703, + "2704" => 2704, + "2705" => 2705, + "2706" => 2706, + "2707" => 2707, + "2708" => 2708, + "2709" => 2709, + "2710" => 2710, + "2711" => 2711, + "2712" => 2712, + "2713" => 2713, + "2714" => 2714, + "2715" => 2715, + "2716" => 2716, + "2717" => 2717, + "2718" => 2718, + "2719" => 2719, + "2720" => 2720, + "2721" => 2721, + "2722" => 2722, + "2723" => 2723, + "2724" => 2724, + "2725" => 2725, + "2726" => 2726, + "2727" => 2727, + "2728" => 2728, + "2729" => 2729, + "2730" => 2730, + "2731" => 2731, + "2732" => 2732, + "2733" => 2733, + "2734" => 2734, + "2735" => 2735, + "2736" => 2736, + "2737" => 2737, + "2738" => 2738, + "2739" => 2739, + "2740" => 2740, + "2741" => 2741, + "2742" => 2742, + "2743" => 2743, + "2744" => 2744, + "2745" => 2745, + "2746" => 2746, + "2747" => 2747, + "2748" => 2748, + "2749" => 2749, + "2750" => 2750, + "2751" => 2751, + "2752" => 2752, + "2753" => 2753, + "2754" => 2754, + "2755" => 2755, + "2756" => 2756, + "2757" => 2757, + "2758" => 2758, + "2759" => 2759, + "2760" => 2760, + "2761" => 2761, + "2762" => 2762, + "2763" => 2763, + "2764" => 2764, + "2765" => 2765, + "2766" => 2766, + "2767" => 2767, + "2768" => 2768, + "2769" => 2769, + "2770" => 2770, + "2771" => 2771, + "2772" => 2772, + "2773" => 2773, + "2774" => 2774, + "2775" => 2775, + "2776" => 2776, + "2777" => 2777, + "2778" => 2778, + "2779" => 2779, + "2780" => 2780, + "2781" => 2781, + "2782" => 2782, + "2783" => 2783, + "2784" => 2784, + "2785" => 2785, + "2786" => 2786, + "2787" => 2787, + "2788" => 2788, + "2789" => 2789, + "2790" => 2790, + "2791" => 2791, + "2792" => 2792, + "2793" => 2793, + "2794" => 2794, + "2795" => 2795, + "2796" => 2796, + "2797" => 2797, + "2798" => 2798, + "2799" => 2799, + "2800" => 2800, + "2801" => 2801, + "2802" => 2802, + "2803" => 2803, + "2804" => 2804, + "2805" => 2805, + "2806" => 2806, + "2807" => 2807, + "2808" => 2808, + "2809" => 2809, + "2810" => 2810, + "2811" => 2811, + "2812" => 2812, + "2813" => 2813, + "2814" => 2814, + "2815" => 2815, + "2816" => 2816, + "2817" => 2817, + "2818" => 2818, + "2819" => 2819, + "2820" => 2820, + "2821" => 2821, + "2822" => 2822, + "2823" => 2823, + "2824" => 2824, + "2825" => 2825, + "2826" => 2826, + "2827" => 2827, + "2828" => 2828, + "2829" => 2829, + "2830" => 2830, + "2831" => 2831, + "2832" => 2832, + "2833" => 2833, + "2834" => 2834, + "2835" => 2835, + "2836" => 2836, + "2837" => 2837, + "2838" => 2838, + "2839" => 2839, + "2840" => 2840, + "2841" => 2841, + "2842" => 2842, + "2843" => 2843, + "2844" => 2844, + "2845" => 2845, + "2846" => 2846, + "2847" => 2847, + "2848" => 2848, + "2849" => 2849, + "2850" => 2850, + "2851" => 2851, + "2852" => 2852, + "2853" => 2853, + "2854" => 2854, + "2855" => 2855, + "2856" => 2856, + "2857" => 2857, + "2858" => 2858, + "2859" => 2859, + "2860" => 2860, + "2861" => 2861, + "2862" => 2862, + "2863" => 2863, + "2864" => 2864, + "2865" => 2865, + "2866" => 2866, + "2867" => 2867, + "2868" => 2868, + "2869" => 2869, + "2870" => 2870, + "2871" => 2871, + "2872" => 2872, + "2873" => 2873, + "2874" => 2874, + "2875" => 2875, + "2876" => 2876, + "2877" => 2877, + "2878" => 2878, + "2879" => 2879, + "2880" => 2880, + "2881" => 2881, + "2882" => 2882, + "2883" => 2883, + "2884" => 2884, + "2885" => 2885, + "2886" => 2886, + "2887" => 2887, + "2888" => 2888, + "2889" => 2889, + "2890" => 2890, + "2891" => 2891, + "2892" => 2892, + "2893" => 2893, + "2894" => 2894, + "2895" => 2895, + "2896" => 2896, + "2897" => 2897, + "2898" => 2898, + "2899" => 2899, + "2900" => 2900, + "2901" => 2901, + "2902" => 2902, + "2903" => 2903, + "2904" => 2904, + "2905" => 2905, + "2906" => 2906, + "2907" => 2907, + "2908" => 2908, + "2909" => 2909, + "2910" => 2910, + "2911" => 2911, + "2912" => 2912, + "2913" => 2913, + "2914" => 2914, + "2915" => 2915, + "2916" => 2916, + "2917" => 2917, + "2918" => 2918, + "2919" => 2919, + "2920" => 2920, + "2921" => 2921, + "2922" => 2922, + "2923" => 2923, + "2924" => 2924, + "2925" => 2925, + "2926" => 2926, + "2927" => 2927, + "2928" => 2928, + "2929" => 2929, + "2930" => 2930, + "2931" => 2931, + "2932" => 2932, + "2933" => 2933, + "2934" => 2934, + "2935" => 2935, + "2936" => 2936, + "2937" => 2937, + "2938" => 2938, + "2939" => 2939, + "2940" => 2940, + "2941" => 2941, + "2942" => 2942, + "2943" => 2943, + "2944" => 2944, + "2945" => 2945, + "2946" => 2946, + "2947" => 2947, + "2948" => 2948, + "2949" => 2949, + "2950" => 2950, + "2951" => 2951, + "2952" => 2952, + "2953" => 2953, + "2954" => 2954, + "2955" => 2955, + "2956" => 2956, + "2957" => 2957, + "2958" => 2958, + "2959" => 2959, + "2960" => 2960, + "2961" => 2961, + "2962" => 2962, + "2963" => 2963, + "2964" => 2964, + "2965" => 2965, + "2966" => 2966, + "2967" => 2967, + "2968" => 2968, + "2969" => 2969, + "2970" => 2970, + "2971" => 2971, + "2972" => 2972, + "2973" => 2973, + "2974" => 2974, + "2975" => 2975, + "2976" => 2976, + "2977" => 2977, + "2978" => 2978, + "2979" => 2979, + "2980" => 2980, + "2981" => 2981, + "2982" => 2982, + "2983" => 2983, + "2984" => 2984, + "2985" => 2985, + "2986" => 2986, + "2987" => 2987, + "2988" => 2988, + "2989" => 2989, + "2990" => 2990, + "2991" => 2991, + "2992" => 2992, + "2993" => 2993, + "2994" => 2994, + "2995" => 2995, + "2996" => 2996, + "2997" => 2997, + "2998" => 2998, + "2999" => 2999, + "3000" => 3000, + "3001" => 3001, + "3002" => 3002, + "3003" => 3003, + "3004" => 3004, + "3005" => 3005, + "3006" => 3006, + "3007" => 3007, + "3008" => 3008, + "3009" => 3009, + "3010" => 3010, + "3011" => 3011, + "3012" => 3012, + "3013" => 3013, + "3014" => 3014, + "3015" => 3015, + "3016" => 3016, + "3017" => 3017, + "3018" => 3018, + "3019" => 3019, + "3020" => 3020, + "3021" => 3021, + "3022" => 3022, + "3023" => 3023, + "3024" => 3024, + "3025" => 3025, + "3026" => 3026, + "3027" => 3027, + "3028" => 3028, + "3029" => 3029, + "3030" => 3030, + "3031" => 3031, + "3032" => 3032, + "3033" => 3033, + "3034" => 3034, + "3035" => 3035, + "3036" => 3036, + "3037" => 3037, + "3038" => 3038, + "3039" => 3039, + "3040" => 3040, + "3041" => 3041, + "3042" => 3042, + "3043" => 3043, + "3044" => 3044, + "3045" => 3045, + "3046" => 3046, + "3047" => 3047, + "3048" => 3048, + "3049" => 3049, + "3050" => 3050, + "3051" => 3051, + "3052" => 3052, + "3053" => 3053, + "3054" => 3054, + "3055" => 3055, + "3056" => 3056, + "3057" => 3057, + "3058" => 3058, + "3059" => 3059, + "3060" => 3060, + "3061" => 3061, + "3062" => 3062, + "3063" => 3063, + "3064" => 3064, + "3065" => 3065, + "3066" => 3066, + "3067" => 3067, + "3068" => 3068, + "3069" => 3069, + "3070" => 3070, + "3071" => 3071, + "3072" => 3072, + "3073" => 3073, + "3074" => 3074, + "3075" => 3075, + "3076" => 3076, + "3077" => 3077, + "3078" => 3078, + "3079" => 3079, + "3080" => 3080, + "3081" => 3081, + "3082" => 3082, + "3083" => 3083, + "3084" => 3084, + "3085" => 3085, + "3086" => 3086, + "3087" => 3087, + "3088" => 3088, + "3089" => 3089, + "3090" => 3090, + "3091" => 3091, + "3092" => 3092, + "3093" => 3093, + "3094" => 3094, + "3095" => 3095, + "3096" => 3096, + "3097" => 3097, + "3098" => 3098, + "3099" => 3099, + "3100" => 3100, + "3101" => 3101, + "3102" => 3102, + "3103" => 3103, + "3104" => 3104, + "3105" => 3105, + "3106" => 3106, + "3107" => 3107, + "3108" => 3108, + "3109" => 3109, + "3110" => 3110, + "3111" => 3111, + "3112" => 3112, + "3113" => 3113, + "3114" => 3114, + "3115" => 3115, + "3116" => 3116, + "3117" => 3117, + "3118" => 3118, + "3119" => 3119, + "3120" => 3120, + "3121" => 3121, + "3122" => 3122, + "3123" => 3123, + "3124" => 3124, + "3125" => 3125, + "3126" => 3126, + "3127" => 3127, + "3128" => 3128, + "3129" => 3129, + "3130" => 3130, + "3131" => 3131, + "3132" => 3132, + "3133" => 3133, + "3134" => 3134, + "3135" => 3135, + "3136" => 3136, + "3137" => 3137, + "3138" => 3138, + "3139" => 3139, + "3140" => 3140, + "3141" => 3141, + "3142" => 3142, + "3143" => 3143, + "3144" => 3144, + "3145" => 3145, + "3146" => 3146, + "3147" => 3147, + "3148" => 3148, + "3149" => 3149, + "3150" => 3150, + "3151" => 3151, + "3152" => 3152, + "3153" => 3153, + "3154" => 3154, + "3155" => 3155, + "3156" => 3156, + "3157" => 3157, + "3158" => 3158, + "3159" => 3159, + "3160" => 3160, + "3161" => 3161, + "3162" => 3162, + "3163" => 3163, + "3164" => 3164, + "3165" => 3165, + "3166" => 3166, + "3167" => 3167, + "3168" => 3168, + "3169" => 3169, + "3170" => 3170, + "3171" => 3171, + "3172" => 3172, + "3173" => 3173, + "3174" => 3174, + "3175" => 3175, + "3176" => 3176, + "3177" => 3177, + "3178" => 3178, + "3179" => 3179, + "3180" => 3180, + "3181" => 3181, + "3182" => 3182, + "3183" => 3183, + "3184" => 3184, + "3185" => 3185, + "3186" => 3186, + "3187" => 3187, + "3188" => 3188, + "3189" => 3189, + "3190" => 3190, + "3191" => 3191, + "3192" => 3192, + "3193" => 3193, + "3194" => 3194, + "3195" => 3195, + "3196" => 3196, + "3197" => 3197, + "3198" => 3198, + "3199" => 3199, + "3200" => 3200, + "3201" => 3201, + "3202" => 3202, + "3203" => 3203, + "3204" => 3204, + "3205" => 3205, + "3206" => 3206, + "3207" => 3207, + "3208" => 3208, + "3209" => 3209, + "3210" => 3210, + "3211" => 3211, + "3212" => 3212, + "3213" => 3213, + "3214" => 3214, + "3215" => 3215, + "3216" => 3216, + "3217" => 3217, + "3218" => 3218, + "3219" => 3219, + "3220" => 3220, + "3221" => 3221, + "3222" => 3222, + "3223" => 3223, + "3224" => 3224, + "3225" => 3225, + "3226" => 3226, + "3227" => 3227, + "3228" => 3228, + "3229" => 3229, + "3230" => 3230, + "3231" => 3231, + "3232" => 3232, + "3233" => 3233, + "3234" => 3234, + "3235" => 3235, + "3236" => 3236, + "3237" => 3237, + "3238" => 3238, + "3239" => 3239, + "3240" => 3240, + "3241" => 3241, + "3242" => 3242, + "3243" => 3243, + "3244" => 3244, + "3245" => 3245, + "3246" => 3246, + "3247" => 3247, + "3248" => 3248, + "3249" => 3249, + "3250" => 3250, + "3251" => 3251, + "3252" => 3252, + "3253" => 3253, + "3254" => 3254, + "3255" => 3255, + "3256" => 3256, + "3257" => 3257, + "3258" => 3258, + "3259" => 3259, + "3260" => 3260, + "3261" => 3261, + "3262" => 3262, + "3263" => 3263, + "3264" => 3264, + "3265" => 3265, + "3266" => 3266, + "3267" => 3267, + "3268" => 3268, + "3269" => 3269, + "3270" => 3270, + "3271" => 3271, + "3272" => 3272, + "3273" => 3273, + "3274" => 3274, + "3275" => 3275, + "3276" => 3276, + "3277" => 3277, + "3278" => 3278, + "3279" => 3279, + "3280" => 3280, + "3281" => 3281, + "3282" => 3282, + "3283" => 3283, + "3284" => 3284, + "3285" => 3285, + "3286" => 3286, + "3287" => 3287, + "3288" => 3288, + "3289" => 3289, + "3290" => 3290, + "3291" => 3291, + "3292" => 3292, + "3293" => 3293, + "3294" => 3294, + "3295" => 3295, + "3296" => 3296, + "3297" => 3297, + "3298" => 3298, + "3299" => 3299, + "3300" => 3300, + "3301" => 3301, + "3302" => 3302, + "3303" => 3303, + "3304" => 3304, + "3305" => 3305, + "3306" => 3306, + "3307" => 3307, + "3308" => 3308, + "3309" => 3309, + "3310" => 3310, + "3311" => 3311, + "3312" => 3312, + "3313" => 3313, + "3314" => 3314, + "3315" => 3315, + "3316" => 3316, + "3317" => 3317, + "3318" => 3318, + "3319" => 3319, + "3320" => 3320, + "3321" => 3321, + "3322" => 3322, + "3323" => 3323, + "3324" => 3324, + "3325" => 3325, + "3326" => 3326, + "3327" => 3327, + "3328" => 3328, + "3329" => 3329, + "3330" => 3330, + "3331" => 3331, + "3332" => 3332, + "3333" => 3333, + "3334" => 3334, + "3335" => 3335, + "3336" => 3336, + "3337" => 3337, + "3338" => 3338, + "3339" => 3339, + "3340" => 3340, + "3341" => 3341, + "3342" => 3342, + "3343" => 3343, + "3344" => 3344, + "3345" => 3345, + "3346" => 3346, + "3347" => 3347, + "3348" => 3348, + "3349" => 3349, + "3350" => 3350, + "3351" => 3351, + "3352" => 3352, + "3353" => 3353, + "3354" => 3354, + "3355" => 3355, + "3356" => 3356, + "3357" => 3357, + "3358" => 3358, + "3359" => 3359, + "3360" => 3360, + "3361" => 3361, + "3362" => 3362, + "3363" => 3363, + "3364" => 3364, + "3365" => 3365, + "3366" => 3366, + "3367" => 3367, + "3368" => 3368, + "3369" => 3369, + "3370" => 3370, + "3371" => 3371, + "3372" => 3372, + "3373" => 3373, + "3374" => 3374, + "3375" => 3375, + "3376" => 3376, + "3377" => 3377, + "3378" => 3378, + "3379" => 3379, + "3380" => 3380, + "3381" => 3381, + "3382" => 3382, + "3383" => 3383, + "3384" => 3384, + "3385" => 3385, + "3386" => 3386, + "3387" => 3387, + "3388" => 3388, + "3389" => 3389, + "3390" => 3390, + "3391" => 3391, + "3392" => 3392, + "3393" => 3393, + "3394" => 3394, + "3395" => 3395, + "3396" => 3396, + "3397" => 3397, + "3398" => 3398, + "3399" => 3399, + "3400" => 3400, + "3401" => 3401, + "3402" => 3402, + "3403" => 3403, + "3404" => 3404, + "3405" => 3405, + "3406" => 3406, + "3407" => 3407, + "3408" => 3408, + "3409" => 3409, + "3410" => 3410, + "3411" => 3411, + "3412" => 3412, + "3413" => 3413, + "3414" => 3414, + "3415" => 3415, + "3416" => 3416, + "3417" => 3417, + "3418" => 3418, + "3419" => 3419, + "3420" => 3420, + "3421" => 3421, + "3422" => 3422, + "3423" => 3423, + "3424" => 3424, + "3425" => 3425, + "3426" => 3426, + "3427" => 3427, + "3428" => 3428, + "3429" => 3429, + "3430" => 3430, + "3431" => 3431, + "3432" => 3432, + "3433" => 3433, + "3434" => 3434, + "3435" => 3435, + "3436" => 3436, + "3437" => 3437, + "3438" => 3438, + "3439" => 3439, + "3440" => 3440, + "3441" => 3441, + "3442" => 3442, + "3443" => 3443, + "3444" => 3444, + "3445" => 3445, + "3446" => 3446, + "3447" => 3447, + "3448" => 3448, + "3449" => 3449, + "3450" => 3450, + "3451" => 3451, + "3452" => 3452, + "3453" => 3453, + "3454" => 3454, + "3455" => 3455, + "3456" => 3456, + "3457" => 3457, + "3458" => 3458, + "3459" => 3459, + "3460" => 3460, + "3461" => 3461, + "3462" => 3462, + "3463" => 3463, + "3464" => 3464, + "3465" => 3465, + "3466" => 3466, + "3467" => 3467, + "3468" => 3468, + "3469" => 3469, + "3470" => 3470, + "3471" => 3471, + "3472" => 3472, + "3473" => 3473, + "3474" => 3474, + "3475" => 3475, + "3476" => 3476, + "3477" => 3477, + "3478" => 3478, + "3479" => 3479, + "3480" => 3480, + "3481" => 3481, + "3482" => 3482, + "3483" => 3483, + "3484" => 3484, + "3485" => 3485, + "3486" => 3486, + "3487" => 3487, + "3488" => 3488, + "3489" => 3489, + "3490" => 3490, + "3491" => 3491, + "3492" => 3492, + "3493" => 3493, + "3494" => 3494, + "3495" => 3495, + "3496" => 3496, + "3497" => 3497, + "3498" => 3498, + "3499" => 3499, + "3500" => 3500, + "3501" => 3501, + "3502" => 3502, + "3503" => 3503, + "3504" => 3504, + "3505" => 3505, + "3506" => 3506, + "3507" => 3507, + "3508" => 3508, + "3509" => 3509, + "3510" => 3510, + "3511" => 3511, + "3512" => 3512, + "3513" => 3513, + "3514" => 3514, + "3515" => 3515, + "3516" => 3516, + "3517" => 3517, + "3518" => 3518, + "3519" => 3519, + "3520" => 3520, + "3521" => 3521, + "3522" => 3522, + "3523" => 3523, + "3524" => 3524, + "3525" => 3525, + "3526" => 3526, + "3527" => 3527, + "3528" => 3528, + "3529" => 3529, + "3530" => 3530, + "3531" => 3531, + "3532" => 3532, + "3533" => 3533, + "3534" => 3534, + "3535" => 3535, + "3536" => 3536, + "3537" => 3537, + "3538" => 3538, + "3539" => 3539, + "3540" => 3540, + "3541" => 3541, + "3542" => 3542, + "3543" => 3543, + "3544" => 3544, + "3545" => 3545, + "3546" => 3546, + "3547" => 3547, + "3548" => 3548, + "3549" => 3549, + "3550" => 3550, + "3551" => 3551, + "3552" => 3552, + "3553" => 3553, + "3554" => 3554, + "3555" => 3555, + "3556" => 3556, + "3557" => 3557, + "3558" => 3558, + "3559" => 3559, + "3560" => 3560, + "3561" => 3561, + "3562" => 3562, + "3563" => 3563, + "3564" => 3564, + "3565" => 3565, + "3566" => 3566, + "3567" => 3567, + "3568" => 3568, + "3569" => 3569, + "3570" => 3570, + "3571" => 3571, + "3572" => 3572, + "3573" => 3573, + "3574" => 3574, + "3575" => 3575, + "3576" => 3576, + "3577" => 3577, + "3578" => 3578, + "3579" => 3579, + "3580" => 3580, + "3581" => 3581, + "3582" => 3582, + "3583" => 3583, + "3584" => 3584, + "3585" => 3585, + "3586" => 3586, + "3587" => 3587, + "3588" => 3588, + "3589" => 3589, + "3590" => 3590, + "3591" => 3591, + "3592" => 3592, + "3593" => 3593, + "3594" => 3594, + "3595" => 3595, + "3596" => 3596, + "3597" => 3597, + "3598" => 3598, + "3599" => 3599, + "3600" => 3600, + "3601" => 3601, + "3602" => 3602, + "3603" => 3603, + "3604" => 3604, + "3605" => 3605, + "3606" => 3606, + "3607" => 3607, + "3608" => 3608, + "3609" => 3609, + "3610" => 3610, + "3611" => 3611, + "3612" => 3612, + "3613" => 3613, + "3614" => 3614, + "3615" => 3615, + "3616" => 3616, + "3617" => 3617, + "3618" => 3618, + "3619" => 3619, + "3620" => 3620, + "3621" => 3621, + "3622" => 3622, + "3623" => 3623, + "3624" => 3624, + "3625" => 3625, + "3626" => 3626, + "3627" => 3627, + "3628" => 3628, + "3629" => 3629, + "3630" => 3630, + "3631" => 3631, + "3632" => 3632, + "3633" => 3633, + "3634" => 3634, + "3635" => 3635, + "3636" => 3636, + "3637" => 3637, + "3638" => 3638, + "3639" => 3639, + "3640" => 3640, + "3641" => 3641, + "3642" => 3642, + "3643" => 3643, + "3644" => 3644, + "3645" => 3645, + "3646" => 3646, + "3647" => 3647, + "3648" => 3648, + "3649" => 3649, + "3650" => 3650, + "3651" => 3651, + "3652" => 3652, + "3653" => 3653, + "3654" => 3654, + "3655" => 3655, + "3656" => 3656, + "3657" => 3657, + "3658" => 3658, + "3659" => 3659, + "3660" => 3660, + "3661" => 3661, + "3662" => 3662, + "3663" => 3663, + "3664" => 3664, + "3665" => 3665, + "3666" => 3666, + "3667" => 3667, + "3668" => 3668, + "3669" => 3669, + "3670" => 3670, + "3671" => 3671, + "3672" => 3672, + "3673" => 3673, + "3674" => 3674, + "3675" => 3675, + "3676" => 3676, + "3677" => 3677, + "3678" => 3678, + "3679" => 3679, + "3680" => 3680, + "3681" => 3681, + "3682" => 3682, + "3683" => 3683, + "3684" => 3684, + "3685" => 3685, + "3686" => 3686, + "3687" => 3687, + "3688" => 3688, + "3689" => 3689, + "3690" => 3690, + "3691" => 3691, + "3692" => 3692, + "3693" => 3693, + "3694" => 3694, + "3695" => 3695, + "3696" => 3696, + "3697" => 3697, + "3698" => 3698, + "3699" => 3699, + "3700" => 3700, + "3701" => 3701, + "3702" => 3702, + "3703" => 3703, + "3704" => 3704, + "3705" => 3705, + "3706" => 3706, + "3707" => 3707, + "3708" => 3708, + "3709" => 3709, + "3710" => 3710, + "3711" => 3711, + "3712" => 3712, + "3713" => 3713, + "3714" => 3714, + "3715" => 3715, + "3716" => 3716, + "3717" => 3717, + "3718" => 3718, + "3719" => 3719, + "3720" => 3720, + "3721" => 3721, + "3722" => 3722, + "3723" => 3723, + "3724" => 3724, + "3725" => 3725, + "3726" => 3726, + "3727" => 3727, + "3728" => 3728, + "3729" => 3729, + "3730" => 3730, + "3731" => 3731, + "3732" => 3732, + "3733" => 3733, + "3734" => 3734, + "3735" => 3735, + "3736" => 3736, + "3737" => 3737, + "3738" => 3738, + "3739" => 3739, + "3740" => 3740, + "3741" => 3741, + "3742" => 3742, + "3743" => 3743, + "3744" => 3744, + "3745" => 3745, + "3746" => 3746, + "3747" => 3747, + "3748" => 3748, + "3749" => 3749, + "3750" => 3750, + "3751" => 3751, + "3752" => 3752, + "3753" => 3753, + "3754" => 3754, + "3755" => 3755, + "3756" => 3756, + "3757" => 3757, + "3758" => 3758, + "3759" => 3759, + "3760" => 3760, + "3761" => 3761, + "3762" => 3762, + "3763" => 3763, + "3764" => 3764, + "3765" => 3765, + "3766" => 3766, + "3767" => 3767, + "3768" => 3768, + "3769" => 3769, + "3770" => 3770, + "3771" => 3771, + "3772" => 3772, + "3773" => 3773, + "3774" => 3774, + "3775" => 3775, + "3776" => 3776, + "3777" => 3777, + "3778" => 3778, + "3779" => 3779, + "3780" => 3780, + "3781" => 3781, + "3782" => 3782, + "3783" => 3783, + "3784" => 3784, + "3785" => 3785, + "3786" => 3786, + "3787" => 3787, + "3788" => 3788, + "3789" => 3789, + "3790" => 3790, + "3791" => 3791, + "3792" => 3792, + "3793" => 3793, + "3794" => 3794, + "3795" => 3795, + "3796" => 3796, + "3797" => 3797, + "3798" => 3798, + "3799" => 3799, + "3800" => 3800, + "3801" => 3801, + "3802" => 3802, + "3803" => 3803, + "3804" => 3804, + "3805" => 3805, + "3806" => 3806, + "3807" => 3807, + "3808" => 3808, + "3809" => 3809, + "3810" => 3810, + "3811" => 3811, + "3812" => 3812, + "3813" => 3813, + "3814" => 3814, + "3815" => 3815, + "3816" => 3816, + "3817" => 3817, + "3818" => 3818, + "3819" => 3819, + "3820" => 3820, + "3821" => 3821, + "3822" => 3822, + "3823" => 3823, + "3824" => 3824, + "3825" => 3825, + "3826" => 3826, + "3827" => 3827, + "3828" => 3828, + "3829" => 3829, + "3830" => 3830, + "3831" => 3831, + "3832" => 3832, + "3833" => 3833, + "3834" => 3834, + "3835" => 3835, + "3836" => 3836, + "3837" => 3837, + "3838" => 3838, + "3839" => 3839, + "3840" => 3840, + "3841" => 3841, + "3842" => 3842, + "3843" => 3843, + "3844" => 3844, + "3845" => 3845, + "3846" => 3846, + "3847" => 3847, + "3848" => 3848, + "3849" => 3849, + "3850" => 3850, + "3851" => 3851, + "3852" => 3852, + "3853" => 3853, + "3854" => 3854, + "3855" => 3855, + "3856" => 3856, + "3857" => 3857, + "3858" => 3858, + "3859" => 3859, + "3860" => 3860, + "3861" => 3861, + "3862" => 3862, + "3863" => 3863, + "3864" => 3864, + "3865" => 3865, + "3866" => 3866, + "3867" => 3867, + "3868" => 3868, + "3869" => 3869, + "3870" => 3870, + "3871" => 3871, + "3872" => 3872, + "3873" => 3873, + "3874" => 3874, + "3875" => 3875, + "3876" => 3876, + "3877" => 3877, + "3878" => 3878, + "3879" => 3879, + "3880" => 3880, + "3881" => 3881, + "3882" => 3882, + "3883" => 3883, + "3884" => 3884, + "3885" => 3885, + "3886" => 3886, + "3887" => 3887, + "3888" => 3888, + "3889" => 3889, + "3890" => 3890, + "3891" => 3891, + "3892" => 3892, + "3893" => 3893, + "3894" => 3894, + "3895" => 3895, + "3896" => 3896, + "3897" => 3897, + "3898" => 3898, + "3899" => 3899, + "3900" => 3900, + "3901" => 3901, + "3902" => 3902, + "3903" => 3903, + "3904" => 3904, + "3905" => 3905, + "3906" => 3906, + "3907" => 3907, + "3908" => 3908, + "3909" => 3909, + "3910" => 3910, + "3911" => 3911, + "3912" => 3912, + "3913" => 3913, + "3914" => 3914, + "3915" => 3915, + "3916" => 3916, + "3917" => 3917, + "3918" => 3918, + "3919" => 3919, + "3920" => 3920, + "3921" => 3921, + "3922" => 3922, + "3923" => 3923, + "3924" => 3924, + "3925" => 3925, + "3926" => 3926, + "3927" => 3927, + "3928" => 3928, + "3929" => 3929, + "3930" => 3930, + "3931" => 3931, + "3932" => 3932, + "3933" => 3933, + "3934" => 3934, + "3935" => 3935, + "3936" => 3936, + "3937" => 3937, + "3938" => 3938, + "3939" => 3939, + "3940" => 3940, + "3941" => 3941, + "3942" => 3942, + "3943" => 3943, + "3944" => 3944, + "3945" => 3945, + "3946" => 3946, + "3947" => 3947, + "3948" => 3948, + "3949" => 3949, + "3950" => 3950, + "3951" => 3951, + "3952" => 3952, + "3953" => 3953, + "3954" => 3954, + "3955" => 3955, + "3956" => 3956, + "3957" => 3957, + "3958" => 3958, + "3959" => 3959, + "3960" => 3960, + "3961" => 3961, + "3962" => 3962, + "3963" => 3963, + "3964" => 3964, + "3965" => 3965, + "3966" => 3966, + "3967" => 3967, + "3968" => 3968, + "3969" => 3969, + "3970" => 3970, + "3971" => 3971, + "3972" => 3972, + "3973" => 3973, + "3974" => 3974, + "3975" => 3975, + "3976" => 3976, + "3977" => 3977, + "3978" => 3978, + "3979" => 3979, + "3980" => 3980, + "3981" => 3981, + "3982" => 3982, + "3983" => 3983, + "3984" => 3984, + "3985" => 3985, + "3986" => 3986, + "3987" => 3987, + "3988" => 3988, + "3989" => 3989, + "3990" => 3990, + "3991" => 3991, + "3992" => 3992, + "3993" => 3993, + "3994" => 3994, + "3995" => 3995, + "3996" => 3996, + "3997" => 3997, + "3998" => 3998, + "3999" => 3999, + "4000" => 4000, + "4001" => 4001, + "4002" => 4002, + "4003" => 4003, + "4004" => 4004, + "4005" => 4005, + "4006" => 4006, + "4007" => 4007, + "4008" => 4008, + "4009" => 4009, + "4010" => 4010, + "4011" => 4011, + "4012" => 4012, + "4013" => 4013, + "4014" => 4014, + "4015" => 4015, + "4016" => 4016, + "4017" => 4017, + "4018" => 4018, + "4019" => 4019, + "4020" => 4020, + "4021" => 4021, + "4022" => 4022, + "4023" => 4023, + "4024" => 4024, + "4025" => 4025, + "4026" => 4026, + "4027" => 4027, + "4028" => 4028, + "4029" => 4029, + "4030" => 4030, + "4031" => 4031, + "4032" => 4032, + "4033" => 4033, + "4034" => 4034, + "4035" => 4035, + "4036" => 4036, + "4037" => 4037, + "4038" => 4038, + "4039" => 4039, + "4040" => 4040, + "4041" => 4041, + "4042" => 4042, + "4043" => 4043, + "4044" => 4044, + "4045" => 4045, + "4046" => 4046, + "4047" => 4047, + "4048" => 4048, + "4049" => 4049, + "4050" => 4050, + "4051" => 4051, + "4052" => 4052, + "4053" => 4053, + "4054" => 4054, + "4055" => 4055, + "4056" => 4056, + "4057" => 4057, + "4058" => 4058, + "4059" => 4059, + "4060" => 4060, + "4061" => 4061, + "4062" => 4062, + "4063" => 4063, + "4064" => 4064, + "4065" => 4065, + "4066" => 4066, + "4067" => 4067, + "4068" => 4068, + "4069" => 4069, + "4070" => 4070, + "4071" => 4071, + "4072" => 4072, + "4073" => 4073, + "4074" => 4074, + "4075" => 4075, + "4076" => 4076, + "4077" => 4077, + "4078" => 4078, + "4079" => 4079, + "4080" => 4080, + "4081" => 4081, + "4082" => 4082, + "4083" => 4083, + "4084" => 4084, + "4085" => 4085, + "4086" => 4086, + "4087" => 4087, + "4088" => 4088, + "4089" => 4089, + "4090" => 4090, + "4091" => 4091, + "4092" => 4092, + "4093" => 4093, + "4094" => 4094, + "4095" => 4095, + "4096" => 4096, + "4097" => 4097, + "4098" => 4098, + "4099" => 4099, + "4100" => 4100, + "4101" => 4101, + "4102" => 4102, + "4103" => 4103, + "4104" => 4104, + "4105" => 4105, + "4106" => 4106, + "4107" => 4107, + "4108" => 4108, + "4109" => 4109, + "4110" => 4110, + "4111" => 4111, + "4112" => 4112, + "4113" => 4113, + "4114" => 4114, + "4115" => 4115, + "4116" => 4116, + "4117" => 4117, + "4118" => 4118, + "4119" => 4119, + "4120" => 4120, + "4121" => 4121, + "4122" => 4122, + "4123" => 4123, + "4124" => 4124, + "4125" => 4125, + "4126" => 4126, + "4127" => 4127, + "4128" => 4128, + "4129" => 4129, + "4130" => 4130, + "4131" => 4131, + "4132" => 4132, + "4133" => 4133, + "4134" => 4134, + "4135" => 4135, + "4136" => 4136, + "4137" => 4137, + "4138" => 4138, + "4139" => 4139, + "4140" => 4140, + "4141" => 4141, + "4142" => 4142, + "4143" => 4143, + "4144" => 4144, + "4145" => 4145, + "4146" => 4146, + "4147" => 4147, + "4148" => 4148, + "4149" => 4149, + "4150" => 4150, + "4151" => 4151, + "4152" => 4152, + "4153" => 4153, + "4154" => 4154, + "4155" => 4155, + "4156" => 4156, + "4157" => 4157, + "4158" => 4158, + "4159" => 4159, + "4160" => 4160, + "4161" => 4161, + "4162" => 4162, + "4163" => 4163, + "4164" => 4164, + "4165" => 4165, + "4166" => 4166, + "4167" => 4167, + "4168" => 4168, + "4169" => 4169, + "4170" => 4170, + "4171" => 4171, + "4172" => 4172, + "4173" => 4173, + "4174" => 4174, + "4175" => 4175, + "4176" => 4176, + "4177" => 4177, + "4178" => 4178, + "4179" => 4179, + "4180" => 4180, + "4181" => 4181, + "4182" => 4182, + "4183" => 4183, + "4184" => 4184, + "4185" => 4185, + "4186" => 4186, + "4187" => 4187, + "4188" => 4188, + "4189" => 4189, + "4190" => 4190, + "4191" => 4191, + "4192" => 4192, + "4193" => 4193, + "4194" => 4194, + "4195" => 4195, + "4196" => 4196, + "4197" => 4197, + "4198" => 4198, + "4199" => 4199, + "4200" => 4200, + "4201" => 4201, + "4202" => 4202, + "4203" => 4203, + "4204" => 4204, + "4205" => 4205, + "4206" => 4206, + "4207" => 4207, + "4208" => 4208, + "4209" => 4209, + "4210" => 4210, + "4211" => 4211, + "4212" => 4212, + "4213" => 4213, + "4214" => 4214, + "4215" => 4215, + "4216" => 4216, + "4217" => 4217, + "4218" => 4218, + "4219" => 4219, + "4220" => 4220, + "4221" => 4221, + "4222" => 4222, + "4223" => 4223, + "4224" => 4224, + "4225" => 4225, + "4226" => 4226, + "4227" => 4227, + "4228" => 4228, + "4229" => 4229, + "4230" => 4230, + "4231" => 4231, + "4232" => 4232, + "4233" => 4233, + "4234" => 4234, + "4235" => 4235, + "4236" => 4236, + "4237" => 4237, + "4238" => 4238, + "4239" => 4239, + "4240" => 4240, + "4241" => 4241, + "4242" => 4242, + "4243" => 4243, + "4244" => 4244, + "4245" => 4245, + "4246" => 4246, + "4247" => 4247, + "4248" => 4248, + "4249" => 4249, + "4250" => 4250, + "4251" => 4251, + "4252" => 4252, + "4253" => 4253, + "4254" => 4254, + "4255" => 4255, + "4256" => 4256, + "4257" => 4257, + "4258" => 4258, + "4259" => 4259, + "4260" => 4260, + "4261" => 4261, + "4262" => 4262, + "4263" => 4263, + "4264" => 4264, + "4265" => 4265, + "4266" => 4266, + "4267" => 4267, + "4268" => 4268, + "4269" => 4269, + "4270" => 4270, + "4271" => 4271, + "4272" => 4272, + "4273" => 4273, + "4274" => 4274, + "4275" => 4275, + "4276" => 4276, + "4277" => 4277, + "4278" => 4278, + "4279" => 4279, + "4280" => 4280, + "4281" => 4281, + "4282" => 4282, + "4283" => 4283, + "4284" => 4284, + "4285" => 4285, + "4286" => 4286, + "4287" => 4287, + "4288" => 4288, + "4289" => 4289, + "4290" => 4290, + "4291" => 4291, + "4292" => 4292, + "4293" => 4293, + "4294" => 4294, + "4295" => 4295, + "4296" => 4296, + "4297" => 4297, + "4298" => 4298, + "4299" => 4299, + "4300" => 4300, + "4301" => 4301, + "4302" => 4302, + "4303" => 4303, + "4304" => 4304, + "4305" => 4305, + "4306" => 4306, + "4307" => 4307, + "4308" => 4308, + "4309" => 4309, + "4310" => 4310, + "4311" => 4311, + "4312" => 4312, + "4313" => 4313, + "4314" => 4314, + "4315" => 4315, + "4316" => 4316, + "4317" => 4317, + "4318" => 4318, + "4319" => 4319, + "4320" => 4320, + "4321" => 4321, + "4322" => 4322, + "4323" => 4323, + "4324" => 4324, + "4325" => 4325, + "4326" => 4326, + "4327" => 4327, + "4328" => 4328, + "4329" => 4329, + "4330" => 4330, + "4331" => 4331, + "4332" => 4332, + "4333" => 4333, + "4334" => 4334, + "4335" => 4335, + "4336" => 4336, + "4337" => 4337, + "4338" => 4338, + "4339" => 4339, + "4340" => 4340, + "4341" => 4341, + "4342" => 4342, + "4343" => 4343, + "4344" => 4344, + "4345" => 4345, + "4346" => 4346, + "4347" => 4347, + "4348" => 4348, + "4349" => 4349, + "4350" => 4350, + "4351" => 4351, + "4352" => 4352, + "4353" => 4353, + "4354" => 4354, + "4355" => 4355, + "4356" => 4356, + "4357" => 4357, + "4358" => 4358, + "4359" => 4359, + "4360" => 4360, + "4361" => 4361, + "4362" => 4362, + "4363" => 4363, + "4364" => 4364, + "4365" => 4365, + "4366" => 4366, + "4367" => 4367, + "4368" => 4368, + "4369" => 4369, + "4370" => 4370, + "4371" => 4371, + "4372" => 4372, + "4373" => 4373, + "4374" => 4374, + "4375" => 4375, + "4376" => 4376, + "4377" => 4377, + "4378" => 4378, + "4379" => 4379, + "4380" => 4380, + "4381" => 4381, + "4382" => 4382, + "4383" => 4383, + "4384" => 4384, + "4385" => 4385, + "4386" => 4386, + "4387" => 4387, + "4388" => 4388, + "4389" => 4389, + "4390" => 4390, + "4391" => 4391, + "4392" => 4392, + "4393" => 4393, + "4394" => 4394, + "4395" => 4395, + "4396" => 4396, + "4397" => 4397, + "4398" => 4398, + "4399" => 4399, + "4400" => 4400, + "4401" => 4401, + "4402" => 4402, + "4403" => 4403, + "4404" => 4404, + "4405" => 4405, + "4406" => 4406, + "4407" => 4407, + "4408" => 4408, + "4409" => 4409, + "4410" => 4410, + "4411" => 4411, + "4412" => 4412, + "4413" => 4413, + "4414" => 4414, + "4415" => 4415, + "4416" => 4416, + "4417" => 4417, + "4418" => 4418, + "4419" => 4419, + "4420" => 4420, + "4421" => 4421, + "4422" => 4422, + "4423" => 4423, + "4424" => 4424, + "4425" => 4425, + "4426" => 4426, + "4427" => 4427, + "4428" => 4428, + "4429" => 4429, + "4430" => 4430, + "4431" => 4431, + "4432" => 4432, + "4433" => 4433, + "4434" => 4434, + "4435" => 4435, + "4436" => 4436, + "4437" => 4437, + "4438" => 4438, + "4439" => 4439, + "4440" => 4440, + "4441" => 4441, + "4442" => 4442, + "4443" => 4443, + "4444" => 4444, + "4445" => 4445, + "4446" => 4446, + "4447" => 4447, + "4448" => 4448, + "4449" => 4449, + "4450" => 4450, + "4451" => 4451, + "4452" => 4452, + "4453" => 4453, + "4454" => 4454, + "4455" => 4455, + "4456" => 4456, + "4457" => 4457, + "4458" => 4458, + "4459" => 4459, + "4460" => 4460, + "4461" => 4461, + "4462" => 4462, + "4463" => 4463, + "4464" => 4464, + "4465" => 4465, + "4466" => 4466, + "4467" => 4467, + "4468" => 4468, + "4469" => 4469, + "4470" => 4470, + "4471" => 4471, + "4472" => 4472, + "4473" => 4473, + "4474" => 4474, + "4475" => 4475, + "4476" => 4476, + "4477" => 4477, + "4478" => 4478, + "4479" => 4479, + "4480" => 4480, + "4481" => 4481, + "4482" => 4482, + "4483" => 4483, + "4484" => 4484, + "4485" => 4485, + "4486" => 4486, + "4487" => 4487, + "4488" => 4488, + "4489" => 4489, + "4490" => 4490, + "4491" => 4491, + "4492" => 4492, + "4493" => 4493, + "4494" => 4494, + "4495" => 4495, + "4496" => 4496, + "4497" => 4497, + "4498" => 4498, + "4499" => 4499, + "4500" => 4500, + "4501" => 4501, + "4502" => 4502, + "4503" => 4503, + "4504" => 4504, + "4505" => 4505, + "4506" => 4506, + "4507" => 4507, + "4508" => 4508, + "4509" => 4509, + "4510" => 4510, + "4511" => 4511, + "4512" => 4512, + "4513" => 4513, + "4514" => 4514, + "4515" => 4515, + "4516" => 4516, + "4517" => 4517, + "4518" => 4518, + "4519" => 4519, + "4520" => 4520, + "4521" => 4521, + "4522" => 4522, + "4523" => 4523, + "4524" => 4524, + "4525" => 4525, + "4526" => 4526, + "4527" => 4527, + "4528" => 4528, + "4529" => 4529, + "4530" => 4530, + "4531" => 4531, + "4532" => 4532, + "4533" => 4533, + "4534" => 4534, + "4535" => 4535, + "4536" => 4536, + "4537" => 4537, + "4538" => 4538, + "4539" => 4539, + "4540" => 4540, + "4541" => 4541, + "4542" => 4542, + "4543" => 4543, + "4544" => 4544, + "4545" => 4545, + "4546" => 4546, + "4547" => 4547, + "4548" => 4548, + "4549" => 4549, + "4550" => 4550, + "4551" => 4551, + "4552" => 4552, + "4553" => 4553, + "4554" => 4554, + "4555" => 4555, + "4556" => 4556, + "4557" => 4557, + "4558" => 4558, + "4559" => 4559, + "4560" => 4560, + "4561" => 4561, + "4562" => 4562, + "4563" => 4563, + "4564" => 4564, + "4565" => 4565, + "4566" => 4566, + "4567" => 4567, + "4568" => 4568, + "4569" => 4569, + "4570" => 4570, + "4571" => 4571, + "4572" => 4572, + "4573" => 4573, + "4574" => 4574, + "4575" => 4575, + "4576" => 4576, + "4577" => 4577, + "4578" => 4578, + "4579" => 4579, + "4580" => 4580, + "4581" => 4581, + "4582" => 4582, + "4583" => 4583, + "4584" => 4584, + "4585" => 4585, + "4586" => 4586, + "4587" => 4587, + "4588" => 4588, + "4589" => 4589, + "4590" => 4590, + "4591" => 4591, + "4592" => 4592, + "4593" => 4593, + "4594" => 4594, + "4595" => 4595, + "4596" => 4596, + "4597" => 4597, + "4598" => 4598, + "4599" => 4599, + "4600" => 4600, + "4601" => 4601, + "4602" => 4602, + "4603" => 4603, + "4604" => 4604, + "4605" => 4605, + "4606" => 4606, + "4607" => 4607, + "4608" => 4608, + "4609" => 4609, + "4610" => 4610, + "4611" => 4611, + "4612" => 4612, + "4613" => 4613, + "4614" => 4614, + "4615" => 4615, + "4616" => 4616, + "4617" => 4617, + "4618" => 4618, + "4619" => 4619, + "4620" => 4620, + "4621" => 4621, + "4622" => 4622, + "4623" => 4623, + "4624" => 4624, + "4625" => 4625, + "4626" => 4626, + "4627" => 4627, + "4628" => 4628, + "4629" => 4629, + "4630" => 4630, + "4631" => 4631, + "4632" => 4632, + "4633" => 4633, + "4634" => 4634, + "4635" => 4635, + "4636" => 4636, + "4637" => 4637, + "4638" => 4638, + "4639" => 4639, + "4640" => 4640, + "4641" => 4641, + "4642" => 4642, + "4643" => 4643, + "4644" => 4644, + "4645" => 4645, + "4646" => 4646, + "4647" => 4647, + "4648" => 4648, + "4649" => 4649, + "4650" => 4650, + "4651" => 4651, + "4652" => 4652, + "4653" => 4653, + "4654" => 4654, + "4655" => 4655, + "4656" => 4656, + "4657" => 4657, + "4658" => 4658, + "4659" => 4659, + "4660" => 4660, + "4661" => 4661, + "4662" => 4662, + "4663" => 4663, + "4664" => 4664, + "4665" => 4665, + "4666" => 4666, + "4667" => 4667, + "4668" => 4668, + "4669" => 4669, + "4670" => 4670, + "4671" => 4671, + "4672" => 4672, + "4673" => 4673, + "4674" => 4674, + "4675" => 4675, + "4676" => 4676, + "4677" => 4677, + "4678" => 4678, + "4679" => 4679, + "4680" => 4680, + "4681" => 4681, + "4682" => 4682, + "4683" => 4683, + "4684" => 4684, + "4685" => 4685, + "4686" => 4686, + "4687" => 4687, + "4688" => 4688, + "4689" => 4689, + "4690" => 4690, + "4691" => 4691, + "4692" => 4692, + "4693" => 4693, + "4694" => 4694, + "4695" => 4695, + "4696" => 4696, + "4697" => 4697, + "4698" => 4698, + "4699" => 4699, + "4700" => 4700, + "4701" => 4701, + "4702" => 4702, + "4703" => 4703, + "4704" => 4704, + "4705" => 4705, + "4706" => 4706, + "4707" => 4707, + "4708" => 4708, + "4709" => 4709, + "4710" => 4710, + "4711" => 4711, + "4712" => 4712, + "4713" => 4713, + "4714" => 4714, + "4715" => 4715, + "4716" => 4716, + "4717" => 4717, + "4718" => 4718, + "4719" => 4719, + "4720" => 4720, + "4721" => 4721, + "4722" => 4722, + "4723" => 4723, + "4724" => 4724, + "4725" => 4725, + "4726" => 4726, + "4727" => 4727, + "4728" => 4728, + "4729" => 4729, + "4730" => 4730, + "4731" => 4731, + "4732" => 4732, + "4733" => 4733, + "4734" => 4734, + "4735" => 4735, + "4736" => 4736, + "4737" => 4737, + "4738" => 4738, + "4739" => 4739, + "4740" => 4740, + "4741" => 4741, + "4742" => 4742, + "4743" => 4743, + "4744" => 4744, + "4745" => 4745, + "4746" => 4746, + "4747" => 4747, + "4748" => 4748, + "4749" => 4749, + "4750" => 4750, + "4751" => 4751, + "4752" => 4752, + "4753" => 4753, + "4754" => 4754, + "4755" => 4755, + "4756" => 4756, + "4757" => 4757, + "4758" => 4758, + "4759" => 4759, + "4760" => 4760, + "4761" => 4761, + "4762" => 4762, + "4763" => 4763, + "4764" => 4764, + "4765" => 4765, + "4766" => 4766, + "4767" => 4767, + "4768" => 4768, + "4769" => 4769, + "4770" => 4770, + "4771" => 4771, + "4772" => 4772, + "4773" => 4773, + "4774" => 4774, + "4775" => 4775, + "4776" => 4776, + "4777" => 4777, + "4778" => 4778, + "4779" => 4779, + "4780" => 4780, + "4781" => 4781, + "4782" => 4782, + "4783" => 4783, + "4784" => 4784, + "4785" => 4785, + "4786" => 4786, + "4787" => 4787, + "4788" => 4788, + "4789" => 4789, + "4790" => 4790, + "4791" => 4791, + "4792" => 4792, + "4793" => 4793, + "4794" => 4794, + "4795" => 4795, + "4796" => 4796, + "4797" => 4797, + "4798" => 4798, + "4799" => 4799, + "4800" => 4800, + "4801" => 4801, + "4802" => 4802, + "4803" => 4803, + "4804" => 4804, + "4805" => 4805, + "4806" => 4806, + "4807" => 4807, + "4808" => 4808, + "4809" => 4809, + "4810" => 4810, + "4811" => 4811, + "4812" => 4812, + "4813" => 4813, + "4814" => 4814, + "4815" => 4815, + "4816" => 4816, + "4817" => 4817, + "4818" => 4818, + "4819" => 4819, + "4820" => 4820, + "4821" => 4821, + "4822" => 4822, + "4823" => 4823, + "4824" => 4824, + "4825" => 4825, + "4826" => 4826, + "4827" => 4827, + "4828" => 4828, + "4829" => 4829, + "4830" => 4830, + "4831" => 4831, + "4832" => 4832, + "4833" => 4833, + "4834" => 4834, + "4835" => 4835, + "4836" => 4836, + "4837" => 4837, + "4838" => 4838, + "4839" => 4839, + "4840" => 4840, + "4841" => 4841, + "4842" => 4842, + "4843" => 4843, + "4844" => 4844, + "4845" => 4845, + "4846" => 4846, + "4847" => 4847, + "4848" => 4848, + "4849" => 4849, + "4850" => 4850, + "4851" => 4851, + "4852" => 4852, + "4853" => 4853, + "4854" => 4854, + "4855" => 4855, + "4856" => 4856, + "4857" => 4857, + "4858" => 4858, + "4859" => 4859, + "4860" => 4860, + "4861" => 4861, + "4862" => 4862, + "4863" => 4863, + "4864" => 4864, + "4865" => 4865, + "4866" => 4866, + "4867" => 4867, + "4868" => 4868, + "4869" => 4869, + "4870" => 4870, + "4871" => 4871, + "4872" => 4872, + "4873" => 4873, + "4874" => 4874, + "4875" => 4875, + "4876" => 4876, + "4877" => 4877, + "4878" => 4878, + "4879" => 4879, + "4880" => 4880, + "4881" => 4881, + "4882" => 4882, + "4883" => 4883, + "4884" => 4884, + "4885" => 4885, + "4886" => 4886, + "4887" => 4887, + "4888" => 4888, + "4889" => 4889, + "4890" => 4890, + "4891" => 4891, + "4892" => 4892, + "4893" => 4893, + "4894" => 4894, + "4895" => 4895, + "4896" => 4896, + "4897" => 4897, + "4898" => 4898, + "4899" => 4899, + "4900" => 4900, + "4901" => 4901, + "4902" => 4902, + "4903" => 4903, + "4904" => 4904, + "4905" => 4905, + "4906" => 4906, + "4907" => 4907, + "4908" => 4908, + "4909" => 4909, + "4910" => 4910, + "4911" => 4911, + "4912" => 4912, + "4913" => 4913, + "4914" => 4914, + "4915" => 4915, + "4916" => 4916, + "4917" => 4917, + "4918" => 4918, + "4919" => 4919, + "4920" => 4920, + "4921" => 4921, + "4922" => 4922, + "4923" => 4923, + "4924" => 4924, + "4925" => 4925, + "4926" => 4926, + "4927" => 4927, + "4928" => 4928, + "4929" => 4929, + "4930" => 4930, + "4931" => 4931, + "4932" => 4932, + "4933" => 4933, + "4934" => 4934, + "4935" => 4935, + "4936" => 4936, + "4937" => 4937, + "4938" => 4938, + "4939" => 4939, + "4940" => 4940, + "4941" => 4941, + "4942" => 4942, + "4943" => 4943, + "4944" => 4944, + "4945" => 4945, + "4946" => 4946, + "4947" => 4947, + "4948" => 4948, + "4949" => 4949, + "4950" => 4950, + "4951" => 4951, + "4952" => 4952, + "4953" => 4953, + "4954" => 4954, + "4955" => 4955, + "4956" => 4956, + "4957" => 4957, + "4958" => 4958, + "4959" => 4959, + "4960" => 4960, + "4961" => 4961, + "4962" => 4962, + "4963" => 4963, + "4964" => 4964, + "4965" => 4965, + "4966" => 4966, + "4967" => 4967, + "4968" => 4968, + "4969" => 4969, + "4970" => 4970, + "4971" => 4971, + "4972" => 4972, + "4973" => 4973, + "4974" => 4974, + "4975" => 4975, + "4976" => 4976, + "4977" => 4977, + "4978" => 4978, + "4979" => 4979, + "4980" => 4980, + "4981" => 4981, + "4982" => 4982, + "4983" => 4983, + "4984" => 4984, + "4985" => 4985, + "4986" => 4986, + "4987" => 4987, + "4988" => 4988, + "4989" => 4989, + "4990" => 4990, + "4991" => 4991, + "4992" => 4992, + "4993" => 4993, + "4994" => 4994, + "4995" => 4995, + "4996" => 4996, + "4997" => 4997, + "4998" => 4998, + "4999" => 4999, + "5000" => 5000, + "5001" => 5001, + "5002" => 5002, + "5003" => 5003, + "5004" => 5004, + "5005" => 5005, + "5006" => 5006, + "5007" => 5007, + "5008" => 5008, + "5009" => 5009, + "5010" => 5010, + "5011" => 5011, + "5012" => 5012, + "5013" => 5013, + "5014" => 5014, + "5015" => 5015, + "5016" => 5016, + "5017" => 5017, + "5018" => 5018, + "5019" => 5019, + "5020" => 5020, + "5021" => 5021, + "5022" => 5022, + "5023" => 5023, + "5024" => 5024, + "5025" => 5025, + "5026" => 5026, + "5027" => 5027, + "5028" => 5028, + "5029" => 5029, + "5030" => 5030, + "5031" => 5031, + "5032" => 5032, + "5033" => 5033, + "5034" => 5034, + "5035" => 5035, + "5036" => 5036, + "5037" => 5037, + "5038" => 5038, + "5039" => 5039, + "5040" => 5040, + "5041" => 5041, + "5042" => 5042, + "5043" => 5043, + "5044" => 5044, + "5045" => 5045, + "5046" => 5046, + "5047" => 5047, + "5048" => 5048, + "5049" => 5049, + "5050" => 5050, + "5051" => 5051, + "5052" => 5052, + "5053" => 5053, + "5054" => 5054, + "5055" => 5055, + "5056" => 5056, + "5057" => 5057, + "5058" => 5058, + "5059" => 5059, + "5060" => 5060, + "5061" => 5061, + "5062" => 5062, + "5063" => 5063, + "5064" => 5064, + "5065" => 5065, + "5066" => 5066, + "5067" => 5067, + "5068" => 5068, + "5069" => 5069, + "5070" => 5070, + "5071" => 5071, + "5072" => 5072, + "5073" => 5073, + "5074" => 5074, + "5075" => 5075, + "5076" => 5076, + "5077" => 5077, + "5078" => 5078, + "5079" => 5079, + "5080" => 5080, + "5081" => 5081, + "5082" => 5082, + "5083" => 5083, + "5084" => 5084, + "5085" => 5085, + "5086" => 5086, + "5087" => 5087, + "5088" => 5088, + "5089" => 5089, + "5090" => 5090, + "5091" => 5091, + "5092" => 5092, + "5093" => 5093, + "5094" => 5094, + "5095" => 5095, + "5096" => 5096, + "5097" => 5097, + "5098" => 5098, + "5099" => 5099, + "5100" => 5100, + "5101" => 5101, + "5102" => 5102, + "5103" => 5103, + "5104" => 5104, + "5105" => 5105, + "5106" => 5106, + "5107" => 5107, + "5108" => 5108, + "5109" => 5109, + "5110" => 5110, + "5111" => 5111, + "5112" => 5112, + "5113" => 5113, + "5114" => 5114, + "5115" => 5115, + "5116" => 5116, + "5117" => 5117, + "5118" => 5118, + "5119" => 5119, + "5120" => 5120, + "5121" => 5121, + "5122" => 5122, + "5123" => 5123, + "5124" => 5124, + "5125" => 5125, + "5126" => 5126, + "5127" => 5127, + "5128" => 5128, + "5129" => 5129, + "5130" => 5130, + "5131" => 5131, + "5132" => 5132, + "5133" => 5133, + "5134" => 5134, + "5135" => 5135, + "5136" => 5136, + "5137" => 5137, + "5138" => 5138, + "5139" => 5139, + "5140" => 5140, + "5141" => 5141, + "5142" => 5142, + "5143" => 5143, + "5144" => 5144, + "5145" => 5145, + "5146" => 5146, + "5147" => 5147, + "5148" => 5148, + "5149" => 5149, + "5150" => 5150, + "5151" => 5151, + "5152" => 5152, + "5153" => 5153, + "5154" => 5154, + "5155" => 5155, + "5156" => 5156, + "5157" => 5157, + "5158" => 5158, + "5159" => 5159, + "5160" => 5160, + "5161" => 5161, + "5162" => 5162, + "5163" => 5163, + "5164" => 5164, + "5165" => 5165, + "5166" => 5166, + "5167" => 5167, + "5168" => 5168, + "5169" => 5169, + "5170" => 5170, + "5171" => 5171, + "5172" => 5172, + "5173" => 5173, + "5174" => 5174, + "5175" => 5175, + "5176" => 5176, + "5177" => 5177, + "5178" => 5178, + "5179" => 5179, + "5180" => 5180, + "5181" => 5181, + "5182" => 5182, + "5183" => 5183, + "5184" => 5184, + "5185" => 5185, + "5186" => 5186, + "5187" => 5187, + "5188" => 5188, + "5189" => 5189, + "5190" => 5190, + "5191" => 5191, + "5192" => 5192, + "5193" => 5193, + "5194" => 5194, + "5195" => 5195, + "5196" => 5196, + "5197" => 5197, + "5198" => 5198, + _ => 5199, + } +} 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-8460.rs b/src/test/ui/issues/issue-8460.rs index 3fd576a8d3580..a7de4bd74aae4 100644 --- a/src/test/ui/issues/issue-8460.rs +++ b/src/test/ui/issues/issue-8460.rs @@ -27,21 +27,21 @@ macro_rules! check { fn main() { check![ - isize::min_value() / -isize::one(), - i8::min_value() / -i8::one(), - i16::min_value() / -i16::one(), - i32::min_value() / -i32::one(), - i64::min_value() / -i64::one(), + isize::MIN / -isize::one(), + i8::MIN / -i8::one(), + i16::MIN / -i16::one(), + i32::MIN / -i32::one(), + i64::MIN / -i64::one(), 1isize / isize::zero(), 1i8 / i8::zero(), 1i16 / i16::zero(), 1i32 / i32::zero(), 1i64 / i64::zero(), - isize::min_value() % -isize::one(), - i8::min_value() % -i8::one(), - i16::min_value() % -i16::one(), - i32::min_value() % -i32::one(), - i64::min_value() % -i64::one(), + isize::MIN % -isize::one(), + i8::MIN % -i8::one(), + i16::MIN % -i16::one(), + i32::MIN % -i32::one(), + i64::MIN % -i64::one(), 1isize % isize::zero(), 1i8 % i8::zero(), 1i16 % i16::zero(), diff --git a/src/test/ui/issues/issue-8727.rs b/src/test/ui/issues/issue-8727.rs index 80f360155cb49..14bdd8511119e 100644 --- a/src/test/ui/issues/issue-8727.rs +++ b/src/test/ui/issues/issue-8727.rs @@ -3,12 +3,10 @@ // build-fail -fn generic() { +fn generic() { //~ WARN function cannot return without recursing generic::>(); } -//~^^^ ERROR reached the recursion limit while instantiating `generic::>(); = help: a `loop` may express intention better if this is on purpose error: reached the recursion limit while instantiating `generic::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/issue-8727.rs:7:5 + | +LL | generic::>(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: `generic` defined here --> $DIR/issue-8727.rs:6:1 | LL | / fn generic() { @@ -17,5 +23,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/iterators/iter-step-overflow-debug.rs b/src/test/ui/iterators/iter-step-overflow-debug.rs index 5d67c7cbb4256..67605d2fcc253 100644 --- a/src/test/ui/iterators/iter-step-overflow-debug.rs +++ b/src/test/ui/iterators/iter-step-overflow-debug.rs @@ -6,14 +6,14 @@ use std::panic; fn main() { let r = panic::catch_unwind(|| { - let mut it = u8::max_value()..; + let mut it = u8::MAX..; it.next().unwrap(); // 255 it.next().unwrap(); }); assert!(r.is_err()); let r = panic::catch_unwind(|| { - let mut it = i8::max_value()..; + let mut it = i8::MAX..; it.next().unwrap(); // 127 it.next().unwrap(); }); diff --git a/src/test/ui/iterators/iter-step-overflow-ndebug.rs b/src/test/ui/iterators/iter-step-overflow-ndebug.rs index a0ad92071b66c..33e708769badb 100644 --- a/src/test/ui/iterators/iter-step-overflow-ndebug.rs +++ b/src/test/ui/iterators/iter-step-overflow-ndebug.rs @@ -2,11 +2,11 @@ // compile-flags: -C debug_assertions=no fn main() { - let mut it = u8::max_value()..; + let mut it = u8::MAX..; assert_eq!(it.next().unwrap(), 255); - assert_eq!(it.next().unwrap(), u8::min_value()); + assert_eq!(it.next().unwrap(), u8::MIN); - let mut it = i8::max_value()..; + let mut it = i8::MAX..; assert_eq!(it.next().unwrap(), 127); - assert_eq!(it.next().unwrap(), i8::min_value()); + assert_eq!(it.next().unwrap(), i8::MIN); } diff --git a/src/test/ui/iterators/iter-sum-overflow-debug.rs b/src/test/ui/iterators/iter-sum-overflow-debug.rs index ee4ab4d24c6ab..b7667d1bbf6d9 100644 --- a/src/test/ui/iterators/iter-sum-overflow-debug.rs +++ b/src/test/ui/iterators/iter-sum-overflow-debug.rs @@ -6,22 +6,22 @@ use std::panic; fn main() { let r = panic::catch_unwind(|| { - [1, i32::max_value()].iter().sum::(); + [1, i32::MAX].iter().sum::(); }); assert!(r.is_err()); let r = panic::catch_unwind(|| { - [2, i32::max_value()].iter().product::(); + [2, i32::MAX].iter().product::(); }); assert!(r.is_err()); let r = panic::catch_unwind(|| { - [1, i32::max_value()].iter().cloned().sum::(); + [1, i32::MAX].iter().cloned().sum::(); }); assert!(r.is_err()); let r = panic::catch_unwind(|| { - [2, i32::max_value()].iter().cloned().product::(); + [2, i32::MAX].iter().cloned().product::(); }); assert!(r.is_err()); } diff --git a/src/test/ui/iterators/iter-sum-overflow-ndebug.rs b/src/test/ui/iterators/iter-sum-overflow-ndebug.rs index 61d63d41fb87e..69f4744cc2a1a 100644 --- a/src/test/ui/iterators/iter-sum-overflow-ndebug.rs +++ b/src/test/ui/iterators/iter-sum-overflow-ndebug.rs @@ -2,13 +2,13 @@ // compile-flags: -C debug_assertions=no fn main() { - assert_eq!([1i32, i32::max_value()].iter().sum::(), - 1i32.wrapping_add(i32::max_value())); - assert_eq!([2i32, i32::max_value()].iter().product::(), - 2i32.wrapping_mul(i32::max_value())); + assert_eq!([1i32, i32::MAX].iter().sum::(), + 1i32.wrapping_add(i32::MAX)); + assert_eq!([2i32, i32::MAX].iter().product::(), + 2i32.wrapping_mul(i32::MAX)); - assert_eq!([1i32, i32::max_value()].iter().cloned().sum::(), - 1i32.wrapping_add(i32::max_value())); - assert_eq!([2i32, i32::max_value()].iter().cloned().product::(), - 2i32.wrapping_mul(i32::max_value())); + assert_eq!([1i32, i32::MAX].iter().cloned().sum::(), + 1i32.wrapping_add(i32::MAX)); + assert_eq!([2i32, i32::MAX].iter().cloned().product::(), + 2i32.wrapping_mul(i32::MAX)); } diff --git a/src/test/ui/iterators/iter-sum-overflow-overflow-checks.rs b/src/test/ui/iterators/iter-sum-overflow-overflow-checks.rs index 429f8e0bc9648..04ca7f8a31534 100644 --- a/src/test/ui/iterators/iter-sum-overflow-overflow-checks.rs +++ b/src/test/ui/iterators/iter-sum-overflow-overflow-checks.rs @@ -6,22 +6,22 @@ use std::panic; fn main() { let r = panic::catch_unwind(|| { - [1, i32::max_value()].iter().sum::(); + [1, i32::MAX].iter().sum::(); }); assert!(r.is_err()); let r = panic::catch_unwind(|| { - [2, i32::max_value()].iter().product::(); + [2, i32::MAX].iter().product::(); }); assert!(r.is_err()); let r = panic::catch_unwind(|| { - [1, i32::max_value()].iter().cloned().sum::(); + [1, i32::MAX].iter().cloned().sum::(); }); assert!(r.is_err()); let r = panic::catch_unwind(|| { - [2, i32::max_value()].iter().cloned().product::(); + [2, i32::MAX].iter().cloned().product::(); }); assert!(r.is_err()); } diff --git a/src/test/ui/iterators/skip-count-overflow.rs b/src/test/ui/iterators/skip-count-overflow.rs index d8efc948664ff..64dee3e3c8b20 100644 --- a/src/test/ui/iterators/skip-count-overflow.rs +++ b/src/test/ui/iterators/skip-count-overflow.rs @@ -3,6 +3,6 @@ // compile-flags: -C overflow-checks -C opt-level=3 fn main() { - let i = (0..usize::max_value()).chain(0..10).skip(usize::max_value()); + let i = (0..usize::MAX).chain(0..10).skip(usize::MAX); assert_eq!(i.count(), 10); } diff --git a/src/test/ui/json-bom-plus-crlf-multifile.stderr b/src/test/ui/json-bom-plus-crlf-multifile.stderr index ab0feb3c451ad..8d3c316e467bd 100644 --- a/src/test/ui/json-bom-plus-crlf-multifile.stderr +++ b/src/test/ui/json-bom-plus-crlf-multifile.stderr @@ -1,10 +1,6 @@ -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -15,15 +11,16 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +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":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -34,15 +31,16 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +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":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -53,15 +51,16 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +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":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -72,6 +71,11 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +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 1dd898db3ad8c..ed6b583f329d6 100644 --- a/src/test/ui/json-bom-plus-crlf.stderr +++ b/src/test/ui/json-bom-plus-crlf.stderr @@ -1,10 +1,6 @@ -{"message":"mismatched types","code":{"code":"E0308","explanation":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -15,15 +11,16 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +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":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -34,15 +31,16 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +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":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -53,15 +51,16 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +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":"This error occurs when the compiler was unable to infer the concrete type of a -variable. It can occur for several cases, the most common of which is a -mismatch in the expected type that the compiler inferred for a variable's -initializing expression, and the actual type explicitly assigned to the -variable. +{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type. -For example: +Erroneous code example: ```compile_fail,E0308 let x: i32 = \"I am not a number!\"; @@ -72,6 +71,11 @@ let x: i32 = \"I am not a number!\"; // | // type `i32` assigned to variable `x` ``` + +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/json-short.stderr b/src/test/ui/json-short.stderr index 60c2582b11eae..3bd85b083d002 100644 --- a/src/test/ui/json-short.stderr +++ b/src/test/ui/json-short.stderr @@ -1,5 +1,6 @@ -{"message":"`main` function not found in crate `json_short`","code":{"code":"E0601","explanation":"No `main` function was found in a binary crate. To fix this error, add a -`main` function. For example: +{"message":"`main` function not found in crate `json_short`","code":{"code":"E0601","explanation":"No `main` function was found in a binary crate. + +To fix this error, add a `main` function: ``` fn main() { 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/keyword/extern/keyword-extern-as-identifier-use.rs b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.rs index b07de3e341c41..a46ce67d40d5a 100644 --- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.rs +++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.rs @@ -1,3 +1,4 @@ use extern::foo; //~ ERROR expected identifier, found keyword `extern` + //~| ERROR unresolved import `r#extern` fn main() {} diff --git a/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr index 05802f2d36710..edbb36452b6ce 100644 --- a/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr +++ b/src/test/ui/keyword/extern/keyword-extern-as-identifier-use.stderr @@ -9,5 +9,12 @@ help: you can escape reserved keywords to use them as identifiers LL | use r#extern::foo; | ^^^^^^^^ -error: aborting due to previous error +error[E0432]: unresolved import `r#extern` + --> $DIR/keyword-extern-as-identifier-use.rs:1:5 + | +LL | use extern::foo; + | ^^^^^^ maybe a missing crate `r#extern`? + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0432`. 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/label/label_break_value_desugared_break.rs b/src/test/ui/label/label_break_value_desugared_break.rs new file mode 100644 index 0000000000000..de883b61111ce --- /dev/null +++ b/src/test/ui/label/label_break_value_desugared_break.rs @@ -0,0 +1,12 @@ +// compile-flags: --edition 2018 +#![feature(label_break_value, try_blocks)] + +// run-pass +fn main() { + let _: Result<(), ()> = try { + 'foo: { + Err(())?; + break 'foo; + } + }; +} diff --git a/src/test/ui/layout/debug.rs b/src/test/ui/layout/debug.rs new file mode 100644 index 0000000000000..299151df66493 --- /dev/null +++ b/src/test/ui/layout/debug.rs @@ -0,0 +1,22 @@ +// normalize-stderr-test "pref: Align \{\n *pow2: [1-3],\n *\}" -> "pref: $$PREF_ALIGN" +#![feature(never_type, rustc_attrs, type_alias_impl_trait)] +#![crate_type = "lib"] + +#[rustc_layout(debug)] +enum E { Foo, Bar(!, i32, i32) } //~ ERROR: layout_of + +#[rustc_layout(debug)] +struct S { f1: i32, f2: (), f3: i32 } //~ ERROR: layout_of + +#[rustc_layout(debug)] +union U { f1: (i32, i32), f3: i32 } //~ ERROR: layout_of + +#[rustc_layout(debug)] +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 new file mode 100644 index 0000000000000..1a371c6b17000 --- /dev/null +++ b/src/test/ui/layout/debug.stderr @@ -0,0 +1,349 @@ +error: layout_of(E) = Layout { + fields: Arbitrary { + offsets: [ + Size { + raw: 0, + }, + ], + memory_index: [ + 0, + ], + }, + variants: Multiple { + tag: Scalar { + value: Int( + I32, + false, + ), + valid_range: 0..=0, + }, + tag_encoding: Direct, + tag_field: 0, + variants: [ + Layout { + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + variants: Single { + index: 0, + }, + abi: Aggregate { + sized: true, + }, + largest_niche: None, + align: AbiAndPrefAlign { + abi: Align { + pow2: 0, + }, + pref: $PREF_ALIGN, + }, + size: Size { + raw: 4, + }, + }, + Layout { + fields: Arbitrary { + offsets: [ + Size { + raw: 4, + }, + Size { + raw: 4, + }, + Size { + raw: 8, + }, + ], + memory_index: [ + 0, + 1, + 2, + ], + }, + variants: Single { + index: 1, + }, + abi: Uninhabited, + largest_niche: None, + align: AbiAndPrefAlign { + abi: Align { + pow2: 2, + }, + pref: $PREF_ALIGN, + }, + size: Size { + raw: 12, + }, + }, + ], + }, + abi: Aggregate { + sized: true, + }, + largest_niche: Some( + Niche { + offset: Size { + raw: 0, + }, + scalar: Scalar { + value: Int( + I32, + false, + ), + valid_range: 0..=0, + }, + }, + ), + align: AbiAndPrefAlign { + abi: Align { + pow2: 2, + }, + pref: $PREF_ALIGN, + }, + size: Size { + raw: 12, + }, +} + --> $DIR/debug.rs:6:1 + | +LL | enum E { Foo, Bar(!, i32, i32) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: layout_of(S) = Layout { + fields: Arbitrary { + offsets: [ + Size { + raw: 0, + }, + Size { + raw: 0, + }, + Size { + raw: 4, + }, + ], + memory_index: [ + 1, + 0, + 2, + ], + }, + variants: Single { + index: 0, + }, + abi: ScalarPair( + Scalar { + value: Int( + I32, + true, + ), + valid_range: 0..=4294967295, + }, + Scalar { + value: Int( + I32, + true, + ), + valid_range: 0..=4294967295, + }, + ), + largest_niche: None, + align: AbiAndPrefAlign { + abi: Align { + pow2: 2, + }, + pref: $PREF_ALIGN, + }, + size: Size { + raw: 8, + }, +} + --> $DIR/debug.rs:9:1 + | +LL | struct S { f1: i32, f2: (), f3: i32 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: layout_of(U) = Layout { + fields: Union( + 2, + ), + variants: Single { + index: 0, + }, + abi: Aggregate { + sized: true, + }, + largest_niche: None, + align: AbiAndPrefAlign { + abi: Align { + pow2: 2, + }, + pref: $PREF_ALIGN, + }, + size: Size { + raw: 8, + }, +} + --> $DIR/debug.rs:12:1 + | +LL | union U { f1: (i32, i32), f3: i32 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: layout_of(std::result::Result) = Layout { + fields: Arbitrary { + offsets: [ + Size { + raw: 0, + }, + ], + memory_index: [ + 0, + ], + }, + variants: Multiple { + tag: Scalar { + value: Int( + I32, + false, + ), + valid_range: 0..=1, + }, + tag_encoding: Direct, + tag_field: 0, + variants: [ + Layout { + fields: Arbitrary { + offsets: [ + Size { + raw: 4, + }, + ], + memory_index: [ + 0, + ], + }, + variants: Single { + index: 0, + }, + abi: Aggregate { + sized: true, + }, + largest_niche: None, + align: AbiAndPrefAlign { + abi: Align { + pow2: 2, + }, + pref: $PREF_ALIGN, + }, + size: Size { + raw: 8, + }, + }, + Layout { + fields: Arbitrary { + offsets: [ + Size { + raw: 4, + }, + ], + memory_index: [ + 0, + ], + }, + variants: Single { + index: 1, + }, + abi: Aggregate { + sized: true, + }, + largest_niche: None, + align: AbiAndPrefAlign { + abi: Align { + pow2: 2, + }, + pref: $PREF_ALIGN, + }, + size: Size { + raw: 8, + }, + }, + ], + }, + abi: ScalarPair( + Scalar { + value: Int( + I32, + false, + ), + valid_range: 0..=1, + }, + Scalar { + value: Int( + I32, + true, + ), + valid_range: 0..=4294967295, + }, + ), + largest_niche: Some( + Niche { + offset: Size { + raw: 0, + }, + scalar: Scalar { + value: Int( + I32, + false, + ), + valid_range: 0..=1, + }, + }, + ), + align: AbiAndPrefAlign { + abi: Align { + pow2: 2, + }, + pref: $PREF_ALIGN, + }, + size: Size { + raw: 8, + }, +} + --> $DIR/debug.rs:15:1 + | +LL | type Test = Result; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +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..1e6a35ed479f6 100644 --- a/src/test/ui/lexical-scopes.stderr +++ b/src/test/ui/lexical-scopes.stderr @@ -3,11 +3,6 @@ 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 - | -LL | use T; - | error[E0599]: no function or associated item named `f` found for type parameter `Foo` in the current scope --> $DIR/lexical-scopes.rs:10:10 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-doesnt-live-long-enough.stderr b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr index e60c461743c8f..d682478db0eef 100644 --- a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr +++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr @@ -4,13 +4,7 @@ error[E0310]: the parameter type `T` may not live long enough LL | struct Foo { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | foo: &'static T - | ^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'static T` does not outlive the data it points at - --> $DIR/lifetime-doesnt-live-long-enough.rs:19:5 - | -LL | foo: &'static T - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at error[E0309]: the parameter type `K` may not live long enough --> $DIR/lifetime-doesnt-live-long-enough.rs:24:19 @@ -18,13 +12,7 @@ error[E0309]: the parameter type `K` may not live long enough LL | trait X: Sized { | - help: consider adding an explicit lifetime bound...: `K: 'a` LL | fn foo<'a, L: X<&'a Nested>>(); - | ^^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'a Nested` does not outlive the data it points at - --> $DIR/lifetime-doesnt-live-long-enough.rs:24:19 - | -LL | fn foo<'a, L: X<&'a Nested>>(); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested` does not outlive the data it points at error[E0309]: the parameter type `Self` may not live long enough --> $DIR/lifetime-doesnt-live-long-enough.rs:28:19 @@ -33,25 +21,15 @@ LL | fn bar<'a, L: X<&'a Nested>>(); | ^^^^^^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `Self: 'a`... -note: ...so that the reference type `&'a Nested` does not outlive the data it points at - --> $DIR/lifetime-doesnt-live-long-enough.rs:28:19 - | -LL | fn bar<'a, L: X<&'a Nested>>(); - | ^^^^^^^^^^^^^^^^^^^ + = note: ...so that the reference type `&'a Nested` does not outlive the data it points at error[E0309]: the parameter type `L` may not live long enough --> $DIR/lifetime-doesnt-live-long-enough.rs:32:22 | LL | fn baz<'a, L, M: X<&'a Nested>>() { - | - ^^^^^^^^^^^^^^^^ + | - ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested` does not outlive the data it points at | | | help: consider adding an explicit lifetime bound...: `L: 'a` - | -note: ...so that the reference type `&'a Nested` does not outlive the data it points at - --> $DIR/lifetime-doesnt-live-long-enough.rs:32:22 - | -LL | fn baz<'a, L, M: X<&'a Nested>>() { - | ^^^^^^^^^^^^^^^^ error[E0309]: the parameter type `K` may not live long enough --> $DIR/lifetime-doesnt-live-long-enough.rs:41:33 @@ -59,25 +37,15 @@ error[E0309]: the parameter type `K` may not live long enough LL | impl Nested { | - help: consider adding an explicit lifetime bound...: `K: 'a` LL | fn generic_in_parent<'a, L: X<&'a Nested>>() { - | ^^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'a Nested` does not outlive the data it points at - --> $DIR/lifetime-doesnt-live-long-enough.rs:41:33 - | -LL | fn generic_in_parent<'a, L: X<&'a Nested>>() { - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested` does not outlive the data it points at error[E0309]: the parameter type `M` may not live long enough --> $DIR/lifetime-doesnt-live-long-enough.rs:44:36 | LL | fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { | ^^^^^^^^^^^^^^^^ -- help: consider adding an explicit lifetime bound...: `M: 'a +` - | -note: ...so that the reference type `&'a Nested` does not outlive the data it points at - --> $DIR/lifetime-doesnt-live-long-enough.rs:44:36 - | -LL | fn generic_in_child<'a, 'b, L: X<&'a Nested>, M: 'b>() { - | ^^^^^^^^^^^^^^^^ + | | + | ...so that the reference type `&'a Nested` does not outlive the data it points at error: aborting due to 6 previous errors 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/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr index d07f305954b6e..060e6954403c0 100644 --- a/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr +++ b/src/test/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr @@ -2,13 +2,15 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/lifetime-mismatch-between-trait-and-impl.rs:6:5 | LL | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32; - | ------------------------------------------- expected fn(&i32, &'a i32) -> &'a i32 + | ------------------------------------------- expected `fn(&i32, &'a i32) -> &'a i32` ... LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&i32, &i32) -> &i32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&i32, &i32) -> &i32` | = note: expected `fn(&i32, &'a i32) -> &'a i32` found `fn(&i32, &i32) -> &i32` + = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output error: aborting due to previous error 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/auxiliary/external_extern_fn.rs b/src/test/ui/lint/auxiliary/external_extern_fn.rs new file mode 100644 index 0000000000000..b2caebc6fee0e --- /dev/null +++ b/src/test/ui/lint/auxiliary/external_extern_fn.rs @@ -0,0 +1,3 @@ +extern { + pub fn extern_fn(x: u8); +} diff --git a/src/test/ui/lint/clashing-extern-fn.rs b/src/test/ui/lint/clashing-extern-fn.rs new file mode 100644 index 0000000000000..32f3a78f4e980 --- /dev/null +++ b/src/test/ui/lint/clashing-extern-fn.rs @@ -0,0 +1,159 @@ +// check-pass +// aux-build:external_extern_fn.rs +#![crate_type = "lib"] +#![warn(clashing_extern_decl)] + +extern crate external_extern_fn; + +extern { + fn clash(x: u8); + fn no_clash(x: u8); +} + +fn redeclared_different_signature() { + extern { + fn clash(x: u64); //~ WARN `clash` redeclared with a different signature + } + + unsafe { + clash(123); + no_clash(123); + } +} + +fn redeclared_same_signature() { + extern { + fn no_clash(x: u8); + } + unsafe { + no_clash(123); + } +} + +extern { + fn extern_fn(x: u64); +} + +fn extern_clash() { + extern { + fn extern_fn(x: u32); //~ WARN `extern_fn` redeclared with a different signature + } + unsafe { + extern_fn(123); + } +} + +fn extern_no_clash() { + unsafe { + external_extern_fn::extern_fn(123); + crate::extern_fn(123); + } +} +extern { + fn some_other_new_name(x: i16); + + #[link_name = "extern_link_name"] + fn some_new_name(x: i16); + + #[link_name = "link_name_same"] + fn both_names_different(x: i16); +} + +fn link_name_clash() { + extern { + fn extern_link_name(x: u32); + //~^ WARN `extern_link_name` redeclared with a different signature + + #[link_name = "some_other_new_name"] + //~^ WARN `some_other_extern_link_name` redeclares `some_other_new_name` with a different + fn some_other_extern_link_name(x: u32); + + #[link_name = "link_name_same"] + //~^ WARN `other_both_names_different` redeclares `link_name_same` with a different + fn other_both_names_different(x: u32); + } +} + +mod a { + extern { + fn different_mod(x: u8); + } +} +mod b { + extern { + fn different_mod(x: u64); //~ WARN `different_mod` redeclared with a different signature + } +} + +extern { + fn variadic_decl(x: u8, ...); +} + +fn variadic_clash() { + extern { + fn variadic_decl(x: u8); //~ WARN `variadic_decl` redeclared with a different signature + } +} + +#[no_mangle] +fn no_mangle_name(x: u8) { } + +extern { + #[link_name = "unique_link_name"] + fn link_name_specified(x: u8); +} + +fn tricky_no_clash() { + extern { + // Shouldn't warn, because the declaration above actually declares a different symbol (and + // Rust's name resolution rules around shadowing will handle this gracefully). + fn link_name_specified() -> u32; + + // The case of a no_mangle name colliding with an extern decl (see #28179) is related but + // shouldn't be reported by ClashingExternDecl, because this is an example of unmangled + // name clash causing bad behaviour in functions with a defined body. + fn no_mangle_name() -> u32; + } +} + +mod banana { + mod one { + #[repr(C)] struct Banana { weight: u32, length: u16 } + extern "C" { fn weigh_banana(count: *const Banana) -> u64; } + } + + mod two { + #[repr(C)] struct Banana { weight: u32, length: u16 } // note: distinct type + // This should not trigger the lint because two::Banana is structurally equivalent to + // one::Banana. + extern "C" { fn weigh_banana(count: *const Banana) -> u64; } + } + + mod three { + // This _should_ trigger the lint, because repr(packed) should generate a struct that has a + // different layout. + #[repr(packed)] struct Banana { weight: u32, length: u16 } + #[allow(improper_ctypes)] + extern "C" { fn weigh_banana(count: *const Banana) -> u64; } + //~^ WARN `weigh_banana` redeclared with a different signature + } +} + +mod sameish_members { + mod a { + #[repr(C)] + struct Point { x: i16, y: i16 } + + extern "C" { fn draw_point(p: Point); } + } + mod b { + #[repr(C)] + struct Point { coordinates: [i16; 2] } + + // It's possible we are overconservative for this case, as accessing the elements of the + // coordinates array might end up correctly accessing `.x` and `.y`. However, this may not + // always be the case, for every architecture and situation. This is also a really odd + // thing to do anyway. + extern "C" { fn draw_point(p: Point); } //~ WARN `draw_point` redeclared with a different + } +} diff --git a/src/test/ui/lint/clashing-extern-fn.stderr b/src/test/ui/lint/clashing-extern-fn.stderr new file mode 100644 index 0000000000000..fb7bf135f538c --- /dev/null +++ b/src/test/ui/lint/clashing-extern-fn.stderr @@ -0,0 +1,121 @@ +warning: `clash` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:15:9 + | +LL | fn clash(x: u8); + | ---------------- `clash` previously declared here +... +LL | fn clash(x: u64); + | ^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | +note: the lint level is defined here + --> $DIR/clashing-extern-fn.rs:4:9 + | +LL | #![warn(clashing_extern_decl)] + | ^^^^^^^^^^^^^^^^^^^^ + = note: expected `unsafe extern "C" fn(u8)` + found `unsafe extern "C" fn(u64)` + +warning: `extern_fn` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:39:9 + | +LL | fn extern_fn(x: u64); + | --------------------- `extern_fn` previously declared here +... +LL | fn extern_fn(x: u32); + | ^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn(u64)` + found `unsafe extern "C" fn(u32)` + +warning: `extern_link_name` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:64:9 + | +LL | / #[link_name = "extern_link_name"] +LL | | fn some_new_name(x: i16); + | |_____________________________- `extern_link_name` previously declared here +... +LL | fn extern_link_name(x: u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn(i16)` + found `unsafe extern "C" fn(u32)` + +warning: `some_other_extern_link_name` redeclares `some_other_new_name` with a different signature + --> $DIR/clashing-extern-fn.rs:67:9 + | +LL | fn some_other_new_name(x: i16); + | ------------------------------- `some_other_new_name` previously declared here +... +LL | / #[link_name = "some_other_new_name"] +LL | | +LL | | fn some_other_extern_link_name(x: u32); + | |_______________________________________________^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn(i16)` + found `unsafe extern "C" fn(u32)` + +warning: `other_both_names_different` redeclares `link_name_same` with a different signature + --> $DIR/clashing-extern-fn.rs:71:9 + | +LL | / #[link_name = "link_name_same"] +LL | | fn both_names_different(x: i16); + | |____________________________________- `link_name_same` previously declared here +... +LL | / #[link_name = "link_name_same"] +LL | | +LL | | fn other_both_names_different(x: u32); + | |______________________________________________^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn(i16)` + found `unsafe extern "C" fn(u32)` + +warning: `different_mod` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:84:9 + | +LL | fn different_mod(x: u8); + | ------------------------ `different_mod` previously declared here +... +LL | fn different_mod(x: u64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn(u8)` + found `unsafe extern "C" fn(u64)` + +warning: `variadic_decl` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:94:9 + | +LL | fn variadic_decl(x: u8, ...); + | ----------------------------- `variadic_decl` previously declared here +... +LL | fn variadic_decl(x: u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn(u8, ...)` + found `unsafe extern "C" fn(u8)` + +warning: `weigh_banana` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:137:22 + | +LL | extern "C" { fn weigh_banana(count: *const Banana) -> u64; } + | --------------------------------------------- `weigh_banana` previously declared here +... +LL | extern "C" { fn weigh_banana(count: *const Banana) -> u64; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn(*const banana::one::Banana) -> u64` + found `unsafe extern "C" fn(*const banana::three::Banana) -> u64` + +warning: `draw_point` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:157:22 + | +LL | extern "C" { fn draw_point(p: Point); } + | ------------------------ `draw_point` previously declared here +... +LL | extern "C" { fn draw_point(p: Point); } + | ^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn(sameish_members::a::Point)` + found `unsafe extern "C" fn(sameish_members::b::Point)` + +warning: 9 warnings emitted + 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/crate_level_only_lint.rs b/src/test/ui/lint/crate_level_only_lint.rs new file mode 100644 index 0000000000000..d9673faa2142e --- /dev/null +++ b/src/test/ui/lint/crate_level_only_lint.rs @@ -0,0 +1,22 @@ +#![deny(uncommon_codepoints, unused_attributes)] + +mod foo { +#![allow(uncommon_codepoints)] +//~^ ERROR allow(uncommon_codepoints) is ignored unless specified at crate level [unused_attributes] +//~| ERROR allow(uncommon_codepoints) is ignored unless specified at crate level [unused_attributes] +//~| ERROR allow(uncommon_codepoints) is ignored unless specified at crate level [unused_attributes] + +#[allow(uncommon_codepoints)] +//~^ ERROR allow(uncommon_codepoints) is ignored unless specified at crate level [unused_attributes] +//~| ERROR allow(uncommon_codepoints) is ignored unless specified at crate level [unused_attributes] +//~| ERROR allow(uncommon_codepoints) is ignored unless specified at crate level [unused_attributes] +const BAR: f64 = 0.000001; + +} + +#[allow(uncommon_codepoints)] +//~^ ERROR allow(uncommon_codepoints) is ignored unless specified at crate level [unused_attributes] +//~| ERROR allow(uncommon_codepoints) is ignored unless specified at crate level [unused_attributes] +//~| ERROR allow(uncommon_codepoints) is ignored unless specified at crate level [unused_attributes] +fn main() { +} diff --git a/src/test/ui/lint/crate_level_only_lint.stderr b/src/test/ui/lint/crate_level_only_lint.stderr new file mode 100644 index 0000000000000..8fb06df2a481a --- /dev/null +++ b/src/test/ui/lint/crate_level_only_lint.stderr @@ -0,0 +1,62 @@ +error: allow(uncommon_codepoints) is ignored unless specified at crate level + --> $DIR/crate_level_only_lint.rs:4:10 + | +LL | #![allow(uncommon_codepoints)] + | ^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/crate_level_only_lint.rs:1:30 + | +LL | #![deny(uncommon_codepoints, unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error: allow(uncommon_codepoints) is ignored unless specified at crate level + --> $DIR/crate_level_only_lint.rs:9:9 + | +LL | #[allow(uncommon_codepoints)] + | ^^^^^^^^^^^^^^^^^^^ + +error: allow(uncommon_codepoints) is ignored unless specified at crate level + --> $DIR/crate_level_only_lint.rs:17:9 + | +LL | #[allow(uncommon_codepoints)] + | ^^^^^^^^^^^^^^^^^^^ + +error: allow(uncommon_codepoints) is ignored unless specified at crate level + --> $DIR/crate_level_only_lint.rs:4:10 + | +LL | #![allow(uncommon_codepoints)] + | ^^^^^^^^^^^^^^^^^^^ + +error: allow(uncommon_codepoints) is ignored unless specified at crate level + --> $DIR/crate_level_only_lint.rs:9:9 + | +LL | #[allow(uncommon_codepoints)] + | ^^^^^^^^^^^^^^^^^^^ + +error: allow(uncommon_codepoints) is ignored unless specified at crate level + --> $DIR/crate_level_only_lint.rs:17:9 + | +LL | #[allow(uncommon_codepoints)] + | ^^^^^^^^^^^^^^^^^^^ + +error: allow(uncommon_codepoints) is ignored unless specified at crate level + --> $DIR/crate_level_only_lint.rs:4:10 + | +LL | #![allow(uncommon_codepoints)] + | ^^^^^^^^^^^^^^^^^^^ + +error: allow(uncommon_codepoints) is ignored unless specified at crate level + --> $DIR/crate_level_only_lint.rs:9:9 + | +LL | #[allow(uncommon_codepoints)] + | ^^^^^^^^^^^^^^^^^^^ + +error: allow(uncommon_codepoints) is ignored unless specified at crate level + --> $DIR/crate_level_only_lint.rs:17:9 + | +LL | #[allow(uncommon_codepoints)] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + 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..ff33abfa64586 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 @@ -1,5 +1,6 @@ #![allow(unused_variables)] #![allow(non_camel_case_types)] +#![allow(clashing_extern_decl)] #![deny(dead_code)] #![crate_type="lib"] @@ -12,7 +13,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 +59,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..cf8f01ea19f0c 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 @@ -1,35 +1,35 @@ error: struct is never constructed: `Foo` - --> $DIR/lint-dead-code-3.rs:13:8 + --> $DIR/lint-dead-code-3.rs:14:8 | LL | struct Foo; | ^^^ | note: the lint level is defined here - --> $DIR/lint-dead-code-3.rs:3:9 + --> $DIR/lint-dead-code-3.rs:4:9 | 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:16:8 | LL | fn foo(&self) { - | ^^^^^^^^^^^^^ + | ^^^ error: function is never used: `bar` - --> $DIR/lint-dead-code-3.rs:20:4 + --> $DIR/lint-dead-code-3.rs:21:4 | LL | fn bar() { | ^^^ error: enum is never used: `c_void` - --> $DIR/lint-dead-code-3.rs:59:6 + --> $DIR/lint-dead-code-3.rs:60:6 | LL | enum c_void {} | ^^^^^^ -error: foreign function is never used: `free` - --> $DIR/lint-dead-code-3.rs:61:5 +error: function is never used: `free` + --> $DIR/lint-dead-code-3.rs:62: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/expansion-time-include.rs b/src/test/ui/lint/expansion-time-include.rs new file mode 100644 index 0000000000000..4ea89d5adff94 --- /dev/null +++ b/src/test/ui/lint/expansion-time-include.rs @@ -0,0 +1,4 @@ +// ignore-test auxiliary file for expansion-time.rs + +1 +2 diff --git a/src/test/ui/lint/expansion-time.rs b/src/test/ui/lint/expansion-time.rs new file mode 100644 index 0000000000000..6e420c51f0a7f --- /dev/null +++ b/src/test/ui/lint/expansion-time.rs @@ -0,0 +1,23 @@ +// check-pass + +#[warn(meta_variable_misuse)] +macro_rules! foo { + ( $($i:ident)* ) => { $($i)+ }; //~ WARN meta-variable repeats with different Kleene operator +} + +#[warn(missing_fragment_specifier)] +macro_rules! m { ($i) => {} } //~ WARN missing fragment specifier + //~| WARN this was previously accepted + +#[warn(soft_unstable)] +mod benches { + #[bench] //~ WARN use of unstable library feature 'test' + //~| WARN this was previously accepted + fn foo() {} +} + +#[warn(incomplete_include)] +fn main() { + // WARN see in the stderr file, the warning points to the included file. + include!("expansion-time-include.rs"); +} diff --git a/src/test/ui/lint/expansion-time.stderr b/src/test/ui/lint/expansion-time.stderr new file mode 100644 index 0000000000000..e6b5cf67e3904 --- /dev/null +++ b/src/test/ui/lint/expansion-time.stderr @@ -0,0 +1,56 @@ +warning: meta-variable repeats with different Kleene operator + --> $DIR/expansion-time.rs:5:29 + | +LL | ( $($i:ident)* ) => { $($i)+ }; + | - ^^ - conflicting repetition + | | + | expected repetition + | +note: the lint level is defined here + --> $DIR/expansion-time.rs:3:8 + | +LL | #[warn(meta_variable_misuse)] + | ^^^^^^^^^^^^^^^^^^^^ + +warning: missing fragment specifier + --> $DIR/expansion-time.rs:9:19 + | +LL | macro_rules! m { ($i) => {} } + | ^^ + | +note: the lint level is defined here + --> $DIR/expansion-time.rs:8:8 + | +LL | #[warn(missing_fragment_specifier)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 + +warning: use of unstable library feature 'test': `bench` is a part of custom test frameworks which are unstable + --> $DIR/expansion-time.rs:14:7 + | +LL | #[bench] + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/expansion-time.rs:12:8 + | +LL | #[warn(soft_unstable)] + | ^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #64266 + +warning: include macro expected single expression in source + --> $DIR/expansion-time-include.rs:4:1 + | +LL | 2 + | ^ + | +note: the lint level is defined here + --> $DIR/expansion-time.rs:19:8 + | +LL | #[warn(incomplete_include)] + | ^^^^^^^^^^^^^^^^^^ + +warning: 4 warnings emitted + 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..ae04612a4dd69 100644 --- a/src/test/ui/lint/inline-trait-and-foreign-items.stderr +++ b/src/test/ui/lint/inline-trait-and-foreign-items.stderr @@ -62,11 +62,11 @@ LL | type U = impl Trait; | -------------------- not a function or closure error: could not find defining uses - --> $DIR/inline-trait-and-foreign-items.rs:26:5 + --> $DIR/inline-trait-and-foreign-items.rs:26:14 | 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 b07474bb48673..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 @@ -2,7 +2,7 @@ warning: unused variable: `i_think_continually` --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:26:9 | LL | let i_think_continually = 2; - | ^^^^^^^^^^^^^^^^^^^ help: consider prefixing with an underscore: `_i_think_continually` + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_i_think_continually` | note: the lint level is defined here --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:5:9 @@ -12,22 +12,22 @@ 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: consider prefixing 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: consider prefixing 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 | LL | let (mut var, unused_var) = (1, 2); - | ^^^^^^^^^^ help: consider prefixing with an underscore: `_unused_var` + | ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_var` warning: unused variable: `corridors_of_light` --> $DIR/issue-47390-unused-variable-in-struct-pattern.rs:45:26 @@ -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-69485-var-size-diffs-too-large.rs b/src/test/ui/lint/issue-69485-var-size-diffs-too-large.rs new file mode 100644 index 0000000000000..49d489d916837 --- /dev/null +++ b/src/test/ui/lint/issue-69485-var-size-diffs-too-large.rs @@ -0,0 +1,10 @@ +// build-fail +// only-x86_64 + +fn main() { + Bug::V([0; !0]); //~ ERROR is too big for the current +} + +enum Bug { + V([u8; !0]), +} diff --git a/src/test/ui/lint/issue-69485-var-size-diffs-too-large.stderr b/src/test/ui/lint/issue-69485-var-size-diffs-too-large.stderr new file mode 100644 index 0000000000000..d31ce9cfe0c2b --- /dev/null +++ b/src/test/ui/lint/issue-69485-var-size-diffs-too-large.stderr @@ -0,0 +1,8 @@ +error: the type `[u8; 18446744073709551615]` is too big for the current architecture + --> $DIR/issue-69485-var-size-diffs-too-large.rs:5:12 + | +LL | Bug::V([0; !0]); + | ^^^^^^^ + +error: aborting due to previous error + 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-ctypes-66202.rs b/src/test/ui/lint/lint-ctypes-66202.rs new file mode 100644 index 0000000000000..ebab41d143e67 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-66202.rs @@ -0,0 +1,17 @@ +// check-pass + +#![deny(improper_ctypes)] + +// This test checks that return types are normalized before being checked for FFI-safety, and that +// transparent newtype wrappers are FFI-safe if the type being wrapped is FFI-safe. + +#[repr(transparent)] +pub struct W(T); + +extern "C" { + pub fn bare() -> (); + pub fn normalize() -> <() as ToOwned>::Owned; + pub fn transparent() -> W<()>; +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-73249-1.rs b/src/test/ui/lint/lint-ctypes-73249-1.rs new file mode 100644 index 0000000000000..cf416c3fe8b12 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-1.rs @@ -0,0 +1,21 @@ +// check-pass +#![deny(improper_ctypes)] + +pub trait Foo { + type Assoc: 'static; +} + +impl Foo for () { + type Assoc = u32; +} + +extern "C" { + pub fn lint_me(x: Bar<()>); +} + +#[repr(transparent)] +pub struct Bar { + value: &'static ::Assoc, +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-73249-2.rs b/src/test/ui/lint/lint-ctypes-73249-2.rs new file mode 100644 index 0000000000000..86cc5e2c31e81 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-2.rs @@ -0,0 +1,29 @@ +#![feature(type_alias_impl_trait)] +#![deny(improper_ctypes)] + +pub trait Baz { } + +impl Baz for () { } + +type Qux = impl Baz; + +fn assign() -> Qux {} + +pub trait Foo { + type Assoc: 'static; +} + +impl Foo for () { + type Assoc = Qux; +} + +#[repr(transparent)] +pub struct A { + x: &'static ::Assoc, +} + +extern "C" { + pub fn lint_me() -> A<()>; //~ ERROR: uses type `impl Baz` +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-73249-2.stderr b/src/test/ui/lint/lint-ctypes-73249-2.stderr new file mode 100644 index 0000000000000..36dbe3217d75a --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-2.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73249-2.rs:26:25 + | +LL | pub fn lint_me() -> A<()>; + | ^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73249-2.rs:2:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73249-3.rs b/src/test/ui/lint/lint-ctypes-73249-3.rs new file mode 100644 index 0000000000000..25c4e7c92a854 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-3.rs @@ -0,0 +1,21 @@ +#![feature(type_alias_impl_trait)] +#![deny(improper_ctypes)] + +pub trait Baz { } + +impl Baz for u32 { } + +type Qux = impl Baz; + +fn assign() -> Qux { 3 } + +#[repr(C)] +pub struct A { + x: Qux, +} + +extern "C" { + pub fn lint_me() -> A; //~ ERROR: uses type `impl Baz` +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-73249-3.stderr b/src/test/ui/lint/lint-ctypes-73249-3.stderr new file mode 100644 index 0000000000000..7d133287bd73e --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-3.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73249-3.rs:18:25 + | +LL | pub fn lint_me() -> A; + | ^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73249-3.rs:2:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73249-4.rs b/src/test/ui/lint/lint-ctypes-73249-4.rs new file mode 100644 index 0000000000000..6c72bd691b17c --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-4.rs @@ -0,0 +1,24 @@ +// check-pass +#![deny(improper_ctypes)] + +use std::marker::PhantomData; + +trait Foo { + type Assoc; +} + +impl Foo for () { + type Assoc = PhantomData<()>; +} + +#[repr(transparent)] +struct Wow where T: Foo> { + x: ::Assoc, + v: u32, +} + +extern "C" { + fn test(v: Wow<()>); +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-73249-5.rs b/src/test/ui/lint/lint-ctypes-73249-5.rs new file mode 100644 index 0000000000000..61e46983ede65 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-5.rs @@ -0,0 +1,21 @@ +#![feature(type_alias_impl_trait)] +#![deny(improper_ctypes)] + +pub trait Baz { } + +impl Baz for u32 { } + +type Qux = impl Baz; + +fn assign() -> Qux { 3 } + +#[repr(transparent)] +pub struct A { + x: Qux, +} + +extern "C" { + pub fn lint_me() -> A; //~ ERROR: uses type `impl Baz` +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-73249-5.stderr b/src/test/ui/lint/lint-ctypes-73249-5.stderr new file mode 100644 index 0000000000000..d2780cb60e7dd --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249-5.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73249-5.rs:18:25 + | +LL | pub fn lint_me() -> A; + | ^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73249-5.rs:2:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73249.rs b/src/test/ui/lint/lint-ctypes-73249.rs new file mode 100644 index 0000000000000..5b48fa9b7376f --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73249.rs @@ -0,0 +1,21 @@ +// check-pass +#![deny(improper_ctypes)] + +pub trait Foo { + type Assoc; +} + +impl Foo for () { + type Assoc = u32; +} + +extern "C" { + pub fn lint_me(x: Bar<()>); +} + +#[repr(transparent)] +pub struct Bar { + value: ::Assoc, +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-73251-1.rs b/src/test/ui/lint/lint-ctypes-73251-1.rs new file mode 100644 index 0000000000000..2ce80982f5ca1 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251-1.rs @@ -0,0 +1,24 @@ +#![feature(type_alias_impl_trait)] +#![deny(improper_ctypes)] + +pub trait Baz { } + +impl Baz for u32 { } + +type Qux = impl Baz; + +pub trait Foo { + type Assoc; +} + +impl Foo for u32 { + type Assoc = Qux; +} + +fn assign() -> Qux { 1 } + +extern "C" { + pub fn lint_me() -> ::Assoc; //~ ERROR: uses type `impl Baz` +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-73251-1.stderr b/src/test/ui/lint/lint-ctypes-73251-1.stderr new file mode 100644 index 0000000000000..0b4237bb96fb7 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251-1.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl Baz`, which is not FFI-safe + --> $DIR/lint-ctypes-73251-1.rs:21:25 + | +LL | pub fn lint_me() -> ::Assoc; + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73251-1.rs:2:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73251-2.rs b/src/test/ui/lint/lint-ctypes-73251-2.rs new file mode 100644 index 0000000000000..3427c657b42ac --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251-2.rs @@ -0,0 +1,32 @@ +#![feature(type_alias_impl_trait)] +#![deny(improper_ctypes)] + +pub trait TraitA { + type Assoc; +} + +impl TraitA for u32 { + type Assoc = u32; +} + +pub trait TraitB { + type Assoc; +} + +impl TraitB for T where T: TraitA { + type Assoc = ::Assoc; +} + +type AliasA = impl TraitA; + +type AliasB = impl TraitB; + +fn use_of_a() -> AliasA { 3 } + +fn use_of_b() -> AliasB { 3 } + +extern "C" { + pub fn lint_me() -> ::Assoc; //~ ERROR: uses type `impl TraitA` +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-73251-2.stderr b/src/test/ui/lint/lint-ctypes-73251-2.stderr new file mode 100644 index 0000000000000..43f7629b043a9 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251-2.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `impl TraitA`, which is not FFI-safe + --> $DIR/lint-ctypes-73251-2.rs:29:25 + | +LL | pub fn lint_me() -> ::Assoc; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-73251-2.rs:2:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = note: opaque types have no C equivalent + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-ctypes-73251.rs b/src/test/ui/lint/lint-ctypes-73251.rs new file mode 100644 index 0000000000000..ebc2ca77b67a1 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-73251.rs @@ -0,0 +1,22 @@ +// check-pass + +#![feature(type_alias_impl_trait)] +#![deny(improper_ctypes)] + +pub trait Foo { + type Assoc; +} + +impl Foo for () { + type Assoc = u32; +} + +type Bar = impl Foo; + +fn assign() -> Bar {} + +extern "C" { + pub fn lint_me() -> ::Assoc; +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-fn.rs b/src/test/ui/lint/lint-ctypes-fn.rs new file mode 100644 index 0000000000000..67dd7abcf79ef --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-fn.rs @@ -0,0 +1,182 @@ +#![feature(rustc_private)] + +#![allow(private_in_public)] +#![deny(improper_ctypes_definitions)] + +extern crate libc; + +use std::default::Default; +use std::marker::PhantomData; + +trait Mirror { type It: ?Sized; } + +impl Mirror for T { type It = Self; } + +#[repr(C)] +pub struct StructWithProjection(*mut ::It); + +#[repr(C)] +pub struct StructWithProjectionAndLifetime<'a>( + &'a mut as Mirror>::It +); + +pub type I32Pair = (i32, i32); + +#[repr(C)] +pub struct ZeroSize; + +pub type RustFn = fn(); + +pub type RustBadRet = extern fn() -> Box; + +pub type CVoidRet = (); + +pub struct Foo; + +#[repr(transparent)] +pub struct TransparentI128(i128); + +#[repr(transparent)] +pub struct TransparentStr(&'static str); + +#[repr(transparent)] +pub struct TransparentBadFn(RustBadRet); + +#[repr(transparent)] +pub struct TransparentInt(u32); + +#[repr(transparent)] +pub struct TransparentRef<'a>(&'a TransparentInt); + +#[repr(transparent)] +pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>); + +#[repr(transparent)] +pub struct TransparentUnit(f32, PhantomData); + +#[repr(transparent)] +pub struct TransparentCustomZst(i32, ZeroSize); + +#[repr(C)] +pub struct ZeroSizeWithPhantomData(PhantomData); + +pub extern "C" fn ptr_type1(size: *const Foo) { } + +pub extern "C" fn ptr_type2(size: *const Foo) { } + +pub extern "C" fn slice_type(p: &[u32]) { } +//~^ ERROR: uses type `[u32]` + +pub extern "C" fn str_type(p: &str) { } +//~^ ERROR: uses type `str` + +pub extern "C" fn box_type(p: Box) { } +//~^ ERROR uses type `std::boxed::Box` + +pub extern "C" fn char_type(p: char) { } +//~^ ERROR uses type `char` + +pub extern "C" fn i128_type(p: i128) { } +//~^ ERROR uses type `i128` + +pub extern "C" fn u128_type(p: u128) { } +//~^ ERROR uses type `u128` + +pub extern "C" fn tuple_type(p: (i32, i32)) { } +//~^ ERROR uses type `(i32, i32)` + +pub extern "C" fn tuple_type2(p: I32Pair) { } +//~^ ERROR uses type `(i32, i32)` + +pub extern "C" fn zero_size(p: ZeroSize) { } +//~^ ERROR uses type `ZeroSize` + +pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } +//~^ ERROR uses type `ZeroSizeWithPhantomData` + +pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { +//~^ ERROR uses type `std::marker::PhantomData` + Default::default() +} + +pub extern "C" fn fn_type(p: RustFn) { } +//~^ ERROR uses type `fn()` + +pub extern "C" fn fn_type2(p: fn()) { } +//~^ ERROR uses type `fn()` + +pub extern "C" fn fn_contained(p: RustBadRet) { } +//~^ ERROR: uses type `std::boxed::Box` + +pub extern "C" fn transparent_i128(p: TransparentI128) { } +//~^ ERROR: uses type `i128` + +pub extern "C" fn transparent_str(p: TransparentStr) { } +//~^ ERROR: uses type `str` + +pub extern "C" fn transparent_fn(p: TransparentBadFn) { } +//~^ ERROR: uses type `std::boxed::Box` + +pub extern "C" fn good3(fptr: Option) { } + +pub extern "C" fn good4(aptr: &[u8; 4 as usize]) { } + +pub extern "C" fn good5(s: StructWithProjection) { } + +pub extern "C" fn good6(s: StructWithProjectionAndLifetime) { } + +pub extern "C" fn good7(fptr: extern fn() -> ()) { } + +pub extern "C" fn good8(fptr: extern fn() -> !) { } + +pub extern "C" fn good9() -> () { } + +pub extern "C" fn good10() -> CVoidRet { } + +pub extern "C" fn good11(size: isize) { } + +pub extern "C" fn good12(size: usize) { } + +pub extern "C" fn good13(n: TransparentInt) { } + +pub extern "C" fn good14(p: TransparentRef) { } + +pub extern "C" fn good15(p: TransparentLifetime) { } + +pub extern "C" fn good16(p: TransparentUnit) { } + +pub extern "C" fn good17(p: TransparentCustomZst) { } + +#[allow(improper_ctypes_definitions)] +pub extern "C" fn good18(_: &String) { } + +#[cfg(not(target_arch = "wasm32"))] +pub extern "C" fn good1(size: *const libc::c_int) { } + +#[cfg(not(target_arch = "wasm32"))] +pub extern "C" fn good2(size: *const libc::c_uint) { } + +pub extern "C" fn unused_generic1(size: *const Foo) { } + +pub extern "C" fn unused_generic2() -> PhantomData { +//~^ ERROR uses type `std::marker::PhantomData` + Default::default() +} + +pub extern "C" fn used_generic1(x: T) { } + +pub extern "C" fn used_generic2(x: T, size: *const Foo) { } + +pub extern "C" fn used_generic3() -> T { + Default::default() +} + +pub extern "C" fn used_generic4(x: Vec) { } +//~^ ERROR: uses type `std::vec::Vec` + +pub extern "C" fn used_generic5() -> Vec { +//~^ ERROR: uses type `std::vec::Vec` + Default::default() +} + +fn main() {} diff --git a/src/test/ui/lint/lint-ctypes-fn.stderr b/src/test/ui/lint/lint-ctypes-fn.stderr new file mode 100644 index 0000000000000..66cf195327890 --- /dev/null +++ b/src/test/ui/lint/lint-ctypes-fn.stderr @@ -0,0 +1,191 @@ +error: `extern` fn uses type `[u32]`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:67:33 + | +LL | pub extern "C" fn slice_type(p: &[u32]) { } + | ^^^^^^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/lint-ctypes-fn.rs:4:9 + | +LL | #![deny(improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `str`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:70:31 + | +LL | pub extern "C" fn str_type(p: &str) { } + | ^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: string slices have no C equivalent + +error: `extern` fn uses type `std::boxed::Box`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:73:31 + | +LL | pub extern "C" fn box_type(p: Box) { } + | ^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout + +error: `extern` fn uses type `char`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:76:32 + | +LL | pub extern "C" fn char_type(p: char) { } + | ^^^^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:79:32 + | +LL | pub extern "C" fn i128_type(p: i128) { } + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `u128`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:82:32 + | +LL | pub extern "C" fn u128_type(p: u128) { } + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:85:33 + | +LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } + | ^^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:88:34 + | +LL | pub extern "C" fn tuple_type2(p: I32Pair) { } + | ^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `ZeroSize`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:91:32 + | +LL | pub extern "C" fn zero_size(p: ZeroSize) { } + | ^^^^^^^^ not FFI-safe + | + = help: consider adding a member to this struct + = note: this struct has no fields +note: the type is defined here + --> $DIR/lint-ctypes-fn.rs:26:1 + | +LL | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:94:40 + | +LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } + | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: composed only of `PhantomData` +note: the type is defined here + --> $DIR/lint-ctypes-fn.rs:61:1 + | +LL | pub struct ZeroSizeWithPhantomData(PhantomData); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `std::marker::PhantomData`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:97:51 + | +LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { + | ^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: composed only of `PhantomData` + +error: `extern` fn uses type `fn()`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:102:30 + | +LL | pub extern "C" fn fn_type(p: RustFn) { } + | ^^^^^^ not FFI-safe + | + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `extern` fn uses type `fn()`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:105:31 + | +LL | pub extern "C" fn fn_type2(p: fn()) { } + | ^^^^ not FFI-safe + | + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `extern` fn uses type `std::boxed::Box`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:108:35 + | +LL | pub extern "C" fn fn_contained(p: RustBadRet) { } + | ^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout + +error: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:111:39 + | +LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `str`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:114:38 + | +LL | pub extern "C" fn transparent_str(p: TransparentStr) { } + | ^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: string slices have no C equivalent + +error: `extern` fn uses type `std::boxed::Box`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:117:37 + | +LL | pub extern "C" fn transparent_fn(p: TransparentBadFn) { } + | ^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout + +error: `extern` fn uses type `std::marker::PhantomData`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:161:43 + | +LL | pub extern "C" fn unused_generic2() -> PhantomData { + | ^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: composed only of `PhantomData` + +error: `extern` fn uses type `std::vec::Vec`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:174:39 + | +LL | pub extern "C" fn used_generic4(x: Vec) { } + | ^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout + +error: `extern` fn uses type `std::vec::Vec`, which is not FFI-safe + --> $DIR/lint-ctypes-fn.rs:177:41 + | +LL | pub extern "C" fn used_generic5() -> Vec { + | ^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout + +error: aborting due to 20 previous errors + 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-match-arms.stderr b/src/test/ui/lint/lint-match-arms.stderr index b124971f90512..1bc0e41fd550c 100644 --- a/src/test/ui/lint/lint-match-arms.stderr +++ b/src/test/ui/lint/lint-match-arms.stderr @@ -2,7 +2,7 @@ error: unused variable: `y` --> $DIR/lint-match-arms.rs:5:9 | LL | y => (), - | ^ help: consider prefixing with an underscore: `_y` + | ^ help: if this is intentional, prefix it with an underscore: `_y` | note: the lint level is defined here --> $DIR/lint-match-arms.rs:3:16 diff --git a/src/test/ui/lint/lint-missing-doc.rs b/src/test/ui/lint/lint-missing-doc.rs index 77f9a3770a339..bab6f4e9e5e15 100644 --- a/src/test/ui/lint/lint-missing-doc.rs +++ b/src/test/ui/lint/lint-missing-doc.rs @@ -50,8 +50,8 @@ trait B { } pub trait C { //~ ERROR: missing documentation for a trait - fn foo(&self); //~ ERROR: missing documentation for a trait method - fn foo_with_impl(&self) {} //~ ERROR: missing documentation for a trait method + fn foo(&self); //~ ERROR: missing documentation for an associated function + fn foo_with_impl(&self) {} //~ ERROR: missing documentation for an associated function } #[allow(missing_docs)] @@ -78,7 +78,7 @@ impl Foo { } impl PubFoo { - pub fn foo() {} //~ ERROR: missing documentation for a method + pub fn foo() {} //~ ERROR: missing documentation for an associated function /// dox pub fn foo1() {} fn foo2() {} diff --git a/src/test/ui/lint/lint-missing-doc.stderr b/src/test/ui/lint/lint-missing-doc.stderr index a18a97e5f7fb5..21da4fae4c161 100644 --- a/src/test/ui/lint/lint-missing-doc.stderr +++ b/src/test/ui/lint/lint-missing-doc.stderr @@ -40,13 +40,13 @@ error: missing documentation for a trait LL | pub trait C { | ^^^^^^^^^^^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/lint-missing-doc.rs:53:5 | LL | fn foo(&self); | ^^^^^^^^^^^^^^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/lint-missing-doc.rs:54:5 | LL | fn foo_with_impl(&self) {} @@ -64,7 +64,7 @@ error: missing documentation for an associated type LL | type AssociatedTypeDef = Self; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/lint-missing-doc.rs:81:5 | LL | pub fn foo() {} 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.rs b/src/test/ui/lint/lint-pre-expansion-extern-module.rs new file mode 100644 index 0000000000000..30e2ed8b7a623 --- /dev/null +++ b/src/test/ui/lint/lint-pre-expansion-extern-module.rs @@ -0,0 +1,7 @@ +// check-pass +// compile-flags: -W rust-2018-compatibility +// error-pattern: `try` is a keyword in the 2018 edition + +fn main() {} + +mod lint_pre_expansion_extern_module_aux; diff --git a/src/test/ui/lint/lint-pre-expansion-extern-module.stderr b/src/test/ui/lint/lint-pre-expansion-extern-module.stderr new file mode 100644 index 0000000000000..6efd03f14a1dc --- /dev/null +++ b/src/test/ui/lint/lint-pre-expansion-extern-module.stderr @@ -0,0 +1,12 @@ +warning: `try` is a keyword in the 2018 edition + --> $DIR/lint_pre_expansion_extern_module_aux.rs:3:8 + | +LL | pub fn try() {} + | ^^^ help: you can use a raw identifier to stay compatible: `r#try` + | + = note: `-W keyword-idents` implied by `-W 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/lint/lint-removed-allow.stderr b/src/test/ui/lint/lint-removed-allow.stderr index 5ab95c89b9c7e..029334c2eb67c 100644 --- a/src/test/ui/lint/lint-removed-allow.stderr +++ b/src/test/ui/lint/lint-removed-allow.stderr @@ -2,7 +2,7 @@ error: unused variable: `unused` --> $DIR/lint-removed-allow.rs:8:17 | LL | fn main() { let unused = (); } - | ^^^^^^ help: consider prefixing with an underscore: `_unused` + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here --> $DIR/lint-removed-allow.rs:7:8 diff --git a/src/test/ui/lint/lint-removed-cmdline.stderr b/src/test/ui/lint/lint-removed-cmdline.stderr index a9ebd3e32712c..1c45c38774e5a 100644 --- a/src/test/ui/lint/lint-removed-cmdline.stderr +++ b/src/test/ui/lint/lint-removed-cmdline.stderr @@ -18,7 +18,7 @@ error: unused variable: `unused` --> $DIR/lint-removed-cmdline.rs:12:17 | LL | fn main() { let unused = (); } - | ^^^^^^ help: consider prefixing with an underscore: `_unused` + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here --> $DIR/lint-removed-cmdline.rs:11:8 @@ -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 2c043392f098c..44480d84203bd 100644 --- a/src/test/ui/lint/lint-removed.stderr +++ b/src/test/ui/lint/lint-removed.stderr @@ -10,7 +10,7 @@ error: unused variable: `unused` --> $DIR/lint-removed.rs:8:17 | LL | fn main() { let unused = (); } - | ^^^^^^ help: consider prefixing with an underscore: `_unused` + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here --> $DIR/lint-removed.rs:7:8 @@ -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-allow.stderr b/src/test/ui/lint/lint-renamed-allow.stderr index 9da74f61b7569..46f6a10de2743 100644 --- a/src/test/ui/lint/lint-renamed-allow.stderr +++ b/src/test/ui/lint/lint-renamed-allow.stderr @@ -2,7 +2,7 @@ error: unused variable: `unused` --> $DIR/lint-renamed-allow.rs:8:17 | LL | fn main() { let unused = (); } - | ^^^^^^ help: consider prefixing with an underscore: `_unused` + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here --> $DIR/lint-renamed-allow.rs:7:8 diff --git a/src/test/ui/lint/lint-renamed-cmdline.stderr b/src/test/ui/lint/lint-renamed-cmdline.stderr index 235215598a2bd..1c37a5baa6db0 100644 --- a/src/test/ui/lint/lint-renamed-cmdline.stderr +++ b/src/test/ui/lint/lint-renamed-cmdline.stderr @@ -18,7 +18,7 @@ error: unused variable: `unused` --> $DIR/lint-renamed-cmdline.rs:8:17 | LL | fn main() { let unused = (); } - | ^^^^^^ help: consider prefixing with an underscore: `_unused` + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here --> $DIR/lint-renamed-cmdline.rs:7:8 @@ -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 dc43f2e4c46da..9842545714529 100644 --- a/src/test/ui/lint/lint-renamed.stderr +++ b/src/test/ui/lint/lint-renamed.stderr @@ -10,7 +10,7 @@ error: unused variable: `unused` --> $DIR/lint-renamed.rs:4:17 | LL | fn main() { let unused = (); } - | ^^^^^^ help: consider prefixing with an underscore: `_unused` + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | note: the lint level is defined here --> $DIR/lint-renamed.rs:3:8 @@ -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-unused-variables.stderr b/src/test/ui/lint/lint-unused-variables.stderr index 57389f8d12010..d6e684e830651 100644 --- a/src/test/ui/lint/lint-unused-variables.stderr +++ b/src/test/ui/lint/lint-unused-variables.stderr @@ -2,7 +2,7 @@ error: unused variable: `a` --> $DIR/lint-unused-variables.rs:8:5 | LL | a: i32, - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: the lint level is defined here --> $DIR/lint-unused-variables.rs:5:9 @@ -14,61 +14,61 @@ error: unused variable: `b` --> $DIR/lint-unused-variables.rs:14:5 | LL | b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` --> $DIR/lint-unused-variables.rs:68:9 | LL | a: i32, - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` --> $DIR/lint-unused-variables.rs:74:9 | LL | b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `b` --> $DIR/lint-unused-variables.rs:42:9 | LL | b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `b` --> $DIR/lint-unused-variables.rs:47:9 | LL | b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` --> $DIR/lint-unused-variables.rs:22:9 | LL | a: i32, - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` --> $DIR/lint-unused-variables.rs:29:9 | LL | b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `b` --> $DIR/lint-unused-variables.rs:34:9 | LL | b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `b` --> $DIR/lint-unused-variables.rs:55:9 | LL | b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `b` --> $DIR/lint-unused-variables.rs:60:9 | LL | b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: aborting due to 11 previous errors diff --git a/src/test/ui/lint/lint-uppercase-variables.stderr b/src/test/ui/lint/lint-uppercase-variables.stderr index 7c2497758d955..d476d856e24c5 100644 --- a/src/test/ui/lint/lint-uppercase-variables.stderr +++ b/src/test/ui/lint/lint-uppercase-variables.stderr @@ -22,7 +22,7 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:22:9 | LL | Foo => {} - | ^^^ help: consider prefixing with an underscore: `_Foo` + | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` | note: the lint level is defined here --> $DIR/lint-uppercase-variables.rs:1:9 @@ -35,13 +35,13 @@ warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:28:9 | LL | let Foo = foo::Foo::Foo; - | ^^^ help: consider prefixing with an underscore: `_Foo` + | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` warning: unused variable: `Foo` --> $DIR/lint-uppercase-variables.rs:33:17 | LL | fn in_param(Foo: foo::Foo) {} - | ^^^ help: consider prefixing with an underscore: `_Foo` + | ^^^ help: if this is intentional, prefix it with an underscore: `_Foo` error: structure field `X` should have a snake case name --> $DIR/lint-uppercase-variables.rs:10:5 @@ -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/lint_pre_expansion_extern_module_aux.rs b/src/test/ui/lint/lint_pre_expansion_extern_module_aux.rs new file mode 100644 index 0000000000000..71dec40ea44f0 --- /dev/null +++ b/src/test/ui/lint/lint_pre_expansion_extern_module_aux.rs @@ -0,0 +1,3 @@ +// ignore-test: not a test + +pub fn try() {} diff --git a/src/test/ui/lint/lints-in-foreign-macros.rs b/src/test/ui/lint/lints-in-foreign-macros.rs index c96b8f1a5cf4a..1e8b6788a60bb 100644 --- a/src/test/ui/lint/lints-in-foreign-macros.rs +++ b/src/test/ui/lint/lints-in-foreign-macros.rs @@ -1,7 +1,7 @@ // aux-build:lints-in-foreign-macros.rs // check-pass -#![warn(unused_imports)] //~ missing documentation for crate [missing_docs] +#![warn(unused_imports)] //~ missing documentation for the crate [missing_docs] #![warn(missing_docs)] #[macro_use] diff --git a/src/test/ui/lint/lints-in-foreign-macros.stderr b/src/test/ui/lint/lints-in-foreign-macros.stderr index 207d85a89c723..ea8d4bf964115 100644 --- a/src/test/ui/lint/lints-in-foreign-macros.stderr +++ b/src/test/ui/lint/lints-in-foreign-macros.stderr @@ -26,7 +26,7 @@ warning: unused import: `std::string::ToString` LL | mod d { baz2!(use std::string::ToString;); } | ^^^^^^^^^^^^^^^^^^^^^ -warning: missing documentation for crate +warning: missing documentation for the crate --> $DIR/lints-in-foreign-macros.rs:4:1 | LL | / #![warn(unused_imports)] @@ -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/opaque-ty-ffi-unsafe.rs b/src/test/ui/lint/opaque-ty-ffi-unsafe.rs index 25d5f8ec68aa0..3cbc084ecae7c 100644 --- a/src/test/ui/lint/opaque-ty-ffi-unsafe.rs +++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.rs @@ -1,5 +1,4 @@ #![feature(type_alias_impl_trait)] - #![deny(improper_ctypes)] type A = impl Fn(); @@ -10,7 +9,7 @@ pub fn ret_closure() -> A { extern "C" { pub fn a(_: A); - //~^ ERROR `extern` block uses type `A`, which is not FFI-safe +//~^ ERROR `extern` block uses type `impl std::ops::Fn<()>`, which is not FFI-safe } fn main() {} diff --git a/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr b/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr index 712095e3208bd..06dfb7b8fbeca 100644 --- a/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr +++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr @@ -1,11 +1,11 @@ -error: `extern` block uses type `A`, which is not FFI-safe - --> $DIR/opaque-ty-ffi-unsafe.rs:12:17 +error: `extern` block uses type `impl std::ops::Fn<()>`, which is not FFI-safe + --> $DIR/opaque-ty-ffi-unsafe.rs:11:17 | LL | pub fn a(_: A); | ^ not FFI-safe | note: the lint level is defined here - --> $DIR/opaque-ty-ffi-unsafe.rs:3:9 + --> $DIR/opaque-ty-ffi-unsafe.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ 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/unaligned_references.rs b/src/test/ui/lint/unaligned_references.rs new file mode 100644 index 0000000000000..c4e5d065643c8 --- /dev/null +++ b/src/test/ui/lint/unaligned_references.rs @@ -0,0 +1,29 @@ +#![deny(unaligned_references)] + +#[repr(packed)] +pub struct Good { + data: u64, + ptr: &'static u64, + data2: [u64; 2], + aligned: [u8; 32], +} + +fn main() { + unsafe { + let good = Good { data: 0, ptr: &0, data2: [0, 0], aligned: [0; 32] }; + + let _ = &good.ptr; //~ ERROR reference to packed field + let _ = &good.data; //~ ERROR reference to packed field + // Error even when turned into raw pointer immediately. + let _ = &good.data as *const _; //~ ERROR reference to packed field + let _: *const _ = &good.data; //~ ERROR reference to packed field + // Error on method call. + let _ = good.data.clone(); //~ ERROR reference to packed field + // Error for nested fields. + let _ = &good.data2[0]; //~ ERROR reference to packed field + + let _ = &*good.ptr; // ok, behind a pointer + let _ = &good.aligned; // ok, has align 1 + let _ = &good.aligned[2]; // ok, has align 1 + } +} diff --git a/src/test/ui/lint/unaligned_references.stderr b/src/test/ui/lint/unaligned_references.stderr new file mode 100644 index 0000000000000..8786b9c05db27 --- /dev/null +++ b/src/test/ui/lint/unaligned_references.stderr @@ -0,0 +1,55 @@ +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:15:17 + | +LL | let _ = &good.ptr; + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unaligned_references.rs:1:9 + | +LL | #![deny(unaligned_references)] + | ^^^^^^^^^^^^^^^^^^^^ + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:16:17 + | +LL | let _ = &good.data; + | ^^^^^^^^^^ + | + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:18:17 + | +LL | let _ = &good.data as *const _; + | ^^^^^^^^^^ + | + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:19:27 + | +LL | let _: *const _ = &good.data; + | ^^^^^^^^^^ + | + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:21:17 + | +LL | let _ = good.data.clone(); + | ^^^^^^^^^ + | + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:23:17 + | +LL | let _ = &good.data2[0]; + | ^^^^^^^^^^^^^^ + | + = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/lint/uninitialized-zeroed.stderr b/src/test/ui/lint/uninitialized-zeroed.stderr index 6d669184deb3e..bf0562713a497 100644 --- a/src/test/ui/lint/uninitialized-zeroed.stderr +++ b/src/test/ui/lint/uninitialized-zeroed.stderr @@ -1,4 +1,4 @@ -error: the type `&'static T` does not permit zero-initialization +error: the type `&T` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:29:32 | LL | let _val: &'static T = mem::zeroed(); @@ -14,7 +14,7 @@ LL | #![deny(invalid_value)] | ^^^^^^^^^^^^^ = note: references must be non-null -error: the type `&'static T` does not permit being left uninitialized +error: the type `&T` does not permit being left uninitialized --> $DIR/uninitialized-zeroed.rs:30:32 | LL | let _val: &'static T = mem::uninitialized(); @@ -25,7 +25,7 @@ LL | let _val: &'static T = mem::uninitialized(); | = note: references must be non-null -error: the type `Wrap<&'static T>` does not permit zero-initialization +error: the type `Wrap<&T>` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:32:38 | LL | let _val: Wrap<&'static T> = mem::zeroed(); @@ -40,7 +40,7 @@ note: references must be non-null (in this struct field) LL | struct Wrap { wrapped: T } | ^^^^^^^^^^ -error: the type `Wrap<&'static T>` does not permit being left uninitialized +error: the type `Wrap<&T>` does not permit being left uninitialized --> $DIR/uninitialized-zeroed.rs:33:38 | LL | let _val: Wrap<&'static T> = mem::uninitialized(); @@ -121,7 +121,7 @@ LL | let _val: Void = mem::uninitialized(); | = note: enums with no variants have no valid value -error: the type `&'static i32` does not permit zero-initialization +error: the type `&i32` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:49:34 | LL | let _val: &'static i32 = mem::zeroed(); @@ -132,7 +132,7 @@ LL | let _val: &'static i32 = mem::zeroed(); | = note: references must be non-null -error: the type `&'static i32` does not permit being left uninitialized +error: the type `&i32` does not permit being left uninitialized --> $DIR/uninitialized-zeroed.rs:50:34 | LL | let _val: &'static i32 = mem::uninitialized(); @@ -366,7 +366,7 @@ LL | let _val: NonBig = mem::uninitialized(); | = note: `NonBig` must be initialized inside its custom valid range -error: the type `&'static i32` does not permit zero-initialization +error: the type `&i32` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:84:34 | LL | let _val: &'static i32 = mem::transmute(0usize); @@ -377,7 +377,7 @@ LL | let _val: &'static i32 = mem::transmute(0usize); | = note: references must be non-null -error: the type `&'static [i32]` does not permit zero-initialization +error: the type `&[i32]` does not permit zero-initialization --> $DIR/uninitialized-zeroed.rs:85:36 | LL | let _val: &'static [i32] = mem::transmute((0usize, 0usize)); 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 7adb6a3295b0e..2c5550ac47f22 100644 --- a/src/test/ui/liveness/liveness-unused.stderr +++ b/src/test/ui/liveness/liveness-unused.stderr @@ -17,7 +17,7 @@ error: unused variable: `x` --> $DIR/liveness-unused.rs:8:7 | LL | fn f1(x: isize) { - | ^ help: consider prefixing with an underscore: `_x` + | ^ help: if this is intentional, prefix it with an underscore: `_x` | note: the lint level is defined here --> $DIR/liveness-unused.rs:2:9 @@ -29,25 +29,25 @@ error: unused variable: `x` --> $DIR/liveness-unused.rs:12:8 | LL | fn f1b(x: &mut isize) { - | ^ help: consider prefixing with an underscore: `_x` + | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` --> $DIR/liveness-unused.rs:20:9 | LL | let x: isize; - | ^ help: consider prefixing with an underscore: `_x` + | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` --> $DIR/liveness-unused.rs:25:9 | LL | let x = 3; - | ^ help: consider prefixing with an underscore: `_x` + | ^ 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 @@ -76,25 +76,25 @@ error: unused variable: `i` --> $DIR/liveness-unused.rs:59:12 | LL | Some(i) => { - | ^ help: consider prefixing with an underscore: `_i` + | ^ help: if this is intentional, prefix it with an underscore: `_i` error: unused variable: `x` --> $DIR/liveness-unused.rs:79:9 | LL | for x in 1..10 { } - | ^ help: consider prefixing with an underscore: `_x` + | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` --> $DIR/liveness-unused.rs:84:10 | LL | for (x, _) in [1, 2, 3].iter().enumerate() { } - | ^ help: consider prefixing with an underscore: `_x` + | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` --> $DIR/liveness-unused.rs:89:13 | LL | for (_, x) in [1, 2, 3].iter().enumerate() { - | ^ help: consider prefixing with an underscore: `_x` + | ^ help: if this is intentional, prefix it with an underscore: `_x` error: variable `x` is assigned to, but never used --> $DIR/liveness-unused.rs:112:9 @@ -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/liveness/liveness-upvars.rs b/src/test/ui/liveness/liveness-upvars.rs new file mode 100644 index 0000000000000..b2837e74b8c51 --- /dev/null +++ b/src/test/ui/liveness/liveness-upvars.rs @@ -0,0 +1,108 @@ +// edition:2018 +// check-pass +#![warn(unused)] +#![allow(unreachable_code)] + +pub fn unintentional_copy_one() { + let mut last = None; + let mut f = move |s| { + last = Some(s); //~ WARN value assigned to `last` is never read + //~| WARN unused variable: `last` + }; + f("a"); + f("b"); + f("c"); + dbg!(last.unwrap()); +} + +pub fn unintentional_copy_two() { + let mut sum = 0; + (1..10).for_each(move |x| { + sum += x; //~ WARN unused variable: `sum` + }); + dbg!(sum); +} + +pub fn f() { + let mut c = 0; + + // Captured by value, but variable is dead on entry. + move || { + c = 1; //~ WARN value captured by `c` is never read + println!("{}", c); + }; + let _ = async move { + c = 1; //~ WARN value captured by `c` is never read + println!("{}", c); + }; + + // Read and written to, but never actually used. + move || { + c += 1; //~ WARN unused variable: `c` + }; + let _ = async move { + c += 1; //~ WARN value assigned to `c` is never read + //~| WARN unused variable: `c` + }; + + move || { + println!("{}", c); + // Value is read by closure itself on later invocations. + c += 1; + }; + let b = Box::new(42); + move || { + println!("{}", c); + // Never read because this is FnOnce closure. + c += 1; //~ WARN value assigned to `c` is never read + drop(b); + }; + let _ = async move { + println!("{}", c); + // Never read because this is a generator. + c += 1; //~ WARN value assigned to `c` is never read + }; +} + +pub fn nested() { + let mut d = None; + let mut e = None; + || { + || { + d = Some("d1"); //~ WARN value assigned to `d` is never read + d = Some("d2"); + }; + move || { + e = Some("e1"); //~ WARN value assigned to `e` is never read + //~| WARN unused variable: `e` + e = Some("e2"); //~ WARN value assigned to `e` is never read + }; + }; +} + +pub fn g(mut v: T) { + |r| { + if r { + v = T::default(); //~ WARN value assigned to `v` is never read + } else { + drop(v); + } + }; +} + +pub fn h() { + let mut z = T::default(); + move |b| { + loop { + if b { + z = T::default(); //~ WARN value assigned to `z` is never read + //~| WARN unused variable: `z` + } else { + return; + } + } + dbg!(z); + }; +} + +fn main() {} diff --git a/src/test/ui/liveness/liveness-upvars.stderr b/src/test/ui/liveness/liveness-upvars.stderr new file mode 100644 index 0000000000000..14fed91786436 --- /dev/null +++ b/src/test/ui/liveness/liveness-upvars.stderr @@ -0,0 +1,150 @@ +warning: value assigned to `last` is never read + --> $DIR/liveness-upvars.rs:9:9 + | +LL | last = Some(s); + | ^^^^ + | +note: the lint level is defined here + --> $DIR/liveness-upvars.rs:3:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]` + = help: maybe it is overwritten before being read? + +warning: unused variable: `last` + --> $DIR/liveness-upvars.rs:9:9 + | +LL | last = Some(s); + | ^^^^ + | +note: the lint level is defined here + --> $DIR/liveness-upvars.rs:3:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + = help: did you mean to capture by reference instead? + +warning: unused variable: `sum` + --> $DIR/liveness-upvars.rs:21:9 + | +LL | sum += x; + | ^^^ + | + = help: did you mean to capture by reference instead? + +warning: value captured by `c` is never read + --> $DIR/liveness-upvars.rs:31:9 + | +LL | c = 1; + | ^ + | + = help: did you mean to capture by reference instead? + +warning: value captured by `c` is never read + --> $DIR/liveness-upvars.rs:35:9 + | +LL | c = 1; + | ^ + | + = help: did you mean to capture by reference instead? + +warning: unused variable: `c` + --> $DIR/liveness-upvars.rs:41:9 + | +LL | c += 1; + | ^ + | + = help: did you mean to capture by reference instead? + +warning: value assigned to `c` is never read + --> $DIR/liveness-upvars.rs:44:9 + | +LL | c += 1; + | ^ + | + = help: maybe it is overwritten before being read? + +warning: unused variable: `c` + --> $DIR/liveness-upvars.rs:44:9 + | +LL | c += 1; + | ^ + | + = help: did you mean to capture by reference instead? + +warning: value assigned to `c` is never read + --> $DIR/liveness-upvars.rs:57:9 + | +LL | c += 1; + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `c` is never read + --> $DIR/liveness-upvars.rs:63:9 + | +LL | c += 1; + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `d` is never read + --> $DIR/liveness-upvars.rs:72:13 + | +LL | d = Some("d1"); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `e` is never read + --> $DIR/liveness-upvars.rs:76:13 + | +LL | e = Some("e1"); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `e` is never read + --> $DIR/liveness-upvars.rs:78:13 + | +LL | e = Some("e2"); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: unused variable: `e` + --> $DIR/liveness-upvars.rs:76:13 + | +LL | e = Some("e1"); + | ^ + | + = help: did you mean to capture by reference instead? + +warning: value assigned to `v` is never read + --> $DIR/liveness-upvars.rs:86:13 + | +LL | v = T::default(); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: value assigned to `z` is never read + --> $DIR/liveness-upvars.rs:98:17 + | +LL | z = T::default(); + | ^ + | + = help: maybe it is overwritten before being read? + +warning: unused variable: `z` + --> $DIR/liveness-upvars.rs:98:17 + | +LL | z = T::default(); + | ^ + | + = help: did you mean to capture by reference instead? + +warning: 17 warnings 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..96c019b760e95 --- /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: 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..2ca86cf7c1b99 --- /dev/null +++ b/src/test/ui/llvm-asm/issue-69092.stderr @@ -0,0 +1,14 @@ +error: expected string in '.ascii' directive + --> $DIR/issue-69092.rs:8:14 + | +LL | unsafe { llvm_asm!(".ascii \"Xen\0\""); } + | ^ + | +note: instantiated into assembly here + --> :1:9 + | +LL | .ascii "Xen + | ^ + +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..2868d3d396da6 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-bad-clobber.rs @@ -0,0 +1,26 @@ +// ignore-android +// ignore-arm +// ignore-aarch64 +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// 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..5fbafe60b913f --- /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:23: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..e3bc7d2994132 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-in-bad-modifier.rs @@ -0,0 +1,35 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// 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..11c3ed08ddf3f --- /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:24:44 + | +LL | llvm_asm!("mov $1, $0" : "=r"(x) : "=r"(5)); + | ^^^^ + +error[E0663]: input operand constraint contains '+' + --> $DIR/llvm-asm-in-bad-modifier.rs:25: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..daae0c8147a76 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-misplaced-option.rs @@ -0,0 +1,36 @@ +// check-pass +// ignore-android +// ignore-arm +// ignore-aarch64 +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// 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..644ccdf2293e8 --- /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:25: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:32: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..9c62532c824f2 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.rs @@ -0,0 +1,35 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// 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..9b0aa6be1e91e --- /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:25: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..72edb339b19bb --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-no-modifier.rs @@ -0,0 +1,32 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// 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..afed53e2921f3 --- /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:23: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..acf4cf9ff95d7 --- /dev/null +++ b/src/test/ui/llvm-asm/llvm-asm-out-read-uninit.rs @@ -0,0 +1,33 @@ +// ignore-s390x +// ignore-emscripten +// ignore-powerpc +// ignore-powerpc64 +// ignore-powerpc64le +// ignore-riscv64 +// 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..ac034ab52871b --- /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:23: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/lub-glb/old-lub-glb-hr-eq.rs b/src/test/ui/lub-glb/old-lub-glb-hr-eq.rs new file mode 100644 index 0000000000000..fbf4aee02045d --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-hr-eq.rs @@ -0,0 +1,27 @@ +// Test that we give a note when the old LUB/GLB algorithm would have +// succeeded but the new code (which requires equality) gives an +// error. However, now that we handle subtyping correctly, we no +// longer get an error, because we recognize these two types as +// equivalent! +// +// check-pass + +fn foo(x: fn(&u8, &u8), y: for<'a> fn(&'a u8, &'a u8)) { + // The two types above are actually equivalent. With the older + // leak check, though, we didn't consider them as equivalent, and + // hence we gave errors. But now we've fixed that. + let z = match 22 { + 0 => x, + _ => y, + }; +} + +fn foo_cast(x: fn(&u8, &u8), y: for<'a> fn(&'a u8, &'a u8)) { + let z = match 22 { + // No error with an explicit cast: + 0 => x as for<'a> fn(&'a u8, &'a u8), + _ => y, + }; +} + +fn main() {} diff --git a/src/test/ui/lub-glb/old-lub-glb-hr-noteq1.nll.stderr b/src/test/ui/lub-glb/old-lub-glb-hr-noteq1.nll.stderr new file mode 100644 index 0000000000000..b95e247d2a8cf --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-hr-noteq1.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/old-lub-glb-hr-noteq1.rs:11:14 + | +LL | _ => y, + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/lub-glb/old-lub-glb-hr-noteq1.rs b/src/test/ui/lub-glb/old-lub-glb-hr-noteq1.rs new file mode 100644 index 0000000000000..918542d471b58 --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-hr-noteq1.rs @@ -0,0 +1,24 @@ +// Test taking the LUB of two function types that are not equatable but where one is more +// general than the other. Test the case where the more general type (`x`) is the first +// match arm specifically. + +fn foo(x: for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8, y: for<'a> fn(&'a u8, &'a u8) -> &'a u8) { + // The two types above are not equivalent. With the older LUB/GLB + // algorithm, this may have worked (I don't remember), but now it + // doesn't because we require equality. + let z = match 22 { + 0 => x, + _ => y, //~ ERROR `match` arms have incompatible types + }; +} + +fn foo_cast(x: for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8, y: for<'a> fn(&'a u8, &'a u8) -> &'a u8) { + // But we can *upcast* explicitly the type of `x` and figure + // things out: + let z = match 22 { + 0 => x as for<'a> fn(&'a u8, &'a u8) -> &'a u8, + _ => y, + }; +} + +fn main() {} diff --git a/src/test/ui/lub-glb/old-lub-glb-hr-noteq1.stderr b/src/test/ui/lub-glb/old-lub-glb-hr-noteq1.stderr new file mode 100644 index 0000000000000..305e952d6046b --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-hr-noteq1.stderr @@ -0,0 +1,18 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/old-lub-glb-hr-noteq1.rs:11:14 + | +LL | let z = match 22 { + | _____________- +LL | | 0 => x, + | | - this is found to be of type `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8` +LL | | _ => y, + | | ^ one type is more general than the other +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected fn pointer `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8` + found fn pointer `for<'a> fn(&'a u8, &'a u8) -> &'a u8` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/lub-glb/old-lub-glb-hr-noteq2.rs b/src/test/ui/lub-glb/old-lub-glb-hr-noteq2.rs new file mode 100644 index 0000000000000..4bdd05b4f92fa --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-hr-noteq2.rs @@ -0,0 +1,33 @@ +// Test taking the LUB of two function types that are not equatable but where +// one is more general than the other. Test the case where the more general type +// (`x`) is the second match arm specifically. +// +// FIXME(#73154) Skip for compare-mode because the pure NLL checker accepts this +// test. (Note that it still errors in old-lub-glb-hr-noteq1.rs). What happens +// is that, due to the ordering of the match arms, we pick the correct "more +// general" fn type, and we ignore the errors from the non-NLL type checker that +// requires equality. The NLL type checker only requires a subtyping +// relationship, and that holds. +// +// ignore-compare-mode-nll + +fn foo(x: for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8, y: for<'a> fn(&'a u8, &'a u8) -> &'a u8) { + // The two types above are not equivalent. With the older LUB/GLB + // algorithm, this may have worked (I don't remember), but now it + // doesn't because we require equality. + let z = match 22 { + 0 => y, + _ => x, //~ ERROR `match` arms have incompatible types + }; +} + +fn foo_cast(x: for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8, y: for<'a> fn(&'a u8, &'a u8) -> &'a u8) { + // But we can *upcast* explicitly the type of `x` and figure + // things out: + let z = match 22 { + 0 => x as for<'a> fn(&'a u8, &'a u8) -> &'a u8, + _ => y, + }; +} + +fn main() {} diff --git a/src/test/ui/lub-glb/old-lub-glb-hr-noteq2.stderr b/src/test/ui/lub-glb/old-lub-glb-hr-noteq2.stderr new file mode 100644 index 0000000000000..252e13aada054 --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-hr-noteq2.stderr @@ -0,0 +1,18 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/old-lub-glb-hr-noteq2.rs:20:14 + | +LL | let z = match 22 { + | _____________- +LL | | 0 => y, + | | - this is found to be of type `for<'a> fn(&'a u8, &'a u8) -> &'a u8` +LL | | _ => x, + | | ^ one type is more general than the other +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected fn pointer `for<'a> fn(&'a u8, &'a u8) -> &'a u8` + found fn pointer `for<'a, 'b> fn(&'a u8, &'b u8) -> &'a u8` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/lub-glb/old-lub-glb-hr.rs b/src/test/ui/lub-glb/old-lub-glb-hr.rs deleted file mode 100644 index bc7b787cd65ac..0000000000000 --- a/src/test/ui/lub-glb/old-lub-glb-hr.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Test that we give a note when the old LUB/GLB algorithm would have -// succeeded but the new code (which requires equality) gives an -// error. However, now that we handle subtyping correctly, we no -// longer get an error, because we recognize these two types as -// equivalent! -// -// Whoops -- now that we reinstituted the leak-check, we get an error -// again. - -fn foo( - x: fn(&u8, &u8), - y: for<'a> fn(&'a u8, &'a u8), -) { - let z = match 22 { - 0 => x, - _ => y, //~ ERROR `match` arms have incompatible types - }; -} - -fn bar( - x: fn(&u8, &u8), - y: for<'a> fn(&'a u8, &'a u8), -) { - let z = match 22 { - // No error with an explicit cast: - 0 => x as for<'a> fn(&'a u8, &'a u8), - _ => y, - }; -} - -fn main() { -} diff --git a/src/test/ui/lub-glb/old-lub-glb-hr.stderr b/src/test/ui/lub-glb/old-lub-glb-hr.stderr deleted file mode 100644 index 6d5d51174699f..0000000000000 --- a/src/test/ui/lub-glb/old-lub-glb-hr.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0308]: `match` arms have incompatible types - --> $DIR/old-lub-glb-hr.rs:16:14 - | -LL | let z = match 22 { - | _____________- -LL | | 0 => x, - | | - this is found to be of type `for<'r, 's> fn(&'r u8, &'s u8)` -LL | | _ => y, - | | ^ expected bound lifetime parameter, found concrete lifetime -LL | | }; - | |_____- `match` arms have incompatible types - | - = note: expected type `for<'r, 's> fn(&'r u8, &'s u8)` - found fn pointer `for<'a> fn(&'a u8, &'a u8)` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/lub-glb/old-lub-glb-object.nll.stderr b/src/test/ui/lub-glb/old-lub-glb-object.nll.stderr new file mode 100644 index 0000000000000..51bf96f32335b --- /dev/null +++ b/src/test/ui/lub-glb/old-lub-glb-object.nll.stderr @@ -0,0 +1,14 @@ +error: higher-ranked subtype error + --> $DIR/old-lub-glb-object.rs:10:14 + | +LL | _ => y, + | ^ + +error: higher-ranked subtype error + --> $DIR/old-lub-glb-object.rs:10:14 + | +LL | _ => y, + | ^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/lub-glb/old-lub-glb-object.rs b/src/test/ui/lub-glb/old-lub-glb-object.rs index 63bbae59991bc..39d351c235574 100644 --- a/src/test/ui/lub-glb/old-lub-glb-object.rs +++ b/src/test/ui/lub-glb/old-lub-glb-object.rs @@ -1,22 +1,17 @@ // Test that we give a note when the old LUB/GLB algorithm would have // succeeded but the new code (which is stricter) gives an error. -trait Foo { } +trait Foo {} -fn foo( - x: &dyn for<'a, 'b> Foo<&'a u8, &'b u8>, - y: &dyn for<'a> Foo<&'a u8, &'a u8>, -) { +fn foo(x: &dyn for<'a, 'b> Foo<&'a u8, &'b u8>, y: &dyn for<'a> Foo<&'a u8, &'a u8>) { let z = match 22 { + //~^ ERROR mismatched types 0 => x, - _ => y, //~ ERROR `match` arms have incompatible types + _ => y, }; } -fn bar( - x: &dyn for<'a, 'b> Foo<&'a u8, &'b u8>, - y: &dyn for<'a> Foo<&'a u8, &'a u8>, -) { +fn bar(x: &dyn for<'a, 'b> Foo<&'a u8, &'b u8>, y: &dyn for<'a> Foo<&'a u8, &'a u8>) { // Accepted with explicit case: let z = match 22 { 0 => x as &dyn for<'a> Foo<&'a u8, &'a u8>, @@ -24,5 +19,4 @@ fn bar( }; } -fn main() { -} +fn main() {} diff --git a/src/test/ui/lub-glb/old-lub-glb-object.stderr b/src/test/ui/lub-glb/old-lub-glb-object.stderr index 65c797f6b19d7..6eabe5eaeeeeb 100644 --- a/src/test/ui/lub-glb/old-lub-glb-object.stderr +++ b/src/test/ui/lub-glb/old-lub-glb-object.stderr @@ -1,17 +1,16 @@ -error[E0308]: `match` arms have incompatible types - --> $DIR/old-lub-glb-object.rs:12:14 +error[E0308]: mismatched types + --> $DIR/old-lub-glb-object.rs:7:13 | LL | let z = match 22 { - | _____________- + | _____________^ +LL | | LL | | 0 => x, - | | - this is found to be of type `&dyn for<'a, 'b> Foo<&'a u8, &'b u8>` LL | | _ => y, - | | ^ expected bound lifetime parameter 'a, found concrete lifetime LL | | }; - | |_____- `match` arms have incompatible types + | |_____^ one type is more general than the other | - = note: expected type `&dyn for<'a, 'b> Foo<&'a u8, &'b u8>` - found reference `&dyn for<'a> Foo<&'a u8, &'a u8>` + = note: expected trait object `dyn for<'a, 'b> Foo<&'a u8, &'b u8>` + found trait object `dyn for<'a> Foo<&'a u8, &'a u8>` error: aborting due to previous error 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-61033-1.rs b/src/test/ui/macros/issue-61033-1.rs index 8f85dec017f2a..18df3f6ee94c1 100644 --- a/src/test/ui/macros/issue-61033-1.rs +++ b/src/test/ui/macros/issue-61033-1.rs @@ -1,9 +1,10 @@ // Regression test for issue #61033. macro_rules! test1 { - ($x:ident, $($tt:tt)*) => { $($tt)+ } //~ERROR this must repeat at least once + ($x:ident, $($tt:tt)*) => { $($tt)+ } //~ ERROR this must repeat at least once } fn main() { test1!(x,); + let _recovery_witness: () = 0; //~ ERROR mismatched types } diff --git a/src/test/ui/macros/issue-61033-1.stderr b/src/test/ui/macros/issue-61033-1.stderr index f3c68f4928dbb..18205c3436b0b 100644 --- a/src/test/ui/macros/issue-61033-1.stderr +++ b/src/test/ui/macros/issue-61033-1.stderr @@ -4,5 +4,14 @@ error: this must repeat at least once LL | ($x:ident, $($tt:tt)*) => { $($tt)+ } | ^^^^^ -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/issue-61033-1.rs:9:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/macros/issue-61033-2.rs b/src/test/ui/macros/issue-61033-2.rs index 0799be10b96c7..1760ba1584d0b 100644 --- a/src/test/ui/macros/issue-61033-2.rs +++ b/src/test/ui/macros/issue-61033-2.rs @@ -5,7 +5,9 @@ macro_rules! test2 { $(* $id1:ident)* $(+ $id2:ident)* ) => { - $( //~ERROR meta-variable `id1` repeats 2 times + $( + //~^ ERROR meta-variable `id1` repeats 2 times + //~| ERROR meta-variable `id1` repeats 2 times $id1 + $id2 // $id1 and $id2 may repeat different numbers of times )* } @@ -16,4 +18,8 @@ fn main() { * a * b + a + b + c } + test2! { + * a * b + + a + b + c + d + } } diff --git a/src/test/ui/macros/issue-61033-2.stderr b/src/test/ui/macros/issue-61033-2.stderr index bf502919cf794..cdfe7934a0cac 100644 --- a/src/test/ui/macros/issue-61033-2.stderr +++ b/src/test/ui/macros/issue-61033-2.stderr @@ -3,9 +3,22 @@ error: meta-variable `id1` repeats 2 times, but `id2` repeats 3 times | LL | $( | __________^ +LL | | +LL | | LL | | $id1 + $id2 // $id1 and $id2 may repeat different numbers of times LL | | )* | |_________^ -error: aborting due to previous error +error: meta-variable `id1` repeats 2 times, but `id2` repeats 4 times + --> $DIR/issue-61033-2.rs:8:10 + | +LL | $( + | __________^ +LL | | +LL | | +LL | | $id1 + $id2 // $id1 and $id2 may repeat different numbers of times +LL | | )* + | |_________^ + +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-69838-dir/bar.rs b/src/test/ui/macros/issue-69838-dir/bar.rs new file mode 100644 index 0000000000000..ec12f8c5cb442 --- /dev/null +++ b/src/test/ui/macros/issue-69838-dir/bar.rs @@ -0,0 +1,3 @@ +// ignore-test -- this is an auxiliary file as part of another test. + +pub fn i_am_in_bar() {} diff --git a/src/test/ui/macros/issue-69838-dir/included.rs b/src/test/ui/macros/issue-69838-dir/included.rs new file mode 100644 index 0000000000000..9900b8fd5092c --- /dev/null +++ b/src/test/ui/macros/issue-69838-dir/included.rs @@ -0,0 +1,3 @@ +// ignore-test -- this is an auxiliary file as part of another test. + +pub mod bar; diff --git a/src/test/ui/macros/issue-69838-mods-relative-to-included-path.rs b/src/test/ui/macros/issue-69838-mods-relative-to-included-path.rs new file mode 100644 index 0000000000000..2a4e97f0ef5f1 --- /dev/null +++ b/src/test/ui/macros/issue-69838-mods-relative-to-included-path.rs @@ -0,0 +1,7 @@ +// check-pass + +include!("issue-69838-dir/included.rs"); + +fn main() { + bar::i_am_in_bar(); +} 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/local-ambiguity-multiple-parsing-options.rs b/src/test/ui/macros/local-ambiguity-multiple-parsing-options.rs new file mode 100644 index 0000000000000..3967481098cff --- /dev/null +++ b/src/test/ui/macros/local-ambiguity-multiple-parsing-options.rs @@ -0,0 +1,8 @@ +fn main() {} + +macro_rules! ambiguity { + ($($i:ident)* $j:ident) => {}; +} + +ambiguity!(error); //~ ERROR local ambiguity +ambiguity!(error); //~ ERROR local ambiguity diff --git a/src/test/ui/macros/local-ambiguity-multiple-parsing-options.stderr b/src/test/ui/macros/local-ambiguity-multiple-parsing-options.stderr new file mode 100644 index 0000000000000..0ae56c422213c --- /dev/null +++ b/src/test/ui/macros/local-ambiguity-multiple-parsing-options.stderr @@ -0,0 +1,14 @@ +error: local ambiguity: multiple parsing options: built-in NTs ident ('i') or ident ('j'). + --> $DIR/local-ambiguity-multiple-parsing-options.rs:7:12 + | +LL | ambiguity!(error); + | ^^^^^ + +error: local ambiguity: multiple parsing options: built-in NTs ident ('i') or ident ('j'). + --> $DIR/local-ambiguity-multiple-parsing-options.rs:8:12 + | +LL | ambiguity!(error); + | ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/macros/macro-context.rs b/src/test/ui/macros/macro-context.rs index 9130c3d921c52..13e179578ad01 100644 --- a/src/test/ui/macros/macro-context.rs +++ b/src/test/ui/macros/macro-context.rs @@ -4,6 +4,8 @@ macro_rules! m { //~| ERROR macro expansion ignores token `typeof` //~| ERROR macro expansion ignores token `;` //~| ERROR macro expansion ignores token `;` + //~| ERROR cannot find type `i` in this scope + //~| ERROR cannot find value `i` in this scope } fn main() { diff --git a/src/test/ui/macros/macro-context.stderr b/src/test/ui/macros/macro-context.stderr index 2e712110689f8..17c7389812475 100644 --- a/src/test/ui/macros/macro-context.stderr +++ b/src/test/ui/macros/macro-context.stderr @@ -42,5 +42,29 @@ LL | m!(); | = 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 +error[E0412]: cannot find type `i` in this scope + --> $DIR/macro-context.rs:3:13 + | +LL | () => ( i ; typeof ); + | ^ help: a builtin type with a similar name exists: `i8` +... +LL | let a: m!(); + | ---- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find value `i` in this scope + --> $DIR/macro-context.rs:3:13 + | +LL | () => ( i ; typeof ); + | ^ help: a local variable with a similar name exists: `a` +... +LL | let i = m!(); + | ---- 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 6 previous errors +Some errors have detailed explanations: E0412, E0425. +For more information about an error, try `rustc --explain E0412`. 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-local-data-key-priv.stderr b/src/test/ui/macros/macro-local-data-key-priv.stderr index c53a09aad5783..3092d76c268ad 100644 --- a/src/test/ui/macros/macro-local-data-key-priv.stderr +++ b/src/test/ui/macros/macro-local-data-key-priv.stderr @@ -2,7 +2,7 @@ error[E0603]: constant `baz` is private --> $DIR/macro-local-data-key-priv.rs:8:10 | LL | bar::baz.with(|_| ()); - | ^^^ this constant is private + | ^^^ private constant | note: the constant `baz` is defined here --> $DIR/macro-local-data-key-priv.rs:4:5 diff --git a/src/test/ui/macros/macro-match-nonterminal.rs b/src/test/ui/macros/macro-match-nonterminal.rs index 6d4b32c9bc9a0..b23e5c71c03f0 100644 --- a/src/test/ui/macros/macro-match-nonterminal.rs +++ b/src/test/ui/macros/macro-match-nonterminal.rs @@ -1,4 +1,11 @@ -macro_rules! test { ($a, $b) => (()); } //~ ERROR missing fragment +macro_rules! test { + ($a, $b) => { + //~^ ERROR missing fragment + //~| ERROR missing fragment + //~| WARN this was previously accepted + () + }; +} fn main() { test!() diff --git a/src/test/ui/macros/macro-match-nonterminal.stderr b/src/test/ui/macros/macro-match-nonterminal.stderr index 1de8c5bd4b472..674ce3434aac6 100644 --- a/src/test/ui/macros/macro-match-nonterminal.stderr +++ b/src/test/ui/macros/macro-match-nonterminal.stderr @@ -1,8 +1,18 @@ error: missing fragment specifier - --> $DIR/macro-match-nonterminal.rs:1:24 + --> $DIR/macro-match-nonterminal.rs:2:8 | -LL | macro_rules! test { ($a, $b) => (()); } - | ^ +LL | ($a, $b) => { + | ^ -error: aborting due to previous error +error: missing fragment specifier + --> $DIR/macro-match-nonterminal.rs:2:10 + | +LL | ($a, $b) => { + | ^^ + | + = note: `#[deny(missing_fragment_specifier)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #40107 + +error: aborting due to 2 previous errors diff --git a/src/test/ui/macros/macro-name-typo.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/macro_undefined.stderr b/src/test/ui/macros/macro_undefined.stderr index b2caba893e072..4ab16bd10173d 100644 --- a/src/test/ui/macros/macro_undefined.stderr +++ b/src/test/ui/macros/macro_undefined.stderr @@ -1,13 +1,11 @@ error: cannot find macro `k` in this scope --> $DIR/macro_undefined.rs:11:5 | -LL | / macro_rules! kl { -LL | | () => () -LL | | } - | |_____- similarly named macro `kl` defined here +LL | macro_rules! kl { + | --------------- similarly named macro `kl` defined here ... -LL | k!(); - | ^ help: a macro with a similar name exists: `kl` +LL | k!(); + | ^ help: a macro with a similar name exists: `kl` error: aborting due to previous error 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..42954ebcdc1cd 100644 --- a/src/test/ui/macros/macros-nonfatal-errors.stderr +++ b/src/test/ui/macros/macros-nonfatal-errors.stderr @@ -6,44 +6,52 @@ 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"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) 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 +62,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 +82,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 +96,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/trace-macro.stderr b/src/test/ui/macros/trace-macro.stderr index 202a9235adbac..6217decd8ef88 100644 --- a/src/test/ui/macros/trace-macro.stderr +++ b/src/test/ui/macros/trace-macro.stderr @@ -5,5 +5,5 @@ LL | println!("Hello, World!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expanding `println! { "Hello, World!" }` - = note: to `{ $crate :: io :: _print ($crate :: format_args_nl ! ("Hello, World!")) ; }` + = note: to `{ $crate :: io :: _print($crate :: format_args_nl ! ("Hello, World!")) ; }` diff --git a/src/test/ui/macros/trace_faulty_macros.rs b/src/test/ui/macros/trace_faulty_macros.rs index 627d58abf4ca2..5a8e2f50ce33d 100644 --- a/src/test/ui/macros/trace_faulty_macros.rs +++ b/src/test/ui/macros/trace_faulty_macros.rs @@ -1,6 +1,6 @@ // compile-flags: -Z trace-macros -#![recursion_limit="4"] +#![recursion_limit = "4"] macro_rules! my_faulty_macro { () => { @@ -13,7 +13,7 @@ macro_rules! pat_macro { pat_macro!(A{a:a, b:0, c:_, ..}); }; ($a:pat) => { - $a + $a //~ ERROR expected expression }; } @@ -24,9 +24,7 @@ macro_rules! my_recursive_macro { } macro_rules! my_macro { - () => { - - }; + () => {}; } fn main() { @@ -39,7 +37,7 @@ fn main() { } #[my_macro] -fn use_bang_macro_as_attr(){} +fn use_bang_macro_as_attr() {} -#[derive(Debug)] -fn use_derive_macro_as_attr(){} +#[derive(Debug)] //~ ERROR `derive` may only be applied to structs +fn use_derive_macro_as_attr() {} diff --git a/src/test/ui/macros/trace_faulty_macros.stderr b/src/test/ui/macros/trace_faulty_macros.stderr index a18e22e07f8bc..aec9d1ab191af 100644 --- a/src/test/ui/macros/trace_faulty_macros.stderr +++ b/src/test/ui/macros/trace_faulty_macros.stderr @@ -13,7 +13,7 @@ LL | my_faulty_macro!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) note: trace_macro - --> $DIR/trace_faulty_macros.rs:33:5 + --> $DIR/trace_faulty_macros.rs:31:5 | LL | my_faulty_macro!(); | ^^^^^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | my_recursive_macro!(); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) note: trace_macro - --> $DIR/trace_faulty_macros.rs:34:5 + --> $DIR/trace_faulty_macros.rs:32:5 | LL | my_recursive_macro!(); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -49,5 +49,33 @@ LL | my_recursive_macro!(); = note: expanding `my_recursive_macro! { }` = note: to `my_recursive_macro ! () ;` -error: aborting due to 2 previous errors +error: expected expression, found `A { a: a, b: 0, c: _, .. }` + --> $DIR/trace_faulty_macros.rs:16:9 + | +LL | $a + | ^^ expected expression +... +LL | let a = pat_macro!(); + | ------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `derive` may only be applied to structs, enums and unions + --> $DIR/trace_faulty_macros.rs:42:1 + | +LL | #[derive(Debug)] + | ^^^^^^^^^^^^^^^^ + +note: trace_macro + --> $DIR/trace_faulty_macros.rs:36:13 + | +LL | let a = pat_macro!(); + | ^^^^^^^^^^^^ + | + = note: expanding `pat_macro! { }` + = note: to `pat_macro ! (A { a : a, b : 0, c : _, .. }) ;` + = note: expanding `pat_macro! { A { a : a, b : 0, c : _, .. } }` + = note: to `A { a: a, b: 0, c: _, .. }` + +error: aborting due to 4 previous errors 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/ui/match/issue-72896.rs b/src/test/ui/match/issue-72896.rs new file mode 100644 index 0000000000000..3a8b82037310a --- /dev/null +++ b/src/test/ui/match/issue-72896.rs @@ -0,0 +1,23 @@ +// run-pass +trait EnumSetType { + type Repr; +} + +enum Enum8 { } +impl EnumSetType for Enum8 { + type Repr = u8; +} + +#[derive(PartialEq, Eq)] +struct EnumSet { + __enumset_underlying: T::Repr, +} + +const CONST_SET: EnumSet = EnumSet { __enumset_underlying: 3 }; + +fn main() { + match CONST_SET { + CONST_SET => { /* ok */ } + _ => panic!("match fell through?"), + } +} 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/assign-to-method.stderr b/src/test/ui/methods/assign-to-method.stderr index c0dd529b6818f..cafe9abae045d 100644 --- a/src/test/ui/methods/assign-to-method.stderr +++ b/src/test/ui/methods/assign-to-method.stderr @@ -2,7 +2,7 @@ error[E0615]: attempted to take value of method `speak` on type `Cat` --> $DIR/assign-to-method.rs:22:10 | LL | nyan.speak = || println!("meow"); - | ^^^^^ + | ^^^^^ method, not a field | = help: methods are immutable and cannot be assigned to @@ -10,7 +10,7 @@ error[E0615]: attempted to take value of method `speak` on type `Cat` --> $DIR/assign-to-method.rs:23:10 | LL | nyan.speak += || println!("meow"); - | ^^^^^ + | ^^^^^ method, not a field | = help: methods are immutable and cannot be assigned to 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 44f85071505d2..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 @@ -1,8 +1,8 @@ error[E0034]: multiple applicable items in scope - --> $DIR/method-ambig-two-traits-from-impls2.rs:15:5 + --> $DIR/method-ambig-two-traits-from-impls2.rs:15:9 | LL | AB::foo(); - | ^^^^^^^ multiple `foo` found + | ^^^ multiple `foo` found | note: candidate #1 is defined in an impl of the trait `A` for the type `AB` --> $DIR/method-ambig-two-traits-from-impls2.rs:7:5 @@ -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-missing-call.stderr b/src/test/ui/methods/method-missing-call.stderr index 3ab5f66a0c3f6..bc8a1c85e561a 100644 --- a/src/test/ui/methods/method-missing-call.stderr +++ b/src/test/ui/methods/method-missing-call.stderr @@ -2,13 +2,23 @@ error[E0615]: attempted to take value of method `get_x` on type `Point` --> $DIR/method-missing-call.rs:22:26 | LL | .get_x; - | ^^^^^ help: use parentheses to call the method: `get_x()` + | ^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | .get_x(); + | ^^ error[E0615]: attempted to take value of method `filter_map` on type `std::iter::Filter, [closure@$DIR/method-missing-call.rs:27:20: 27:25]>, [closure@$DIR/method-missing-call.rs:28:23: 28:35]>` --> $DIR/method-missing-call.rs:29:16 | LL | .filter_map; - | ^^^^^^^^^^ help: use parentheses to call the method: `filter_map(...)` + | ^^^^^^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | .filter_map(_); + | ^^^ error: aborting due to 2 previous errors 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/ui/mir/issue-66930.rs b/src/test/ui/mir/issue-66930.rs new file mode 100644 index 0000000000000..5f9eb2bf437fd --- /dev/null +++ b/src/test/ui/mir/issue-66930.rs @@ -0,0 +1,11 @@ +// check-pass +// compile-flags: --emit=mir,link +// Regression test for #66930, this ICE requires `--emit=mir` flag. + +static UTF8_CHAR_WIDTH: [u8; 0] = []; + +pub fn utf8_char_width(b: u8) -> usize { + UTF8_CHAR_WIDTH[b as usize] as usize +} + +fn main() {} diff --git a/src/test/ui/mir/mir_cast_fn_ret.rs b/src/test/ui/mir/mir_cast_fn_ret.rs index 69fd64c1c092c..4574dbd8529aa 100644 --- a/src/test/ui/mir/mir_cast_fn_ret.rs +++ b/src/test/ui/mir/mir_cast_fn_ret.rs @@ -1,8 +1,10 @@ // run-pass +#[allow(improper_ctypes_definitions)] pub extern "C" fn tuple2() -> (u16, u8) { (1, 2) } +#[allow(improper_ctypes_definitions)] pub extern "C" fn tuple3() -> (u8, u8, u8) { (1, 2, 3) } diff --git a/src/test/ui/mir/mir_codegen_calls.rs b/src/test/ui/mir/mir_codegen_calls.rs index fc0db03e3a968..d93a25c8ef4d3 100644 --- a/src/test/ui/mir/mir_codegen_calls.rs +++ b/src/test/ui/mir/mir_codegen_calls.rs @@ -74,6 +74,7 @@ fn test8() -> isize { Two::two() } +#[allow(improper_ctypes_definitions)] extern fn simple_extern(x: u32, y: (u32, u32)) -> u32 { x + y.0 * y.1 } 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..136c03cd9f1bc --- /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]; + } +} 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..0b6dbfd7c3d85 --- /dev/null +++ b/src/test/ui/mir/mir_detects_invalid_ops.stderr @@ -0,0 +1,16 @@ +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: aborting due to 2 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/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index f94dfd100a6f4..95936de218b8f 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -24,41 +24,31 @@ error[E0605]: non-primitive cast: `*const u8` as `&u8` --> $DIR/cast-rfc0401.rs:29:13 | LL | let _ = v as &u8; - | ^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `E` --> $DIR/cast-rfc0401.rs:30:13 | LL | let _ = v as E; - | ^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `*const u8` as `fn()` --> $DIR/cast-rfc0401.rs:31:13 | LL | let _ = v as fn(); - | ^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^ invalid cast error[E0605]: non-primitive cast: `*const u8` as `(u32,)` --> $DIR/cast-rfc0401.rs:32:13 | LL | let _ = v as (u32,); - | ^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `std::option::Option<&*const u8>` as `*const u8` --> $DIR/cast-rfc0401.rs:33:13 | LL | let _ = Some(&v) as *const u8; - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0606]: casting `*const u8` as `f32` is invalid --> $DIR/cast-rfc0401.rs:35:13 @@ -102,7 +92,7 @@ error[E0604]: only `u8` can be cast as `char`, not `u32` --> $DIR/cast-rfc0401.rs:41:13 | LL | let _ = 0x61u32 as char; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ invalid cast error[E0606]: casting `bool` as `f32` is invalid --> $DIR/cast-rfc0401.rs:43:13 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.nll.stderr b/src/test/ui/mismatched_types/closure-arg-type-mismatch.nll.stderr new file mode 100644 index 0000000000000..6ed91b20ab8a6 --- /dev/null +++ b/src/test/ui/mismatched_types/closure-arg-type-mismatch.nll.stderr @@ -0,0 +1,27 @@ +error[E0631]: type mismatch in closure arguments + --> $DIR/closure-arg-type-mismatch.rs:3:14 + | +LL | a.iter().map(|_: (u32, u32)| 45); + | ^^^ ------------------ found signature of `fn((u32, u32)) -> _` + | | + | expected signature of `fn(&(u32, u32)) -> _` + +error[E0631]: type mismatch in closure arguments + --> $DIR/closure-arg-type-mismatch.rs:4:14 + | +LL | a.iter().map(|_: &(u16, u16)| 45); + | ^^^ ------------------- found signature of `for<'r> fn(&'r (u16, u16)) -> _` + | | + | expected signature of `fn(&(u32, u32)) -> _` + +error[E0631]: type mismatch in closure arguments + --> $DIR/closure-arg-type-mismatch.rs:5:14 + | +LL | a.iter().map(|_: (u16, u16)| 45); + | ^^^ ------------------ found signature of `fn((u16, u16)) -> _` + | | + | expected signature of `fn(&(u32, u32)) -> _` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0631`. diff --git a/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs b/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs index 521bd3695dfe5..e278049c8cc42 100644 --- a/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs +++ b/src/test/ui/mismatched_types/closure-arg-type-mismatch.rs @@ -7,6 +7,9 @@ fn main() { fn baz(_: F) {} fn _test<'a>(f: fn(*mut &'a u32)) { - baz(f); //~ ERROR type mismatch - //~| ERROR type mismatch + baz(f); + //~^ ERROR mismatched types + //~| ERROR mismatched types + //~| ERROR mismatched types + //~| ERROR mismatched types } 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..664fa4bcaf328 100644 --- a/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr +++ b/src/test/ui/mismatched_types/closure-arg-type-mismatch.stderr @@ -22,28 +22,43 @@ LL | a.iter().map(|_: (u16, u16)| 45); | | | expected signature of `fn(&(u32, u32)) -> _` -error[E0631]: type mismatch in function arguments - --> $DIR/closure-arg-type-mismatch.rs:10:9 +error[E0308]: mismatched types + --> $DIR/closure-arg-type-mismatch.rs:10:5 | -LL | fn baz(_: F) {} - | --- ------------- required by this bound in `baz` -LL | fn _test<'a>(f: fn(*mut &'a u32)) { LL | baz(f); - | ^ - | | - | expected signature of `for<'r> fn(*mut &'r u32) -> _` - | found signature of `fn(*mut &'a u32) -> _` + | ^^^ one type is more general than the other + | + = note: expected type `for<'r> std::ops::Fn<(*mut &'r u32,)>` + found type `std::ops::Fn<(*mut &'a u32,)>` + +error[E0308]: mismatched types + --> $DIR/closure-arg-type-mismatch.rs:10:5 + | +LL | baz(f); + | ^^^ one type is more general than the other + | + = note: expected type `std::ops::FnOnce<(*mut &u32,)>` + found type `std::ops::FnOnce<(*mut &'a u32,)>` -error[E0271]: type mismatch resolving `for<'r> >::Output == ()` +error[E0308]: mismatched types --> $DIR/closure-arg-type-mismatch.rs:10:5 | -LL | fn baz(_: F) {} - | --- ------------- 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 + | ^^^ one type is more general than the other + | + = note: expected type `for<'r> std::ops::Fn<(*mut &'r u32,)>` + found type `std::ops::Fn<(*mut &'a u32,)>` + +error[E0308]: mismatched types + --> $DIR/closure-arg-type-mismatch.rs:10:5 + | +LL | baz(f); + | ^^^ one type is more general than the other + | + = note: expected type `std::ops::FnOnce<(*mut &u32,)>` + found type `std::ops::FnOnce<(*mut &'a u32,)>` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0271, E0631. -For more information about an error, try `rustc --explain E0271`. +Some errors have detailed explanations: E0308, E0631. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/mismatched_types/closure-mismatch.nll.stderr b/src/test/ui/mismatched_types/closure-mismatch.nll.stderr new file mode 100644 index 0000000000000..745a61b866ed2 --- /dev/null +++ b/src/test/ui/mismatched_types/closure-mismatch.nll.stderr @@ -0,0 +1,14 @@ +error: higher-ranked subtype error + --> $DIR/closure-mismatch.rs:8:5 + | +LL | baz(|_| ()); + | ^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/closure-mismatch.rs:8:5 + | +LL | baz(|_| ()); + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/mismatched_types/closure-mismatch.rs b/src/test/ui/mismatched_types/closure-mismatch.rs index 40a4641fe7196..d2b78b4b7dba5 100644 --- a/src/test/ui/mismatched_types/closure-mismatch.rs +++ b/src/test/ui/mismatched_types/closure-mismatch.rs @@ -5,6 +5,5 @@ impl Foo for T {} fn baz(_: T) {} fn main() { - baz(|_| ()); //~ ERROR type mismatch - //~^ ERROR type mismatch + baz(|_| ()); //~ ERROR mismatched types } diff --git a/src/test/ui/mismatched_types/closure-mismatch.stderr b/src/test/ui/mismatched_types/closure-mismatch.stderr index f3874c0907be0..d6c17d125cf1e 100644 --- a/src/test/ui/mismatched_types/closure-mismatch.stderr +++ b/src/test/ui/mismatched_types/closure-mismatch.stderr @@ -1,28 +1,12 @@ -error[E0271]: type mismatch resolving `for<'r> <[closure@$DIR/closure-mismatch.rs:8:9: 8:15] as std::ops::FnOnce<(&'r (),)>>::Output == ()` +error[E0308]: mismatched types --> $DIR/closure-mismatch.rs:8:5 | -LL | fn baz(_: T) {} - | --- --- required by this bound in `baz` -... LL | baz(|_| ()); - | ^^^ expected bound lifetime parameter, found concrete lifetime + | ^^^ one type is more general than the other | - = note: required because of the requirements on the impl of `Foo` for `[closure@$DIR/closure-mismatch.rs:8:9: 8:15]` + = note: expected type `for<'r> std::ops::Fn<(&'r (),)>` + found type `std::ops::Fn<(&(),)>` -error[E0631]: type mismatch in closure arguments - --> $DIR/closure-mismatch.rs:8:5 - | -LL | fn baz(_: T) {} - | --- --- required by this bound in `baz` -... -LL | baz(|_| ()); - | ^^^ ------ found signature of `fn(_) -> _` - | | - | expected signature of `for<'r> fn(&'r ()) -> _` - | - = note: required because of the requirements on the impl of `Foo` for `[closure@$DIR/closure-mismatch.rs:8:9: 8:15]` - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0271, E0631. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0308`. 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-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr index 69a9d03e474ba..d39b0a3207763 100644 --- a/src/test/ui/mismatched_types/issue-26480.stderr +++ b/src/test/ui/mismatched_types/issue-26480.stderr @@ -17,12 +17,11 @@ error[E0605]: non-primitive cast: `{integer}` as `()` --> $DIR/issue-26480.rs:22:19 | LL | ($x:expr) => ($x as ()) - | ^^^^^^^^ + | ^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object ... LL | cast!(2); | --------- in this macro invocation | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait = 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 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/missing_non_modrs_mod/missing_non_modrs_mod.stderr b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr index 98b74e5f5cbca..91b3fe15c4be7 100644 --- a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr +++ b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod.stderr @@ -1,10 +1,10 @@ error[E0583]: file not found for module `missing` - --> $DIR/foo.rs:4:5 + --> $DIR/foo.rs:4:1 | LL | mod missing; - | ^^^^^^^ + | ^^^^^^^^^^^^ | - = help: name the file either foo/missing.rs or foo/missing/mod.rs inside the directory "$DIR" + = help: to create the module `missing`, create file "$DIR/foo/missing.rs" error: aborting due to previous error diff --git a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr index 457e8fcccbfb3..f519de46c767f 100644 --- a/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr +++ b/src/test/ui/missing_non_modrs_mod/missing_non_modrs_mod_inline.stderr @@ -1,10 +1,10 @@ error[E0583]: file not found for module `missing` - --> $DIR/foo_inline.rs:4:9 + --> $DIR/foo_inline.rs:4:5 | LL | mod missing; - | ^^^^^^^ + | ^^^^^^^^^^^^ | - = help: name the file either missing.rs or missing/mod.rs inside the directory "$DIR/foo_inline/inline" + = help: to create the module `missing`, create file "$DIR/foo_inline/inline/missing.rs" error: aborting due to previous error diff --git a/src/test/ui/mod/mod_file_disambig.rs b/src/test/ui/mod/mod_file_disambig.rs index ef203ef082b22..7b182421d34e3 100644 --- a/src/test/ui/mod/mod_file_disambig.rs +++ b/src/test/ui/mod/mod_file_disambig.rs @@ -2,4 +2,5 @@ mod mod_file_disambig_aux; //~ ERROR file for module `mod_file_disambig_aux` fou fn main() { assert_eq!(mod_file_aux::bar(), 10); + //~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux` } diff --git a/src/test/ui/mod/mod_file_disambig.stderr b/src/test/ui/mod/mod_file_disambig.stderr index 2b77d866fb30b..2cb99b7514277 100644 --- a/src/test/ui/mod/mod_file_disambig.stderr +++ b/src/test/ui/mod/mod_file_disambig.stderr @@ -1,11 +1,18 @@ -error[E0584]: file for module `mod_file_disambig_aux` found at both mod_file_disambig_aux.rs and mod_file_disambig_aux/mod.rs - --> $DIR/mod_file_disambig.rs:1:5 +error[E0761]: file for module `mod_file_disambig_aux` found at both mod_file_disambig_aux.rs and mod_file_disambig_aux/mod.rs + --> $DIR/mod_file_disambig.rs:1:1 | LL | mod mod_file_disambig_aux; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: delete or rename one of them to remove the ambiguity -error: aborting due to previous error +error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux` + --> $DIR/mod_file_disambig.rs:4:16 + | +LL | assert_eq!(mod_file_aux::bar(), 10); + | ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0584`. +Some errors have detailed explanations: E0433, E0761. +For more information about an error, try `rustc --explain E0433`. diff --git a/src/test/ui/moves/issue-46099-move-in-macro.rs b/src/test/ui/moves/issue-46099-move-in-macro.rs new file mode 100644 index 0000000000000..576fe1f4c8905 --- /dev/null +++ b/src/test/ui/moves/issue-46099-move-in-macro.rs @@ -0,0 +1,15 @@ +// Regression test for issue #46099 +// Tests that we don't emit spurious +// 'value moved in previous iteration of loop' message + +macro_rules! test { + ($v:expr) => {{ + drop(&$v); + $v + }} +} + +fn main() { + let b = Box::new(true); + test!({b}); //~ ERROR use of moved value +} diff --git a/src/test/ui/moves/issue-46099-move-in-macro.stderr b/src/test/ui/moves/issue-46099-move-in-macro.stderr new file mode 100644 index 0000000000000..83c99db870951 --- /dev/null +++ b/src/test/ui/moves/issue-46099-move-in-macro.stderr @@ -0,0 +1,14 @@ +error[E0382]: use of moved value: `b` + --> $DIR/issue-46099-move-in-macro.rs:14:12 + | +LL | let b = Box::new(true); + | - move occurs because `b` has type `std::boxed::Box`, which does not implement the `Copy` trait +LL | test!({b}); + | ^ + | | + | value moved here + | value used here after move + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/moves/move-in-guard-2.stderr b/src/test/ui/moves/move-in-guard-2.stderr index 8bd405279c526..00d89f550714c 100644 --- a/src/test/ui/moves/move-in-guard-2.stderr +++ b/src/test/ui/moves/move-in-guard-2.stderr @@ -5,7 +5,10 @@ LL | let x: Box<_> = box 1; | - move occurs because `x` has type `std::boxed::Box`, which does not implement the `Copy` trait ... LL | (_, 2) if take(x) => (), - | ^ value moved here, in previous iteration of loop + | ^ + | | + | value moved here + | value used here after move 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..ee730910ee441 100644 --- a/src/test/ui/namespace/namespace-mix.stderr +++ b/src/test/ui/namespace/namespace-mix.stderr @@ -12,11 +12,11 @@ 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; | -LL | use namespace_mix::xm2::S; +LL | use xm2::S; | error[E0423]: expected value, found type alias `xm1::S` @@ -35,11 +35,11 @@ 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; | -LL | use namespace_mix::xm2::S; +LL | use xm2::S; | error[E0423]: expected value, found struct variant `m7::V` @@ -57,11 +57,11 @@ 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; | -LL | use namespace_mix::xm8::V; +LL | use xm8::V; | error[E0423]: expected value, found struct variant `xm7::V` @@ -79,18 +79,18 @@ 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; | -LL | use namespace_mix::xm8::V; +LL | use xm8::V; | 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/issue-51506.rs b/src/test/ui/never_type/issue-51506.rs new file mode 100644 index 0000000000000..d0fe6a0f59a87 --- /dev/null +++ b/src/test/ui/never_type/issue-51506.rs @@ -0,0 +1,41 @@ +#![feature(never_type, specialization)] +#![allow(incomplete_features)] + +use std::iter::{self, Empty}; + +trait Trait { + type Out: Iterator; + + fn f(&self) -> Option; +} + +impl Trait for T { + default type Out = !; //~ ERROR: `!` is not an iterator + + default fn f(&self) -> Option { + None + } +} + +struct X; + +impl Trait for X { + type Out = Empty; + + fn f(&self) -> Option { + Some(iter::empty()) + } +} + +fn f(a: T) { + if let Some(iter) = a.f() { + println!("Some"); + for x in iter { + println!("x = {}", x); + } + } +} + +pub fn main() { + f(10); +} diff --git a/src/test/ui/never_type/issue-51506.stderr b/src/test/ui/never_type/issue-51506.stderr new file mode 100644 index 0000000000000..73865a9b5a02c --- /dev/null +++ b/src/test/ui/never_type/issue-51506.stderr @@ -0,0 +1,14 @@ +error[E0277]: `!` is not an iterator + --> $DIR/issue-51506.rs:13:5 + | +LL | type Out: Iterator; + | ------------------------------- required by `Trait::Out` +... +LL | default type Out = !; + | ^^^^^^^^^^^^^^^^^^^^^ `!` is not an iterator + | + = help: the trait `std::iter::Iterator` is not implemented for `!` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. 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 6002f8e1eb75e..5c5cafadc85fb 100644 --- a/src/test/ui/never_type/never-assign-dead-code.stderr +++ b/src/test/ui/never_type/never-assign-dead-code.stderr @@ -25,7 +25,7 @@ warning: unused variable: `x` --> $DIR/never-assign-dead-code.rs:9:9 | LL | let x: ! = panic!("aah"); - | ^ help: consider prefixing with an underscore: `_x` + | ^ help: if this is intentional, prefix it with an underscore: `_x` | note: the lint level is defined here --> $DIR/never-assign-dead-code.rs:6:9 @@ -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/closure-requirements/escape-argument-callee.stderr b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr index b4e18c229fdfd..f0d169f419c73 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -7,6 +7,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); = note: defining type: test::{{closure}}#0 with closure substs [ i16, for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) mut &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) i32)), + (), ] error: lifetime may not live long enough diff --git a/src/test/ui/nll/closure-requirements/escape-argument.stderr b/src/test/ui/nll/closure-requirements/escape-argument.stderr index 533a17bdd128b..e251e69997eba 100644 --- a/src/test/ui/nll/closure-requirements/escape-argument.stderr +++ b/src/test/ui/nll/closure-requirements/escape-argument.stderr @@ -7,6 +7,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); = note: defining type: test::{{closure}}#0 with closure substs [ i16, for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) mut &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32)), + (), ] note: no external requirements diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr index 60d02066e2676..36257700bef0c 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -7,8 +7,7 @@ LL | let mut closure1 = || p = &y; = note: defining type: test::{{closure}}#0::{{closure}}#0 with closure substs [ i16, extern "rust-call" fn(()), - &'_#1r i32, - &'_#2r mut &'_#3r i32, + (&'_#1r i32, &'_#2r mut &'_#3r i32), ] = note: number of external vids: 4 = note: where '_#1r: '_#3r @@ -26,8 +25,7 @@ LL | | }; = note: defining type: test::{{closure}}#0 with closure substs [ i16, extern "rust-call" fn(()), - &'_#1r i32, - &'_#2r mut &'_#3r i32, + (&'_#1r i32, &'_#2r mut &'_#3r i32), ] = note: number of external vids: 4 = note: where '_#1r: '_#3r diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr index f64ccf14ac482..d1c64fac3c1e3 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -7,8 +7,7 @@ LL | let mut closure = || p = &y; = note: defining type: test::{{closure}}#0 with closure substs [ i16, extern "rust-call" fn(()), - &'_#1r i32, - &'_#2r mut &'_#3r i32, + (&'_#1r i32, &'_#2r mut &'_#3r i32), ] = note: number of external vids: 4 = note: where '_#1r: '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index e1e0cdc153a6c..549ebb78d7887 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -11,6 +11,7 @@ LL | | }, = note: defining type: supply::{{closure}}#0 with closure substs [ i16, for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)), + (), ] = note: late-bound region is '_#4r = note: late-bound region is '_#5r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr index b6535024a4a76..346b4af6caac8 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -12,6 +12,7 @@ LL | | }); = note: defining type: supply::{{closure}}#0 with closure substs [ i16, for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)), + (), ] = note: late-bound region is '_#3r = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index f5723ba5da5ba..3b1769ed3a2ee 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -11,6 +11,7 @@ LL | | }) = note: defining type: case1::{{closure}}#0 with closure substs [ i32, for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>)), + (), ] error[E0521]: borrowed data escapes outside of closure @@ -49,6 +50,7 @@ LL | | }) = note: defining type: case2::{{closure}}#0 with closure substs [ i32, for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>)), + (), ] = note: number of external vids: 2 = note: where '_#1r: '_#0r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index 17d33e82ba7e3..b167dafff0136 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -13,6 +13,7 @@ LL | | }); = note: defining type: supply::{{closure}}#0 with closure substs [ i16, for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t1)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t2)) u32>)), + (), ] = note: late-bound region is '_#2r = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index 5dce8d087d6cd..91aacc3dff60f 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -13,6 +13,7 @@ LL | | }); = note: defining type: supply::{{closure}}#0 with closure substs [ i16, for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)), + (), ] = note: late-bound region is '_#3r = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr index 5c5d510805bdf..ae447708621ed 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -12,6 +12,7 @@ LL | | }); = note: defining type: test::{{closure}}#0 with closure substs [ i16, for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)), + (), ] = note: late-bound region is '_#3r = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr index c111e651832ba..256446a6e8d8d 100644 --- a/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr @@ -11,6 +11,7 @@ LL | | }, = note: defining type: supply::{{closure}}#0 with closure substs [ i16, for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#2r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)), + (), ] = note: late-bound region is '_#3r = note: number of external vids: 4 diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 52df46ed3453f..5c156d0d1e378 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -12,6 +12,7 @@ LL | | }); = note: defining type: supply::{{closure}}#0 with closure substs [ i16, for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>)), + (), ] = note: late-bound region is '_#2r = note: late-bound region is '_#3r diff --git a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 0270cc40de6fc..46e3f2e75f49e 100644 --- a/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -12,6 +12,7 @@ LL | | }); = note: defining type: supply::{{closure}}#0 with closure substs [ i16, for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) &'_#1r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t0)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) &'_#2r u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t2)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('s)) u32>, &ReLateBound(DebruijnIndex(0), BrNamed('t3)) std::cell::Cell<&ReLateBound(DebruijnIndex(0), BrNamed('t1)) u32>)), + (), ] = note: late-bound region is '_#3r = note: late-bound region is '_#4r diff --git a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr index b705ad9009a29..ef941472894b2 100644 --- a/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-from-trait-match.stderr @@ -14,6 +14,7 @@ LL | | }); = note: defining type: supply::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((T,)), + (), ] = note: number of external vids: 2 = note: where T: '_#1r diff --git a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr index 79ed1501524bd..2a382030f935c 100644 --- a/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -7,6 +7,7 @@ LL | expect_sig(|a, b| b); // ought to return `a` = note: defining type: test::{{closure}}#0 with closure substs [ i16, for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex(0), BrNamed('r)) i32, &ReLateBound(DebruijnIndex(0), BrNamed('s)) i32)) -> &ReLateBound(DebruijnIndex(0), BrNamed('r)) i32, + (), ] error: lifetime may not live long enough 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-53040.stderr b/src/test/ui/nll/issue-53040.stderr index 7cba32c67432c..87ffe9b1abf45 100644 --- a/src/test/ui/nll/issue-53040.stderr +++ b/src/test/ui/nll/issue-53040.stderr @@ -1,9 +1,13 @@ error: captured variable cannot escape `FnMut` closure body --> $DIR/issue-53040.rs:3:8 | +LL | let mut v: Vec<()> = Vec::new(); + | ----- variable defined here LL | || &mut v; - | - ^^^^^^ returns a reference to a captured variable which escapes the closure body - | | + | - ^^^^^- + | | | | + | | | variable captured here + | | returns a reference to a captured variable which escapes the closure body | inferred to be a `FnMut` closure | = note: `FnMut` closures only have access to their captured variables while they are executing... 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-55394.stderr b/src/test/ui/nll/issue-55394.stderr index 69a6ab004fd91..ba8d91b8455bf 100644 --- a/src/test/ui/nll/issue-55394.stderr +++ b/src/test/ui/nll/issue-55394.stderr @@ -26,8 +26,8 @@ note: ...so that the expression is assignable | LL | Foo { bar } | ^^^^^^^^^^^ - = note: expected `Foo<'_>` - found `Foo<'_>` + = note: expected `Foo<'_>` + found `Foo<'_>` error: aborting due to previous error 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/normalization-bounds-error.stderr b/src/test/ui/nll/normalization-bounds-error.stderr index 58f206742f4f5..d003acd879a77 100644 --- a/src/test/ui/nll/normalization-bounds-error.stderr +++ b/src/test/ui/nll/normalization-bounds-error.stderr @@ -19,8 +19,8 @@ note: ...so that the types are compatible | LL | fn visit_seq<'d, 'a: 'd>() -> <&'a () as Visitor<'d>>::Value {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `Visitor<'d>` - found `Visitor<'_>` + = note: expected `Visitor<'d>` + found `Visitor<'_>` error: aborting due to previous error 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/ty-outlives/projection-no-regions-closure.stderr b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr index 84365465eda86..38e59ae3e26ba 100644 --- a/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -7,6 +7,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) = note: defining type: no_region::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#2r)>, + (), ] = note: number of external vids: 3 = note: where ::Item: '_#2r @@ -42,6 +43,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) = note: defining type: correct_region::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#2r)>, + (), ] = note: number of external vids: 3 = note: where ::Item: '_#2r @@ -68,6 +70,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) = note: defining type: wrong_region::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#3r)>, + (), ] = note: number of external vids: 4 = note: where ::Item: '_#3r @@ -103,6 +106,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) = note: defining type: outlives_region::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn Anything + '_#3r)>, + (), ] = note: number of external vids: 4 = note: where ::Item: '_#3r diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr index 118a849f98416..d551ccf9cf669 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -7,6 +7,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: no_relationships_late::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), ] = note: late-bound region is '_#3r = note: number of external vids: 4 @@ -57,6 +58,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: no_relationships_early::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] = note: number of external vids: 4 = note: where T: '_#3r @@ -106,9 +108,10 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: projection_outlives::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] = note: number of external vids: 4 - = note: where >::AssocType: '_#3r + = note: where >::AssocType: '_#3r note: no external requirements --> $DIR/projection-one-region-closure.rs:62:1 @@ -133,6 +136,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: elements_outlive::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] = note: number of external vids: 4 = note: where T: '_#3r diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr index 59d8aa484bdac..3e17de1bf0f56 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -7,6 +7,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: no_relationships_late::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), ] = note: late-bound region is '_#3r = note: number of external vids: 4 @@ -48,6 +49,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: no_relationships_early::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] = note: number of external vids: 4 = note: where '_#2r: '_#3r @@ -88,9 +90,10 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: projection_outlives::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] = note: number of external vids: 4 - = note: where >::AssocType: '_#3r + = note: where >::AssocType: '_#3r note: no external requirements --> $DIR/projection-one-region-trait-bound-closure.rs:52:1 @@ -115,6 +118,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: elements_outlive::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] = note: number of external vids: 4 = note: where '_#2r: '_#3r @@ -142,6 +146,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: one_region::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), ] = note: number of external vids: 3 = note: where '_#1r: '_#2r diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr index c3b924577ab47..3d9a01fec101d 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-static-closure.stderr @@ -7,6 +7,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: no_relationships_late::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), ] = note: late-bound region is '_#3r @@ -32,6 +33,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: no_relationships_early::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] note: no external requirements @@ -57,6 +59,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: projection_outlives::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] note: no external requirements @@ -82,6 +85,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: elements_outlive::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] note: no external requirements @@ -107,6 +111,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: one_region::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), ] note: no external requirements diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr index ff402f89ae861..e354f1b5f7e63 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -7,10 +7,11 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: no_relationships_late::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] = note: late-bound region is '_#4r = note: number of external vids: 5 - = note: where >::AssocType: '_#3r + = note: where >::AssocType: '_#3r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:34:1 @@ -26,13 +27,13 @@ LL | | } | = note: defining type: no_relationships_late::<'_#1r, '_#2r, T> -error[E0309]: the associated type `>::AssocType` may not live long enough +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-two-region-trait-bound-closure.rs:38:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... + = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... note: external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:48:29 @@ -43,9 +44,10 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: no_relationships_early::<'_#1r, '_#2r, '_#3r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), + (), ] = note: number of external vids: 5 - = note: where >::AssocType: '_#4r + = note: where >::AssocType: '_#4r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:43:1 @@ -61,13 +63,13 @@ LL | | } | = note: defining type: no_relationships_early::<'_#1r, '_#2r, '_#3r, T> -error[E0309]: the associated type `>::AssocType` may not live long enough +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-two-region-trait-bound-closure.rs:48:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... + = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... note: external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:61:29 @@ -78,9 +80,10 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: projection_outlives::<'_#1r, '_#2r, '_#3r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), + (), ] = note: number of external vids: 5 - = note: where >::AssocType: '_#4r + = note: where >::AssocType: '_#4r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:53:1 @@ -105,9 +108,10 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: elements_outlive1::<'_#1r, '_#2r, '_#3r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), + (), ] = note: number of external vids: 5 - = note: where >::AssocType: '_#4r + = note: where >::AssocType: '_#4r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:65:1 @@ -132,9 +136,10 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: elements_outlive2::<'_#1r, '_#2r, '_#3r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#4r ()>, T)), + (), ] = note: number of external vids: 5 - = note: where >::AssocType: '_#4r + = note: where >::AssocType: '_#4r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:74:1 @@ -159,10 +164,11 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: two_regions::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), ] = note: late-bound region is '_#3r = note: number of external vids: 4 - = note: where >::AssocType: '_#2r + = note: where >::AssocType: '_#2r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:83:1 @@ -200,9 +206,10 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: two_regions_outlive::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] = note: number of external vids: 4 - = note: where >::AssocType: '_#3r + = note: where >::AssocType: '_#3r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:92:1 @@ -227,9 +234,10 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: defining type: one_region::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), ] = note: number of external vids: 3 - = note: where >::AssocType: '_#2r + = note: where >::AssocType: '_#2r note: no external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:101:1 diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr index 1a5a3719fd86d..eba00c5a9454e 100644 --- a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr @@ -5,11 +5,7 @@ LL | bar::() | ^^^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `>::Output: 'a`... -note: ...so that the type `>::Output` will meet its required lifetime bounds - --> $DIR/projection-where-clause-env-wrong-bound.rs:15:5 - | -LL | bar::() - | ^^^^^^^^^^^^^^^^ + = note: ...so that the type `>::Output` will meet its required lifetime bounds error: aborting due to previous error diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr index d6ade2a603e82..34b83859a6bd2 100644 --- a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr @@ -5,11 +5,7 @@ LL | bar::<>::Output>() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `>::Output: 'a`... -note: ...so that the type `>::Output` will meet its required lifetime bounds - --> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5 - | -LL | bar::<>::Output>() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...so that the type `>::Output` will meet its required lifetime bounds error: aborting due to previous error diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr index 9b08a10749673..167ca740c657c 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr @@ -7,6 +7,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); = note: defining type: generic::::{{closure}}#0 with closure substs [ i16, for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BrNamed('s)) T)), + (), ] = note: number of external vids: 2 = note: where T: '_#1r @@ -31,6 +32,7 @@ LL | twice(cell, value, |a, b| invoke(a, b)); = note: defining type: generic_fail::::{{closure}}#0 with closure substs [ i16, for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex(0), BrNamed('s)) T)), + (), ] = note: late-bound region is '_#2r = note: number of external vids: 3 diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr index 3cd1f4358710f..528da502b9d40 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-return-type.stderr @@ -7,6 +7,7 @@ LL | with_signature(x, |y| y) = note: defining type: no_region::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::boxed::Box,)) -> std::boxed::Box<(dyn std::fmt::Debug + '_#2r)>, + (), ] = note: number of external vids: 3 = note: where T: '_#2r diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr index 4740ed645f1da..e341ee48291a9 100644 --- a/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-outlives-from-where-clause.stderr @@ -14,6 +14,7 @@ LL | | }) = note: defining type: no_region::::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#1r ()>, T)), + (), ] = note: late-bound region is '_#2r = note: number of external vids: 3 @@ -64,6 +65,7 @@ LL | | }) = note: defining type: correct_region::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), ] = note: number of external vids: 3 = note: where T: '_#2r @@ -96,6 +98,7 @@ LL | | }) = note: defining type: wrong_region::<'_#1r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#2r ()>, T)), + (), ] = note: late-bound region is '_#3r = note: number of external vids: 4 @@ -141,6 +144,7 @@ LL | | }) = note: defining type: outlives_region::<'_#1r, '_#2r, T>::{{closure}}#0 with closure substs [ i32, extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)), + (), ] = note: number of external vids: 4 = note: where T: '_#3r diff --git a/src/test/ui/nll/type-alias-free-regions.stderr b/src/test/ui/nll/type-alias-free-regions.stderr index 5191deca281cc..3317aae83bb08 100644 --- a/src/test/ui/nll/type-alias-free-regions.stderr +++ b/src/test/ui/nll/type-alias-free-regions.stderr @@ -16,8 +16,8 @@ note: ...so that the expression is assignable | LL | C { f: b } | ^ - = note: expected `std::boxed::Box>` - found `std::boxed::Box>` + = note: expected `std::boxed::Box>` + found `std::boxed::Box>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 15:6... --> $DIR/type-alias-free-regions.rs:15:6 | @@ -28,8 +28,8 @@ note: ...so that the expression is assignable | LL | C { f: b } | ^^^^^^^^^^ - = note: expected `C<'a>` - found `C<'_>` + = note: expected `C<'a>` + found `C<'_>` error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> $DIR/type-alias-free-regions.rs:27:16 @@ -49,8 +49,8 @@ note: ...so that the expression is assignable | LL | C { f: Box::new(b.0) } | ^^^ - = note: expected `std::boxed::Box<&isize>` - found `std::boxed::Box<&isize>` + = note: expected `std::boxed::Box<&isize>` + found `std::boxed::Box<&isize>` note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 25:6... --> $DIR/type-alias-free-regions.rs:25:6 | @@ -61,8 +61,8 @@ note: ...so that the expression is assignable | LL | C { f: Box::new(b.0) } | ^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `C<'a>` - found `C<'_>` + = note: expected `C<'a>` + found `C<'_>` error: aborting due to 2 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/nll/user-annotations/constant-in-expr-inherent-1.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr index 37be450fd0a79..8421dc1d0c130 100644 --- a/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr +++ b/src/test/ui/nll/user-annotations/constant-in-expr-inherent-1.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | >::C | ^^^^^^^^^^^^ - = note: expected `Foo<'_>` - found `Foo<'a>` + = note: expected `Foo<'_>` + found `Foo<'a>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that reference does not outlive borrowed content --> $DIR/constant-in-expr-inherent-1.rs:8:5 diff --git a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr index 4ee32847c5ec8..ba0a1748c5e9f 100644 --- a/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr +++ b/src/test/ui/nll/user-annotations/constant-in-expr-trait-item-3.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | T::C | ^^^^ - = note: expected `Foo<'_>` - found `Foo<'a>` + = note: expected `Foo<'_>` + found `Foo<'a>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that reference does not outlive borrowed content --> $DIR/constant-in-expr-trait-item-3.rs:10:5 diff --git a/src/test/ui/no-implicit-prelude-nested.stderr b/src/test/ui/no-implicit-prelude-nested.stderr index e57d8af5f99b9..198b630c52c8f 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,12 +15,10 @@ 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 this trait instead | LL | use std::clone::Clone; | -LL | use std::prelude::v1::Clone; - | error[E0405]: cannot find trait `Iterator` in this scope --> $DIR/no-implicit-prelude-nested.rs:13:14 @@ -28,12 +26,10 @@ 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 this trait | LL | use std::iter::Iterator; | -LL | use std::prelude::v1::Iterator; - | error[E0405]: cannot find trait `ToString` in this scope --> $DIR/no-implicit-prelude-nested.rs:14:14 @@ -41,9 +37,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 - | -LL | use std::prelude::v1::ToString; +help: consider importing this trait | LL | use std::string::ToString; | @@ -60,12 +54,10 @@ 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 this function | LL | use std::mem::drop; | -LL | use std::prelude::v1::drop; - | error[E0405]: cannot find trait `Add` in this scope --> $DIR/no-implicit-prelude-nested.rs:23:10 @@ -73,7 +65,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,12 +76,10 @@ 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 this trait instead | LL | use std::clone::Clone; | -LL | use std::prelude::v1::Clone; - | error[E0405]: cannot find trait `Iterator` in this scope --> $DIR/no-implicit-prelude-nested.rs:25:10 @@ -97,12 +87,10 @@ 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 this trait | LL | use std::iter::Iterator; | -LL | use std::prelude::v1::Iterator; - | error[E0405]: cannot find trait `ToString` in this scope --> $DIR/no-implicit-prelude-nested.rs:26:10 @@ -110,9 +98,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 - | -LL | use std::prelude::v1::ToString; +help: consider importing this trait | LL | use std::string::ToString; | @@ -129,12 +115,10 @@ 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 this function | LL | use std::mem::drop; | -LL | use std::prelude::v1::drop; - | error[E0405]: cannot find trait `Add` in this scope --> $DIR/no-implicit-prelude-nested.rs:38:14 @@ -142,7 +126,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,12 +137,10 @@ 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 this trait instead | LL | use std::clone::Clone; | -LL | use std::prelude::v1::Clone; - | error[E0405]: cannot find trait `Iterator` in this scope --> $DIR/no-implicit-prelude-nested.rs:40:14 @@ -166,12 +148,10 @@ 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 this trait | LL | use std::iter::Iterator; | -LL | use std::prelude::v1::Iterator; - | error[E0405]: cannot find trait `ToString` in this scope --> $DIR/no-implicit-prelude-nested.rs:41:14 @@ -179,9 +159,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 - | -LL | use std::prelude::v1::ToString; +help: consider importing this trait | LL | use std::string::ToString; | @@ -198,12 +176,10 @@ 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 this function | LL | use std::mem::drop; | -LL | use std::prelude::v1::drop; - | error: aborting due to 18 previous errors diff --git a/src/test/ui/no-implicit-prelude.stderr b/src/test/ui/no-implicit-prelude.stderr index 8b99529f4dd7c..36a9b65b7d161 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,12 +15,10 @@ 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 this trait instead | LL | use std::clone::Clone; | -LL | use std::prelude::v1::Clone; - | error[E0405]: cannot find trait `Iterator` in this scope --> $DIR/no-implicit-prelude.rs:12:6 @@ -28,12 +26,10 @@ 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 this trait | LL | use std::iter::Iterator; | -LL | use std::prelude::v1::Iterator; - | error[E0405]: cannot find trait `ToString` in this scope --> $DIR/no-implicit-prelude.rs:13:6 @@ -41,9 +37,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 - | -LL | use std::prelude::v1::ToString; +help: consider importing this trait | LL | use std::string::ToString; | @@ -60,12 +54,10 @@ 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 this function | LL | use std::mem::drop; | -LL | use std::prelude::v1::drop; - | error: aborting due to 6 previous errors 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.stderr b/src/test/ui/non-copyable-void.stderr index 074ed66a26183..78d212f7a7ba7 100644 --- a/src/test/ui/non-copyable-void.stderr +++ b/src/test/ui/non-copyable-void.stderr @@ -3,6 +3,14 @@ error[E0599]: no method named `clone` found for enum `libc::c_void` in the curre | LL | let _z = (*y).clone(); | ^^^^^ method not found in `libc::c_void` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `std::sync::Arc` here + | the method is available for `std::rc::Rc` here error: aborting due to previous error 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.stderr b/src/test/ui/noncopyable-class.stderr index 6c3c4a6ac9888..994eb65ae15bf 100644 --- a/src/test/ui/noncopyable-class.stderr +++ b/src/test/ui/noncopyable-class.stderr @@ -6,6 +6,14 @@ LL | struct Foo { ... LL | let _y = x.clone(); | ^^^^^ method not found in `Foo` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `std::sync::Arc` here + | the method is available for `std::rc::Rc` here | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `clone`, perhaps you need to implement it: diff --git a/src/test/ui/nonscalar-cast.fixed b/src/test/ui/nonscalar-cast.fixed new file mode 100644 index 0000000000000..0a4b98469b2b6 --- /dev/null +++ b/src/test/ui/nonscalar-cast.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +#[derive(Debug)] +struct Foo { + x: isize +} + +impl From for isize { + fn from(val: Foo) -> isize { + val.x + } +} + +fn main() { + println!("{}", isize::from(Foo { x: 1 })); //~ non-primitive cast: `Foo` as `isize` [E0605] +} diff --git a/src/test/ui/nonscalar-cast.rs b/src/test/ui/nonscalar-cast.rs index 7e6f1fd038fb7..59fcf09666b24 100644 --- a/src/test/ui/nonscalar-cast.rs +++ b/src/test/ui/nonscalar-cast.rs @@ -1,8 +1,16 @@ +// run-rustfix + #[derive(Debug)] struct Foo { x: isize } +impl From for isize { + fn from(val: Foo) -> isize { + val.x + } +} + fn main() { println!("{}", Foo { x: 1 } as isize); //~ non-primitive cast: `Foo` as `isize` [E0605] } diff --git a/src/test/ui/nonscalar-cast.stderr b/src/test/ui/nonscalar-cast.stderr index 9338688b037ff..2a7037121876d 100644 --- a/src/test/ui/nonscalar-cast.stderr +++ b/src/test/ui/nonscalar-cast.stderr @@ -1,10 +1,10 @@ error[E0605]: non-primitive cast: `Foo` as `isize` - --> $DIR/nonscalar-cast.rs:7:20 + --> $DIR/nonscalar-cast.rs:15:20 | LL | println!("{}", Foo { x: 1 } as isize); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(Foo { x: 1 })` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error 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/i128.rs b/src/test/ui/numbers-arithmetic/i128.rs index ef558c0aa0c02..d61a1ab03b6b3 100644 --- a/src/test/ui/numbers-arithmetic/i128.rs +++ b/src/test/ui/numbers-arithmetic/i128.rs @@ -87,7 +87,7 @@ fn main() { assert_eq!((-z).checked_mul(-z), Some(0x734C_C2F2_A521)); assert_eq!((z).checked_mul(z), Some(0x734C_C2F2_A521)); assert_eq!((k).checked_mul(k), None); - let l: i128 = b(i128::min_value()); + let l: i128 = b(i128::MIN); let o: i128 = b(17); assert_eq!(l.checked_sub(b(2)), None); assert_eq!(l.checked_add(l), None); diff --git a/src/test/ui/numbers-arithmetic/int-abs-overflow.rs b/src/test/ui/numbers-arithmetic/int-abs-overflow.rs index 2bc018445db9e..10ec3f0c6624e 100644 --- a/src/test/ui/numbers-arithmetic/int-abs-overflow.rs +++ b/src/test/ui/numbers-arithmetic/int-abs-overflow.rs @@ -5,9 +5,9 @@ use std::thread; fn main() { - assert!(thread::spawn(|| i8::min_value().abs()).join().is_err()); - assert!(thread::spawn(|| i16::min_value().abs()).join().is_err()); - assert!(thread::spawn(|| i32::min_value().abs()).join().is_err()); - assert!(thread::spawn(|| i64::min_value().abs()).join().is_err()); - assert!(thread::spawn(|| isize::min_value().abs()).join().is_err()); + assert!(thread::spawn(|| i8::MIN.abs()).join().is_err()); + assert!(thread::spawn(|| i16::MIN.abs()).join().is_err()); + assert!(thread::spawn(|| i32::MIN.abs()).join().is_err()); + assert!(thread::spawn(|| i64::MIN.abs()).join().is_err()); + assert!(thread::spawn(|| isize::MIN.abs()).join().is_err()); } 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/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs b/src/test/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs index e9927304f23f8..101c5d50b20b9 100644 --- a/src/test/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs +++ b/src/test/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs @@ -9,12 +9,12 @@ fn main() { macro_rules! overflow_test { ($t:ident) => ( let r = panic::catch_unwind(|| { - ($t::max_value()).next_power_of_two() + ($t::MAX).next_power_of_two() }); assert!(r.is_err()); let r = panic::catch_unwind(|| { - (($t::max_value() >> 1) + 2).next_power_of_two() + (($t::MAX >> 1) + 2).next_power_of_two() }); assert!(r.is_err()); ) 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/promoted_overflow_opt.rs b/src/test/ui/numbers-arithmetic/promoted_overflow_opt.rs index a3b8ff58a7359..4785abbc55470 100644 --- a/src/test/ui/numbers-arithmetic/promoted_overflow_opt.rs +++ b/src/test/ui/numbers-arithmetic/promoted_overflow_opt.rs @@ -5,5 +5,5 @@ fn main() { let x = &(0u32 - 1); - assert_eq!(*x, u32::max_value()) + assert_eq!(*x, u32::MAX) } 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/numbers-arithmetic/u128.rs b/src/test/ui/numbers-arithmetic/u128.rs index 0b2305c6e8b1a..d7e28055b2154 100644 --- a/src/test/ui/numbers-arithmetic/u128.rs +++ b/src/test/ui/numbers-arithmetic/u128.rs @@ -53,14 +53,14 @@ fn main() { assert_eq!("10000000000000000000000000000000000000000000000000000000000000000000", format!("{:b}", j)); assert_eq!("340282366920938463463374607431768211455", - format!("{}", u128::max_value())); + format!("{}", u128::MAX)); assert_eq!("147573952589676412928", format!("{:?}", j)); // common traits assert_eq!(x, b(x.clone())); // overflow checks assert_eq!((z).checked_mul(z), Some(0x734C_C2F2_A521)); assert_eq!((k).checked_mul(k), None); - let l: u128 = b(u128::max_value() - 10); + let l: u128 = b(u128::MAX - 10); let o: u128 = b(17); assert_eq!(l.checked_add(b(11)), None); assert_eq!(l.checked_sub(l), Some(0)); diff --git a/src/test/ui/numeric/const-scope.stderr b/src/test/ui/numeric/const-scope.stderr index 6e1990e3a7222..d7f18e19b41bd 100644 --- a/src/test/ui/numeric/const-scope.stderr +++ b/src/test/ui/numeric/const-scope.stderr @@ -3,6 +3,11 @@ error[E0308]: mismatched types | LL | const C: i32 = 1i8; | ^^^ expected `i32`, found `i8` + | +help: change the type of the numeric literal from `i8` to `i32` + | +LL | const C: i32 = 1i32; + | ^^^^ error[E0308]: mismatched types --> $DIR/const-scope.rs:2:15 @@ -17,6 +22,11 @@ LL | let c: i32 = 1i8; | --- ^^^ expected `i32`, found `i8` | | | expected due to this + | +help: change the type of the numeric literal from `i8` to `i32` + | +LL | let c: i32 = 1i32; + | ^^^^ error[E0308]: mismatched types --> $DIR/const-scope.rs:6:17 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-binop.fixed b/src/test/ui/numeric/numeric-cast-binop.fixed new file mode 100644 index 0000000000000..edb085e71d324 --- /dev/null +++ b/src/test/ui/numeric/numeric-cast-binop.fixed @@ -0,0 +1,320 @@ +// run-rustfix + +// The `try_into` suggestion doesn't include this, but we do suggest it after applying it +use std::convert::TryInto; + +#[allow(unused_must_use)] +fn main() { + let x_usize: usize = 1; + let x_u128: u128 = 2; + let x_u64: u64 = 3; + let x_u32: u32 = 4; + let x_u16: u16 = 5; + let x_u8: u8 = 6; + let x_isize: isize = 7; + let x_i64: i64 = 8; + let x_i32: i32 = 9; + let x_i16: i16 = 10; + let x_i8: i8 = 11; + let x_i128: i128 = 12; + + /* u<->u */ + { + u16::from(x_u8) > x_u16; + //~^ ERROR mismatched types + u32::from(x_u8) > x_u32; + //~^ ERROR mismatched types + u64::from(x_u8) > x_u64; + //~^ ERROR mismatched types + u128::from(x_u8) > x_u128; + //~^ ERROR mismatched types + usize::from(x_u8) > x_usize; + //~^ ERROR mismatched types + + x_u16 > x_u8.into(); + //~^ ERROR mismatched types + u32::from(x_u16) > x_u32; + //~^ ERROR mismatched types + u64::from(x_u16) > x_u64; + //~^ ERROR mismatched types + u128::from(x_u16) > x_u128; + //~^ ERROR mismatched types + usize::from(x_u16) > x_usize; + //~^ ERROR mismatched types + + x_u32 > x_u8.into(); + //~^ ERROR mismatched types + x_u32 > x_u16.into(); + //~^ ERROR mismatched types + u64::from(x_u32) > x_u64; + //~^ ERROR mismatched types + u128::from(x_u32) > x_u128; + //~^ ERROR mismatched types + x_u32 > x_usize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_u64 > x_u8.into(); + //~^ ERROR mismatched types + x_u64 > x_u16.into(); + //~^ ERROR mismatched types + x_u64 > x_u32.into(); + //~^ ERROR mismatched types + u128::from(x_u64) > x_u128; + //~^ ERROR mismatched types + x_u64 > x_usize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_u128 > x_u8.into(); + //~^ ERROR mismatched types + x_u128 > x_u16.into(); + //~^ ERROR mismatched types + x_u128 > x_u32.into(); + //~^ ERROR mismatched types + x_u128 > x_u64.into(); + //~^ ERROR mismatched types + x_u128 > x_usize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_usize > x_u8.into(); + //~^ ERROR mismatched types + x_usize > x_u16.into(); + //~^ ERROR mismatched types + x_usize > x_u32.try_into().unwrap(); + //~^ ERROR mismatched types + x_usize > x_u64.try_into().unwrap(); + //~^ ERROR mismatched types + x_usize > x_u128.try_into().unwrap(); + //~^ ERROR mismatched types + } + + /* i<->i */ + { + i16::from(x_i8) > x_i16; + //~^ ERROR mismatched types + i32::from(x_i8) > x_i32; + //~^ ERROR mismatched types + i64::from(x_i8) > x_i64; + //~^ ERROR mismatched types + i128::from(x_i8) > x_i128; + //~^ ERROR mismatched types + isize::from(x_i8) > x_isize; + //~^ ERROR mismatched types + + x_i16 > x_i8.into(); + //~^ ERROR mismatched types + i32::from(x_i16) > x_i32; + //~^ ERROR mismatched types + i64::from(x_i16) > x_i64; + //~^ ERROR mismatched types + i128::from(x_i16) > x_i128; + //~^ ERROR mismatched types + isize::from(x_i16) > x_isize; + //~^ ERROR mismatched types + + x_i32 > x_i8.into(); + //~^ ERROR mismatched types + x_i32 > x_i16.into(); + //~^ ERROR mismatched types + i64::from(x_i32) > x_i64; + //~^ ERROR mismatched types + i128::from(x_i32) > x_i128; + //~^ ERROR mismatched types + x_i32 > x_isize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_i64 > x_i8.into(); + //~^ ERROR mismatched types + x_i64 > x_i16.into(); + //~^ ERROR mismatched types + x_i64 > x_i32.into(); + //~^ ERROR mismatched types + i128::from(x_i64) > x_i128; + //~^ ERROR mismatched types + x_i64 > x_isize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_i128 > x_i8.into(); + //~^ ERROR mismatched types + x_i128 > x_i16.into(); + //~^ ERROR mismatched types + x_i128 > x_i32.into(); + //~^ ERROR mismatched types + x_i128 > x_i64.into(); + //~^ ERROR mismatched types + x_i128 > x_isize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_isize > x_i8.into(); + //~^ ERROR mismatched types + x_isize > x_i16.into(); + //~^ ERROR mismatched types + x_isize > x_i32.try_into().unwrap(); + //~^ ERROR mismatched types + x_isize > x_i64.try_into().unwrap(); + //~^ ERROR mismatched types + x_isize > x_i128.try_into().unwrap(); + //~^ ERROR mismatched types + } + + /* u<->i */ + { + x_u8 > x_i8.try_into().unwrap(); + //~^ ERROR mismatched types + i16::from(x_u8) > x_i16; + //~^ ERROR mismatched types + i32::from(x_u8) > x_i32; + //~^ ERROR mismatched types + i64::from(x_u8) > x_i64; + //~^ ERROR mismatched types + i128::from(x_u8) > x_i128; + //~^ ERROR mismatched types + isize::from(x_u8) > x_isize; + //~^ ERROR mismatched types + + x_u16 > x_i8.try_into().unwrap(); + //~^ ERROR mismatched types + x_u16 > x_i16.try_into().unwrap(); + //~^ ERROR mismatched types + i32::from(x_u16) > x_i32; + //~^ ERROR mismatched types + i64::from(x_u16) > x_i64; + //~^ ERROR mismatched types + i128::from(x_u16) > x_i128; + //~^ ERROR mismatched types + x_u16 > x_isize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_u32 > x_i8.try_into().unwrap(); + //~^ ERROR mismatched types + x_u32 > x_i16.try_into().unwrap(); + //~^ ERROR mismatched types + x_u32 > x_i32.try_into().unwrap(); + //~^ ERROR mismatched types + i64::from(x_u32) > x_i64; + //~^ ERROR mismatched types + i128::from(x_u32) > x_i128; + //~^ ERROR mismatched types + x_u32 > x_isize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_u64 > x_i8.try_into().unwrap(); + //~^ ERROR mismatched types + x_u64 > x_i16.try_into().unwrap(); + //~^ ERROR mismatched types + x_u64 > x_i32.try_into().unwrap(); + //~^ ERROR mismatched types + x_u64 > x_i64.try_into().unwrap(); + //~^ ERROR mismatched types + i128::from(x_u64) > x_i128; + //~^ ERROR mismatched types + x_u64 > x_isize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_u128 > x_i8.try_into().unwrap(); + //~^ ERROR mismatched types + x_u128 > x_i16.try_into().unwrap(); + //~^ ERROR mismatched types + x_u128 > x_i32.try_into().unwrap(); + //~^ ERROR mismatched types + x_u128 > x_i64.try_into().unwrap(); + //~^ ERROR mismatched types + x_u128 > x_i128.try_into().unwrap(); + //~^ ERROR mismatched types + x_u128 > x_isize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_usize > x_i8.try_into().unwrap(); + //~^ ERROR mismatched types + x_usize > x_i16.try_into().unwrap(); + //~^ ERROR mismatched types + x_usize > x_i32.try_into().unwrap(); + //~^ ERROR mismatched types + x_usize > x_i64.try_into().unwrap(); + //~^ ERROR mismatched types + x_usize > x_i128.try_into().unwrap(); + //~^ ERROR mismatched types + x_usize > x_isize.try_into().unwrap(); + //~^ ERROR mismatched types + } + + /* i<->u */ + { + x_i8 > x_u8.try_into().unwrap(); + //~^ ERROR mismatched types + x_i8 > x_u16.try_into().unwrap(); + //~^ ERROR mismatched types + x_i8 > x_u32.try_into().unwrap(); + //~^ ERROR mismatched types + x_i8 > x_u64.try_into().unwrap(); + //~^ ERROR mismatched types + x_i8 > x_u128.try_into().unwrap(); + //~^ ERROR mismatched types + x_i8 > x_usize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_i16 > x_u8.into(); + //~^ ERROR mismatched types + x_i16 > x_u16.try_into().unwrap(); + //~^ ERROR mismatched types + x_i16 > x_u32.try_into().unwrap(); + //~^ ERROR mismatched types + x_i16 > x_u64.try_into().unwrap(); + //~^ ERROR mismatched types + x_i16 > x_u128.try_into().unwrap(); + //~^ ERROR mismatched types + x_i16 > x_usize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_i32 > x_u8.into(); + //~^ ERROR mismatched types + x_i32 > x_u16.into(); + //~^ ERROR mismatched types + x_i32 > x_u32.try_into().unwrap(); + //~^ ERROR mismatched types + x_i32 > x_u64.try_into().unwrap(); + //~^ ERROR mismatched types + x_i32 > x_u128.try_into().unwrap(); + //~^ ERROR mismatched types + x_i32 > x_usize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_i64 > x_u8.into(); + //~^ ERROR mismatched types + x_i64 > x_u16.into(); + //~^ ERROR mismatched types + x_i64 > x_u32.into(); + //~^ ERROR mismatched types + x_i64 > x_u64.try_into().unwrap(); + //~^ ERROR mismatched types + x_i64 > x_u128.try_into().unwrap(); + //~^ ERROR mismatched types + x_i64 > x_usize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_i128 > x_u8.into(); + //~^ ERROR mismatched types + x_i128 > x_u16.into(); + //~^ ERROR mismatched types + x_i128 > x_u32.into(); + //~^ ERROR mismatched types + x_i128 > x_u64.into(); + //~^ ERROR mismatched types + x_i128 > x_u128.try_into().unwrap(); + //~^ ERROR mismatched types + x_i128 > x_usize.try_into().unwrap(); + //~^ ERROR mismatched types + + x_isize > x_u8.into(); + //~^ ERROR mismatched types + x_isize > x_u16.try_into().unwrap(); + //~^ ERROR mismatched types + x_isize > x_u32.try_into().unwrap(); + //~^ ERROR mismatched types + x_isize > x_u64.try_into().unwrap(); + //~^ ERROR mismatched types + x_isize > x_u128.try_into().unwrap(); + //~^ ERROR mismatched types + x_isize > x_usize.try_into().unwrap(); + //~^ ERROR mismatched types + } +} diff --git a/src/test/ui/numeric/numeric-cast-binop.rs b/src/test/ui/numeric/numeric-cast-binop.rs new file mode 100644 index 0000000000000..c1ed8de8ad8c3 --- /dev/null +++ b/src/test/ui/numeric/numeric-cast-binop.rs @@ -0,0 +1,320 @@ +// run-rustfix + +// The `try_into` suggestion doesn't include this, but we do suggest it after applying it +use std::convert::TryInto; + +#[allow(unused_must_use)] +fn main() { + let x_usize: usize = 1; + let x_u128: u128 = 2; + let x_u64: u64 = 3; + let x_u32: u32 = 4; + let x_u16: u16 = 5; + let x_u8: u8 = 6; + let x_isize: isize = 7; + let x_i64: i64 = 8; + let x_i32: i32 = 9; + let x_i16: i16 = 10; + let x_i8: i8 = 11; + let x_i128: i128 = 12; + + /* u<->u */ + { + x_u8 > x_u16; + //~^ ERROR mismatched types + x_u8 > x_u32; + //~^ ERROR mismatched types + x_u8 > x_u64; + //~^ ERROR mismatched types + x_u8 > x_u128; + //~^ ERROR mismatched types + x_u8 > x_usize; + //~^ ERROR mismatched types + + x_u16 > x_u8; + //~^ ERROR mismatched types + x_u16 > x_u32; + //~^ ERROR mismatched types + x_u16 > x_u64; + //~^ ERROR mismatched types + x_u16 > x_u128; + //~^ ERROR mismatched types + x_u16 > x_usize; + //~^ ERROR mismatched types + + x_u32 > x_u8; + //~^ ERROR mismatched types + x_u32 > x_u16; + //~^ ERROR mismatched types + x_u32 > x_u64; + //~^ ERROR mismatched types + x_u32 > x_u128; + //~^ ERROR mismatched types + x_u32 > x_usize; + //~^ ERROR mismatched types + + x_u64 > x_u8; + //~^ ERROR mismatched types + x_u64 > x_u16; + //~^ ERROR mismatched types + x_u64 > x_u32; + //~^ ERROR mismatched types + x_u64 > x_u128; + //~^ ERROR mismatched types + x_u64 > x_usize; + //~^ ERROR mismatched types + + x_u128 > x_u8; + //~^ ERROR mismatched types + x_u128 > x_u16; + //~^ ERROR mismatched types + x_u128 > x_u32; + //~^ ERROR mismatched types + x_u128 > x_u64; + //~^ ERROR mismatched types + x_u128 > x_usize; + //~^ ERROR mismatched types + + x_usize > x_u8; + //~^ ERROR mismatched types + x_usize > x_u16; + //~^ ERROR mismatched types + x_usize > x_u32; + //~^ ERROR mismatched types + x_usize > x_u64; + //~^ ERROR mismatched types + x_usize > x_u128; + //~^ ERROR mismatched types + } + + /* i<->i */ + { + x_i8 > x_i16; + //~^ ERROR mismatched types + x_i8 > x_i32; + //~^ ERROR mismatched types + x_i8 > x_i64; + //~^ ERROR mismatched types + x_i8 > x_i128; + //~^ ERROR mismatched types + x_i8 > x_isize; + //~^ ERROR mismatched types + + x_i16 > x_i8; + //~^ ERROR mismatched types + x_i16 > x_i32; + //~^ ERROR mismatched types + x_i16 > x_i64; + //~^ ERROR mismatched types + x_i16 > x_i128; + //~^ ERROR mismatched types + x_i16 > x_isize; + //~^ ERROR mismatched types + + x_i32 > x_i8; + //~^ ERROR mismatched types + x_i32 > x_i16; + //~^ ERROR mismatched types + x_i32 > x_i64; + //~^ ERROR mismatched types + x_i32 > x_i128; + //~^ ERROR mismatched types + x_i32 > x_isize; + //~^ ERROR mismatched types + + x_i64 > x_i8; + //~^ ERROR mismatched types + x_i64 > x_i16; + //~^ ERROR mismatched types + x_i64 > x_i32; + //~^ ERROR mismatched types + x_i64 > x_i128; + //~^ ERROR mismatched types + x_i64 > x_isize; + //~^ ERROR mismatched types + + x_i128 > x_i8; + //~^ ERROR mismatched types + x_i128 > x_i16; + //~^ ERROR mismatched types + x_i128 > x_i32; + //~^ ERROR mismatched types + x_i128 > x_i64; + //~^ ERROR mismatched types + x_i128 > x_isize; + //~^ ERROR mismatched types + + x_isize > x_i8; + //~^ ERROR mismatched types + x_isize > x_i16; + //~^ ERROR mismatched types + x_isize > x_i32; + //~^ ERROR mismatched types + x_isize > x_i64; + //~^ ERROR mismatched types + x_isize > x_i128; + //~^ ERROR mismatched types + } + + /* u<->i */ + { + x_u8 > x_i8; + //~^ ERROR mismatched types + x_u8 > x_i16; + //~^ ERROR mismatched types + x_u8 > x_i32; + //~^ ERROR mismatched types + x_u8 > x_i64; + //~^ ERROR mismatched types + x_u8 > x_i128; + //~^ ERROR mismatched types + x_u8 > x_isize; + //~^ ERROR mismatched types + + x_u16 > x_i8; + //~^ ERROR mismatched types + x_u16 > x_i16; + //~^ ERROR mismatched types + x_u16 > x_i32; + //~^ ERROR mismatched types + x_u16 > x_i64; + //~^ ERROR mismatched types + x_u16 > x_i128; + //~^ ERROR mismatched types + x_u16 > x_isize; + //~^ ERROR mismatched types + + x_u32 > x_i8; + //~^ ERROR mismatched types + x_u32 > x_i16; + //~^ ERROR mismatched types + x_u32 > x_i32; + //~^ ERROR mismatched types + x_u32 > x_i64; + //~^ ERROR mismatched types + x_u32 > x_i128; + //~^ ERROR mismatched types + x_u32 > x_isize; + //~^ ERROR mismatched types + + x_u64 > x_i8; + //~^ ERROR mismatched types + x_u64 > x_i16; + //~^ ERROR mismatched types + x_u64 > x_i32; + //~^ ERROR mismatched types + x_u64 > x_i64; + //~^ ERROR mismatched types + x_u64 > x_i128; + //~^ ERROR mismatched types + x_u64 > x_isize; + //~^ ERROR mismatched types + + x_u128 > x_i8; + //~^ ERROR mismatched types + x_u128 > x_i16; + //~^ ERROR mismatched types + x_u128 > x_i32; + //~^ ERROR mismatched types + x_u128 > x_i64; + //~^ ERROR mismatched types + x_u128 > x_i128; + //~^ ERROR mismatched types + x_u128 > x_isize; + //~^ ERROR mismatched types + + x_usize > x_i8; + //~^ ERROR mismatched types + x_usize > x_i16; + //~^ ERROR mismatched types + x_usize > x_i32; + //~^ ERROR mismatched types + x_usize > x_i64; + //~^ ERROR mismatched types + x_usize > x_i128; + //~^ ERROR mismatched types + x_usize > x_isize; + //~^ ERROR mismatched types + } + + /* i<->u */ + { + x_i8 > x_u8; + //~^ ERROR mismatched types + x_i8 > x_u16; + //~^ ERROR mismatched types + x_i8 > x_u32; + //~^ ERROR mismatched types + x_i8 > x_u64; + //~^ ERROR mismatched types + x_i8 > x_u128; + //~^ ERROR mismatched types + x_i8 > x_usize; + //~^ ERROR mismatched types + + x_i16 > x_u8; + //~^ ERROR mismatched types + x_i16 > x_u16; + //~^ ERROR mismatched types + x_i16 > x_u32; + //~^ ERROR mismatched types + x_i16 > x_u64; + //~^ ERROR mismatched types + x_i16 > x_u128; + //~^ ERROR mismatched types + x_i16 > x_usize; + //~^ ERROR mismatched types + + x_i32 > x_u8; + //~^ ERROR mismatched types + x_i32 > x_u16; + //~^ ERROR mismatched types + x_i32 > x_u32; + //~^ ERROR mismatched types + x_i32 > x_u64; + //~^ ERROR mismatched types + x_i32 > x_u128; + //~^ ERROR mismatched types + x_i32 > x_usize; + //~^ ERROR mismatched types + + x_i64 > x_u8; + //~^ ERROR mismatched types + x_i64 > x_u16; + //~^ ERROR mismatched types + x_i64 > x_u32; + //~^ ERROR mismatched types + x_i64 > x_u64; + //~^ ERROR mismatched types + x_i64 > x_u128; + //~^ ERROR mismatched types + x_i64 > x_usize; + //~^ ERROR mismatched types + + x_i128 > x_u8; + //~^ ERROR mismatched types + x_i128 > x_u16; + //~^ ERROR mismatched types + x_i128 > x_u32; + //~^ ERROR mismatched types + x_i128 > x_u64; + //~^ ERROR mismatched types + x_i128 > x_u128; + //~^ ERROR mismatched types + x_i128 > x_usize; + //~^ ERROR mismatched types + + x_isize > x_u8; + //~^ ERROR mismatched types + x_isize > x_u16; + //~^ ERROR mismatched types + x_isize > x_u32; + //~^ ERROR mismatched types + x_isize > x_u64; + //~^ ERROR mismatched types + x_isize > x_u128; + //~^ ERROR mismatched types + x_isize > x_usize; + //~^ ERROR mismatched types + } +} diff --git a/src/test/ui/numeric/numeric-cast-binop.stderr b/src/test/ui/numeric/numeric-cast-binop.stderr new file mode 100644 index 0000000000000..47be817b78908 --- /dev/null +++ b/src/test/ui/numeric/numeric-cast-binop.stderr @@ -0,0 +1,1385 @@ +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:23:16 + | +LL | x_u8 > x_u16; + | ^^^^^ expected `u8`, found `u16` + | +help: you can convert `x_u8` from `u8` to `u16`, matching the type of `x_u16` + | +LL | u16::from(x_u8) > x_u16; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:25:16 + | +LL | x_u8 > x_u32; + | ^^^^^ expected `u8`, found `u32` + | +help: you can convert `x_u8` from `u8` to `u32`, matching the type of `x_u32` + | +LL | u32::from(x_u8) > x_u32; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:27:16 + | +LL | x_u8 > x_u64; + | ^^^^^ expected `u8`, found `u64` + | +help: you can convert `x_u8` from `u8` to `u64`, matching the type of `x_u64` + | +LL | u64::from(x_u8) > x_u64; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:29:16 + | +LL | x_u8 > x_u128; + | ^^^^^^ expected `u8`, found `u128` + | +help: you can convert `x_u8` from `u8` to `u128`, matching the type of `x_u128` + | +LL | u128::from(x_u8) > x_u128; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:31:16 + | +LL | x_u8 > x_usize; + | ^^^^^^^ expected `u8`, found `usize` + | +help: you can convert `x_u8` from `u8` to `usize`, matching the type of `x_usize` + | +LL | usize::from(x_u8) > x_usize; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:34:17 + | +LL | x_u16 > x_u8; + | ^^^^ + | | + | expected `u16`, found `u8` + | help: you can convert an `u8` to `u16`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:36:17 + | +LL | x_u16 > x_u32; + | ^^^^^ expected `u16`, found `u32` + | +help: you can convert `x_u16` from `u16` to `u32`, matching the type of `x_u32` + | +LL | u32::from(x_u16) > x_u32; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:38:17 + | +LL | x_u16 > x_u64; + | ^^^^^ expected `u16`, found `u64` + | +help: you can convert `x_u16` from `u16` to `u64`, matching the type of `x_u64` + | +LL | u64::from(x_u16) > x_u64; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:40:17 + | +LL | x_u16 > x_u128; + | ^^^^^^ expected `u16`, found `u128` + | +help: you can convert `x_u16` from `u16` to `u128`, matching the type of `x_u128` + | +LL | u128::from(x_u16) > x_u128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:42:17 + | +LL | x_u16 > x_usize; + | ^^^^^^^ expected `u16`, found `usize` + | +help: you can convert `x_u16` from `u16` to `usize`, matching the type of `x_usize` + | +LL | usize::from(x_u16) > x_usize; + | ^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:45:17 + | +LL | x_u32 > x_u8; + | ^^^^ + | | + | expected `u32`, found `u8` + | help: you can convert an `u8` to `u32`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:47:17 + | +LL | x_u32 > x_u16; + | ^^^^^ + | | + | expected `u32`, found `u16` + | help: you can convert an `u16` to `u32`: `x_u16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:49:17 + | +LL | x_u32 > x_u64; + | ^^^^^ expected `u32`, found `u64` + | +help: you can convert `x_u32` from `u32` to `u64`, matching the type of `x_u64` + | +LL | u64::from(x_u32) > x_u64; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:51:17 + | +LL | x_u32 > x_u128; + | ^^^^^^ expected `u32`, found `u128` + | +help: you can convert `x_u32` from `u32` to `u128`, matching the type of `x_u128` + | +LL | u128::from(x_u32) > x_u128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:53:17 + | +LL | x_u32 > x_usize; + | ^^^^^^^ expected `u32`, found `usize` + | +help: you can convert an `usize` to `u32` and panic if the converted value wouldn't fit + | +LL | x_u32 > x_usize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:56:17 + | +LL | x_u64 > x_u8; + | ^^^^ + | | + | expected `u64`, found `u8` + | help: you can convert an `u8` to `u64`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:58:17 + | +LL | x_u64 > x_u16; + | ^^^^^ + | | + | expected `u64`, found `u16` + | help: you can convert an `u16` to `u64`: `x_u16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:60:17 + | +LL | x_u64 > x_u32; + | ^^^^^ + | | + | expected `u64`, found `u32` + | help: you can convert an `u32` to `u64`: `x_u32.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:62:17 + | +LL | x_u64 > x_u128; + | ^^^^^^ expected `u64`, found `u128` + | +help: you can convert `x_u64` from `u64` to `u128`, matching the type of `x_u128` + | +LL | u128::from(x_u64) > x_u128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:64:17 + | +LL | x_u64 > x_usize; + | ^^^^^^^ expected `u64`, found `usize` + | +help: you can convert an `usize` to `u64` and panic if the converted value wouldn't fit + | +LL | x_u64 > x_usize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:67:18 + | +LL | x_u128 > x_u8; + | ^^^^ + | | + | expected `u128`, found `u8` + | help: you can convert an `u8` to `u128`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:69:18 + | +LL | x_u128 > x_u16; + | ^^^^^ + | | + | expected `u128`, found `u16` + | help: you can convert an `u16` to `u128`: `x_u16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:71:18 + | +LL | x_u128 > x_u32; + | ^^^^^ + | | + | expected `u128`, found `u32` + | help: you can convert an `u32` to `u128`: `x_u32.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:73:18 + | +LL | x_u128 > x_u64; + | ^^^^^ + | | + | expected `u128`, found `u64` + | help: you can convert an `u64` to `u128`: `x_u64.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:75:18 + | +LL | x_u128 > x_usize; + | ^^^^^^^ expected `u128`, found `usize` + | +help: you can convert an `usize` to `u128` and panic if the converted value wouldn't fit + | +LL | x_u128 > x_usize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:78:19 + | +LL | x_usize > x_u8; + | ^^^^ + | | + | expected `usize`, found `u8` + | help: you can convert an `u8` to `usize`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:80:19 + | +LL | x_usize > x_u16; + | ^^^^^ + | | + | expected `usize`, found `u16` + | help: you can convert an `u16` to `usize`: `x_u16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:82:19 + | +LL | x_usize > x_u32; + | ^^^^^ expected `usize`, found `u32` + | +help: you can convert an `u32` to `usize` and panic if the converted value wouldn't fit + | +LL | x_usize > x_u32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:84:19 + | +LL | x_usize > x_u64; + | ^^^^^ expected `usize`, found `u64` + | +help: you can convert an `u64` to `usize` and panic if the converted value wouldn't fit + | +LL | x_usize > x_u64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:86:19 + | +LL | x_usize > x_u128; + | ^^^^^^ expected `usize`, found `u128` + | +help: you can convert an `u128` to `usize` and panic if the converted value wouldn't fit + | +LL | x_usize > x_u128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:92:16 + | +LL | x_i8 > x_i16; + | ^^^^^ expected `i8`, found `i16` + | +help: you can convert `x_i8` from `i8` to `i16`, matching the type of `x_i16` + | +LL | i16::from(x_i8) > x_i16; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:94:16 + | +LL | x_i8 > x_i32; + | ^^^^^ expected `i8`, found `i32` + | +help: you can convert `x_i8` from `i8` to `i32`, matching the type of `x_i32` + | +LL | i32::from(x_i8) > x_i32; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:96:16 + | +LL | x_i8 > x_i64; + | ^^^^^ expected `i8`, found `i64` + | +help: you can convert `x_i8` from `i8` to `i64`, matching the type of `x_i64` + | +LL | i64::from(x_i8) > x_i64; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:98:16 + | +LL | x_i8 > x_i128; + | ^^^^^^ expected `i8`, found `i128` + | +help: you can convert `x_i8` from `i8` to `i128`, matching the type of `x_i128` + | +LL | i128::from(x_i8) > x_i128; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:100:16 + | +LL | x_i8 > x_isize; + | ^^^^^^^ expected `i8`, found `isize` + | +help: you can convert `x_i8` from `i8` to `isize`, matching the type of `x_isize` + | +LL | isize::from(x_i8) > x_isize; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:103:17 + | +LL | x_i16 > x_i8; + | ^^^^ + | | + | expected `i16`, found `i8` + | help: you can convert an `i8` to `i16`: `x_i8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:105:17 + | +LL | x_i16 > x_i32; + | ^^^^^ expected `i16`, found `i32` + | +help: you can convert `x_i16` from `i16` to `i32`, matching the type of `x_i32` + | +LL | i32::from(x_i16) > x_i32; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:107:17 + | +LL | x_i16 > x_i64; + | ^^^^^ expected `i16`, found `i64` + | +help: you can convert `x_i16` from `i16` to `i64`, matching the type of `x_i64` + | +LL | i64::from(x_i16) > x_i64; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:109:17 + | +LL | x_i16 > x_i128; + | ^^^^^^ expected `i16`, found `i128` + | +help: you can convert `x_i16` from `i16` to `i128`, matching the type of `x_i128` + | +LL | i128::from(x_i16) > x_i128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:111:17 + | +LL | x_i16 > x_isize; + | ^^^^^^^ expected `i16`, found `isize` + | +help: you can convert `x_i16` from `i16` to `isize`, matching the type of `x_isize` + | +LL | isize::from(x_i16) > x_isize; + | ^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:114:17 + | +LL | x_i32 > x_i8; + | ^^^^ + | | + | expected `i32`, found `i8` + | help: you can convert an `i8` to `i32`: `x_i8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:116:17 + | +LL | x_i32 > x_i16; + | ^^^^^ + | | + | expected `i32`, found `i16` + | help: you can convert an `i16` to `i32`: `x_i16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:118:17 + | +LL | x_i32 > x_i64; + | ^^^^^ expected `i32`, found `i64` + | +help: you can convert `x_i32` from `i32` to `i64`, matching the type of `x_i64` + | +LL | i64::from(x_i32) > x_i64; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:120:17 + | +LL | x_i32 > x_i128; + | ^^^^^^ expected `i32`, found `i128` + | +help: you can convert `x_i32` from `i32` to `i128`, matching the type of `x_i128` + | +LL | i128::from(x_i32) > x_i128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:122:17 + | +LL | x_i32 > x_isize; + | ^^^^^^^ expected `i32`, found `isize` + | +help: you can convert an `isize` to `i32` and panic if the converted value wouldn't fit + | +LL | x_i32 > x_isize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:125:17 + | +LL | x_i64 > x_i8; + | ^^^^ + | | + | expected `i64`, found `i8` + | help: you can convert an `i8` to `i64`: `x_i8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:127:17 + | +LL | x_i64 > x_i16; + | ^^^^^ + | | + | expected `i64`, found `i16` + | help: you can convert an `i16` to `i64`: `x_i16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:129:17 + | +LL | x_i64 > x_i32; + | ^^^^^ + | | + | expected `i64`, found `i32` + | help: you can convert an `i32` to `i64`: `x_i32.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:131:17 + | +LL | x_i64 > x_i128; + | ^^^^^^ expected `i64`, found `i128` + | +help: you can convert `x_i64` from `i64` to `i128`, matching the type of `x_i128` + | +LL | i128::from(x_i64) > x_i128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:133:17 + | +LL | x_i64 > x_isize; + | ^^^^^^^ expected `i64`, found `isize` + | +help: you can convert an `isize` to `i64` and panic if the converted value wouldn't fit + | +LL | x_i64 > x_isize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:136:18 + | +LL | x_i128 > x_i8; + | ^^^^ + | | + | expected `i128`, found `i8` + | help: you can convert an `i8` to `i128`: `x_i8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:138:18 + | +LL | x_i128 > x_i16; + | ^^^^^ + | | + | expected `i128`, found `i16` + | help: you can convert an `i16` to `i128`: `x_i16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:140:18 + | +LL | x_i128 > x_i32; + | ^^^^^ + | | + | expected `i128`, found `i32` + | help: you can convert an `i32` to `i128`: `x_i32.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:142:18 + | +LL | x_i128 > x_i64; + | ^^^^^ + | | + | expected `i128`, found `i64` + | help: you can convert an `i64` to `i128`: `x_i64.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:144:18 + | +LL | x_i128 > x_isize; + | ^^^^^^^ expected `i128`, found `isize` + | +help: you can convert an `isize` to `i128` and panic if the converted value wouldn't fit + | +LL | x_i128 > x_isize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:147:19 + | +LL | x_isize > x_i8; + | ^^^^ + | | + | expected `isize`, found `i8` + | help: you can convert an `i8` to `isize`: `x_i8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:149:19 + | +LL | x_isize > x_i16; + | ^^^^^ + | | + | expected `isize`, found `i16` + | help: you can convert an `i16` to `isize`: `x_i16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:151:19 + | +LL | x_isize > x_i32; + | ^^^^^ expected `isize`, found `i32` + | +help: you can convert an `i32` to `isize` and panic if the converted value wouldn't fit + | +LL | x_isize > x_i32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:153:19 + | +LL | x_isize > x_i64; + | ^^^^^ expected `isize`, found `i64` + | +help: you can convert an `i64` to `isize` and panic if the converted value wouldn't fit + | +LL | x_isize > x_i64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:155:19 + | +LL | x_isize > x_i128; + | ^^^^^^ expected `isize`, found `i128` + | +help: you can convert an `i128` to `isize` and panic if the converted value wouldn't fit + | +LL | x_isize > x_i128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:161:16 + | +LL | x_u8 > x_i8; + | ^^^^ expected `u8`, found `i8` + | +help: you can convert an `i8` to `u8` and panic if the converted value wouldn't fit + | +LL | x_u8 > x_i8.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:163:16 + | +LL | x_u8 > x_i16; + | ^^^^^ expected `u8`, found `i16` + | +help: you can convert `x_u8` from `u8` to `i16`, matching the type of `x_i16` + | +LL | i16::from(x_u8) > x_i16; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:165:16 + | +LL | x_u8 > x_i32; + | ^^^^^ expected `u8`, found `i32` + | +help: you can convert `x_u8` from `u8` to `i32`, matching the type of `x_i32` + | +LL | i32::from(x_u8) > x_i32; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:167:16 + | +LL | x_u8 > x_i64; + | ^^^^^ expected `u8`, found `i64` + | +help: you can convert `x_u8` from `u8` to `i64`, matching the type of `x_i64` + | +LL | i64::from(x_u8) > x_i64; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:169:16 + | +LL | x_u8 > x_i128; + | ^^^^^^ expected `u8`, found `i128` + | +help: you can convert `x_u8` from `u8` to `i128`, matching the type of `x_i128` + | +LL | i128::from(x_u8) > x_i128; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:171:16 + | +LL | x_u8 > x_isize; + | ^^^^^^^ expected `u8`, found `isize` + | +help: you can convert `x_u8` from `u8` to `isize`, matching the type of `x_isize` + | +LL | isize::from(x_u8) > x_isize; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:174:17 + | +LL | x_u16 > x_i8; + | ^^^^ expected `u16`, found `i8` + | +help: you can convert an `i8` to `u16` and panic if the converted value wouldn't fit + | +LL | x_u16 > x_i8.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:176:17 + | +LL | x_u16 > x_i16; + | ^^^^^ expected `u16`, found `i16` + | +help: you can convert an `i16` to `u16` and panic if the converted value wouldn't fit + | +LL | x_u16 > x_i16.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:178:17 + | +LL | x_u16 > x_i32; + | ^^^^^ expected `u16`, found `i32` + | +help: you can convert `x_u16` from `u16` to `i32`, matching the type of `x_i32` + | +LL | i32::from(x_u16) > x_i32; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:180:17 + | +LL | x_u16 > x_i64; + | ^^^^^ expected `u16`, found `i64` + | +help: you can convert `x_u16` from `u16` to `i64`, matching the type of `x_i64` + | +LL | i64::from(x_u16) > x_i64; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:182:17 + | +LL | x_u16 > x_i128; + | ^^^^^^ expected `u16`, found `i128` + | +help: you can convert `x_u16` from `u16` to `i128`, matching the type of `x_i128` + | +LL | i128::from(x_u16) > x_i128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:184:17 + | +LL | x_u16 > x_isize; + | ^^^^^^^ expected `u16`, found `isize` + | +help: you can convert an `isize` to `u16` and panic if the converted value wouldn't fit + | +LL | x_u16 > x_isize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:187:17 + | +LL | x_u32 > x_i8; + | ^^^^ expected `u32`, found `i8` + | +help: you can convert an `i8` to `u32` and panic if the converted value wouldn't fit + | +LL | x_u32 > x_i8.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:189:17 + | +LL | x_u32 > x_i16; + | ^^^^^ expected `u32`, found `i16` + | +help: you can convert an `i16` to `u32` and panic if the converted value wouldn't fit + | +LL | x_u32 > x_i16.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:191:17 + | +LL | x_u32 > x_i32; + | ^^^^^ expected `u32`, found `i32` + | +help: you can convert an `i32` to `u32` and panic if the converted value wouldn't fit + | +LL | x_u32 > x_i32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:193:17 + | +LL | x_u32 > x_i64; + | ^^^^^ expected `u32`, found `i64` + | +help: you can convert `x_u32` from `u32` to `i64`, matching the type of `x_i64` + | +LL | i64::from(x_u32) > x_i64; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:195:17 + | +LL | x_u32 > x_i128; + | ^^^^^^ expected `u32`, found `i128` + | +help: you can convert `x_u32` from `u32` to `i128`, matching the type of `x_i128` + | +LL | i128::from(x_u32) > x_i128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:197:17 + | +LL | x_u32 > x_isize; + | ^^^^^^^ expected `u32`, found `isize` + | +help: you can convert an `isize` to `u32` and panic if the converted value wouldn't fit + | +LL | x_u32 > x_isize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:200:17 + | +LL | x_u64 > x_i8; + | ^^^^ expected `u64`, found `i8` + | +help: you can convert an `i8` to `u64` and panic if the converted value wouldn't fit + | +LL | x_u64 > x_i8.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:202:17 + | +LL | x_u64 > x_i16; + | ^^^^^ expected `u64`, found `i16` + | +help: you can convert an `i16` to `u64` and panic if the converted value wouldn't fit + | +LL | x_u64 > x_i16.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:204:17 + | +LL | x_u64 > x_i32; + | ^^^^^ expected `u64`, found `i32` + | +help: you can convert an `i32` to `u64` and panic if the converted value wouldn't fit + | +LL | x_u64 > x_i32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:206:17 + | +LL | x_u64 > x_i64; + | ^^^^^ expected `u64`, found `i64` + | +help: you can convert an `i64` to `u64` and panic if the converted value wouldn't fit + | +LL | x_u64 > x_i64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:208:17 + | +LL | x_u64 > x_i128; + | ^^^^^^ expected `u64`, found `i128` + | +help: you can convert `x_u64` from `u64` to `i128`, matching the type of `x_i128` + | +LL | i128::from(x_u64) > x_i128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:210:17 + | +LL | x_u64 > x_isize; + | ^^^^^^^ expected `u64`, found `isize` + | +help: you can convert an `isize` to `u64` and panic if the converted value wouldn't fit + | +LL | x_u64 > x_isize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:213:18 + | +LL | x_u128 > x_i8; + | ^^^^ expected `u128`, found `i8` + | +help: you can convert an `i8` to `u128` and panic if the converted value wouldn't fit + | +LL | x_u128 > x_i8.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:215:18 + | +LL | x_u128 > x_i16; + | ^^^^^ expected `u128`, found `i16` + | +help: you can convert an `i16` to `u128` and panic if the converted value wouldn't fit + | +LL | x_u128 > x_i16.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:217:18 + | +LL | x_u128 > x_i32; + | ^^^^^ expected `u128`, found `i32` + | +help: you can convert an `i32` to `u128` and panic if the converted value wouldn't fit + | +LL | x_u128 > x_i32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:219:18 + | +LL | x_u128 > x_i64; + | ^^^^^ expected `u128`, found `i64` + | +help: you can convert an `i64` to `u128` and panic if the converted value wouldn't fit + | +LL | x_u128 > x_i64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:221:18 + | +LL | x_u128 > x_i128; + | ^^^^^^ expected `u128`, found `i128` + | +help: you can convert an `i128` to `u128` and panic if the converted value wouldn't fit + | +LL | x_u128 > x_i128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:223:18 + | +LL | x_u128 > x_isize; + | ^^^^^^^ expected `u128`, found `isize` + | +help: you can convert an `isize` to `u128` and panic if the converted value wouldn't fit + | +LL | x_u128 > x_isize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:226:19 + | +LL | x_usize > x_i8; + | ^^^^ expected `usize`, found `i8` + | +help: you can convert an `i8` to `usize` and panic if the converted value wouldn't fit + | +LL | x_usize > x_i8.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:228:19 + | +LL | x_usize > x_i16; + | ^^^^^ expected `usize`, found `i16` + | +help: you can convert an `i16` to `usize` and panic if the converted value wouldn't fit + | +LL | x_usize > x_i16.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:230:19 + | +LL | x_usize > x_i32; + | ^^^^^ expected `usize`, found `i32` + | +help: you can convert an `i32` to `usize` and panic if the converted value wouldn't fit + | +LL | x_usize > x_i32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:232:19 + | +LL | x_usize > x_i64; + | ^^^^^ expected `usize`, found `i64` + | +help: you can convert an `i64` to `usize` and panic if the converted value wouldn't fit + | +LL | x_usize > x_i64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:234:19 + | +LL | x_usize > x_i128; + | ^^^^^^ expected `usize`, found `i128` + | +help: you can convert an `i128` to `usize` and panic if the converted value wouldn't fit + | +LL | x_usize > x_i128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:236:19 + | +LL | x_usize > x_isize; + | ^^^^^^^ expected `usize`, found `isize` + | +help: you can convert an `isize` to `usize` and panic if the converted value wouldn't fit + | +LL | x_usize > x_isize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:242:16 + | +LL | x_i8 > x_u8; + | ^^^^ expected `i8`, found `u8` + | +help: you can convert an `u8` to `i8` and panic if the converted value wouldn't fit + | +LL | x_i8 > x_u8.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:244:16 + | +LL | x_i8 > x_u16; + | ^^^^^ expected `i8`, found `u16` + | +help: you can convert an `u16` to `i8` and panic if the converted value wouldn't fit + | +LL | x_i8 > x_u16.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:246:16 + | +LL | x_i8 > x_u32; + | ^^^^^ expected `i8`, found `u32` + | +help: you can convert an `u32` to `i8` and panic if the converted value wouldn't fit + | +LL | x_i8 > x_u32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:248:16 + | +LL | x_i8 > x_u64; + | ^^^^^ expected `i8`, found `u64` + | +help: you can convert an `u64` to `i8` and panic if the converted value wouldn't fit + | +LL | x_i8 > x_u64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:250:16 + | +LL | x_i8 > x_u128; + | ^^^^^^ expected `i8`, found `u128` + | +help: you can convert an `u128` to `i8` and panic if the converted value wouldn't fit + | +LL | x_i8 > x_u128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:252:16 + | +LL | x_i8 > x_usize; + | ^^^^^^^ expected `i8`, found `usize` + | +help: you can convert an `usize` to `i8` and panic if the converted value wouldn't fit + | +LL | x_i8 > x_usize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:255:17 + | +LL | x_i16 > x_u8; + | ^^^^ + | | + | expected `i16`, found `u8` + | help: you can convert an `u8` to `i16`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:257:17 + | +LL | x_i16 > x_u16; + | ^^^^^ expected `i16`, found `u16` + | +help: you can convert an `u16` to `i16` and panic if the converted value wouldn't fit + | +LL | x_i16 > x_u16.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:259:17 + | +LL | x_i16 > x_u32; + | ^^^^^ expected `i16`, found `u32` + | +help: you can convert an `u32` to `i16` and panic if the converted value wouldn't fit + | +LL | x_i16 > x_u32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:261:17 + | +LL | x_i16 > x_u64; + | ^^^^^ expected `i16`, found `u64` + | +help: you can convert an `u64` to `i16` and panic if the converted value wouldn't fit + | +LL | x_i16 > x_u64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:263:17 + | +LL | x_i16 > x_u128; + | ^^^^^^ expected `i16`, found `u128` + | +help: you can convert an `u128` to `i16` and panic if the converted value wouldn't fit + | +LL | x_i16 > x_u128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:265:17 + | +LL | x_i16 > x_usize; + | ^^^^^^^ expected `i16`, found `usize` + | +help: you can convert an `usize` to `i16` and panic if the converted value wouldn't fit + | +LL | x_i16 > x_usize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:268:17 + | +LL | x_i32 > x_u8; + | ^^^^ + | | + | expected `i32`, found `u8` + | help: you can convert an `u8` to `i32`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:270:17 + | +LL | x_i32 > x_u16; + | ^^^^^ + | | + | expected `i32`, found `u16` + | help: you can convert an `u16` to `i32`: `x_u16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:272:17 + | +LL | x_i32 > x_u32; + | ^^^^^ expected `i32`, found `u32` + | +help: you can convert an `u32` to `i32` and panic if the converted value wouldn't fit + | +LL | x_i32 > x_u32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:274:17 + | +LL | x_i32 > x_u64; + | ^^^^^ expected `i32`, found `u64` + | +help: you can convert an `u64` to `i32` and panic if the converted value wouldn't fit + | +LL | x_i32 > x_u64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:276:17 + | +LL | x_i32 > x_u128; + | ^^^^^^ expected `i32`, found `u128` + | +help: you can convert an `u128` to `i32` and panic if the converted value wouldn't fit + | +LL | x_i32 > x_u128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:278:17 + | +LL | x_i32 > x_usize; + | ^^^^^^^ expected `i32`, found `usize` + | +help: you can convert an `usize` to `i32` and panic if the converted value wouldn't fit + | +LL | x_i32 > x_usize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:281:17 + | +LL | x_i64 > x_u8; + | ^^^^ + | | + | expected `i64`, found `u8` + | help: you can convert an `u8` to `i64`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:283:17 + | +LL | x_i64 > x_u16; + | ^^^^^ + | | + | expected `i64`, found `u16` + | help: you can convert an `u16` to `i64`: `x_u16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:285:17 + | +LL | x_i64 > x_u32; + | ^^^^^ + | | + | expected `i64`, found `u32` + | help: you can convert an `u32` to `i64`: `x_u32.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:287:17 + | +LL | x_i64 > x_u64; + | ^^^^^ expected `i64`, found `u64` + | +help: you can convert an `u64` to `i64` and panic if the converted value wouldn't fit + | +LL | x_i64 > x_u64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:289:17 + | +LL | x_i64 > x_u128; + | ^^^^^^ expected `i64`, found `u128` + | +help: you can convert an `u128` to `i64` and panic if the converted value wouldn't fit + | +LL | x_i64 > x_u128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:291:17 + | +LL | x_i64 > x_usize; + | ^^^^^^^ expected `i64`, found `usize` + | +help: you can convert an `usize` to `i64` and panic if the converted value wouldn't fit + | +LL | x_i64 > x_usize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:294:18 + | +LL | x_i128 > x_u8; + | ^^^^ + | | + | expected `i128`, found `u8` + | help: you can convert an `u8` to `i128`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:296:18 + | +LL | x_i128 > x_u16; + | ^^^^^ + | | + | expected `i128`, found `u16` + | help: you can convert an `u16` to `i128`: `x_u16.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:298:18 + | +LL | x_i128 > x_u32; + | ^^^^^ + | | + | expected `i128`, found `u32` + | help: you can convert an `u32` to `i128`: `x_u32.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:300:18 + | +LL | x_i128 > x_u64; + | ^^^^^ + | | + | expected `i128`, found `u64` + | help: you can convert an `u64` to `i128`: `x_u64.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:302:18 + | +LL | x_i128 > x_u128; + | ^^^^^^ expected `i128`, found `u128` + | +help: you can convert an `u128` to `i128` and panic if the converted value wouldn't fit + | +LL | x_i128 > x_u128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:304:18 + | +LL | x_i128 > x_usize; + | ^^^^^^^ expected `i128`, found `usize` + | +help: you can convert an `usize` to `i128` and panic if the converted value wouldn't fit + | +LL | x_i128 > x_usize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:307:19 + | +LL | x_isize > x_u8; + | ^^^^ + | | + | expected `isize`, found `u8` + | help: you can convert an `u8` to `isize`: `x_u8.into()` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:309:19 + | +LL | x_isize > x_u16; + | ^^^^^ expected `isize`, found `u16` + | +help: you can convert an `u16` to `isize` and panic if the converted value wouldn't fit + | +LL | x_isize > x_u16.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:311:19 + | +LL | x_isize > x_u32; + | ^^^^^ expected `isize`, found `u32` + | +help: you can convert an `u32` to `isize` and panic if the converted value wouldn't fit + | +LL | x_isize > x_u32.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:313:19 + | +LL | x_isize > x_u64; + | ^^^^^ expected `isize`, found `u64` + | +help: you can convert an `u64` to `isize` and panic if the converted value wouldn't fit + | +LL | x_isize > x_u64.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:315:19 + | +LL | x_isize > x_u128; + | ^^^^^^ expected `isize`, found `u128` + | +help: you can convert an `u128` to `isize` and panic if the converted value wouldn't fit + | +LL | x_isize > x_u128.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-binop.rs:317:19 + | +LL | x_isize > x_usize; + | ^^^^^^^ expected `isize`, found `usize` + | +help: you can convert an `usize` to `isize` and panic if the converted value wouldn't fit + | +LL | x_isize > x_usize.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 132 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/numeric/numeric-cast-no-fix.rs b/src/test/ui/numeric/numeric-cast-no-fix.rs new file mode 100644 index 0000000000000..63e5f098a25d3 --- /dev/null +++ b/src/test/ui/numeric/numeric-cast-no-fix.rs @@ -0,0 +1,87 @@ +#[allow(unused_must_use)] +fn main() { + let x_usize: usize = 1; + let x_u128: u128 = 2; + let x_u64: u64 = 3; + let x_u32: u32 = 4; + let x_u16: u16 = 5; + let x_u8: u8 = 6; + + x_usize > -1_isize; + //~^ ERROR mismatched types + x_u128 > -1_isize; + //~^ ERROR mismatched types + x_u64 > -1_isize; + //~^ ERROR mismatched types + x_u32 > -1_isize; + //~^ ERROR mismatched types + x_u16 > -1_isize; + //~^ ERROR mismatched types + x_u8 > -1_isize; + //~^ ERROR mismatched types + + x_usize > -1_i128; + //~^ ERROR mismatched types + x_u128 > -1_i128; + //~^ ERROR mismatched types + x_u64 > -1_i128; + //~^ ERROR mismatched types + x_u32 > -1_i128; + //~^ ERROR mismatched types + x_u16 > -1_i128; + //~^ ERROR mismatched types + x_u8 > -1_i128; + //~^ ERROR mismatched types + + x_usize > -1_i64; + //~^ ERROR mismatched types + x_u128 > -1_i64; + //~^ ERROR mismatched types + x_u64 > -1_i64; + //~^ ERROR mismatched types + x_u32 > -1_i64; + //~^ ERROR mismatched types + x_u16 > -1_i64; + //~^ ERROR mismatched types + x_u8 > -1_i64; + //~^ ERROR mismatched types + + x_usize > -1_i32; + //~^ ERROR mismatched types + x_u128 > -1_i32; + //~^ ERROR mismatched types + x_u64 > -1_i32; + //~^ ERROR mismatched types + x_u32 > -1_i32; + //~^ ERROR mismatched types + x_u16 > -1_i32; + //~^ ERROR mismatched types + x_u8 > -1_i32; + //~^ ERROR mismatched types + + x_usize > -1_i16; + //~^ ERROR mismatched types + x_u128 > -1_i16; + //~^ ERROR mismatched types + x_u64 > -1_i16; + //~^ ERROR mismatched types + x_u32 > -1_i16; + //~^ ERROR mismatched types + x_u16 > -1_i16; + //~^ ERROR mismatched types + x_u8 > -1_i16; + //~^ ERROR mismatched types + + x_usize > -1_i8; + //~^ ERROR mismatched types + x_u128 > -1_i8; + //~^ ERROR mismatched types + x_u64 > -1_i8; + //~^ ERROR mismatched types + x_u32 > -1_i8; + //~^ ERROR mismatched types + x_u16 > -1_i8; + //~^ ERROR mismatched types + x_u8 > -1_i8; + //~^ ERROR mismatched types +} diff --git a/src/test/ui/numeric/numeric-cast-no-fix.stderr b/src/test/ui/numeric/numeric-cast-no-fix.stderr new file mode 100644 index 0000000000000..4852e7047b47a --- /dev/null +++ b/src/test/ui/numeric/numeric-cast-no-fix.stderr @@ -0,0 +1,324 @@ +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:10:15 + | +LL | x_usize > -1_isize; + | ^^^^^^^^ expected `usize`, found `isize` + | + = note: `-1_isize` cannot fit into type `usize` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:12:14 + | +LL | x_u128 > -1_isize; + | ^^^^^^^^ expected `u128`, found `isize` + | + = note: `-1_isize` cannot fit into type `u128` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:14:13 + | +LL | x_u64 > -1_isize; + | ^^^^^^^^ expected `u64`, found `isize` + | + = note: `-1_isize` cannot fit into type `u64` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:16:13 + | +LL | x_u32 > -1_isize; + | ^^^^^^^^ expected `u32`, found `isize` + | + = note: `-1_isize` cannot fit into type `u32` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:18:13 + | +LL | x_u16 > -1_isize; + | ^^^^^^^^ expected `u16`, found `isize` + | + = note: `-1_isize` cannot fit into type `u16` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:20:12 + | +LL | x_u8 > -1_isize; + | ^^^^^^^^ expected `u8`, found `isize` + | +help: you can convert `x_u8` from `u8` to `isize`, matching the type of `-1_isize` + | +LL | isize::from(x_u8) > -1_isize; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:23:15 + | +LL | x_usize > -1_i128; + | ^^^^^^^ expected `usize`, found `i128` + | + = note: `-1_i128` cannot fit into type `usize` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:25:14 + | +LL | x_u128 > -1_i128; + | ^^^^^^^ expected `u128`, found `i128` + | + = note: `-1_i128` cannot fit into type `u128` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:27:13 + | +LL | x_u64 > -1_i128; + | ^^^^^^^ expected `u64`, found `i128` + | +help: you can convert `x_u64` from `u64` to `i128`, matching the type of `-1_i128` + | +LL | i128::from(x_u64) > -1_i128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:29:13 + | +LL | x_u32 > -1_i128; + | ^^^^^^^ expected `u32`, found `i128` + | +help: you can convert `x_u32` from `u32` to `i128`, matching the type of `-1_i128` + | +LL | i128::from(x_u32) > -1_i128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:31:13 + | +LL | x_u16 > -1_i128; + | ^^^^^^^ expected `u16`, found `i128` + | +help: you can convert `x_u16` from `u16` to `i128`, matching the type of `-1_i128` + | +LL | i128::from(x_u16) > -1_i128; + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:33:12 + | +LL | x_u8 > -1_i128; + | ^^^^^^^ expected `u8`, found `i128` + | +help: you can convert `x_u8` from `u8` to `i128`, matching the type of `-1_i128` + | +LL | i128::from(x_u8) > -1_i128; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:36:15 + | +LL | x_usize > -1_i64; + | ^^^^^^ expected `usize`, found `i64` + | + = note: `-1_i64` cannot fit into type `usize` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:38:14 + | +LL | x_u128 > -1_i64; + | ^^^^^^ expected `u128`, found `i64` + | + = note: `-1_i64` cannot fit into type `u128` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:40:13 + | +LL | x_u64 > -1_i64; + | ^^^^^^ expected `u64`, found `i64` + | + = note: `-1_i64` cannot fit into type `u64` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:42:13 + | +LL | x_u32 > -1_i64; + | ^^^^^^ expected `u32`, found `i64` + | +help: you can convert `x_u32` from `u32` to `i64`, matching the type of `-1_i64` + | +LL | i64::from(x_u32) > -1_i64; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:44:13 + | +LL | x_u16 > -1_i64; + | ^^^^^^ expected `u16`, found `i64` + | +help: you can convert `x_u16` from `u16` to `i64`, matching the type of `-1_i64` + | +LL | i64::from(x_u16) > -1_i64; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:46:12 + | +LL | x_u8 > -1_i64; + | ^^^^^^ expected `u8`, found `i64` + | +help: you can convert `x_u8` from `u8` to `i64`, matching the type of `-1_i64` + | +LL | i64::from(x_u8) > -1_i64; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:49:15 + | +LL | x_usize > -1_i32; + | ^^^^^^ expected `usize`, found `i32` + | + = note: `-1_i32` cannot fit into type `usize` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:51:14 + | +LL | x_u128 > -1_i32; + | ^^^^^^ expected `u128`, found `i32` + | + = note: `-1_i32` cannot fit into type `u128` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:53:13 + | +LL | x_u64 > -1_i32; + | ^^^^^^ expected `u64`, found `i32` + | + = note: `-1_i32` cannot fit into type `u64` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:55:13 + | +LL | x_u32 > -1_i32; + | ^^^^^^ expected `u32`, found `i32` + | + = note: `-1_i32` cannot fit into type `u32` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:57:13 + | +LL | x_u16 > -1_i32; + | ^^^^^^ expected `u16`, found `i32` + | +help: you can convert `x_u16` from `u16` to `i32`, matching the type of `-1_i32` + | +LL | i32::from(x_u16) > -1_i32; + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:59:12 + | +LL | x_u8 > -1_i32; + | ^^^^^^ expected `u8`, found `i32` + | +help: you can convert `x_u8` from `u8` to `i32`, matching the type of `-1_i32` + | +LL | i32::from(x_u8) > -1_i32; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:62:15 + | +LL | x_usize > -1_i16; + | ^^^^^^ expected `usize`, found `i16` + | + = note: `-1_i16` cannot fit into type `usize` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:64:14 + | +LL | x_u128 > -1_i16; + | ^^^^^^ expected `u128`, found `i16` + | + = note: `-1_i16` cannot fit into type `u128` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:66:13 + | +LL | x_u64 > -1_i16; + | ^^^^^^ expected `u64`, found `i16` + | + = note: `-1_i16` cannot fit into type `u64` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:68:13 + | +LL | x_u32 > -1_i16; + | ^^^^^^ expected `u32`, found `i16` + | + = note: `-1_i16` cannot fit into type `u32` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:70:13 + | +LL | x_u16 > -1_i16; + | ^^^^^^ expected `u16`, found `i16` + | + = note: `-1_i16` cannot fit into type `u16` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:72:12 + | +LL | x_u8 > -1_i16; + | ^^^^^^ expected `u8`, found `i16` + | +help: you can convert `x_u8` from `u8` to `i16`, matching the type of `-1_i16` + | +LL | i16::from(x_u8) > -1_i16; + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:75:15 + | +LL | x_usize > -1_i8; + | ^^^^^ expected `usize`, found `i8` + | + = note: `-1_i8` cannot fit into type `usize` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:77:14 + | +LL | x_u128 > -1_i8; + | ^^^^^ expected `u128`, found `i8` + | + = note: `-1_i8` cannot fit into type `u128` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:79:13 + | +LL | x_u64 > -1_i8; + | ^^^^^ expected `u64`, found `i8` + | + = note: `-1_i8` cannot fit into type `u64` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:81:13 + | +LL | x_u32 > -1_i8; + | ^^^^^ expected `u32`, found `i8` + | + = note: `-1_i8` cannot fit into type `u32` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:83:13 + | +LL | x_u16 > -1_i8; + | ^^^^^ expected `u16`, found `i8` + | + = note: `-1_i8` cannot fit into type `u16` + +error[E0308]: mismatched types + --> $DIR/numeric-cast-no-fix.rs:85:12 + | +LL | x_u8 > -1_i8; + | ^^^^^ expected `u8`, found `i8` + | + = note: `-1_i8` cannot fit into type `u8` + +error: aborting due to 36 previous errors + +For more information about this error, try `rustc --explain E0308`. 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/object-lifetime/object-lifetime-default-elision.stderr b/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr index 1952ee8269d5b..79ded5fc875a2 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr @@ -24,8 +24,8 @@ note: ...so that the expression is assignable | LL | ss | ^^ - = note: expected `&'b (dyn SomeTrait + 'b)` - found `&dyn SomeTrait` + = note: expected `&'b (dyn SomeTrait + 'b)` + found `&dyn SomeTrait` error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> $DIR/object-lifetime-default-elision.rs:71:5 @@ -53,8 +53,8 @@ note: ...so that the expression is assignable | LL | ss | ^^ - = note: expected `&'b (dyn SomeTrait + 'b)` - found `&dyn SomeTrait` + = note: expected `&'b (dyn SomeTrait + 'b)` + found `&dyn SomeTrait` error: aborting due to 2 previous errors diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr index f6252f4ed7977..9563c0dff3644 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr @@ -1,11 +1,11 @@ -error[E0621]: explicit lifetime required in the type of `ss` +error: lifetime may not live long enough --> $DIR/object-lifetime-default-from-box-error.rs:18:5 | LL | fn load(ss: &mut SomeStruct) -> Box { - | --------------- help: add explicit lifetime `'static` to the type of `ss`: `&mut SomeStruct<'static>` + | -- has type `&mut SomeStruct<'1>` ... LL | ss.r - | ^^^^ lifetime `'static` required + | ^^^^ returning this value requires that `'1` must outlive `'static` error[E0507]: cannot move out of `ss.r` which is behind a mutable reference --> $DIR/object-lifetime-default-from-box-error.rs:18:5 diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.rs b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.rs index 587aab1edce38..708ab1cf38297 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.rs +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.rs @@ -15,7 +15,7 @@ fn load(ss: &mut SomeStruct) -> Box { // `Box` defaults to a `'static` bound, so this return // is illegal. - ss.r //~ ERROR explicit lifetime required in the type of `ss` [E0621] + ss.r //~ ERROR cannot infer an appropriate lifetime } fn store(ss: &mut SomeStruct, b: Box) { diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr index 78e4bdd374da9..1b1e0d9610724 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.stderr @@ -1,11 +1,16 @@ -error[E0621]: explicit lifetime required in the type of `ss` +error[E0759]: cannot infer an appropriate lifetime --> $DIR/object-lifetime-default-from-box-error.rs:18:5 | LL | fn load(ss: &mut SomeStruct) -> Box { - | --------------- help: add explicit lifetime `'static` to the type of `ss`: `&mut SomeStruct<'static>` + | --------------- this data with an anonymous lifetime `'_`... ... LL | ss.r - | ^^^^ lifetime `'static` required + | ^^^^ ...is captured and required to live as long as `'static` here + | +help: to declare that the trait object captures data from argument `ss`, you can add an explicit `'_` lifetime bound + | +LL | fn load(ss: &mut SomeStruct) -> Box { + | ^^^^ error[E0621]: explicit lifetime required in the type of `ss` --> $DIR/object-lifetime-default-from-box-error.rs:31:12 @@ -18,4 +23,5 @@ LL | ss.r = b; error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0621`. +Some errors have detailed explanations: E0621, E0759. +For more information about an error, try `rustc --explain E0621`. 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.rs b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs index c8bc4a2a8d51b..31b3407a46e08 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs @@ -4,15 +4,15 @@ // We wrap patterns in a tuple because top-level or-patterns were special-cased. fn main() { match (0u8, 0u8) { - //~^ ERROR non-exhaustive patterns: `(2u8..=std::u8::MAX, _)` + //~^ ERROR non-exhaustive patterns: `(2u8..=u8::MAX, _)` (0 | 1, 2 | 3) => {} } match ((0u8,),) { - //~^ ERROR non-exhaustive patterns: `((4u8..=std::u8::MAX))` + //~^ ERROR non-exhaustive patterns: `((4u8..=u8::MAX))` ((0 | 1,) | (2 | 3,),) => {} } match (Some(0u8),) { - //~^ ERROR non-exhaustive patterns: `(Some(2u8..=std::u8::MAX))` + //~^ ERROR non-exhaustive patterns: `(Some(2u8..=u8::MAX))` (None | Some(0 | 1),) => {} } } diff --git a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr index 3ba26de10d3d5..653f4978ab350 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr @@ -1,26 +1,29 @@ -error[E0004]: non-exhaustive patterns: `(2u8..=std::u8::MAX, _)` not covered +error[E0004]: non-exhaustive patterns: `(2u8..=u8::MAX, _)` not covered --> $DIR/exhaustiveness-non-exhaustive.rs:6:11 | LL | match (0u8, 0u8) { - | ^^^^^^^^^^ pattern `(2u8..=std::u8::MAX, _)` not covered + | ^^^^^^^^^^ pattern `(2u8..=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 +error[E0004]: non-exhaustive patterns: `((4u8..=u8::MAX))` not covered --> $DIR/exhaustiveness-non-exhaustive.rs:10:11 | LL | match ((0u8,),) { - | ^^^^^^^^^ pattern `((4u8..=std::u8::MAX))` not covered + | ^^^^^^^^^ pattern `((4u8..=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 +error[E0004]: non-exhaustive patterns: `(Some(2u8..=u8::MAX))` not covered --> $DIR/exhaustiveness-non-exhaustive.rs:14:11 | LL | match (Some(0u8),) { - | ^^^^^^^^^^^^ pattern `(Some(2u8..=std::u8::MAX))` not covered + | ^^^^^^^^^^^^ pattern `(Some(2u8..=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..2eadef9cb5c11 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 @@ -1,23 +1,25 @@ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered +error[E0005]: refutable pattern in local binding: `i32::MIN..=-1i32` and `3i32..=i32::MAX` not covered --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:4:9 | LL | let 0 | (1 | 2) = 0; - | ^^^^^^^^^^^ patterns `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + | ^^^^^^^^^^^ patterns `i32::MIN..=-1i32` and `3i32..=i32::MAX` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` help: you might want to use `if let` to ignore the variant that isn't matched | LL | if let 0 | (1 | 2) = 0 { /* */ } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0004]: non-exhaustive patterns: `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered +error[E0004]: non-exhaustive patterns: `i32::MIN..=-1i32` and `3i32..=i32::MAX` not covered --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:5:11 | LL | match 0 { - | ^ patterns `std::i32::MIN..=-1i32` and `3i32..=std::i32::MAX` not covered + | ^ patterns `i32::MIN..=-1i32` and `3i32..=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..998577cf4b5e0 --- /dev/null +++ b/src/test/ui/or-patterns/mismatched-bindings-async-fn.stderr @@ -0,0 +1,35 @@ +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: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:11:9 + | +LL | let x | s = String::new(); + | ^ - 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: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0408`. diff --git a/src/test/ui/order-dependent-cast-inference.stderr b/src/test/ui/order-dependent-cast-inference.stderr index ad50b415869dd..9f4ac0fea36ef 100644 --- a/src/test/ui/order-dependent-cast-inference.stderr +++ b/src/test/ui/order-dependent-cast-inference.stderr @@ -1,10 +1,8 @@ error[E0641]: cannot cast to a pointer of an unknown kind - --> $DIR/order-dependent-cast-inference.rs:5:17 + --> $DIR/order-dependent-cast-inference.rs:5:22 | LL | let mut y = 0 as *const _; - | ^^^^^-------- - | | - | help: consider giving more type information + | ^^^^^^^^ needs more type information | = note: the type information given here is insufficient to check whether the pointer cast is valid 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/overlap-doesnt-conflict-with-specialization.rs b/src/test/ui/overlap-doesnt-conflict-with-specialization.rs index dd09d68367ec3..1e413120a3717 100644 --- a/src/test/ui/overlap-doesnt-conflict-with-specialization.rs +++ b/src/test/ui/overlap-doesnt-conflict-with-specialization.rs @@ -1,7 +1,7 @@ // run-pass #![feature(marker_trait_attr)] -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete #[marker] trait MyMarker {} diff --git a/src/test/ui/overlap-doesnt-conflict-with-specialization.stderr b/src/test/ui/overlap-doesnt-conflict-with-specialization.stderr new file mode 100644 index 0000000000000..16df31ba2a88b --- /dev/null +++ b/src/test/ui/overlap-doesnt-conflict-with-specialization.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/overlap-doesnt-conflict-with-specialization.rs:4:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + 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/ui/panic-while-printing.rs b/src/test/ui/panic-while-printing.rs new file mode 100644 index 0000000000000..7e9fa16b0847a --- /dev/null +++ b/src/test/ui/panic-while-printing.rs @@ -0,0 +1,24 @@ +// run-pass +// ignore-emscripten no subprocess support + +#![feature(set_stdio)] + +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::io::set_panic; + +pub struct A; + +impl Display for A { + fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { + panic!(); + } +} + +fn main() { + set_panic(Some(Box::new(Vec::new()))); + assert!(std::panic::catch_unwind(|| { + eprintln!("{}", A); + }) + .is_err()); +} 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/paren-span.stderr b/src/test/ui/paren-span.stderr index 141378752d6f4..ca22401f45bb4 100644 --- a/src/test/ui/paren-span.stderr +++ b/src/test/ui/paren-span.stderr @@ -1,8 +1,8 @@ error[E0616]: field `x` of struct `m::S` is private - --> $DIR/paren-span.rs:19:12 + --> $DIR/paren-span.rs:19:14 | LL | paren!(s.x); - | ^^^ + | ^ private field error: aborting due to previous error diff --git a/src/test/ui/parser/assoc-static-semantic-fail.rs b/src/test/ui/parser/assoc-static-semantic-fail.rs index 215a292131521..a8759d2090d0f 100644 --- a/src/test/ui/parser/assoc-static-semantic-fail.rs +++ b/src/test/ui/parser/assoc-static-semantic-fail.rs @@ -1,6 +1,7 @@ // Semantically, we do not allow e.g., `static X: u8 = 0;` as an associated item. #![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete fn main() {} diff --git a/src/test/ui/parser/assoc-static-semantic-fail.stderr b/src/test/ui/parser/assoc-static-semantic-fail.stderr index 612297c9cd8b1..bc3054c3e3062 100644 --- a/src/test/ui/parser/assoc-static-semantic-fail.stderr +++ b/src/test/ui/parser/assoc-static-semantic-fail.stderr @@ -1,17 +1,17 @@ error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:9:5 + --> $DIR/assoc-static-semantic-fail.rs:10:5 | LL | static IA: u8 = 0; | ^^^^^^^^^^^^^^^^^^ error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:11:5 + --> $DIR/assoc-static-semantic-fail.rs:12:5 | LL | static IB: u8; | ^^^^^^^^^^^^^^ error: a static item cannot be `default` - --> $DIR/assoc-static-semantic-fail.rs:14:5 + --> $DIR/assoc-static-semantic-fail.rs:15:5 | LL | default static IC: u8 = 0; | ^^^^^^^ `default` because of this @@ -19,13 +19,13 @@ LL | default static IC: u8 = 0; = note: only associated `fn`, `const`, and `type` items can be `default` error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:14:5 + --> $DIR/assoc-static-semantic-fail.rs:15:5 | LL | default static IC: u8 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a static item cannot be `default` - --> $DIR/assoc-static-semantic-fail.rs:17:16 + --> $DIR/assoc-static-semantic-fail.rs:18:16 | LL | pub(crate) default static ID: u8; | ^^^^^^^ `default` because of this @@ -33,25 +33,25 @@ LL | pub(crate) default static ID: u8; = note: only associated `fn`, `const`, and `type` items can be `default` error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:17:5 + --> $DIR/assoc-static-semantic-fail.rs:18:5 | LL | pub(crate) default static ID: u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:24:5 + --> $DIR/assoc-static-semantic-fail.rs:25:5 | LL | static TA: u8 = 0; | ^^^^^^^^^^^^^^^^^^ error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:26:5 + --> $DIR/assoc-static-semantic-fail.rs:27:5 | LL | static TB: u8; | ^^^^^^^^^^^^^^ error: a static item cannot be `default` - --> $DIR/assoc-static-semantic-fail.rs:28:5 + --> $DIR/assoc-static-semantic-fail.rs:29:5 | LL | default static TC: u8 = 0; | ^^^^^^^ `default` because of this @@ -59,13 +59,13 @@ LL | default static TC: u8 = 0; = note: only associated `fn`, `const`, and `type` items can be `default` error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:28:5 + --> $DIR/assoc-static-semantic-fail.rs:29:5 | LL | default static TC: u8 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a static item cannot be `default` - --> $DIR/assoc-static-semantic-fail.rs:31:16 + --> $DIR/assoc-static-semantic-fail.rs:32:16 | LL | pub(crate) default static TD: u8; | ^^^^^^^ `default` because of this @@ -73,25 +73,25 @@ LL | pub(crate) default static TD: u8; = note: only associated `fn`, `const`, and `type` items can be `default` error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:31:5 + --> $DIR/assoc-static-semantic-fail.rs:32:5 | LL | pub(crate) default static TD: u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:38:5 + --> $DIR/assoc-static-semantic-fail.rs:39:5 | LL | static TA: u8 = 0; | ^^^^^^^^^^^^^^^^^^ error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:40:5 + --> $DIR/assoc-static-semantic-fail.rs:41:5 | LL | static TB: u8; | ^^^^^^^^^^^^^^ error: a static item cannot be `default` - --> $DIR/assoc-static-semantic-fail.rs:43:5 + --> $DIR/assoc-static-semantic-fail.rs:44:5 | LL | default static TC: u8 = 0; | ^^^^^^^ `default` because of this @@ -99,13 +99,13 @@ LL | default static TC: u8 = 0; = note: only associated `fn`, `const`, and `type` items can be `default` error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:43:5 + --> $DIR/assoc-static-semantic-fail.rs:44:5 | LL | default static TC: u8 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a static item cannot be `default` - --> $DIR/assoc-static-semantic-fail.rs:46:9 + --> $DIR/assoc-static-semantic-fail.rs:47:9 | LL | pub default static TD: u8; | ^^^^^^^ `default` because of this @@ -113,13 +113,13 @@ LL | pub default static TD: u8; = note: only associated `fn`, `const`, and `type` items can be `default` error: associated `static` items are not allowed - --> $DIR/assoc-static-semantic-fail.rs:46:5 + --> $DIR/assoc-static-semantic-fail.rs:47:5 | LL | pub default static TD: u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: associated constant in `impl` without body - --> $DIR/assoc-static-semantic-fail.rs:11:5 + --> $DIR/assoc-static-semantic-fail.rs:12:5 | LL | static IB: u8; | ^^^^^^^^^^^^^- @@ -127,7 +127,7 @@ LL | static IB: u8; | help: provide a definition for the constant: `= ;` error: associated constant in `impl` without body - --> $DIR/assoc-static-semantic-fail.rs:17:5 + --> $DIR/assoc-static-semantic-fail.rs:18:5 | LL | pub(crate) default static ID: u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -135,13 +135,13 @@ LL | pub(crate) default static ID: u8; | help: provide a definition for the constant: `= ;` error[E0449]: unnecessary visibility qualifier - --> $DIR/assoc-static-semantic-fail.rs:31:5 + --> $DIR/assoc-static-semantic-fail.rs:32:5 | LL | pub(crate) default static TD: u8; | ^^^^^^^^^^ error: associated constant in `impl` without body - --> $DIR/assoc-static-semantic-fail.rs:40:5 + --> $DIR/assoc-static-semantic-fail.rs:41:5 | LL | static TB: u8; | ^^^^^^^^^^^^^- @@ -149,7 +149,7 @@ LL | static TB: u8; | help: provide a definition for the constant: `= ;` error: associated constant in `impl` without body - --> $DIR/assoc-static-semantic-fail.rs:46:5 + --> $DIR/assoc-static-semantic-fail.rs:47:5 | LL | pub default static TD: u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -157,11 +157,20 @@ LL | pub default static TD: u8; | help: provide a definition for the constant: `= ;` error[E0449]: unnecessary visibility qualifier - --> $DIR/assoc-static-semantic-fail.rs:46:5 + --> $DIR/assoc-static-semantic-fail.rs:47:5 | LL | pub default static TD: u8; | ^^^ `pub` not permitted here because it's implied -error: aborting due to 24 previous errors +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/assoc-static-semantic-fail.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +error: aborting due to 24 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0449`. diff --git a/src/test/ui/parser/byte-literals.rs b/src/test/ui/parser/byte-literals.rs index dadf3971220f7..9683a83e72095 100644 --- a/src/test/ui/parser/byte-literals.rs +++ b/src/test/ui/parser/byte-literals.rs @@ -8,5 +8,5 @@ pub fn main() { b' '; //~ ERROR byte constant must be escaped b'''; //~ ERROR byte constant must be escaped b'é'; //~ ERROR byte constant must be ASCII - b'a //~ ERROR unterminated byte constant + b'a //~ ERROR unterminated byte constant [E0763] } diff --git a/src/test/ui/parser/byte-literals.stderr b/src/test/ui/parser/byte-literals.stderr index 53d50af88d33b..7bbdc07cd835f 100644 --- a/src/test/ui/parser/byte-literals.stderr +++ b/src/test/ui/parser/byte-literals.stderr @@ -34,7 +34,7 @@ error: byte constant must be ASCII. Use a \xHH escape for a non-ASCII byte LL | b'é'; | ^ -error: unterminated byte constant +error[E0763]: unterminated byte constant --> $DIR/byte-literals.rs:11:6 | LL | b'a @@ -42,3 +42,4 @@ LL | b'a error: aborting due to 7 previous errors +For more information about this error, try `rustc --explain E0763`. 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.rs b/src/test/ui/parser/circular_modules_main.rs index b85003bf0910f..1ae36a1f7605e 100644 --- a/src/test/ui/parser/circular_modules_main.rs +++ b/src/test/ui/parser/circular_modules_main.rs @@ -6,5 +6,5 @@ pub fn hi_str() -> String { } fn main() { - circular_modules_hello::say_hello(); + circular_modules_hello::say_hello(); //~ ERROR cannot find function `say_hello` in module } diff --git a/src/test/ui/parser/circular_modules_main.stderr b/src/test/ui/parser/circular_modules_main.stderr index 33865fb7bca95..5d4db8c31a2c3 100644 --- a/src/test/ui/parser/circular_modules_main.stderr +++ b/src/test/ui/parser/circular_modules_main.stderr @@ -1,8 +1,20 @@ error: circular modules: $DIR/circular_modules_hello.rs -> $DIR/circular_modules_main.rs -> $DIR/circular_modules_hello.rs - --> $DIR/circular_modules_main.rs:2:5 + --> $DIR/circular_modules_main.rs:2:1 | LL | mod circular_modules_hello; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error[E0425]: cannot find function `say_hello` in module `circular_modules_hello` + --> $DIR/circular_modules_main.rs:9:29 + | +LL | circular_modules_hello::say_hello(); + | ^^^^^^^^^ not found in `circular_modules_hello` + | +help: consider importing this function + | +LL | use circular_modules_hello::say_hello; + | + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0425`. 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/default.rs b/src/test/ui/parser/default.rs index 64ba4b5531184..52338c1f13aad 100644 --- a/src/test/ui/parser/default.rs +++ b/src/test/ui/parser/default.rs @@ -1,6 +1,7 @@ // Test successful and unsuccessful parsing of the `default` contextual keyword #![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete trait Foo { fn foo() -> T; diff --git a/src/test/ui/parser/default.stderr b/src/test/ui/parser/default.stderr index 15c49e8b6270b..dea35666f37b5 100644 --- a/src/test/ui/parser/default.stderr +++ b/src/test/ui/parser/default.stderr @@ -1,5 +1,5 @@ error: `default` is not followed by an item - --> $DIR/default.rs:22:5 + --> $DIR/default.rs:23:5 | LL | default pub fn foo() -> T { T::default() } | ^^^^^^^ the `default` qualifier @@ -7,7 +7,7 @@ LL | default pub fn foo() -> T { T::default() } = note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default` error: non-item in item list - --> $DIR/default.rs:22:13 + --> $DIR/default.rs:23:13 | LL | impl Foo for u32 { | - item list starts here @@ -18,13 +18,22 @@ LL | } | - item list ends here error[E0449]: unnecessary visibility qualifier - --> $DIR/default.rs:16:5 + --> $DIR/default.rs:17:5 | LL | pub default fn foo() -> T { | ^^^ `pub` not permitted here because it's implied +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/default.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0046]: not all trait items implemented, missing: `foo` - --> $DIR/default.rs:21:1 + --> $DIR/default.rs:22:1 | LL | fn foo() -> T; | -------------------------- `foo` from trait @@ -32,7 +41,7 @@ LL | fn foo() -> T; LL | impl Foo for u32 { | ^^^^^^^^^^^^^^^^ missing `foo` in implementation -error: aborting due to 4 previous errors +error: aborting due to 4 previous errors; 1 warning emitted Some errors have detailed explanations: E0046, E0449. For more information about an error, try `rustc --explain E0046`. 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/extern-abi-from-mac-literal-frag.rs b/src/test/ui/parser/extern-abi-from-mac-literal-frag.rs index cb23f2c808c34..a516aa44c6576 100644 --- a/src/test/ui/parser/extern-abi-from-mac-literal-frag.rs +++ b/src/test/ui/parser/extern-abi-from-mac-literal-frag.rs @@ -1,7 +1,8 @@ +#![allow(clashing_extern_decl)] // check-pass // In this test we check that the parser accepts an ABI string when it -// comes from a macro `literal` fragment as opposed to a hardcoded string. +// comes from a macro `literal` or `expr` fragment as opposed to a hardcoded string. fn main() {} @@ -17,6 +18,18 @@ macro_rules! abi_from_lit_frag { } } +macro_rules! abi_from_expr_frag { + ($abi:expr) => { + extern $abi { + fn _import(); + } + + extern $abi fn _export() {} + + type _PTR = extern $abi fn(); + }; +} + mod rust { abi_from_lit_frag!("Rust"); } @@ -24,3 +37,11 @@ mod rust { mod c { abi_from_lit_frag!("C"); } + +mod rust_expr { + abi_from_expr_frag!("Rust"); +} + +mod c_expr { + abi_from_expr_frag!("C"); +} 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-5806.stderr b/src/test/ui/parser/issue-5806.stderr index 6cf902ca86e79..bdb5c91ff91eb 100644 --- a/src/test/ui/parser/issue-5806.stderr +++ b/src/test/ui/parser/issue-5806.stderr @@ -1,8 +1,8 @@ error: couldn't read $DIR/../parser: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE) - --> $DIR/issue-5806.rs:5:5 + --> $DIR/issue-5806.rs:5:1 | LL | mod foo; - | ^^^ + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/parser/issue-62894.stderr b/src/test/ui/parser/issue-62894.stderr index 6db380f7a7fe2..73e3552e3ec7a 100644 --- a/src/test/ui/parser/issue-62894.stderr +++ b/src/test/ui/parser/issue-62894.stderr @@ -42,6 +42,11 @@ LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! LL | LL | fn main() {} | ^^ unexpected token + | + ::: $SRC_DIR/libcore/macros/mod.rs:LL:COL + | +LL | ($left:expr, $right:expr) => ({ + | ---------- while parsing argument for this `expr` macro fragment error: aborting due to 4 previous errors 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 d475c79cb27b4..e51c2c0e8428b 100644 --- a/src/test/ui/parser/issue-68890-2.stderr +++ b/src/test/ui/parser/issue-68890-2.stderr @@ -18,5 +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-70050-ntliteral-accepts-negated-lit.rs b/src/test/ui/parser/issue-70050-ntliteral-accepts-negated-lit.rs new file mode 100644 index 0000000000000..aca9d9eb0a5b4 --- /dev/null +++ b/src/test/ui/parser/issue-70050-ntliteral-accepts-negated-lit.rs @@ -0,0 +1,16 @@ +// check-pass + +macro_rules! foo { + ($a:literal) => { + bar!($a) + }; +} + +macro_rules! bar { + ($b:literal) => {}; +} + +fn main() { + foo!(-2); + bar!(-2); +} 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..e33adb239d78d 100644 --- a/src/test/ui/parser/issue-8537.stderr +++ b/src/test/ui/parser/issue-8537.stderr @@ -4,7 +4,8 @@ error[E0703]: invalid ABI: found `invalid-ab_isize` LL | "invalid-ab_isize" | ^^^^^^^^^^^^^^^^^^ invalid ABI | - = help: valid ABIs: cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, Rust, C, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted + = help: valid ABIs: Rust, C, cdecl, stdcall, fastcall, vectorcall, thiscall, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, system, rust-intrinsic, rust-call, platform-intrinsic, unadjusted error: aborting due to previous error +For more information about this error, try `rustc --explain E0703`. diff --git a/src/test/ui/parser/let-binop.rs b/src/test/ui/parser/let-binop.rs new file mode 100644 index 0000000000000..7f58f5df2d412 --- /dev/null +++ b/src/test/ui/parser/let-binop.rs @@ -0,0 +1,8 @@ +fn main() { + let a: i8 *= 1; //~ ERROR can't reassign to an uninitialized variable + let _ = a; + let b += 1; //~ ERROR can't reassign to an uninitialized variable + let _ = b; + let c *= 1; //~ ERROR can't reassign to an uninitialized variable + let _ = c; +} diff --git a/src/test/ui/parser/let-binop.stderr b/src/test/ui/parser/let-binop.stderr new file mode 100644 index 0000000000000..71431499ac70b --- /dev/null +++ b/src/test/ui/parser/let-binop.stderr @@ -0,0 +1,20 @@ +error: can't reassign to an uninitialized variable + --> $DIR/let-binop.rs:2:15 + | +LL | let a: i8 *= 1; + | ^^ help: initialize the variable + +error: can't reassign to an uninitialized variable + --> $DIR/let-binop.rs:4:11 + | +LL | let b += 1; + | ^^ help: initialize the variable + +error: can't reassign to an uninitialized variable + --> $DIR/let-binop.rs:6:11 + | +LL | let c *= 1; + | ^^ help: initialize the variable + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/parser/lex-bad-char-literals-4.stderr b/src/test/ui/parser/lex-bad-char-literals-4.stderr index 8f8f806f6cf61..fec4421c48a7c 100644 --- a/src/test/ui/parser/lex-bad-char-literals-4.stderr +++ b/src/test/ui/parser/lex-bad-char-literals-4.stderr @@ -1,4 +1,4 @@ -error: unterminated character literal +error[E0762]: unterminated character literal --> $DIR/lex-bad-char-literals-4.rs:4:5 | LL | '● @@ -6,3 +6,4 @@ LL | '● error: aborting due to previous error +For more information about this error, try `rustc --explain E0762`. diff --git a/src/test/ui/parser/lex-bad-char-literals-7.stderr b/src/test/ui/parser/lex-bad-char-literals-7.stderr index ee9aa86935299..70ee8087b5163 100644 --- a/src/test/ui/parser/lex-bad-char-literals-7.stderr +++ b/src/test/ui/parser/lex-bad-char-literals-7.stderr @@ -10,7 +10,7 @@ error: empty unicode escape (must have at least 1 hex digit) LL | let _: char = '\u{}'; | ^^^^ -error: unterminated character literal +error[E0762]: unterminated character literal --> $DIR/lex-bad-char-literals-7.rs:11:13 | LL | let _ = ' hello // here's a comment @@ -18,3 +18,4 @@ LL | let _ = ' hello // here's a comment error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0762`. 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/issue-33569.rs b/src/test/ui/parser/macro/issue-33569.rs index 9ed53519ceb31..80e2d7c6545ba 100644 --- a/src/test/ui/parser/macro/issue-33569.rs +++ b/src/test/ui/parser/macro/issue-33569.rs @@ -6,3 +6,5 @@ macro_rules! foo { } foo!(); + +fn main() {} diff --git a/src/test/ui/parser/macro/macro-repeat.rs b/src/test/ui/parser/macro/macro-repeat.rs index 580a1daacbf31..3ffbea217e79e 100644 --- a/src/test/ui/parser/macro/macro-repeat.rs +++ b/src/test/ui/parser/macro/macro-repeat.rs @@ -1,9 +1,12 @@ macro_rules! mac { - ( $($v:tt)* ) => ( - $v //~ ERROR still repeating at this depth - ) + ( $($v:tt)* ) => { + $v + //~^ ERROR still repeating at this depth + //~| ERROR still repeating at this depth + }; } fn main() { mac!(0); + mac!(1); } diff --git a/src/test/ui/parser/macro/macro-repeat.stderr b/src/test/ui/parser/macro/macro-repeat.stderr index c86684de74432..63554b197b91c 100644 --- a/src/test/ui/parser/macro/macro-repeat.stderr +++ b/src/test/ui/parser/macro/macro-repeat.stderr @@ -4,5 +4,11 @@ error: variable 'v' is still repeating at this depth LL | $v | ^^ -error: aborting due to previous error +error: variable 'v' is still repeating at this depth + --> $DIR/macro-repeat.rs:3:9 + | +LL | $v + | ^^ + +error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/macro/pub-item-macro.stderr b/src/test/ui/parser/macro/pub-item-macro.stderr index 4ff96532e03a8..1c5613ac986c4 100644 --- a/src/test/ui/parser/macro/pub-item-macro.stderr +++ b/src/test/ui/parser/macro/pub-item-macro.stderr @@ -14,7 +14,7 @@ error[E0603]: static `x` is private --> $DIR/pub-item-macro.rs:20:23 | LL | let y: u32 = foo::x; - | ^ this static is private + | ^ private static | note: the static `x` is defined here --> $DIR/pub-item-macro.rs:5:9 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 230733371ddd8..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,5 +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/mod_file_not_exist.rs b/src/test/ui/parser/mod_file_not_exist.rs index e662c707a38b9..f4a27b52ec5b4 100644 --- a/src/test/ui/parser/mod_file_not_exist.rs +++ b/src/test/ui/parser/mod_file_not_exist.rs @@ -1,8 +1,9 @@ // ignore-windows mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file` -//~^ HELP name the file either not_a_real_file.rs or not_a_real_file/mod.rs inside the directory +//~^ HELP to create the module `not_a_real_file`, create file fn main() { assert_eq!(mod_file_aux::bar(), 10); + //~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux` } diff --git a/src/test/ui/parser/mod_file_not_exist.stderr b/src/test/ui/parser/mod_file_not_exist.stderr index dadf4b29dcf39..087ae9fe3e016 100644 --- a/src/test/ui/parser/mod_file_not_exist.stderr +++ b/src/test/ui/parser/mod_file_not_exist.stderr @@ -1,11 +1,18 @@ error[E0583]: file not found for module `not_a_real_file` - --> $DIR/mod_file_not_exist.rs:3:5 + --> $DIR/mod_file_not_exist.rs:3:1 | LL | mod not_a_real_file; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ | - = help: name the file either not_a_real_file.rs or not_a_real_file/mod.rs inside the directory "$DIR" + = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" -error: aborting due to previous error +error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux` + --> $DIR/mod_file_not_exist.rs:7:16 + | +LL | assert_eq!(mod_file_aux::bar(), 10); + | ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0583`. +Some errors have detailed explanations: E0433, E0583. +For more information about an error, try `rustc --explain E0433`. diff --git a/src/test/ui/parser/mod_file_not_exist_windows.rs b/src/test/ui/parser/mod_file_not_exist_windows.rs index 0cd9e9c799f1d..4b7d7a02bbe78 100644 --- a/src/test/ui/parser/mod_file_not_exist_windows.rs +++ b/src/test/ui/parser/mod_file_not_exist_windows.rs @@ -1,8 +1,9 @@ // only-windows mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file` -//~^ HELP name the file either not_a_real_file.rs or not_a_real_file\mod.rs inside the directory +//~^ HELP to create the module `not_a_real_file`, create file fn main() { assert_eq!(mod_file_aux::bar(), 10); + //~^ ERROR failed to resolve: use of undeclared type or module `mod_file_aux` } diff --git a/src/test/ui/parser/mod_file_not_exist_windows.stderr b/src/test/ui/parser/mod_file_not_exist_windows.stderr index 60ae00abab1ed..d67205cfdf100 100644 --- a/src/test/ui/parser/mod_file_not_exist_windows.stderr +++ b/src/test/ui/parser/mod_file_not_exist_windows.stderr @@ -1,11 +1,18 @@ error[E0583]: file not found for module `not_a_real_file` - --> $DIR/mod_file_not_exist_windows.rs:3:5 + --> $DIR/mod_file_not_exist_windows.rs:3:1 | LL | mod not_a_real_file; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ | - = help: name the file either not_a_real_file.rs or not_a_real_file/mod.rs inside the directory "$DIR" + = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" -error: aborting due to previous error +error[E0433]: failed to resolve: use of undeclared type or module `mod_file_aux` + --> $DIR/mod_file_not_exist_windows.rs:7:16 + | +LL | assert_eq!(mod_file_aux::bar(), 10); + | ^^^^^^^^^^^^ use of undeclared type or module `mod_file_aux` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0583`. +Some errors have detailed explanations: E0433, E0583. +For more information about an error, try `rustc --explain E0433`. diff --git a/src/test/ui/parser/mod_file_with_path_attr.stderr b/src/test/ui/parser/mod_file_with_path_attr.stderr index 004b5d7963a1d..cd1add73d5840 100644 --- a/src/test/ui/parser/mod_file_with_path_attr.stderr +++ b/src/test/ui/parser/mod_file_with_path_attr.stderr @@ -1,8 +1,8 @@ error: couldn't read $DIR/not_a_real_file.rs: $FILE_NOT_FOUND_MSG (os error 2) - --> $DIR/mod_file_with_path_attr.rs:4:5 + --> $DIR/mod_file_with_path_attr.rs:4:1 | LL | mod m; - | ^ + | ^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/parser/nt-parsing-has-recovery.rs b/src/test/ui/parser/nt-parsing-has-recovery.rs new file mode 100644 index 0000000000000..ccbeb398af5ba --- /dev/null +++ b/src/test/ui/parser/nt-parsing-has-recovery.rs @@ -0,0 +1,10 @@ +macro_rules! foo { + ($e:expr) => {} +} + +foo!(1 + @); //~ ERROR expected expression, found `@` +foo!(1 + @); //~ ERROR expected expression, found `@` + +fn main() { + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/nt-parsing-has-recovery.stderr b/src/test/ui/parser/nt-parsing-has-recovery.stderr new file mode 100644 index 0000000000000..263c4ad53612e --- /dev/null +++ b/src/test/ui/parser/nt-parsing-has-recovery.stderr @@ -0,0 +1,29 @@ +error: expected expression, found `@` + --> $DIR/nt-parsing-has-recovery.rs:5:10 + | +LL | ($e:expr) => {} + | ------- while parsing argument for this `expr` macro fragment +... +LL | foo!(1 + @); + | ^ expected expression + +error: expected expression, found `@` + --> $DIR/nt-parsing-has-recovery.rs:6:10 + | +LL | ($e:expr) => {} + | ------- while parsing argument for this `expr` macro fragment +... +LL | foo!(1 + @); + | ^ expected expression + +error[E0308]: mismatched types + --> $DIR/nt-parsing-has-recovery.rs:9: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/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/recover-quantified-closure.rs b/src/test/ui/parser/recover-quantified-closure.rs new file mode 100644 index 0000000000000..381324738f62b --- /dev/null +++ b/src/test/ui/parser/recover-quantified-closure.rs @@ -0,0 +1,10 @@ +fn main() { + for<'a> |x: &'a u8| *x + 1; + //~^ ERROR cannot introduce explicit parameters for a closure +} + +enum Foo { Bar } +fn foo(x: impl Iterator) { + for ::Bar in x {} + //~^ ERROR expected one of `move`, `static`, `|` +} diff --git a/src/test/ui/parser/recover-quantified-closure.stderr b/src/test/ui/parser/recover-quantified-closure.stderr new file mode 100644 index 0000000000000..0f01132651648 --- /dev/null +++ b/src/test/ui/parser/recover-quantified-closure.stderr @@ -0,0 +1,16 @@ +error: cannot introduce explicit parameters for a closure + --> $DIR/recover-quantified-closure.rs:2:5 + | +LL | for<'a> |x: &'a u8| *x + 1; + | ^^^^^^^ ------------------ the parameters are attached to this closure + | | + | help: remove the parameters + +error: expected one of `move`, `static`, `|`, or `||`, found `::` + --> $DIR/recover-quantified-closure.rs:8:14 + | +LL | for ::Bar in x {} + | ^^ expected one of `move`, `static`, `|`, or `||` + +error: aborting due to 2 previous errors + 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/shebang/issue-71471-ignore-tidy.rs b/src/test/ui/parser/shebang/issue-71471-ignore-tidy.rs new file mode 100644 index 0000000000000..a2505180884aa --- /dev/null +++ b/src/test/ui/parser/shebang/issue-71471-ignore-tidy.rs @@ -0,0 +1,2 @@ + +#!B //~ expected `[`, found `B` diff --git a/src/test/ui/parser/shebang/issue-71471-ignore-tidy.stderr b/src/test/ui/parser/shebang/issue-71471-ignore-tidy.stderr new file mode 100644 index 0000000000000..896a9dc83d8b9 --- /dev/null +++ b/src/test/ui/parser/shebang/issue-71471-ignore-tidy.stderr @@ -0,0 +1,8 @@ +error: expected `[`, found `B` + --> $DIR/issue-71471-ignore-tidy.rs:2:3 + | +LL | #!B + | ^ expected `[` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/shebang/multiline-attrib.rs b/src/test/ui/parser/shebang/multiline-attrib.rs new file mode 100644 index 0000000000000..931c94c7fba03 --- /dev/null +++ b/src/test/ui/parser/shebang/multiline-attrib.rs @@ -0,0 +1,7 @@ +#! +[allow(unused_variables)] +// check-pass + +fn main() { + let x = 5; +} diff --git a/src/test/ui/parser/shebang/regular-attrib.rs b/src/test/ui/parser/shebang/regular-attrib.rs new file mode 100644 index 0000000000000..ca8fb0830ffb1 --- /dev/null +++ b/src/test/ui/parser/shebang/regular-attrib.rs @@ -0,0 +1,5 @@ +#![allow(unused_variables)] +// check-pass +fn main() { + let x = 5; +} diff --git a/src/test/ui/parser/shebang/shebang-and-attrib.rs b/src/test/ui/parser/shebang/shebang-and-attrib.rs new file mode 100644 index 0000000000000..61b89c655a3fc --- /dev/null +++ b/src/test/ui/parser/shebang/shebang-and-attrib.rs @@ -0,0 +1,9 @@ +#!/usr/bin/env run-cargo-script + +// check-pass +#![allow(unused_variables)] + + +fn main() { + let x = 5; +} diff --git a/src/test/ui/parser/shebang/shebang-comment.rs b/src/test/ui/parser/shebang/shebang-comment.rs new file mode 100644 index 0000000000000..2b1ab0c574d26 --- /dev/null +++ b/src/test/ui/parser/shebang/shebang-comment.rs @@ -0,0 +1,6 @@ +#!//bin/bash + +// check-pass +fn main() { + println!("a valid shebang (that is also a rust comment)") +} diff --git a/src/test/ui/parser/shebang/shebang-doc-comment.rs b/src/test/ui/parser/shebang/shebang-doc-comment.rs new file mode 100644 index 0000000000000..7dbb9eebc7571 --- /dev/null +++ b/src/test/ui/parser/shebang/shebang-doc-comment.rs @@ -0,0 +1,6 @@ +#!///bin/bash +[allow(unused_variables)] +//~^^ ERROR expected `[`, found doc comment + +// Doc comment is misinterpreted as a whitespace (regular comment) during shebang detection. +// Even if it wasn't, it would still result in an error, just a different one. diff --git a/src/test/ui/parser/shebang/shebang-doc-comment.stderr b/src/test/ui/parser/shebang/shebang-doc-comment.stderr new file mode 100644 index 0000000000000..f524f556837fb --- /dev/null +++ b/src/test/ui/parser/shebang/shebang-doc-comment.stderr @@ -0,0 +1,8 @@ +error: expected `[`, found doc comment `///bin/bash` + --> $DIR/shebang-doc-comment.rs:1:3 + | +LL | #!///bin/bash + | ^^^^^^^^^^^ expected `[` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/shebang/shebang-must-start-file.rs b/src/test/ui/parser/shebang/shebang-must-start-file.rs new file mode 100644 index 0000000000000..e0392572dc81d --- /dev/null +++ b/src/test/ui/parser/shebang/shebang-must-start-file.rs @@ -0,0 +1,6 @@ +// something on the first line for tidy +#!/bin/bash //~ expected `[`, found `/` + +fn main() { + println!("ok!"); +} diff --git a/src/test/ui/parser/shebang/shebang-must-start-file.stderr b/src/test/ui/parser/shebang/shebang-must-start-file.stderr new file mode 100644 index 0000000000000..50543e8bdb816 --- /dev/null +++ b/src/test/ui/parser/shebang/shebang-must-start-file.stderr @@ -0,0 +1,8 @@ +error: expected `[`, found `/` + --> $DIR/shebang-must-start-file.rs:2:3 + | +LL | #!/bin/bash + | ^ expected `[` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/shebang/sneaky-attrib.rs b/src/test/ui/parser/shebang/sneaky-attrib.rs new file mode 100644 index 0000000000000..b406cc3aa13c7 --- /dev/null +++ b/src/test/ui/parser/shebang/sneaky-attrib.rs @@ -0,0 +1,16 @@ +#!//bin/bash + + +// This could not possibly be a shebang & also a valid rust file, since a Rust file +// can't start with `[` +/* + [ (mixing comments to also test that we ignore both types of comments) + + */ + +[allow(unused_variables)] + +// check-pass +fn main() { + let x = 5; +} diff --git a/src/test/ui/parser/shebang/valid-shebang.rs b/src/test/ui/parser/shebang/valid-shebang.rs new file mode 100644 index 0000000000000..e480d3da3fc8d --- /dev/null +++ b/src/test/ui/parser/shebang/valid-shebang.rs @@ -0,0 +1,6 @@ +#!/usr/bin/env run-cargo-script + +// check-pass +fn main() { + println!("Hello World!"); +} diff --git a/src/test/ui/parser/stripped-nested-outline-mod-pass.rs b/src/test/ui/parser/stripped-nested-outline-mod-pass.rs new file mode 100644 index 0000000000000..1b4669a439ffe --- /dev/null +++ b/src/test/ui/parser/stripped-nested-outline-mod-pass.rs @@ -0,0 +1,13 @@ +// Expansion drives parsing, so conditional compilation will strip +// out outline modules and we will never attempt parsing them. + +// check-pass + +fn main() {} + +#[cfg(FALSE)] +mod foo { + mod bar { + mod baz; // This was an error before. + } +} diff --git a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs index 09f967f161ede..34aee7f69359e 100644 --- a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs +++ b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete fn main() {} diff --git a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr index 6bb946d5b6470..e8ff93f63237d 100644 --- a/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr +++ b/src/test/ui/parser/trait-item-with-defaultness-fail-semantic.stderr @@ -46,5 +46,14 @@ LL | default fn f2() {} | | | `default` because of this -error: aborting due to 6 previous errors +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/trait-item-with-defaultness-fail-semantic.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +error: aborting due to 6 previous errors; 1 warning emitted 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/unbalanced-doublequote.stderr b/src/test/ui/parser/unbalanced-doublequote.stderr index 4d98515c224ad..94b300a7bd765 100644 --- a/src/test/ui/parser/unbalanced-doublequote.stderr +++ b/src/test/ui/parser/unbalanced-doublequote.stderr @@ -1,4 +1,4 @@ -error: unterminated double quote string +error[E0765]: unterminated double quote string --> $DIR/unbalanced-doublequote.rs:5:5 | LL | / " @@ -7,3 +7,4 @@ LL | | } error: aborting due to previous error +For more information about this error, try `rustc --explain E0765`. 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/issue-71042-opaquely-typed-constant-used-in-pattern.rs b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.rs new file mode 100644 index 0000000000000..c5e4a72fb9ff1 --- /dev/null +++ b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.rs @@ -0,0 +1,9 @@ +#![feature(impl_trait_in_bindings)] +#![allow(incomplete_features)] + +fn main() { + const C: impl Copy = 0; + match C { + C | _ => {} //~ ERROR: opaque types cannot be used in patterns + } +} diff --git a/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr new file mode 100644 index 0000000000000..7695223f2cf98 --- /dev/null +++ b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr @@ -0,0 +1,8 @@ +error: opaque types cannot be used in patterns + --> $DIR/issue-71042-opaquely-typed-constant-used-in-pattern.rs:7:9 + | +LL | C | _ => {} + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs index 11eae2af9c95f..7d1cac8a442f5 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs @@ -21,7 +21,7 @@ fn uninhab_union() -> Foo { fn match_on_uninhab() { match uninhab_ref() { - //~^ ERROR non-exhaustive patterns: type `&'static !` is non-empty + //~^ ERROR non-exhaustive patterns: type `&!` is non-empty } match uninhab_union() { 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 1b1096c977ad4..0fa77fb73da1f 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr @@ -1,10 +1,11 @@ -error[E0004]: non-exhaustive patterns: type `&'static !` is non-empty +error[E0004]: non-exhaustive patterns: type `&!` is non-empty --> $DIR/always-inhabited-union-ref.rs:23:11 | 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..6427a30b8f2ed 100644 --- a/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr +++ b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr @@ -10,13 +10,14 @@ note: the lint level is defined here LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ -error[E0004]: non-exhaustive patterns: `128u8..=std::u8::MAX` not covered +error[E0004]: non-exhaustive patterns: `128u8..=u8::MAX` not covered --> $DIR/exhaustive_integer_patterns.rs:28:11 | LL | match x { - | ^ pattern `128u8..=std::u8::MAX` not covered + | ^ pattern `128u8..=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 @@ -32,21 +34,23 @@ error: unreachable pattern LL | -2..=20 => {} | ^^^^^^^ -error[E0004]: non-exhaustive patterns: `std::i8::MIN..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered +error[E0004]: non-exhaustive patterns: `i8::MIN..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered --> $DIR/exhaustive_integer_patterns.rs:41:11 | LL | match x { - | ^ patterns `std::i8::MIN..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered + | ^ patterns `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 +error[E0004]: non-exhaustive patterns: `i8::MIN` not covered --> $DIR/exhaustive_integer_patterns.rs:83:11 | LL | match 0i8 { - | ^^^ pattern `std::i8::MIN` not covered + | ^^^ pattern `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,22 +59,25 @@ 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 +error[E0004]: non-exhaustive patterns: `128u8..=u8::MAX` not covered --> $DIR/exhaustive_integer_patterns.rs:109:11 | LL | match 0u8 { - | ^^^ pattern `128u8..=std::u8::MAX` not covered + | ^^^ pattern `128u8..=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 +error[E0004]: non-exhaustive patterns: `(0u8, Some(_))` and `(2u8..=u8::MAX, Some(_))` not covered --> $DIR/exhaustive_integer_patterns.rs:121:11 | LL | match (0u8, Some(())) { - | ^^^^^^^^^^^^^^^ patterns `(0u8, Some(_))` and `(2u8..=std::u8::MAX, Some(_))` not covered + | ^^^^^^^^^^^^^^^ patterns `(0u8, Some(_))` and `(2u8..=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 @@ -94,21 +102,23 @@ note: the lint level is defined here LL | #![deny(overlapping_patterns)] | ^^^^^^^^^^^^^^^^^^^^ -error[E0004]: non-exhaustive patterns: `std::u128::MAX` not covered +error[E0004]: non-exhaustive patterns: `u128::MAX` not covered --> $DIR/exhaustive_integer_patterns.rs:146:11 | LL | match 0u128 { - | ^^^^^ pattern `std::u128::MAX` not covered + | ^^^^^ pattern `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 +error[E0004]: non-exhaustive patterns: `5u128..=u128::MAX` not covered --> $DIR/exhaustive_integer_patterns.rs:150:11 | LL | match 0u128 { - | ^^^^^ pattern `5u128..=std::u128::MAX` not covered + | ^^^^^ pattern `5u128..=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/issue-72476-associated-type.rs b/src/test/ui/pattern/usefulness/issue-72476-associated-type.rs new file mode 100644 index 0000000000000..1e1d21433b79c --- /dev/null +++ b/src/test/ui/pattern/usefulness/issue-72476-associated-type.rs @@ -0,0 +1,22 @@ +// check-pass + +// From https://github.com/rust-lang/rust/issues/72476 + +trait A { + type Projection; +} + +impl A for () { + type Projection = bool; +} + +struct Next(T::Projection); + +fn f(item: Next<()>) { + match item { + Next(true) => {} + Next(false) => {} + } +} + +fn main() {} 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 8521e37d3fddc..3d329e2e6efb1 100644 --- a/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr +++ b/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr @@ -5,14 +5,24 @@ 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:29:11 | LL | match Some(Some(North)) { | ^^^^^^^^^^^^^^^^^ pattern `Some(Some(West))` not covered + | + ::: $SRC_DIR/libcore/option.rs:LL:COL + | +LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), + | ---- + | | + | not covered + | 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:48:11 @@ -27,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..0e12b89de1b91 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 @@ -1,10 +1,11 @@ -error[E0004]: non-exhaustive patterns: `&[0u8..=64u8, _, _, _]` and `&[66u8..=std::u8::MAX, _, _, _]` not covered +error[E0004]: non-exhaustive patterns: `&[0u8..=64u8, _, _, _]` and `&[66u8..=u8::MAX, _, _, _]` not covered --> $DIR/match-byte-array-patterns-2.rs:4:11 | LL | match buf { - | ^^^ patterns `&[0u8..=64u8, _, _, _]` and `&[66u8..=std::u8::MAX, _, _, _]` not covered + | ^^^ patterns `&[0u8..=64u8, _, _, _]` and `&[66u8..=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..c6a9329f9e8e1 100644 --- a/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr +++ b/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr @@ -1,10 +1,11 @@ -error[E0004]: non-exhaustive patterns: `std::i32::MIN..=0i32` and `2i32..=std::i32::MAX` not covered +error[E0004]: non-exhaustive patterns: `i32::MIN..=0i32` and `2i32..=i32::MAX` not covered --> $DIR/match-non-exhaustive.rs:2:11 | LL | match 0 { 1 => () } - | ^ patterns `std::i32::MIN..=0i32` and `2i32..=std::i32::MAX` not covered + | ^ patterns `i32::MIN..=0i32` and `2i32..=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.stderr b/src/test/ui/pattern/usefulness/match-privately-empty.stderr index f79d180a1b8b5..50a4674def7e8 100644 --- a/src/test/ui/pattern/usefulness/match-privately-empty.stderr +++ b/src/test/ui/pattern/usefulness/match-privately-empty.stderr @@ -3,8 +3,14 @@ error[E0004]: non-exhaustive patterns: `Some(Private { misc: true, .. })` not co | LL | match private::DATA { | ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered + | + ::: $SRC_DIR/libcore/option.rs:LL:COL + | +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 9947989dc1211..9177345bc6f50 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs @@ -11,8 +11,8 @@ fn main() { match Some(10) { //~ ERROR non-exhaustive patterns: `Some(_)` not covered None => {} } - match (2, 3, 4) { //~ ERROR non-exhaustive patterns: `(_, _, std::i32::MIN..=3i32)` - // and `(_, _, 5i32..=std::i32::MAX)` not covered + match (2, 3, 4) { //~ ERROR non-exhaustive patterns: `(_, _, i32::MIN..=3i32)` + // and `(_, _, 5i32..=i32::MAX)` not covered (_, _, 4) => {} } match (T::A, T::A) { //~ ERROR non-exhaustive patterns: `(A, A)` not covered diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr index a06ad5788515c..3cdbd8a3433f4 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr @@ -11,6 +11,7 @@ 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:8:11 @@ -19,22 +20,30 @@ 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:11:11 | LL | match Some(10) { | ^^^^^^^^ pattern `Some(_)` not covered + | + ::: $SRC_DIR/libcore/option.rs:LL:COL + | +LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), + | ---- not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `std::option::Option` -error[E0004]: non-exhaustive patterns: `(_, _, std::i32::MIN..=3i32)` and `(_, _, 5i32..=std::i32::MAX)` not covered +error[E0004]: non-exhaustive patterns: `(_, _, i32::MIN..=3i32)` and `(_, _, 5i32..=i32::MAX)` not covered --> $DIR/non-exhaustive-match.rs:14:11 | LL | match (2, 3, 4) { - | ^^^^^^^^^ patterns `(_, _, std::i32::MIN..=3i32)` and `(_, _, 5i32..=std::i32::MAX)` not covered + | ^^^^^^^^^ patterns `(_, _, i32::MIN..=3i32)` and `(_, _, 5i32..=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:18:11 @@ -43,6 +52,7 @@ 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:22:11 @@ -57,6 +67,7 @@ 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:33:11 @@ -65,6 +76,7 @@ LL | match *vec { | ^^^^ pattern `[]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `[std::option::Option]` error[E0004]: non-exhaustive patterns: `[_, _, _, _, ..]` not covered --> $DIR/non-exhaustive-match.rs:46:11 @@ -73,6 +85,7 @@ LL | match *vec { | ^^^^ pattern `[_, _, _, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `[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.rs b/src/test/ui/pattern/usefulness/refutable-pattern-errors.rs index d4afe17ca748c..3ef2ead32cb7c 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-errors.rs +++ b/src/test/ui/pattern/usefulness/refutable-pattern-errors.rs @@ -5,5 +5,5 @@ fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) { } fn main() { let (1, (Some(1), 2..=3)) = (1, (None, 2)); - //~^ ERROR refutable pattern in local binding: `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered + //~^ ERROR refutable pattern in local binding: `(i32::MIN..=0i32, _)` and `(2i32..=i32::MAX, _)` not covered } diff --git a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr index 0cf5d9cd5f12a..ac729ae9f7cdf 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr +++ b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr @@ -3,15 +3,18 @@ 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 +error[E0005]: refutable pattern in local binding: `(i32::MIN..=0i32, _)` and `(2i32..=i32::MAX, _)` not covered --> $DIR/refutable-pattern-errors.rs:7:9 | LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)); - | ^^^^^^^^^^^^^^^^^^^^^ patterns `(std::i32::MIN..=0i32, _)` and `(2i32..=std::i32::MAX, _)` not covered + | ^^^^^^^^^^^^^^^^^^^^^ patterns `(i32::MIN..=0i32, _)` and `(2i32..=i32::MAX, _)` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `(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..7b9e30f40fbb3 100644 --- a/src/test/ui/precise_pointer_size_matching.stderr +++ b/src/test/ui/precise_pointer_size_matching.stderr @@ -1,18 +1,20 @@ -error[E0004]: non-exhaustive patterns: `std::isize::MIN..=-6isize` and `21isize..=std::isize::MAX` not covered +error[E0004]: non-exhaustive patterns: `isize::MIN..=-6isize` and `21isize..=isize::MAX` not covered --> $DIR/precise_pointer_size_matching.rs:24:11 | LL | match 0isize { - | ^^^^^^ patterns `std::isize::MIN..=-6isize` and `21isize..=std::isize::MAX` not covered + | ^^^^^^ patterns `isize::MIN..=-6isize` and `21isize..=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 +error[E0004]: non-exhaustive patterns: `0usize` and `21usize..=usize::MAX` not covered --> $DIR/precise_pointer_size_matching.rs:29:11 | LL | match 0usize { - | ^^^^^^ patterns `0usize` and `21usize..=std::usize::MAX` not covered + | ^^^^^^ patterns `0usize` and `21usize..=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-inherent.stderr b/src/test/ui/privacy/associated-item-privacy-inherent.stderr index 88561568ea5a4..1e94e7c620d03 100644 --- a/src/test/ui/privacy/associated-item-privacy-inherent.stderr +++ b/src/test/ui/privacy/associated-item-privacy-inherent.stderr @@ -2,7 +2,7 @@ error: type `for<'r> fn(&'r priv_nominal::Pub) {priv_nominal::Pub::method}` is p --> $DIR/associated-item-privacy-inherent.rs:13:21 | LL | let value = Pub::method; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ private type ... LL | priv_nominal::mac!(); | --------------------- in this macro invocation @@ -13,7 +13,7 @@ error: type `for<'r> fn(&'r priv_nominal::Pub) {priv_nominal::Pub::method}` is p --> $DIR/associated-item-privacy-inherent.rs:15:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_nominal::mac!(); | --------------------- in this macro invocation @@ -24,7 +24,7 @@ error: type `for<'r> fn(&'r priv_nominal::Pub) {priv_nominal::Pub::method}` is p --> $DIR/associated-item-privacy-inherent.rs:17:13 | LL | Pub.method(); - | ^^^^^^ + | ^^^^^^ private type ... LL | priv_nominal::mac!(); | --------------------- in this macro invocation @@ -35,7 +35,7 @@ error: associated constant `CONST` is private --> $DIR/associated-item-privacy-inherent.rs:19:9 | LL | Pub::CONST; - | ^^^^^^^^^^ + | ^^^^^^^^^^ private associated constant ... LL | priv_nominal::mac!(); | --------------------- in this macro invocation @@ -46,7 +46,7 @@ error: type `priv_signature::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:37:21 | LL | let value = Pub::method; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ private type ... LL | priv_signature::mac!(); | ----------------------- in this macro invocation @@ -57,7 +57,7 @@ error: type `priv_signature::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:39:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_signature::mac!(); | ----------------------- in this macro invocation @@ -68,7 +68,7 @@ error: type `priv_signature::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:41:13 | LL | Pub.method(loop {}); - | ^^^^^^ + | ^^^^^^ private type ... LL | priv_signature::mac!(); | ----------------------- in this macro invocation @@ -79,7 +79,7 @@ error: type `priv_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:57:21 | LL | let value = Pub::method::; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_substs::mac!(); | -------------------- in this macro invocation @@ -90,7 +90,7 @@ error: type `priv_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:59:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_substs::mac!(); | -------------------- in this macro invocation @@ -101,7 +101,7 @@ error: type `priv_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:61:9 | LL | Pub.method::(); - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_substs::mac!(); | -------------------- in this macro invocation @@ -112,7 +112,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:80:21 | LL | let value = ::method; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -123,7 +123,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:82:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -134,7 +134,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:84:21 | LL | let value = Pub::method; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -145,7 +145,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:86:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -156,7 +156,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:88:21 | LL | let value = ::static_method; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -167,7 +167,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:90:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -178,7 +178,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:92:21 | LL | let value = Pub::static_method; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -189,7 +189,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:94:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -200,7 +200,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:96:19 | LL | Pub(Priv).method(); - | ^^^^^^ + | ^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -211,7 +211,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:99:10 | LL | ::CONST; - | ^^^ + | ^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -222,7 +222,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-inherent.rs:101:9 | LL | Pub::CONST; - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation 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 db24e425a01f6..b9f3e35d72261 100644 --- a/src/test/ui/privacy/associated-item-privacy-trait.stderr +++ b/src/test/ui/privacy/associated-item-privacy-trait.stderr @@ -2,7 +2,7 @@ error: type `for<'r> fn(&'r priv_trait::Pub) { $DIR/associated-item-privacy-trait.rs:17:21 | LL | let value = ::method; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_trait::mac!(); | ------------------- in this macro invocation @@ -13,7 +13,7 @@ error: type `for<'r> fn(&'r priv_trait::Pub) { $DIR/associated-item-privacy-trait.rs:19:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_trait::mac!(); | ------------------- in this macro invocation @@ -24,29 +24,29 @@ error: type `for<'r> fn(&'r Self) {::method}` is pri --> $DIR/associated-item-privacy-trait.rs:21:13 | LL | Pub.method(); - | ^^^^^^ + | ^^^^^^ private type ... LL | priv_trait::mac!(); | ------------------- in this macro invocation | = 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; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ private associated constant ... LL | priv_trait::mac!(); | ------------------- in this macro invocation | = 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; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ private associated type ... LL | priv_trait::mac!(); | ------------------- in this macro invocation @@ -57,7 +57,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-trait.rs:27:34 | LL | pub type InSignatureTy = ::AssocTy; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ private trait ... LL | priv_trait::mac!(); | ------------------- in this macro invocation @@ -68,7 +68,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-trait.rs:29:34 | LL | pub trait InSignatureTr: PrivTr {} - | ^^^^^^ + | ^^^^^^ private trait ... LL | priv_trait::mac!(); | ------------------- in this macro invocation @@ -79,7 +79,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-trait.rs:31:14 | LL | impl PrivTr for u8 {} - | ^^^^^^ + | ^^^^^^ private trait ... LL | priv_trait::mac!(); | ------------------- in this macro invocation @@ -90,7 +90,7 @@ error: type `priv_signature::Priv` is private --> $DIR/associated-item-privacy-trait.rs:48:21 | LL | let value = ::method; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_signature::mac!(); | ----------------------- in this macro invocation @@ -101,7 +101,7 @@ error: type `priv_signature::Priv` is private --> $DIR/associated-item-privacy-trait.rs:50:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_signature::mac!(); | ----------------------- in this macro invocation @@ -112,7 +112,7 @@ error: type `priv_signature::Priv` is private --> $DIR/associated-item-privacy-trait.rs:52:13 | LL | Pub.method(loop {}); - | ^^^^^^ + | ^^^^^^ private type ... LL | priv_signature::mac!(); | ----------------------- in this macro invocation @@ -123,7 +123,7 @@ error: type `priv_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:69:21 | LL | let value = ::method::; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_substs::mac!(); | -------------------- in this macro invocation @@ -134,7 +134,7 @@ error: type `priv_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:71:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_substs::mac!(); | -------------------- in this macro invocation @@ -145,7 +145,7 @@ error: type `priv_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:73:9 | LL | Pub.method::(); - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_substs::mac!(); | -------------------- in this macro invocation @@ -156,7 +156,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:93:21 | LL | let value = ::method; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -167,7 +167,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:95:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -178,7 +178,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:97:21 | LL | let value = >::method; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -189,7 +189,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:99:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -200,7 +200,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:101:9 | LL | Pub.method(); - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -211,7 +211,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:104:21 | LL | let value = >::method; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -222,7 +222,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:106:9 | LL | value; - | ^^^^^ + | ^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -233,7 +233,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:108:9 | LL | Priv.method(); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -244,7 +244,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:111:9 | LL | ::CONST; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -255,7 +255,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:113:9 | LL | >::CONST; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -266,7 +266,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:115:9 | LL | >::CONST; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -277,7 +277,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:119:30 | LL | let _: >::AssocTy; - | ^ + | ^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -288,7 +288,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:121:17 | LL | let _: >::AssocTy; - | ^^^^ + | ^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -299,7 +299,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:124:35 | LL | pub type InSignatureTy1 = ::AssocTy; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -310,7 +310,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:126:35 | LL | pub type InSignatureTy2 = >::AssocTy; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -321,7 +321,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-trait.rs:128:14 | LL | impl PubTr for u8 {} - | ^^^^^ + | ^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation diff --git a/src/test/ui/privacy/associated-item-privacy-type-binding.stderr b/src/test/ui/privacy/associated-item-privacy-type-binding.stderr index fd8b8cf0db652..d8515ccb66920 100644 --- a/src/test/ui/privacy/associated-item-privacy-type-binding.stderr +++ b/src/test/ui/privacy/associated-item-privacy-type-binding.stderr @@ -2,7 +2,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:11:13 | LL | let _: Box>; - | ^ + | ^ private trait ... LL | priv_trait::mac1!(); | -------------------- in this macro invocation @@ -13,7 +13,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:11:16 | LL | let _: Box>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private trait ... LL | priv_trait::mac1!(); | -------------------- in this macro invocation @@ -24,7 +24,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:14:31 | LL | type InSignatureTy2 = Box>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private trait ... LL | priv_trait::mac1!(); | -------------------- in this macro invocation @@ -35,7 +35,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:16:31 | LL | trait InSignatureTr2: PubTr {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ private trait ... LL | priv_trait::mac1!(); | -------------------- in this macro invocation @@ -46,7 +46,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:20:13 | LL | let _: Box>; - | ^ + | ^ private trait ... LL | priv_trait::mac2!(); | -------------------- in this macro invocation @@ -57,7 +57,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:20:16 | LL | let _: Box>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private trait ... LL | priv_trait::mac2!(); | -------------------- in this macro invocation @@ -68,7 +68,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:23:31 | LL | type InSignatureTy1 = Box>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private trait ... LL | priv_trait::mac2!(); | -------------------- in this macro invocation @@ -79,7 +79,7 @@ error: trait `priv_trait::PrivTr` is private --> $DIR/associated-item-privacy-type-binding.rs:25:31 | LL | trait InSignatureTr1: PrivTr {} - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ private trait ... LL | priv_trait::mac2!(); | -------------------- in this macro invocation @@ -90,7 +90,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:44:13 | LL | let _: Box>; - | ^ + | ^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -101,7 +101,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:44:16 | LL | let _: Box>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -112,7 +112,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:47:13 | LL | let _: Box>; - | ^ + | ^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -123,7 +123,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:47:16 | LL | let _: Box>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -134,7 +134,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:50:35 | LL | pub type InSignatureTy1 = Box>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -145,7 +145,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:52:35 | LL | pub type InSignatureTy2 = Box>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -156,7 +156,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:54:31 | LL | trait InSignatureTr1: PubTrWithParam {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation @@ -167,7 +167,7 @@ error: type `priv_parent_substs::Priv` is private --> $DIR/associated-item-privacy-type-binding.rs:56:31 | LL | trait InSignatureTr2: PubTr {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ private type ... LL | priv_parent_substs::mac!(); | --------------------------- in this macro invocation diff --git a/src/test/ui/privacy/decl-macro.stderr b/src/test/ui/privacy/decl-macro.stderr index ae2e1b4b644a3..5bc6f07fffac3 100644 --- a/src/test/ui/privacy/decl-macro.stderr +++ b/src/test/ui/privacy/decl-macro.stderr @@ -2,7 +2,7 @@ error[E0603]: macro `mac` is private --> $DIR/decl-macro.rs:8:8 | LL | m::mac!(); - | ^^^ this macro is private + | ^^^ private macro | note: the macro `mac` is defined here --> $DIR/decl-macro.rs:4:5 diff --git a/src/test/ui/privacy/legacy-ctor-visibility.stderr b/src/test/ui/privacy/legacy-ctor-visibility.stderr index 74a1f1ceeffb4..4f0d72de6f1a4 100644 --- a/src/test/ui/privacy/legacy-ctor-visibility.stderr +++ b/src/test/ui/privacy/legacy-ctor-visibility.stderr @@ -1,12 +1,10 @@ error[E0423]: expected function, tuple struct or tuple variant, found struct `S` --> $DIR/legacy-ctor-visibility.rs:9:13 | -LL | / fn f() { -LL | | S(10); - | | ^ help: a function with a similar name exists: `f` -LL | | -LL | | } - | |_________- similarly named function `f` defined here +LL | fn f() { + | ------ similarly named function `f` defined here +LL | S(10); + | ^ help: a function with a similar name exists: `f` error: aborting due to previous error diff --git a/src/test/ui/privacy/privacy-in-paths.stderr b/src/test/ui/privacy/privacy-in-paths.stderr index 8860d8f15f748..2eb3ebb51c20a 100644 --- a/src/test/ui/privacy/privacy-in-paths.stderr +++ b/src/test/ui/privacy/privacy-in-paths.stderr @@ -2,7 +2,7 @@ error[E0603]: module `bar` is private --> $DIR/privacy-in-paths.rs:24:16 | LL | ::foo::bar::baz::f(); - | ^^^ this module is private + | ^^^ private module | note: the module `bar` is defined here --> $DIR/privacy-in-paths.rs:3:5 @@ -14,7 +14,7 @@ error[E0603]: module `bar` is private --> $DIR/privacy-in-paths.rs:25:16 | LL | ::foo::bar::S::f(); - | ^^^ this module is private + | ^^^ private module | note: the module `bar` is defined here --> $DIR/privacy-in-paths.rs:3:5 @@ -26,7 +26,7 @@ error[E0603]: trait `T` is private --> $DIR/privacy-in-paths.rs:26:23 | LL | <() as ::foo::T>::Assoc::f(); - | ^ this trait is private + | ^ private trait | note: the trait `T` is defined here --> $DIR/privacy-in-paths.rs:8:5 diff --git a/src/test/ui/privacy/privacy-ns1.stderr b/src/test/ui/privacy/privacy-ns1.stderr index 66e9b78f6764a..eda9d4c128d81 100644 --- a/src/test/ui/privacy/privacy-ns1.stderr +++ b/src/test/ui/privacy/privacy-ns1.stderr @@ -11,14 +11,10 @@ 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 - | -LL | use foo1::Bar; +help: consider importing this function instead | LL | use foo2::Bar; | -LL | use foo3::Bar; - | error[E0425]: cannot find function, tuple struct or tuple variant `Bar` in this scope --> $DIR/privacy-ns1.rs:51:5 @@ -33,14 +29,10 @@ 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 - | -LL | use foo1::Bar; +help: consider importing this function | LL | use foo2::Bar; | -LL | use foo3::Bar; - | error[E0412]: cannot find type `Bar` in this scope --> $DIR/privacy-ns1.rs:52:17 @@ -55,18 +47,10 @@ 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 this trait | LL | use foo1::Bar; | -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 8b12109b37307..d7d9b83527509 100644 --- a/src/test/ui/privacy/privacy-ns2.stderr +++ b/src/test/ui/privacy/privacy-ns2.stderr @@ -4,14 +4,10 @@ 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 - | -LL | use foo1::Bar; +help: consider importing this function instead | LL | use foo2::Bar; | -LL | use foo3::Bar; - | error[E0423]: expected function, tuple struct or tuple variant, found trait `Bar` --> $DIR/privacy-ns2.rs:26:5 @@ -26,14 +22,10 @@ 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 - | -LL | use foo1::Bar; +help: consider importing this function instead | LL | use foo2::Bar; | -LL | use foo3::Bar; - | error[E0573]: expected type, found function `Bar` --> $DIR/privacy-ns2.rs:43:14 @@ -45,20 +37,16 @@ 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 this trait instead | LL | use foo1::Bar; | -LL | use foo2::Bar; - | -LL | use foo3::Bar; - | error[E0603]: trait `Bar` is private --> $DIR/privacy-ns2.rs:63:15 | LL | use foo3::Bar; - | ^^^ this trait is private + | ^^^ private trait | note: the trait `Bar` is defined here --> $DIR/privacy-ns2.rs:55:5 @@ -70,7 +58,7 @@ error[E0603]: trait `Bar` is private --> $DIR/privacy-ns2.rs:67:15 | LL | use foo3::Bar; - | ^^^ this trait is private + | ^^^ private trait | note: the trait `Bar` is defined here --> $DIR/privacy-ns2.rs:55:5 @@ -82,7 +70,7 @@ error[E0603]: trait `Bar` is private --> $DIR/privacy-ns2.rs:74:16 | LL | use foo3::{Bar,Baz}; - | ^^^ this trait is private + | ^^^ private trait | note: the trait `Bar` is defined here --> $DIR/privacy-ns2.rs:55:5 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/privacy-ufcs.stderr b/src/test/ui/privacy/privacy-ufcs.stderr index 08640b802a244..e93a458ce6cb6 100644 --- a/src/test/ui/privacy/privacy-ufcs.stderr +++ b/src/test/ui/privacy/privacy-ufcs.stderr @@ -2,7 +2,7 @@ error[E0603]: trait `Bar` is private --> $DIR/privacy-ufcs.rs:12:20 | LL | ::baz(); - | ^^^ this trait is private + | ^^^ private trait | note: the trait `Bar` is defined here --> $DIR/privacy-ufcs.rs:4:5 diff --git a/src/test/ui/privacy/privacy1.stderr b/src/test/ui/privacy/privacy1.stderr index ec2bc0d84ac0e..65c10a7bca75d 100644 --- a/src/test/ui/privacy/privacy1.stderr +++ b/src/test/ui/privacy/privacy1.stderr @@ -2,7 +2,7 @@ error[E0603]: module `baz` is private --> $DIR/privacy1.rs:132:18 | LL | use bar::baz::{foo, bar}; - | ^^^ this module is private + | ^^^ private module | note: the module `baz` is defined here --> $DIR/privacy1.rs:50:5 @@ -14,7 +14,7 @@ error[E0603]: module `baz` is private --> $DIR/privacy1.rs:132:18 | LL | use bar::baz::{foo, bar}; - | ^^^ this module is private + | ^^^ private module | note: the module `baz` is defined here --> $DIR/privacy1.rs:50:5 @@ -26,7 +26,7 @@ error[E0603]: module `baz` is private --> $DIR/privacy1.rs:141:18 | LL | use bar::baz; - | ^^^ this module is private + | ^^^ private module | note: the module `baz` is defined here --> $DIR/privacy1.rs:50:5 @@ -38,7 +38,7 @@ error[E0603]: module `i` is private --> $DIR/privacy1.rs:165:20 | LL | use self::foo::i::A; - | ^ this module is private + | ^ private module | note: the module `i` is defined here --> $DIR/privacy1.rs:170:9 @@ -50,7 +50,7 @@ error[E0603]: module `baz` is private --> $DIR/privacy1.rs:104:16 | LL | ::bar::baz::A::foo(); - | ^^^ this module is private + | ^^^ private module | note: the module `baz` is defined here --> $DIR/privacy1.rs:50:5 @@ -62,7 +62,7 @@ error[E0603]: module `baz` is private --> $DIR/privacy1.rs:105:16 | LL | ::bar::baz::A::bar(); - | ^^^ this module is private + | ^^^ private module | note: the module `baz` is defined here --> $DIR/privacy1.rs:50:5 @@ -74,7 +74,7 @@ error[E0603]: module `baz` is private --> $DIR/privacy1.rs:107:16 | LL | ::bar::baz::A.foo2(); - | ^^^ this module is private + | ^^^ private module | note: the module `baz` is defined here --> $DIR/privacy1.rs:50:5 @@ -86,7 +86,7 @@ error[E0603]: module `baz` is private --> $DIR/privacy1.rs:108:16 | LL | ::bar::baz::A.bar2(); - | ^^^ this module is private + | ^^^ private module | note: the module `baz` is defined here --> $DIR/privacy1.rs:50:5 @@ -98,7 +98,7 @@ error[E0603]: trait `B` is private --> $DIR/privacy1.rs:112:16 | LL | ::bar::B::foo(); - | ^ this trait is private + | ^ private trait | note: the trait `B` is defined here --> $DIR/privacy1.rs:40:5 @@ -110,7 +110,7 @@ error[E0603]: function `epriv` is private --> $DIR/privacy1.rs:118:20 | LL | ::bar::epriv(); - | ^^^^^ this function is private + | ^^^^^ private function | note: the function `epriv` is defined here --> $DIR/privacy1.rs:65:9 @@ -122,7 +122,7 @@ error[E0603]: module `baz` is private --> $DIR/privacy1.rs:127:16 | LL | ::bar::baz::foo(); - | ^^^ this module is private + | ^^^ private module | note: the module `baz` is defined here --> $DIR/privacy1.rs:50:5 @@ -134,7 +134,7 @@ error[E0603]: module `baz` is private --> $DIR/privacy1.rs:128:16 | LL | ::bar::baz::bar(); - | ^^^ this module is private + | ^^^ private module | note: the module `baz` is defined here --> $DIR/privacy1.rs:50:5 @@ -146,7 +146,7 @@ error[E0603]: trait `B` is private --> $DIR/privacy1.rs:157:17 | LL | impl ::bar::B for f32 { fn foo() -> f32 { 1.0 } } - | ^ this trait is private + | ^ private trait | note: the trait `B` is defined here --> $DIR/privacy1.rs:40:5 @@ -155,34 +155,34 @@ LL | trait B { | ^^^^^^^ error[E0624]: associated function `bar` is private - --> $DIR/privacy1.rs:77:9 + --> $DIR/privacy1.rs:77:23 | LL | self::baz::A::bar(); - | ^^^^^^^^^^^^^^^^^ + | ^^^ private associated function error[E0624]: associated function `bar` is private - --> $DIR/privacy1.rs:95:5 + --> $DIR/privacy1.rs:95:13 | LL | bar::A::bar(); - | ^^^^^^^^^^^ + | ^^^ private associated function error[E0624]: associated function `bar` is private - --> $DIR/privacy1.rs:102:9 + --> $DIR/privacy1.rs:102:19 | LL | ::bar::A::bar(); - | ^^^^^^^^^^^^^ + | ^^^ private associated function error[E0624]: associated function `bar` is private - --> $DIR/privacy1.rs:105:9 + --> $DIR/privacy1.rs:105:24 | LL | ::bar::baz::A::bar(); - | ^^^^^^^^^^^^^^^^^^ + | ^^^ private associated function error[E0624]: associated function `bar2` is private --> $DIR/privacy1.rs:108:23 | LL | ::bar::baz::A.bar2(); - | ^^^^ + | ^^^^ private associated function error: aborting due to 18 previous errors diff --git a/src/test/ui/privacy/privacy2.stderr b/src/test/ui/privacy/privacy2.stderr index 719dc27ccf4d6..c2a33ce1f59b3 100644 --- a/src/test/ui/privacy/privacy2.stderr +++ b/src/test/ui/privacy/privacy2.stderr @@ -8,13 +8,18 @@ error[E0603]: function import `foo` is private --> $DIR/privacy2.rs:23:20 | LL | use bar::glob::foo; - | ^^^ this function import is private + | ^^^ private function import | -note: the function import `foo` is defined here +note: the function import `foo` is defined here... --> $DIR/privacy2.rs:10:13 | LL | use foo; | ^^^ +note: ...and refers to the function `foo` which is defined here + --> $DIR/privacy2.rs:14:1 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^ consider importing it directly error: requires `sized` lang_item diff --git a/src/test/ui/privacy/privacy4.stderr b/src/test/ui/privacy/privacy4.stderr index e34b2d5049b9e..7552fa71a3a3a 100644 --- a/src/test/ui/privacy/privacy4.stderr +++ b/src/test/ui/privacy/privacy4.stderr @@ -2,7 +2,7 @@ error[E0603]: module `glob` is private --> $DIR/privacy4.rs:21:14 | LL | use bar::glob::gpriv; - | ^^^^ this module is private + | ^^^^ private module | note: the module `glob` is defined here --> $DIR/privacy4.rs:13:5 diff --git a/src/test/ui/privacy/privacy5.stderr b/src/test/ui/privacy/privacy5.stderr index 197a857cc3dc4..216abfc68b812 100644 --- a/src/test/ui/privacy/privacy5.stderr +++ b/src/test/ui/privacy/privacy5.stderr @@ -5,7 +5,7 @@ LL | pub struct A(()); | -- a constructor is private if any of the fields is private ... LL | let a = a::A(()); - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `A` is defined here --> $DIR/privacy5.rs:6:5 @@ -20,7 +20,7 @@ LL | pub struct B(isize); | ----- a constructor is private if any of the fields is private ... LL | let b = a::B(2); - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `B` is defined here --> $DIR/privacy5.rs:7:5 @@ -35,7 +35,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | let c = a::C(2, 3); - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -50,7 +50,7 @@ LL | pub struct A(()); | -- a constructor is private if any of the fields is private ... LL | let a::A(()) = a; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `A` is defined here --> $DIR/privacy5.rs:6:5 @@ -65,7 +65,7 @@ LL | pub struct A(()); | -- a constructor is private if any of the fields is private ... LL | let a::A(_) = a; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `A` is defined here --> $DIR/privacy5.rs:6:5 @@ -80,7 +80,7 @@ LL | pub struct A(()); | -- a constructor is private if any of the fields is private ... LL | match a { a::A(()) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `A` is defined here --> $DIR/privacy5.rs:6:5 @@ -95,7 +95,7 @@ LL | pub struct A(()); | -- a constructor is private if any of the fields is private ... LL | match a { a::A(_) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `A` is defined here --> $DIR/privacy5.rs:6:5 @@ -110,7 +110,7 @@ LL | pub struct B(isize); | ----- a constructor is private if any of the fields is private ... LL | let a::B(_) = b; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `B` is defined here --> $DIR/privacy5.rs:7:5 @@ -125,7 +125,7 @@ LL | pub struct B(isize); | ----- a constructor is private if any of the fields is private ... LL | let a::B(_b) = b; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `B` is defined here --> $DIR/privacy5.rs:7:5 @@ -140,7 +140,7 @@ LL | pub struct B(isize); | ----- a constructor is private if any of the fields is private ... LL | match b { a::B(_) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `B` is defined here --> $DIR/privacy5.rs:7:5 @@ -155,7 +155,7 @@ LL | pub struct B(isize); | ----- a constructor is private if any of the fields is private ... LL | match b { a::B(_b) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `B` is defined here --> $DIR/privacy5.rs:7:5 @@ -170,7 +170,7 @@ LL | pub struct B(isize); | ----- a constructor is private if any of the fields is private ... LL | match b { a::B(1) => {} a::B(_) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `B` is defined here --> $DIR/privacy5.rs:7:5 @@ -185,7 +185,7 @@ LL | pub struct B(isize); | ----- a constructor is private if any of the fields is private ... LL | match b { a::B(1) => {} a::B(_) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `B` is defined here --> $DIR/privacy5.rs:7:5 @@ -200,7 +200,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | let a::C(_, _) = c; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -215,7 +215,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | let a::C(_a, _) = c; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -230,7 +230,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | let a::C(_, _b) = c; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -245,7 +245,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | let a::C(_a, _b) = c; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -260,7 +260,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | match c { a::C(_, _) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -275,7 +275,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | match c { a::C(_a, _) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -290,7 +290,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | match c { a::C(_, _b) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -305,7 +305,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | match c { a::C(_a, _b) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -320,7 +320,7 @@ LL | pub struct A(()); | -- a constructor is private if any of the fields is private ... LL | let a2 = a::A; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `A` is defined here --> $DIR/privacy5.rs:6:5 @@ -335,7 +335,7 @@ LL | pub struct B(isize); | ----- a constructor is private if any of the fields is private ... LL | let b2 = a::B; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `B` is defined here --> $DIR/privacy5.rs:7:5 @@ -350,7 +350,7 @@ LL | pub struct C(pub isize, isize); | ---------------- a constructor is private if any of the fields is private ... LL | let c2 = a::C; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `C` is defined here --> $DIR/privacy5.rs:8:5 @@ -362,7 +362,7 @@ error[E0603]: tuple struct constructor `A` is private --> $DIR/privacy5.rs:90:20 | LL | let a = other::A(()); - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14 | @@ -379,7 +379,7 @@ error[E0603]: tuple struct constructor `B` is private --> $DIR/privacy5.rs:91:20 | LL | let b = other::B(2); - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14 | @@ -396,7 +396,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:92:20 | LL | let c = other::C(2, 3); - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | @@ -413,7 +413,7 @@ error[E0603]: tuple struct constructor `A` is private --> $DIR/privacy5.rs:95:16 | LL | let other::A(()) = a; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14 | @@ -430,7 +430,7 @@ error[E0603]: tuple struct constructor `A` is private --> $DIR/privacy5.rs:96:16 | LL | let other::A(_) = a; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14 | @@ -447,7 +447,7 @@ error[E0603]: tuple struct constructor `A` is private --> $DIR/privacy5.rs:97:22 | LL | match a { other::A(()) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14 | @@ -464,7 +464,7 @@ error[E0603]: tuple struct constructor `A` is private --> $DIR/privacy5.rs:98:22 | LL | match a { other::A(_) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14 | @@ -481,7 +481,7 @@ error[E0603]: tuple struct constructor `B` is private --> $DIR/privacy5.rs:100:16 | LL | let other::B(_) = b; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14 | @@ -498,7 +498,7 @@ error[E0603]: tuple struct constructor `B` is private --> $DIR/privacy5.rs:101:16 | LL | let other::B(_b) = b; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14 | @@ -515,7 +515,7 @@ error[E0603]: tuple struct constructor `B` is private --> $DIR/privacy5.rs:102:22 | LL | match b { other::B(_) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14 | @@ -532,7 +532,7 @@ error[E0603]: tuple struct constructor `B` is private --> $DIR/privacy5.rs:103:22 | LL | match b { other::B(_b) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14 | @@ -549,7 +549,7 @@ error[E0603]: tuple struct constructor `B` is private --> $DIR/privacy5.rs:104:22 | LL | match b { other::B(1) => {} - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14 | @@ -566,7 +566,7 @@ error[E0603]: tuple struct constructor `B` is private --> $DIR/privacy5.rs:105:16 | LL | other::B(_) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14 | @@ -583,7 +583,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:107:16 | LL | let other::C(_, _) = c; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | @@ -600,7 +600,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:108:16 | LL | let other::C(_a, _) = c; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | @@ -617,7 +617,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:109:16 | LL | let other::C(_, _b) = c; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | @@ -634,7 +634,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:110:16 | LL | let other::C(_a, _b) = c; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | @@ -651,7 +651,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:111:22 | LL | match c { other::C(_, _) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | @@ -668,7 +668,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:112:22 | LL | match c { other::C(_a, _) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | @@ -685,7 +685,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:113:22 | LL | match c { other::C(_, _b) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | @@ -702,7 +702,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:114:22 | LL | match c { other::C(_a, _b) => {} } - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | @@ -719,7 +719,7 @@ error[E0603]: tuple struct constructor `A` is private --> $DIR/privacy5.rs:122:21 | LL | let a2 = other::A; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:1:14 | @@ -736,7 +736,7 @@ error[E0603]: tuple struct constructor `B` is private --> $DIR/privacy5.rs:123:21 | LL | let b2 = other::B; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:2:14 | @@ -753,7 +753,7 @@ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:124:21 | LL | let c2 = other::C; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy_tuple_struct.rs:3:14 | diff --git a/src/test/ui/privacy/private-impl-method.stderr b/src/test/ui/privacy/private-impl-method.stderr index 6833cdb4df9db..444b9180b3f59 100644 --- a/src/test/ui/privacy/private-impl-method.stderr +++ b/src/test/ui/privacy/private-impl-method.stderr @@ -2,7 +2,7 @@ error[E0624]: associated function `foo` is private --> $DIR/private-impl-method.rs:20:7 | LL | s.foo(); - | ^^^ + | ^^^ private associated function error: aborting due to previous error diff --git a/src/test/ui/privacy/private-in-public-assoc-ty.rs b/src/test/ui/privacy/private-in-public-assoc-ty.rs index 62faae1f399e8..cd7c37cb04b22 100644 --- a/src/test/ui/privacy/private-in-public-assoc-ty.rs +++ b/src/test/ui/privacy/private-in-public-assoc-ty.rs @@ -9,7 +9,9 @@ mod m { trait PrivTr {} impl PrivTr for Priv {} pub trait PubTrAux1 {} - pub trait PubTrAux2 { type A; } + pub trait PubTrAux2 { + type A; + } impl PubTrAux1 for u8 {} impl PubTrAux2 for u8 { type A = Priv; @@ -41,8 +43,9 @@ mod m { type Exist = impl PrivTr; //~^ ERROR private trait `m::PrivTr` in public interface - //~| ERROR private trait `m::PrivTr` in public interface - fn infer_exist() -> Self::Exist { Priv } + fn infer_exist() -> Self::Exist { + Priv + } } } diff --git a/src/test/ui/privacy/private-in-public-assoc-ty.stderr b/src/test/ui/privacy/private-in-public-assoc-ty.stderr index c57073a004d8f..1a3ca3f16ed4c 100644 --- a/src/test/ui/privacy/private-in-public-assoc-ty.stderr +++ b/src/test/ui/privacy/private-in-public-assoc-ty.stderr @@ -1,5 +1,5 @@ error[E0446]: private type `m::Priv` in public interface - --> $DIR/private-in-public-assoc-ty.rs:15:9 + --> $DIR/private-in-public-assoc-ty.rs:17:9 | LL | struct Priv; | - `m::Priv` declared as private @@ -8,7 +8,7 @@ LL | type A = Priv; | ^^^^^^^^^^^^^^ can't leak private type warning: private trait `m::PrivTr` in public interface (error E0445) - --> $DIR/private-in-public-assoc-ty.rs:21:5 + --> $DIR/private-in-public-assoc-ty.rs:23:5 | LL | / pub trait PubTr { LL | | @@ -24,7 +24,7 @@ LL | | } = note: for more information, see issue #34537 warning: private type `m::Priv` in public interface (error E0446) - --> $DIR/private-in-public-assoc-ty.rs:21:5 + --> $DIR/private-in-public-assoc-ty.rs:23:5 | LL | / pub trait PubTr { LL | | @@ -39,7 +39,7 @@ LL | | } = note: for more information, see issue #34537 warning: private type `m::Priv` in public interface (error E0446) - --> $DIR/private-in-public-assoc-ty.rs:21:5 + --> $DIR/private-in-public-assoc-ty.rs:23:5 | LL | / pub trait PubTr { LL | | @@ -54,7 +54,7 @@ LL | | } = note: for more information, see issue #34537 error[E0446]: private type `m::Priv` in public interface - --> $DIR/private-in-public-assoc-ty.rs:32:9 + --> $DIR/private-in-public-assoc-ty.rs:34:9 | LL | struct Priv; | - `m::Priv` declared as private @@ -63,7 +63,7 @@ LL | type Alias4 = Priv; | ^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0446]: private type `m::Priv` in public interface - --> $DIR/private-in-public-assoc-ty.rs:39:9 + --> $DIR/private-in-public-assoc-ty.rs:41:9 | LL | struct Priv; | - `m::Priv` declared as private @@ -72,7 +72,7 @@ LL | type Alias1 = Priv; | ^^^^^^^^^^^^^^^^^^^ can't leak private type error[E0445]: private trait `m::PrivTr` in public interface - --> $DIR/private-in-public-assoc-ty.rs:42:9 + --> $DIR/private-in-public-assoc-ty.rs:44:9 | LL | trait PrivTr {} | - `m::PrivTr` declared as private @@ -80,16 +80,7 @@ LL | trait PrivTr {} LL | type Exist = impl PrivTr; | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait -error[E0445]: private trait `m::PrivTr` in public interface - --> $DIR/private-in-public-assoc-ty.rs:42:9 - | -LL | trait PrivTr {} - | - `m::PrivTr` declared as private -... -LL | type Exist = impl PrivTr; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private trait - -error: aborting due to 5 previous errors +error: aborting due to 4 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 2db4925722642..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,8 +1,8 @@ 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 error: aborting due to previous error 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 5d89d8105b119..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 {} @@ -10,7 +11,7 @@ pub fn leak_dyn_nonprincipal() -> Box { loo #[deny(missing_docs)] fn container() { impl dyn PubPrincipal { - pub fn check_doc_lint() {} //~ ERROR missing documentation for a method + pub fn check_doc_lint() {} //~ ERROR missing documentation for an associated function } impl dyn PubPrincipal + PrivNonPrincipal { pub fn check_doc_lint() {} // OK, no missing doc lint 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 2a41fae43c629..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 {} } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,17 +8,17 @@ LL | pub fn leak_dyn_nonprincipal() -> Box = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #34537 -error: missing documentation for a method - --> $DIR/private-in-public-non-principal.rs:13:9 +error: missing documentation for an associated function + --> $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/privacy/private-inferred-type-1.stderr b/src/test/ui/privacy/private-inferred-type-1.stderr index 097b8b9a61eff..576498b2cf8ef 100644 --- a/src/test/ui/privacy/private-inferred-type-1.stderr +++ b/src/test/ui/privacy/private-inferred-type-1.stderr @@ -2,13 +2,13 @@ error: type `m::Priv` is private --> $DIR/private-inferred-type-1.rs:16:5 | LL | [].arr0_secret(); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type-1.rs:17:5 | LL | None.ty_param_secret(); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ private type error: aborting due to 2 previous errors diff --git a/src/test/ui/privacy/private-inferred-type-2.stderr b/src/test/ui/privacy/private-inferred-type-2.stderr index da95cc49241ae..f19e367ef110e 100644 --- a/src/test/ui/privacy/private-inferred-type-2.stderr +++ b/src/test/ui/privacy/private-inferred-type-2.stderr @@ -2,19 +2,19 @@ error: type `m::Priv` is private --> $DIR/private-inferred-type-2.rs:16:5 | LL | m::Pub::get_priv; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type-2.rs:17:5 | LL | m::Pub::static_method; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ private type error: type `ext::Priv` is private --> $DIR/private-inferred-type-2.rs:18:5 | LL | ext::Pub::static_method; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ private type error: aborting due to 3 previous errors diff --git a/src/test/ui/privacy/private-inferred-type-3.stderr b/src/test/ui/privacy/private-inferred-type-3.stderr index 376f1334ff806..39ef6472526c3 100644 --- a/src/test/ui/privacy/private-inferred-type-3.stderr +++ b/src/test/ui/privacy/private-inferred-type-3.stderr @@ -2,7 +2,7 @@ error: type `fn() {ext::priv_fn}` is private --> $DIR/private-inferred-type-3.rs:16:5 | LL | ext::m!(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -10,7 +10,7 @@ error: static `PRIV_STATIC` is private --> $DIR/private-inferred-type-3.rs:16:5 | LL | ext::m!(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ private static | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -18,7 +18,7 @@ error: type `ext::PrivEnum` is private --> $DIR/private-inferred-type-3.rs:16:5 | LL | ext::m!(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -26,7 +26,7 @@ error: type `fn() {::method}` is private --> $DIR/private-inferred-type-3.rs:16:5 | LL | ext::m!(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -34,7 +34,7 @@ error: type `fn(u8) -> ext::PrivTupleStruct {ext::PrivTupleStruct}` is private --> $DIR/private-inferred-type-3.rs:16:5 | LL | ext::m!(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -42,7 +42,7 @@ error: type `fn(u8) -> ext::PubTupleStruct {ext::PubTupleStruct}` is private --> $DIR/private-inferred-type-3.rs:16:5 | LL | ext::m!(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -50,7 +50,7 @@ error: type `for<'r> fn(&'r ext::Pub) {ext::Pub::::priv_method}` is priv --> $DIR/private-inferred-type-3.rs:16:5 | LL | ext::m!(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/privacy/private-inferred-type.stderr b/src/test/ui/privacy/private-inferred-type.stderr index 48c83c2186591..7d1f794bfe459 100644 --- a/src/test/ui/privacy/private-inferred-type.stderr +++ b/src/test/ui/privacy/private-inferred-type.stderr @@ -20,97 +20,97 @@ error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:97:9 | LL | let _: m::Alias; - | ^ + | ^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:97:12 | LL | let _: m::Alias; - | ^^^^^^^^ + | ^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:99:13 | LL | let _: ::AssocTy; - | ^^^^^^^^ + | ^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:100:5 | LL | m::Alias {}; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:101:5 | LL | m::Pub { 0: m::Alias {} }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:103:5 | LL | m::Pub::static_method; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:104:5 | LL | m::Pub::INHERENT_ASSOC_CONST; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:105:5 | LL | m::Pub(0u8).method_with_substs::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:106:17 | LL | m::Pub(0u8).method_with_priv_params(loop{}); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:107:5 | LL | ::TRAIT_ASSOC_CONST; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:108:6 | LL | >::INHERENT_ASSOC_CONST; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:109:5 | LL | >::INHERENT_ASSOC_CONST_GENERIC_SELF; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:110:5 | LL | >::static_method_generic_self; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:112:5 | LL | u8::pub_method; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ private type error: type `adjust::S2` is private --> $DIR/private-inferred-type.rs:114:5 | LL | adjust::S1.method_s3(); - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type error: type `fn() {m::priv_fn}` is private --> $DIR/private-inferred-type.rs:39:9 | LL | priv_fn; - | ^^^^^^^ + | ^^^^^^^ private type ... LL | m::m!(); | -------- in this macro invocation @@ -121,7 +121,7 @@ error: type `m::PrivEnum` is private --> $DIR/private-inferred-type.rs:41:9 | LL | PrivEnum::Variant; - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ private type ... LL | m::m!(); | -------- in this macro invocation @@ -132,7 +132,7 @@ error: type `fn() {::method}` is private --> $DIR/private-inferred-type.rs:43:9 | LL | ::method; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ private type ... LL | m::m!(); | -------- in this macro invocation @@ -143,7 +143,7 @@ error: type `fn(u8) -> m::PrivTupleStruct {m::PrivTupleStruct}` is private --> $DIR/private-inferred-type.rs:45:9 | LL | PrivTupleStruct; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ private type ... LL | m::m!(); | -------- in this macro invocation @@ -154,7 +154,7 @@ error: type `fn(u8) -> m::PubTupleStruct {m::PubTupleStruct}` is private --> $DIR/private-inferred-type.rs:47:9 | LL | PubTupleStruct; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ private type ... LL | m::m!(); | -------- in this macro invocation @@ -165,7 +165,7 @@ error: type `for<'r> fn(&'r m::Pub) {m::Pub::::priv_method}` is private --> $DIR/private-inferred-type.rs:49:18 | LL | Pub(0u8).priv_method(); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ private type ... LL | m::m!(); | -------- in this macro invocation @@ -176,61 +176,61 @@ error: trait `m::Trait` is private --> $DIR/private-inferred-type.rs:118:5 | LL | m::leak_anon1(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ private trait error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:119:5 | LL | m::leak_anon2(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:120:5 | LL | m::leak_anon3(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ private type error: trait `m::Trait` is private --> $DIR/private-inferred-type.rs:122:5 | LL | m::leak_dyn1(); - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ private trait error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:123:5 | LL | m::leak_dyn2(); - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:124:5 | LL | m::leak_dyn3(); - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:127:13 | LL | let a = m::Alias {}; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:128:17 | LL | let mut b = a; - | ^ + | ^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:129:9 | LL | b = a; - | ^ + | ^ private type error: type `m::Priv` is private --> $DIR/private-inferred-type.rs:130:11 | LL | match a { - | ^ + | ^ private type error: aborting due to 33 previous errors diff --git a/src/test/ui/privacy/private-item-simple.stderr b/src/test/ui/privacy/private-item-simple.stderr index f51b74f6cb53b..e3d90150e2e3e 100644 --- a/src/test/ui/privacy/private-item-simple.stderr +++ b/src/test/ui/privacy/private-item-simple.stderr @@ -2,7 +2,7 @@ error[E0603]: function `f` is private --> $DIR/private-item-simple.rs:6:8 | LL | a::f(); - | ^ this function is private + | ^ private function | note: the function `f` is defined here --> $DIR/private-item-simple.rs:2:5 diff --git a/src/test/ui/privacy/private-method-cross-crate.stderr b/src/test/ui/privacy/private-method-cross-crate.stderr index 6b49063815a66..8a47846d667e3 100644 --- a/src/test/ui/privacy/private-method-cross-crate.stderr +++ b/src/test/ui/privacy/private-method-cross-crate.stderr @@ -2,7 +2,7 @@ error[E0624]: associated function `nap` is private --> $DIR/private-method-cross-crate.rs:7:8 | LL | nyan.nap(); - | ^^^ + | ^^^ private associated function error: aborting due to previous error diff --git a/src/test/ui/privacy/private-method-inherited.stderr b/src/test/ui/privacy/private-method-inherited.stderr index 5551e1bd75907..8083b197a5d78 100644 --- a/src/test/ui/privacy/private-method-inherited.stderr +++ b/src/test/ui/privacy/private-method-inherited.stderr @@ -2,7 +2,7 @@ error[E0624]: associated function `f` is private --> $DIR/private-method-inherited.rs:13:7 | LL | x.f(); - | ^ + | ^ private associated function error: aborting due to previous error diff --git a/src/test/ui/privacy/private-method.stderr b/src/test/ui/privacy/private-method.stderr index 583dc123e246b..a15fce46877ce 100644 --- a/src/test/ui/privacy/private-method.stderr +++ b/src/test/ui/privacy/private-method.stderr @@ -2,7 +2,7 @@ error[E0624]: associated function `nap` is private --> $DIR/private-method.rs:22:8 | LL | nyan.nap(); - | ^^^ + | ^^^ private associated function error: aborting due to previous error diff --git a/src/test/ui/privacy/private-struct-field-cross-crate.stderr b/src/test/ui/privacy/private-struct-field-cross-crate.stderr index 857f2436aa829..ac00d82adab42 100644 --- a/src/test/ui/privacy/private-struct-field-cross-crate.stderr +++ b/src/test/ui/privacy/private-struct-field-cross-crate.stderr @@ -1,8 +1,8 @@ error[E0616]: field `meows` of struct `cci_class::kitties::cat` is private - --> $DIR/private-struct-field-cross-crate.rs:7:14 + --> $DIR/private-struct-field-cross-crate.rs:7:19 | LL | assert_eq!(nyan.meows, 52); - | ^^^^^^^^^^ + | ^^^^^ private field error: aborting due to previous error diff --git a/src/test/ui/privacy/private-struct-field-ctor.stderr b/src/test/ui/privacy/private-struct-field-ctor.stderr index 97585c1d8805b..7c32ebc2cf7b9 100644 --- a/src/test/ui/privacy/private-struct-field-ctor.stderr +++ b/src/test/ui/privacy/private-struct-field-ctor.stderr @@ -2,7 +2,7 @@ error[E0451]: field `x` of struct `a::Foo` is private --> $DIR/private-struct-field-ctor.rs:8:22 | LL | let s = a::Foo { x: 1 }; - | ^^^^ field `x` is private + | ^^^^ private field error: aborting due to previous error diff --git a/src/test/ui/privacy/private-struct-field-pattern.stderr b/src/test/ui/privacy/private-struct-field-pattern.stderr index 69bd58aacfc54..9190317403ec1 100644 --- a/src/test/ui/privacy/private-struct-field-pattern.stderr +++ b/src/test/ui/privacy/private-struct-field-pattern.stderr @@ -2,7 +2,7 @@ error[E0451]: field `x` of struct `a::Foo` is private --> $DIR/private-struct-field-pattern.rs:15:15 | LL | Foo { x: _ } => {} - | ^^^^ field `x` is private + | ^^^^ private field error: aborting due to previous error diff --git a/src/test/ui/privacy/private-struct-field.stderr b/src/test/ui/privacy/private-struct-field.stderr index da53c73b4311c..c89ae507ab5fd 100644 --- a/src/test/ui/privacy/private-struct-field.stderr +++ b/src/test/ui/privacy/private-struct-field.stderr @@ -1,8 +1,8 @@ error[E0616]: field `meows` of struct `cat::Cat` is private - --> $DIR/private-struct-field.rs:13:16 + --> $DIR/private-struct-field.rs:13:21 | LL | assert_eq!(nyan.meows, 52); - | ^^^^^^^^^^ + | ^^^^^ private field error: aborting due to previous error diff --git a/src/test/ui/privacy/private-type-in-interface.stderr b/src/test/ui/privacy/private-type-in-interface.stderr index aa4bfb7fc9a19..ea89035c3d006 100644 --- a/src/test/ui/privacy/private-type-in-interface.stderr +++ b/src/test/ui/privacy/private-type-in-interface.stderr @@ -2,55 +2,55 @@ error: type `m::Priv` is private --> $DIR/private-type-in-interface.rs:15:9 | LL | fn f(_: m::Alias) {} - | ^^^^^^^^ + | ^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-type-in-interface.rs:15:6 | LL | fn f(_: m::Alias) {} - | ^ + | ^ private type error: type `ext::Priv` is private --> $DIR/private-type-in-interface.rs:17:13 | LL | fn f_ext(_: ext::Alias) {} - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type error: type `ext::Priv` is private --> $DIR/private-type-in-interface.rs:17:10 | LL | fn f_ext(_: ext::Alias) {} - | ^ + | ^ private type error: type `m::Priv` is private --> $DIR/private-type-in-interface.rs:21:6 | LL | impl m::Alias {} - | ^^^^^^^^ + | ^^^^^^^^ private type error: type `ext::Priv` is private --> $DIR/private-type-in-interface.rs:22:14 | LL | impl Tr1 for ext::Alias {} - | ^^^^^^^^^^ + | ^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-type-in-interface.rs:23:10 | LL | type A = ::X; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ private type error: type `m::Priv` is private --> $DIR/private-type-in-interface.rs:27:11 | LL | fn g() -> impl Tr2 { 0 } - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ private type error: type `ext::Priv` is private --> $DIR/private-type-in-interface.rs:28:15 | LL | fn g_ext() -> impl Tr2 { 0 } - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ private type error: aborting due to 9 previous errors diff --git a/src/test/ui/privacy/restricted/struct-literal-field.stderr b/src/test/ui/privacy/restricted/struct-literal-field.stderr index dd609944a4b3f..591980dc3450d 100644 --- a/src/test/ui/privacy/restricted/struct-literal-field.stderr +++ b/src/test/ui/privacy/restricted/struct-literal-field.stderr @@ -2,7 +2,7 @@ error[E0451]: field `x` of struct `foo::bar::S` is private --> $DIR/struct-literal-field.rs:18:9 | LL | S { x: 0 }; - | ^^^^ field `x` is private + | ^^^^ private field error: aborting due to previous error diff --git a/src/test/ui/privacy/restricted/test.stderr b/src/test/ui/privacy/restricted/test.stderr index e73f723ed0ab6..40512a34bd959 100644 --- a/src/test/ui/privacy/restricted/test.stderr +++ b/src/test/ui/privacy/restricted/test.stderr @@ -26,7 +26,7 @@ error[E0603]: struct `Crate` is private --> $DIR/test.rs:38:25 | LL | use pub_restricted::Crate; - | ^^^^^ this struct is private + | ^^^^^ private struct | note: the struct `Crate` is defined here --> $DIR/auxiliary/pub_restricted.rs:3:1 @@ -38,7 +38,7 @@ error[E0603]: function `f` is private --> $DIR/test.rs:30:19 | LL | use foo::bar::f; - | ^ this function is private + | ^ private function | note: the function `f` is defined here --> $DIR/test.rs:8:9 @@ -47,46 +47,46 @@ LL | pub(super) fn f() {} | ^^^^^^^^^^^^^^^^^ error[E0616]: field `x` of struct `foo::bar::S` is private - --> $DIR/test.rs:31:5 + --> $DIR/test.rs:31:18 | LL | S::default().x; - | ^^^^^^^^^^^^^^ + | ^ private field error[E0624]: associated function `f` is private --> $DIR/test.rs:32:18 | LL | S::default().f(); - | ^ + | ^ private associated function error[E0624]: associated function `g` is private - --> $DIR/test.rs:33:5 + --> $DIR/test.rs:33:8 | LL | S::g(); - | ^^^^ + | ^ private associated function error[E0616]: field `y` of struct `pub_restricted::Universe` is private - --> $DIR/test.rs:42:13 + --> $DIR/test.rs:42:15 | LL | let _ = u.y; - | ^^^ + | ^ private field error[E0616]: field `z` of struct `pub_restricted::Universe` is private - --> $DIR/test.rs:43:13 + --> $DIR/test.rs:43:15 | LL | let _ = u.z; - | ^^^ + | ^ private field error[E0624]: associated function `g` is private --> $DIR/test.rs:45:7 | LL | u.g(); - | ^ + | ^ private associated function error[E0624]: associated function `h` is private --> $DIR/test.rs:46:7 | LL | u.h(); - | ^ + | ^ private associated function error: aborting due to 12 previous errors diff --git a/src/test/ui/privacy/union-field-privacy-1.stderr b/src/test/ui/privacy/union-field-privacy-1.stderr index 96a1d7ed5090a..15096eb113966 100644 --- a/src/test/ui/privacy/union-field-privacy-1.stderr +++ b/src/test/ui/privacy/union-field-privacy-1.stderr @@ -2,13 +2,13 @@ error[E0451]: field `c` of union `m::U` is private --> $DIR/union-field-privacy-1.rs:12:20 | LL | let u = m::U { c: 0 }; - | ^^^^ field `c` is private + | ^^^^ private field error[E0451]: field `c` of union `m::U` is private --> $DIR/union-field-privacy-1.rs:16:16 | LL | let m::U { c } = u; - | ^ field `c` is private + | ^ private field error: aborting due to 2 previous errors diff --git a/src/test/ui/privacy/union-field-privacy-2.stderr b/src/test/ui/privacy/union-field-privacy-2.stderr index 8789178caac26..a23cf90332b02 100644 --- a/src/test/ui/privacy/union-field-privacy-2.stderr +++ b/src/test/ui/privacy/union-field-privacy-2.stderr @@ -1,8 +1,8 @@ error[E0616]: field `c` of union `m::U` is private - --> $DIR/union-field-privacy-2.rs:14:13 + --> $DIR/union-field-privacy-2.rs:14:15 | LL | let c = u.c; - | ^^^ + | ^ private field error: aborting due to previous error 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/attribute-spans-preserved.stdout b/src/test/ui/proc-macro/attribute-spans-preserved.stdout index faf3171215665..cf9a97491f039 100644 --- a/src/test/ui/proc-macro/attribute-spans-preserved.stdout +++ b/src/test/ui/proc-macro/attribute-spans-preserved.stdout @@ -1 +1 @@ -fn main () { let y : u32 = "z" ; { let x : u32 = "y" ; } } +fn main() { let y : u32 = "z" ; { let x : u32 = "y" ; } } 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 27a215de032aa..72c88d5d8b752 100644 --- a/src/test/ui/proc-macro/attributes-included.stderr +++ b/src/test/ui/proc-macro/attributes-included.stderr @@ -2,7 +2,7 @@ warning: unused variable: `a` --> $DIR/attributes-included.rs:17:9 | LL | let a: i32 = "foo"; - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: the lint level is defined here --> $DIR/attributes-included.rs:4:9 @@ -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/derive-unstable.rs b/src/test/ui/proc-macro/auxiliary/derive-unstable.rs index f702df66db1b8..2ccd3f88200e3 100644 --- a/src/test/ui/proc-macro/auxiliary/derive-unstable.rs +++ b/src/test/ui/proc-macro/auxiliary/derive-unstable.rs @@ -10,5 +10,5 @@ use proc_macro::TokenStream; #[proc_macro_derive(Unstable)] pub fn derive(_input: TokenStream) -> TokenStream { - "unsafe fn foo() -> u32 { ::std::intrinsics::init() }".parse().unwrap() + "unsafe fn foo() -> u32 { ::std::intrinsics::abort() }".parse().unwrap() } 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/first-second.rs b/src/test/ui/proc-macro/auxiliary/first-second.rs new file mode 100644 index 0000000000000..6331608fbe543 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/first-second.rs @@ -0,0 +1,20 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, Group, Delimiter}; + +#[proc_macro_attribute] +pub fn first(_attr: TokenStream, item: TokenStream) -> TokenStream { + let tokens: TokenStream = "#[derive(Second)]".parse().unwrap(); + let wrapped = TokenTree::Group(Group::new(Delimiter::None, item.into_iter().collect())); + tokens.into_iter().chain(std::iter::once(wrapped)).collect() +} + +#[proc_macro_derive(Second)] +pub fn second(item: TokenStream) -> TokenStream { + TokenStream::new() +} 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/recollect.rs b/src/test/ui/proc-macro/auxiliary/recollect.rs new file mode 100644 index 0000000000000..d4494a5aff2e9 --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/recollect.rs @@ -0,0 +1,12 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro] +pub fn recollect(tokens: TokenStream) -> TokenStream { + tokens.into_iter().collect() +} 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/auxiliary/test-macros.rs b/src/test/ui/proc-macro/auxiliary/test-macros.rs index 27efa44f98032..fb8016cd43896 100644 --- a/src/test/ui/proc-macro/auxiliary/test-macros.rs +++ b/src/test/ui/proc-macro/auxiliary/test-macros.rs @@ -108,5 +108,6 @@ pub fn print_attr(_: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_derive(Print, attributes(print_helper))] pub fn print_derive(input: TokenStream) -> TokenStream { - print_helper(input, "DERIVE") + print_helper(input, "DERIVE"); + TokenStream::new() } diff --git a/src/test/ui/proc-macro/auxiliary/weird-hygiene.rs b/src/test/ui/proc-macro/auxiliary/weird-hygiene.rs new file mode 100644 index 0000000000000..338e436df500f --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/weird-hygiene.rs @@ -0,0 +1,48 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, Group}; + +fn find_my_ident(tokens: TokenStream) -> Option { + for token in tokens { + if let TokenTree::Ident(ident) = &token { + if ident.to_string() == "hidden_ident" { + return Some(vec![token].into_iter().collect()) + } + } else if let TokenTree::Group(g) = token { + if let Some(stream) = find_my_ident(g.stream()) { + return Some(stream) + } + } + } + return None; +} + + +#[proc_macro_derive(WeirdDerive)] +pub fn weird_derive(item: TokenStream) -> TokenStream { + let my_ident = find_my_ident(item).expect("Missing 'my_ident'!"); + let tokens: TokenStream = "call_it!();".parse().unwrap(); + let final_call = tokens.into_iter().map(|tree| { + if let TokenTree::Group(g) = tree { + return Group::new(g.delimiter(), my_ident.clone()).into() + } else { + return tree + } + }).collect(); + final_call +} + +#[proc_macro] +pub fn recollect(item: TokenStream) -> TokenStream { + item.into_iter().collect() +} + +#[proc_macro_attribute] +pub fn recollect_attr(_attr: TokenStream, mut item: TokenStream) -> TokenStream { + item.into_iter().collect() +} 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/break-token-spans.rs b/src/test/ui/proc-macro/break-token-spans.rs new file mode 100644 index 0000000000000..59dc3b5043cd7 --- /dev/null +++ b/src/test/ui/proc-macro/break-token-spans.rs @@ -0,0 +1,16 @@ +// aux-build:test-macros.rs +// Regression test for issues #68489 and #70987 +// Tests that we properly break tokens in `probably_equal_for_proc_macro` +// See #72306 +// +// Note that the weird spacing in this example is critical +// for testing the issue. + +extern crate test_macros; + +#[test_macros::recollect_attr] +fn repro() { + f :: < Vec < _ > > ( ) ; //~ ERROR cannot find + let a: Option>= true; //~ ERROR mismatched +} +fn main() {} diff --git a/src/test/ui/proc-macro/break-token-spans.stderr b/src/test/ui/proc-macro/break-token-spans.stderr new file mode 100644 index 0000000000000..caca973f252f7 --- /dev/null +++ b/src/test/ui/proc-macro/break-token-spans.stderr @@ -0,0 +1,21 @@ +error[E0425]: cannot find function `f` in this scope + --> $DIR/break-token-spans.rs:13:5 + | +LL | f :: < Vec < _ > > ( ) ; + | ^ not found in this scope + +error[E0308]: mismatched types + --> $DIR/break-token-spans.rs:14:32 + | +LL | let a: Option>= true; + | ------------------ ^^^^ expected enum `std::option::Option`, found `bool` + | | + | expected due to this + | + = note: expected enum `std::option::Option>` + found type `bool` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0425. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/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/capture-macro-rules-invoke.rs b/src/test/ui/proc-macro/capture-macro-rules-invoke.rs new file mode 100644 index 0000000000000..a404ddace9bbe --- /dev/null +++ b/src/test/ui/proc-macro/capture-macro-rules-invoke.rs @@ -0,0 +1,22 @@ +// aux-build:test-macros.rs +// check-pass + +extern crate test_macros; +use test_macros::recollect; + +macro_rules! use_expr { + ($expr:expr) => { + recollect!($expr) + } +} + +#[allow(dead_code)] +struct Foo; +impl Foo { + #[allow(dead_code)] + fn use_self(self) { + drop(use_expr!(self)); + } +} + +fn main() {} 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 new file mode 100644 index 0000000000000..97f6265e3089b --- /dev/null +++ b/src/test/ui/proc-macro/crt-static.rs @@ -0,0 +1,17 @@ +// Test proc-macro crate can be built without addtional RUSTFLAGS +// on musl target +// 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"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_derive(Foo)] +pub fn derive_foo(input: TokenStream) -> TokenStream { + input +} 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-span-debug.rs b/src/test/ui/proc-macro/debug/dump-debug-span-debug.rs new file mode 100644 index 0000000000000..fd34eb974c094 --- /dev/null +++ b/src/test/ui/proc-macro/debug/dump-debug-span-debug.rs @@ -0,0 +1,41 @@ +// run-pass +// aux-build:macro-dump-debug.rs +// compile-flags: -Z span-debug + +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-span-debug.stderr b/src/test/ui/proc-macro/debug/dump-debug-span-debug.stderr new file mode 100644 index 0000000000000..2c05bdbc492a4 --- /dev/null +++ b/src/test/ui/proc-macro/debug/dump-debug-span-debug.stderr @@ -0,0 +1,166 @@ +TokenStream [Ident { ident: "ident", span: $DIR/dump-debug-span-debug.rs:9:5: 9:10 (#0) }, Ident { ident: "r#ident", span: $DIR/dump-debug-span-debug.rs:10:5: 10:12 (#0) }, Punct { ch: ',', spacing: Alone, span: $DIR/dump-debug-span-debug.rs:11:5: 11:6 (#0) }, Punct { ch: '=', spacing: Joint, span: $DIR/dump-debug-span-debug.rs:12:5: 12:7 (#0) }, Punct { ch: '=', spacing: Joint, span: $DIR/dump-debug-span-debug.rs:12:5: 12:7 (#0) }, Punct { ch: '>', spacing: Alone, span: $DIR/dump-debug-span-debug.rs:12:7: 12:8 (#0) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/dump-debug-span-debug.rs:13:5: 13:7 (#0) }, Group { delimiter: Bracket, stream: TokenStream [Ident { ident: "_", span: $DIR/dump-debug-span-debug.rs:14:6: 14:7 (#0) }], span: $DIR/dump-debug-span-debug.rs:14:5: 14:8 (#0) }, Literal { kind: Integer, symbol: "0", suffix: None, span: $DIR/dump-debug-span-debug.rs:17:5: 17:6 (#0) }, Literal { kind: Float, symbol: "1.0", suffix: None, span: $DIR/dump-debug-span-debug.rs:18:5: 18:8 (#0) }, Literal { kind: Str, symbol: "S", suffix: None, span: $DIR/dump-debug-span-debug.rs:19:5: 19:8 (#0) }, Literal { kind: ByteStr, symbol: "B", suffix: None, span: $DIR/dump-debug-span-debug.rs:20:5: 20:9 (#0) }, Literal { kind: StrRaw(0), symbol: "R", suffix: None, span: $DIR/dump-debug-span-debug.rs:21:5: 21:9 (#0) }, Literal { kind: StrRaw(2), symbol: "R", suffix: None, span: $DIR/dump-debug-span-debug.rs:22:5: 22:13 (#0) }, Literal { kind: ByteStrRaw(0), symbol: "BR", suffix: None, span: $DIR/dump-debug-span-debug.rs:23:5: 23:11 (#0) }, Literal { kind: ByteStrRaw(2), symbol: "BR", suffix: None, span: $DIR/dump-debug-span-debug.rs:24:5: 24:15 (#0) }, Literal { kind: Char, symbol: "C", suffix: None, span: $DIR/dump-debug-span-debug.rs:25:5: 25:8 (#0) }, Literal { kind: Byte, symbol: "B", suffix: None, span: $DIR/dump-debug-span-debug.rs:26:5: 26:9 (#0) }, Literal { kind: Integer, symbol: "0", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:29:5: 29:7 (#0) }, Literal { kind: Float, symbol: "1.0", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:30:5: 30:9 (#0) }, Literal { kind: Str, symbol: "S", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:31:5: 31:9 (#0) }, Literal { kind: ByteStr, symbol: "B", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:32:5: 32:10 (#0) }, Literal { kind: StrRaw(0), symbol: "R", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:33:5: 33:10 (#0) }, Literal { kind: StrRaw(2), symbol: "R", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:34:5: 34:14 (#0) }, Literal { kind: ByteStrRaw(0), symbol: "BR", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:35:5: 35:12 (#0) }, Literal { kind: ByteStrRaw(2), symbol: "BR", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:36:5: 36:16 (#0) }, Literal { kind: Char, symbol: "C", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:37:5: 37:9 (#0) }, Literal { kind: Byte, symbol: "B", suffix: Some("q"), span: $DIR/dump-debug-span-debug.rs:38:5: 38:10 (#0) }] +TokenStream [ + Ident { + ident: "ident", + span: $DIR/dump-debug-span-debug.rs:9:5: 9:10 (#0), + }, + Ident { + ident: "r#ident", + span: $DIR/dump-debug-span-debug.rs:10:5: 10:12 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/dump-debug-span-debug.rs:11:5: 11:6 (#0), + }, + Punct { + ch: '=', + spacing: Joint, + span: $DIR/dump-debug-span-debug.rs:12:5: 12:7 (#0), + }, + Punct { + ch: '=', + spacing: Joint, + span: $DIR/dump-debug-span-debug.rs:12:5: 12:7 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/dump-debug-span-debug.rs:12:7: 12:8 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/dump-debug-span-debug.rs:13:5: 13:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "_", + span: $DIR/dump-debug-span-debug.rs:14:6: 14:7 (#0), + }, + ], + span: $DIR/dump-debug-span-debug.rs:14:5: 14:8 (#0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:17:5: 17:6 (#0), + }, + Literal { + kind: Float, + symbol: "1.0", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:18:5: 18:8 (#0), + }, + Literal { + kind: Str, + symbol: "S", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:19:5: 19:8 (#0), + }, + Literal { + kind: ByteStr, + symbol: "B", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:20:5: 20:9 (#0), + }, + Literal { + kind: StrRaw(0), + symbol: "R", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:21:5: 21:9 (#0), + }, + Literal { + kind: StrRaw(2), + symbol: "R", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:22:5: 22:13 (#0), + }, + Literal { + kind: ByteStrRaw(0), + symbol: "BR", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:23:5: 23:11 (#0), + }, + Literal { + kind: ByteStrRaw(2), + symbol: "BR", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:24:5: 24:15 (#0), + }, + Literal { + kind: Char, + symbol: "C", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:25:5: 25:8 (#0), + }, + Literal { + kind: Byte, + symbol: "B", + suffix: None, + span: $DIR/dump-debug-span-debug.rs:26:5: 26:9 (#0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:29:5: 29:7 (#0), + }, + Literal { + kind: Float, + symbol: "1.0", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:30:5: 30:9 (#0), + }, + Literal { + kind: Str, + symbol: "S", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:31:5: 31:9 (#0), + }, + Literal { + kind: ByteStr, + symbol: "B", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:32:5: 32:10 (#0), + }, + Literal { + kind: StrRaw(0), + symbol: "R", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:33:5: 33:10 (#0), + }, + Literal { + kind: StrRaw(2), + symbol: "R", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:34:5: 34:14 (#0), + }, + Literal { + kind: ByteStrRaw(0), + symbol: "BR", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:35:5: 35:12 (#0), + }, + Literal { + kind: ByteStrRaw(2), + symbol: "BR", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:36:5: 36:16 (#0), + }, + Literal { + kind: Char, + symbol: "C", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:37:5: 37:9 (#0), + }, + Literal { + kind: Byte, + symbol: "B", + suffix: Some("q"), + span: $DIR/dump-debug-span-debug.rs:38:5: 38:10 (#0), + }, +] 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/derive-bad.rs b/src/test/ui/proc-macro/derive-bad.rs index 62c0741b6697a..cb5188b5fb43f 100644 --- a/src/test/ui/proc-macro/derive-bad.rs +++ b/src/test/ui/proc-macro/derive-bad.rs @@ -3,11 +3,9 @@ #[macro_use] extern crate derive_bad; -#[derive( - A -)] -//~^^ ERROR proc-macro derive produced unparseable tokens +#[derive(A)] +//~^ ERROR proc-macro derive produced unparseable tokens //~| ERROR expected `:`, found `}` -struct A; +struct A; //~ ERROR the name `A` is defined multiple times fn main() {} diff --git a/src/test/ui/proc-macro/derive-bad.stderr b/src/test/ui/proc-macro/derive-bad.stderr index 8667396c98989..bc5ed9815238a 100644 --- a/src/test/ui/proc-macro/derive-bad.stderr +++ b/src/test/ui/proc-macro/derive-bad.stderr @@ -1,16 +1,28 @@ error: expected `:`, found `}` - --> $DIR/derive-bad.rs:7:5 + --> $DIR/derive-bad.rs:6:10 | -LL | A - | ^ expected `:` +LL | #[derive(A)] + | ^ expected `:` | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: proc-macro derive produced unparseable tokens - --> $DIR/derive-bad.rs:7:5 + --> $DIR/derive-bad.rs:6:10 | -LL | A - | ^ +LL | #[derive(A)] + | ^ -error: aborting due to 2 previous errors +error[E0428]: the name `A` is defined multiple times + --> $DIR/derive-bad.rs:9:1 + | +LL | #[derive(A)] + | - previous definition of the type `A` here +... +LL | struct A; + | ^^^^^^^^^ `A` redefined here + | + = note: `A` must be defined only once in the type namespace of this module + +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0428`. diff --git a/src/test/ui/proc-macro/disappearing-resolution.stderr b/src/test/ui/proc-macro/disappearing-resolution.stderr index 3beaedf61d73a..ff7ddcde6e0c4 100644 --- a/src/test/ui/proc-macro/disappearing-resolution.stderr +++ b/src/test/ui/proc-macro/disappearing-resolution.stderr @@ -8,7 +8,7 @@ error[E0603]: derive macro import `Empty` is private --> $DIR/disappearing-resolution.rs:11:8 | LL | use m::Empty; - | ^^^^^ this derive macro import is private + | ^^^^^ private derive macro import | note: the derive macro import `Empty` is defined here --> $DIR/disappearing-resolution.rs:9:9 diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout index ea06f6c1acaf9..15433bebde967 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout +++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout @@ -1,4 +1,4 @@ -PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ; +PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ; PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ }, ] PRINT-ATTR INPUT (DISPLAY): struct A(crate::S); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct A($crate :: S) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", diff --git a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout index 619b2fd5321ff..73e407918ec8c 100644 --- a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout +++ b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout @@ -1,5 +1,5 @@ PRINT-ATTR INPUT (DISPLAY): struct A(identity!(crate :: S)); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct A (identity ! ($crate :: S)) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct A(identity ! ($crate :: S)) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -55,7 +55,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ }, ] PRINT-ATTR INPUT (DISPLAY): struct B(identity!(::dollar_crate_external :: S)); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct B (identity ! ($crate :: S)) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct B(identity ! ($crate :: S)) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", diff --git a/src/test/ui/proc-macro/dollar-crate.rs b/src/test/ui/proc-macro/dollar-crate.rs index aadd87ffaf203..5f2549376d1ba 100644 --- a/src/test/ui/proc-macro/dollar-crate.rs +++ b/src/test/ui/proc-macro/dollar-crate.rs @@ -1,3 +1,4 @@ +// check-pass // edition:2018 // aux-build:test-macros.rs // aux-build:dollar-crate-external.rs @@ -23,7 +24,7 @@ mod local { struct A($crate::S); #[derive(Print)] - struct D($crate::S); //~ ERROR the name `D` is defined multiple times + struct D($crate::S); }; } @@ -33,7 +34,7 @@ mod local { mod external { use crate::dollar_crate_external; - dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times + dollar_crate_external::external!(); } fn main() {} diff --git a/src/test/ui/proc-macro/dollar-crate.stderr b/src/test/ui/proc-macro/dollar-crate.stderr deleted file mode 100644 index 465f242580dfb..0000000000000 --- a/src/test/ui/proc-macro/dollar-crate.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error[E0428]: the name `D` is defined multiple times - --> $DIR/dollar-crate.rs:26:13 - | -LL | struct D($crate::S); - | ^^^^^^^^^^^^^^^^^^^^ - | | - | `D` redefined here - | previous definition of the type `D` here -... -LL | local!(); - | --------- in this macro invocation - | - = note: `D` must be defined only once in the type namespace of this module - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0428]: the name `D` is defined multiple times - --> $DIR/dollar-crate.rs:36:5 - | -LL | dollar_crate_external::external!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | `D` redefined here - | previous definition of the type `D` here - | - = note: `D` must be defined only once in the type namespace of this module - = 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 E0428`. diff --git a/src/test/ui/proc-macro/dollar-crate.stdout b/src/test/ui/proc-macro/dollar-crate.stdout index 5fdc6f8ee96ca..e125a3e7f1737 100644 --- a/src/test/ui/proc-macro/dollar-crate.stdout +++ b/src/test/ui/proc-macro/dollar-crate.stdout @@ -1,4 +1,4 @@ -PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ; +PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ; PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ }, ] PRINT-ATTR INPUT (DISPLAY): struct A(crate::S); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct A($crate :: S) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -80,7 +80,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ }, ] PRINT-DERIVE INPUT (DISPLAY): struct D(crate::S); -PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ($crate :: S) ; +PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D($crate :: S) ; PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -120,7 +120,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ span: #3 bytes(LO..HI), }, ] -PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ; +PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ; PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -161,7 +161,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ }, ] PRINT-ATTR INPUT (DISPLAY): struct A(::dollar_crate_external::S); -PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ; +PRINT-ATTR RE-COLLECTED (DISPLAY): struct A($crate :: S) ; PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", @@ -202,7 +202,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ }, ] PRINT-DERIVE INPUT (DISPLAY): struct D(::dollar_crate_external::S); -PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ($crate :: S) ; +PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D($crate :: S) ; PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", diff --git a/src/test/ui/proc-macro/empty-where-clause.rs b/src/test/ui/proc-macro/empty-where-clause.rs new file mode 100644 index 0000000000000..719555c092a78 --- /dev/null +++ b/src/test/ui/proc-macro/empty-where-clause.rs @@ -0,0 +1,18 @@ +// aux-build:test-macros.rs + +extern crate test_macros; +use test_macros::recollect_attr; + +#[recollect_attr] +struct FieldStruct where { + field: MissingType1 //~ ERROR cannot find +} + +#[recollect_attr] +struct TupleStruct(MissingType2) where; //~ ERROR cannot find + +enum MyEnum where { + Variant(MissingType3) //~ ERROR cannot find +} + +fn main() {} diff --git a/src/test/ui/proc-macro/empty-where-clause.stderr b/src/test/ui/proc-macro/empty-where-clause.stderr new file mode 100644 index 0000000000000..192a2b30f0dcd --- /dev/null +++ b/src/test/ui/proc-macro/empty-where-clause.stderr @@ -0,0 +1,21 @@ +error[E0412]: cannot find type `MissingType1` in this scope + --> $DIR/empty-where-clause.rs:8:12 + | +LL | field: MissingType1 + | ^^^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `MissingType2` in this scope + --> $DIR/empty-where-clause.rs:12:20 + | +LL | struct TupleStruct(MissingType2) where; + | ^^^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `MissingType3` in this scope + --> $DIR/empty-where-clause.rs:15:13 + | +LL | Variant(MissingType3) + | ^^^^^^^^^^^^ not found in this scope + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0412`. 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/export-macro.stderr b/src/test/ui/proc-macro/export-macro.stderr index bc64caa07f972..36a6a9bb3e72b 100644 --- a/src/test/ui/proc-macro/export-macro.stderr +++ b/src/test/ui/proc-macro/export-macro.stderr @@ -1,10 +1,8 @@ error: cannot export macro_rules! macros from a `proc-macro` crate type currently --> $DIR/export-macro.rs:9:1 | -LL | / macro_rules! foo { -LL | | ($e:expr) => ($e) -LL | | } - | |_^ +LL | macro_rules! foo { + | ^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/proc-macro/exports.stderr b/src/test/ui/proc-macro/exports.stderr index 0ecbdf98dd31c..7b23d08f2a8a5 100644 --- a/src/test/ui/proc-macro/exports.stderr +++ b/src/test/ui/proc-macro/exports.stderr @@ -2,7 +2,7 @@ error: `proc-macro` crate types currently cannot export any items other than fun --> $DIR/exports.rs:7:1 | LL | pub fn a() {} - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^ error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` --> $DIR/exports.rs:8:1 @@ -14,13 +14,13 @@ error: `proc-macro` crate types currently cannot export any items other than fun --> $DIR/exports.rs:9:1 | LL | pub enum C {} - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^ error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` --> $DIR/exports.rs:10:1 | LL | pub mod d {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to 4 previous errors 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/input-interpolated.rs b/src/test/ui/proc-macro/input-interpolated.rs new file mode 100644 index 0000000000000..b57ce99b13841 --- /dev/null +++ b/src/test/ui/proc-macro/input-interpolated.rs @@ -0,0 +1,25 @@ +// Check what token streams proc macros see when interpolated tokens are passed to them as input. + +// check-pass +// aux-build:test-macros.rs + +#[macro_use] +extern crate test_macros; + +macro_rules! pass_ident { + ($i:ident) => { + fn f() { + print_bang!($i); + } + + #[print_attr] + const $i: u8 = 0; + + #[derive(Print)] + struct $i {} + }; +} + +pass_ident!(A); + +fn main() {} diff --git a/src/test/ui/proc-macro/input-interpolated.stdout b/src/test/ui/proc-macro/input-interpolated.stdout new file mode 100644 index 0000000000000..7529db3bd06f8 --- /dev/null +++ b/src/test/ui/proc-macro/input-interpolated.stdout @@ -0,0 +1,69 @@ +PRINT-BANG INPUT (DISPLAY): A +PRINT-BANG RE-COLLECTED (DISPLAY): A +PRINT-BANG INPUT (DEBUG): TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "A", + span: #0 bytes(402..403), + }, + ], + span: #3 bytes(269..271), + }, +] +PRINT-ATTR INPUT (DISPLAY): const A: u8 = 0; +PRINT-ATTR RE-COLLECTED (DISPLAY): const A : u8 = 0 ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "const", + span: #0 bytes(0..0), + }, + Ident { + ident: "A", + span: #0 bytes(0..0), + }, + Punct { + ch: ':', + spacing: Alone, + span: #0 bytes(0..0), + }, + Ident { + ident: "u8", + span: #0 bytes(0..0), + }, + Punct { + ch: '=', + spacing: Alone, + span: #0 bytes(0..0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: #0 bytes(0..0), + }, + Punct { + ch: ';', + spacing: Alone, + span: #0 bytes(0..0), + }, +] +PRINT-DERIVE INPUT (DISPLAY): struct A { +} +PRINT-DERIVE RE-COLLECTED (DISPLAY): struct A { } +PRINT-DERIVE INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: #0 bytes(0..0), + }, + Ident { + ident: "A", + span: #0 bytes(0..0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: #0 bytes(0..0), + }, +] diff --git a/src/test/ui/proc-macro/invalid-punct-ident-1.rs b/src/test/ui/proc-macro/invalid-punct-ident-1.rs index 9de57da5af434..3f78dea917b19 100644 --- a/src/test/ui/proc-macro/invalid-punct-ident-1.rs +++ b/src/test/ui/proc-macro/invalid-punct-ident-1.rs @@ -14,3 +14,5 @@ extern crate invalid_punct_ident; invalid_punct!(); //~ ERROR proc macro panicked + +fn main() {} diff --git a/src/test/ui/proc-macro/invalid-punct-ident-2.rs b/src/test/ui/proc-macro/invalid-punct-ident-2.rs index 79f72324b1d89..4e89e80ae7c41 100644 --- a/src/test/ui/proc-macro/invalid-punct-ident-2.rs +++ b/src/test/ui/proc-macro/invalid-punct-ident-2.rs @@ -14,3 +14,5 @@ extern crate invalid_punct_ident; invalid_ident!(); //~ ERROR proc macro panicked + +fn main() {} diff --git a/src/test/ui/proc-macro/invalid-punct-ident-3.rs b/src/test/ui/proc-macro/invalid-punct-ident-3.rs index d01e9b699cac5..8d8ce8f932e71 100644 --- a/src/test/ui/proc-macro/invalid-punct-ident-3.rs +++ b/src/test/ui/proc-macro/invalid-punct-ident-3.rs @@ -14,3 +14,5 @@ extern crate invalid_punct_ident; invalid_raw_ident!(); //~ ERROR proc macro panicked + +fn main() {} diff --git a/src/test/ui/proc-macro/invalid-punct-ident-4.rs b/src/test/ui/proc-macro/invalid-punct-ident-4.rs index e50f24deae343..59b347dac679c 100644 --- a/src/test/ui/proc-macro/invalid-punct-ident-4.rs +++ b/src/test/ui/proc-macro/invalid-punct-ident-4.rs @@ -3,5 +3,10 @@ #[macro_use] extern crate invalid_punct_ident; -lexer_failure!(); //~ ERROR proc macro panicked - //~| ERROR unexpected closing delimiter: `)` +lexer_failure!(); +//~^ ERROR proc macro panicked +//~| ERROR unexpected closing delimiter: `)` + +fn main() { + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/proc-macro/invalid-punct-ident-4.stderr b/src/test/ui/proc-macro/invalid-punct-ident-4.stderr index fe3e55b31be5d..3b357aecea864 100644 --- a/src/test/ui/proc-macro/invalid-punct-ident-4.stderr +++ b/src/test/ui/proc-macro/invalid-punct-ident-4.stderr @@ -12,5 +12,14 @@ error: proc macro panicked LL | lexer_failure!(); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/invalid-punct-ident-4.rs:11: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/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/issue-36935.rs b/src/test/ui/proc-macro/issue-36935.rs index f809592d5f449..5c43a564c00c2 100644 --- a/src/test/ui/proc-macro/issue-36935.rs +++ b/src/test/ui/proc-macro/issue-36935.rs @@ -5,6 +5,7 @@ extern crate test_macros; #[derive(Identity, Panic)] //~ ERROR proc-macro derive panicked struct Baz { + //~^ ERROR the name `Baz` is defined multiple times a: i32, b: i32, } diff --git a/src/test/ui/proc-macro/issue-36935.stderr b/src/test/ui/proc-macro/issue-36935.stderr index da4366eb668d6..2b2e28fdb2fda 100644 --- a/src/test/ui/proc-macro/issue-36935.stderr +++ b/src/test/ui/proc-macro/issue-36935.stderr @@ -6,5 +6,17 @@ LL | #[derive(Identity, Panic)] | = help: message: panic-derive -error: aborting due to previous error +error[E0428]: the name `Baz` is defined multiple times + --> $DIR/issue-36935.rs:7:1 + | +LL | struct Baz { + | ^^^^^^^^^^ + | | + | `Baz` redefined here + | previous definition of the type `Baz` here + | + = note: `Baz` must be defined only once in the type namespace of this module + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0428`. diff --git a/src/test/ui/proc-macro/issue-50493.stderr b/src/test/ui/proc-macro/issue-50493.stderr index 7997786b50bc9..e378a56713400 100644 --- a/src/test/ui/proc-macro/issue-50493.stderr +++ b/src/test/ui/proc-macro/issue-50493.stderr @@ -8,7 +8,7 @@ error[E0616]: field `field` of struct `Restricted` is private --> $DIR/issue-50493.rs:6:10 | LL | #[derive(Derive)] - | ^^^^^^ + | ^^^^^^ private field | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) @@ -16,7 +16,7 @@ error[E0616]: field `field` of struct `Restricted` is private --> $DIR/issue-50493.rs:6:10 | LL | #[derive(Derive)] - | ^^^^^^ + | ^^^^^^ private field | = 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/proc-macro/keep-expr-tokens.rs b/src/test/ui/proc-macro/keep-expr-tokens.rs new file mode 100644 index 0000000000000..888785363cfe6 --- /dev/null +++ b/src/test/ui/proc-macro/keep-expr-tokens.rs @@ -0,0 +1,15 @@ +// aux-build:test-macros.rs + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +extern crate test_macros; + +use test_macros::recollect_attr; + +fn main() { + #[test_macros::recollect_attr] + for item in missing_fn() {} //~ ERROR cannot find + + (#[recollect_attr] #[recollect_attr] ((#[recollect_attr] bad))); //~ ERROR cannot +} diff --git a/src/test/ui/proc-macro/keep-expr-tokens.stderr b/src/test/ui/proc-macro/keep-expr-tokens.stderr new file mode 100644 index 0000000000000..2be8c0184da1c --- /dev/null +++ b/src/test/ui/proc-macro/keep-expr-tokens.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find function `missing_fn` in this scope + --> $DIR/keep-expr-tokens.rs:12:17 + | +LL | for item in missing_fn() {} + | ^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find value `bad` in this scope + --> $DIR/keep-expr-tokens.rs:14:62 + | +LL | (#[recollect_attr] #[recollect_attr] ((#[recollect_attr] bad))); + | ^^^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. 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-rules-derive.rs b/src/test/ui/proc-macro/macro-rules-derive.rs new file mode 100644 index 0000000000000..5b4d577a1acc4 --- /dev/null +++ b/src/test/ui/proc-macro/macro-rules-derive.rs @@ -0,0 +1,20 @@ +// aux-build:first-second.rs +// FIXME: The spans here are bad, see PR #73084 + +extern crate first_second; +use first_second::*; + +macro_rules! produce_it { + ($name:ident) => { + #[first] //~ ERROR cannot find type + struct $name { + field: MissingType + } + } +} + +produce_it!(MyName); + +fn main() { + println!("Hello, world!"); +} diff --git a/src/test/ui/proc-macro/macro-rules-derive.stderr b/src/test/ui/proc-macro/macro-rules-derive.stderr new file mode 100644 index 0000000000000..4b72d29fe8ae0 --- /dev/null +++ b/src/test/ui/proc-macro/macro-rules-derive.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `MissingType` in this scope + --> $DIR/macro-rules-derive.rs:9:9 + | +LL | #[first] + | ^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/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..6244ffc47a64b 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,22 +15,18 @@ 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 - | -LL | use ItemUse; - | error: aborting due to 4 previous errors 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/non-root.stderr b/src/test/ui/proc-macro/non-root.stderr index 8f84ddeeddb20..90f94b677e90f 100644 --- a/src/test/ui/proc-macro/non-root.stderr +++ b/src/test/ui/proc-macro/non-root.stderr @@ -2,7 +2,7 @@ error: functions tagged with `#[proc_macro]` must currently reside in the root o --> $DIR/non-root.rs:11:5 | LL | pub fn foo(arg: TokenStream) -> TokenStream { arg } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/proc-macro/out-of-line-mod.rs b/src/test/ui/proc-macro/out-of-line-mod.rs new file mode 100644 index 0000000000000..658ed6c18e05a --- /dev/null +++ b/src/test/ui/proc-macro/out-of-line-mod.rs @@ -0,0 +1,13 @@ +// Out-of-line module is found on the filesystem if passed through a proc macro (issue #58818). + +// check-pass +// aux-build:test-macros.rs + +#[macro_use] +extern crate test_macros; + +mod outer { + identity! { mod inner; } +} + +fn main() {} 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/pub-at-crate-root.stderr b/src/test/ui/proc-macro/pub-at-crate-root.stderr index 3b69b7875bde0..2e7536a0c4f09 100644 --- a/src/test/ui/proc-macro/pub-at-crate-root.stderr +++ b/src/test/ui/proc-macro/pub-at-crate-root.stderr @@ -1,32 +1,20 @@ error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]` --> $DIR/pub-at-crate-root.rs:8:1 | -LL | / pub mod a { -LL | | use proc_macro::TokenStream; -LL | | -LL | | #[proc_macro_derive(B)] -... | -LL | | } -LL | | } - | |_^ +LL | pub mod a { + | ^^^^^^^^^ error: functions tagged with `#[proc_macro_derive]` must currently reside in the root of the crate --> $DIR/pub-at-crate-root.rs:12:5 | -LL | / pub fn bar(a: TokenStream) -> TokenStream { -LL | | -LL | | a -LL | | } - | |_____^ +LL | pub fn bar(a: TokenStream) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: functions tagged with `#[proc_macro_derive]` must be `pub` --> $DIR/pub-at-crate-root.rs:19:1 | -LL | / fn bar(a: proc_macro::TokenStream) -> proc_macro::TokenStream { -LL | | -LL | | a -LL | | } - | |_^ +LL | fn bar(a: proc_macro::TokenStream) -> proc_macro::TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors 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 73a6ab1cfb910..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,47 +10,43 @@ 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 { -LL | | () => {} -LL | | } - | |_- similarly named macro `attr_proc_mac` defined here +LL | macro_rules! attr_proc_mac { + | -------------------------- similarly named macro `attr_proc_mac` defined here ... -LL | attr_proc_macra!(); - | ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `attr_proc_mac` +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 { -LL | | () => {} -LL | | } - | |_- similarly named macro `FooWithLongNam` defined here +LL | macro_rules! FooWithLongNam { + | --------------------------- similarly named macro `FooWithLongNam` defined here ... -LL | FooWithLongNama!(); - | ^^^^^^^^^^^^^^^ help: a macro with a similar name exists: `FooWithLongNam` +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` @@ -61,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` @@ -72,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` @@ -83,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` @@ -94,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` @@ -111,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` @@ -122,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/visibility-path.rs b/src/test/ui/proc-macro/visibility-path.rs new file mode 100644 index 0000000000000..a73430db2c19a --- /dev/null +++ b/src/test/ui/proc-macro/visibility-path.rs @@ -0,0 +1,25 @@ +// Proc macro defined with `pub(path)` doesn't ICEs due to resolving the `path` (issue #68921). + +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro] +pub(self) fn outer(input: TokenStream) -> TokenStream { + //~^ ERROR functions tagged with `#[proc_macro]` must be `pub` + input +} + +mod m { + use proc_macro::*; + + #[proc_macro] + pub(super) fn inner(input: TokenStream) -> TokenStream { + //~^ ERROR functions tagged with `#[proc_macro]` must currently reside in the root + input + } +} diff --git a/src/test/ui/proc-macro/visibility-path.stderr b/src/test/ui/proc-macro/visibility-path.stderr new file mode 100644 index 0000000000000..1a73cc1963f9c --- /dev/null +++ b/src/test/ui/proc-macro/visibility-path.stderr @@ -0,0 +1,14 @@ +error: functions tagged with `#[proc_macro]` must be `pub` + --> $DIR/visibility-path.rs:12:1 + | +LL | pub(self) fn outer(input: TokenStream) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions tagged with `#[proc_macro]` must currently reside in the root of the crate + --> $DIR/visibility-path.rs:21:5 + | +LL | pub(super) fn inner(input: TokenStream) -> TokenStream { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/proc-macro/weird-hygiene.rs b/src/test/ui/proc-macro/weird-hygiene.rs new file mode 100644 index 0000000000000..3f48191b5b26e --- /dev/null +++ b/src/test/ui/proc-macro/weird-hygiene.rs @@ -0,0 +1,48 @@ +// aux-build:weird-hygiene.rs +// check-pass +// FIXME: This should actually error, see PR #73084 + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +extern crate weird_hygiene; +use weird_hygiene::*; + +macro_rules! other { + ($tokens:expr) => { + macro_rules! call_it { + ($outer_ident:ident) => { + macro_rules! inner { + () => { + $outer_ident; + } + } + } + } + + #[derive(WeirdDerive)] + enum MyEnum { + Value = (stringify!($tokens + hidden_ident), 1).1 + } + + inner!(); + } +} + +macro_rules! invoke_it { + ($token:expr) => { + #[recollect_attr] { + $token; + hidden_ident + } + } +} + +fn main() { + // `other` and `invoke_it` are both macro_rules! macros, + // so it should be impossible for them to ever see `hidden_ident`, + // even if they invoke a proc macro. + let hidden_ident = "Hello1"; + other!(50); + invoke_it!(25); +} 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 7911701946cd3..64d8f685637fb 100644 --- a/src/test/ui/question-mark-type-infer.stderr +++ b/src/test/ui/question-mark-type-infer.stderr @@ -2,12 +2,13 @@ error[E0284]: type annotations needed --> $DIR/question-mark-type-infer.rs:12:21 | LL | l.iter().map(f).collect()? - | ^^^^^^^ - | | - | cannot infer type - | help: consider specifying the type argument in the method call: `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::()? + | ^^^^^ error: aborting due to previous error 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/issue-73553-misinterp-range-literal.rs b/src/test/ui/range/issue-73553-misinterp-range-literal.rs new file mode 100644 index 0000000000000..e65dba0a03821 --- /dev/null +++ b/src/test/ui/range/issue-73553-misinterp-range-literal.rs @@ -0,0 +1,16 @@ +type Range = std::ops::Range; + +fn demo(r: &Range) { + println!("{:?}", r); +} + +fn tell(x: usize) -> usize { + x +} + +fn main() { + demo(tell(1)..tell(10)); + //~^ ERROR mismatched types + demo(1..10); + //~^ ERROR mismatched types +} diff --git a/src/test/ui/range/issue-73553-misinterp-range-literal.stderr b/src/test/ui/range/issue-73553-misinterp-range-literal.stderr new file mode 100644 index 0000000000000..5167b87fd27b8 --- /dev/null +++ b/src/test/ui/range/issue-73553-misinterp-range-literal.stderr @@ -0,0 +1,27 @@ +error[E0308]: mismatched types + --> $DIR/issue-73553-misinterp-range-literal.rs:12:10 + | +LL | demo(tell(1)..tell(10)); + | ^^^^^^^^^^^^^^^^^ + | | + | expected reference, found struct `std::ops::Range` + | help: consider borrowing here: `&(tell(1)..tell(10))` + | + = note: expected reference `&std::ops::Range` + found struct `std::ops::Range` + +error[E0308]: mismatched types + --> $DIR/issue-73553-misinterp-range-literal.rs:14:10 + | +LL | demo(1..10); + | ^^^^^ + | | + | expected reference, found struct `std::ops::Range` + | help: consider borrowing here: `&(1..10)` + | + = note: expected reference `&std::ops::Range` + found struct `std::ops::Range<{integer}>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. 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/reachable/unreachable-variant.stderr b/src/test/ui/reachable/unreachable-variant.stderr index c2e1d774e28ac..6c27a2756f7fb 100644 --- a/src/test/ui/reachable/unreachable-variant.stderr +++ b/src/test/ui/reachable/unreachable-variant.stderr @@ -2,7 +2,7 @@ error[E0603]: module `super_sekrit` is private --> $DIR/unreachable-variant.rs:6:21 | LL | let _x = other::super_sekrit::sooper_sekrit::baz; - | ^^^^^^^^^^^^ this module is private + | ^^^^^^^^^^^^ private module | note: the module `super_sekrit` is defined here --> $DIR/auxiliary/unreachable_variant.rs:1:1 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/issue-26548-recursion-via-normalize.rs b/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs index 6ee8c0fcfdade..4d1cd059c27b5 100644 --- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs +++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.rs @@ -4,10 +4,15 @@ // build-fail -trait Mirror { type It: ?Sized; } -impl Mirror for T { type It = Self; } +trait Mirror { + type It: ?Sized; +} +impl Mirror for T { + type It = Self; +} struct S(Option<::It>); -fn main() { //~ NOTE cycle used when processing `main` +fn main() { + //~^ NOTE cycle used when optimizing MIR for `main` let _s = S(None); } diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr index 6a83f91ce5b32..be55890c08c88 100644 --- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr +++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr @@ -2,8 +2,8 @@ error[E0391]: cycle detected when computing layout of `std::option::Option` | = note: ...which requires computing layout of `S`... = note: ...which again requires computing layout of `std::option::Option`, completing the cycle -note: cycle used when processing `main` - --> $DIR/issue-26548-recursion-via-normalize.rs:11:1 +note: cycle used when optimizing MIR for `main` + --> $DIR/issue-26548-recursion-via-normalize.rs:15:1 | LL | fn main() { | ^^^^^^^^^ diff --git a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr index de6df4cd0268c..0552847c48ca9 100644 --- a/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr +++ b/src/test/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr @@ -1,4 +1,22 @@ error: reached the recursion limit while instantiating `std::intrinsics::drop_in_place::> - shim(Some(S))` + --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL + | +LL | / pub unsafe fn drop_in_place(to_drop: *mut T) { +LL | | // Code here does not matter - this is replaced by the +LL | | // real drop glue by the compiler. +LL | | drop_in_place(to_drop) +LL | | } + | |_^ + | +note: `std::intrinsics::drop_in_place` defined here + --> $SRC_DIR/libcore/ptr/mod.rs:LL:COL + | +LL | / pub unsafe fn drop_in_place(to_drop: *mut T) { +LL | | // Code here does not matter - this is replaced by the +LL | | // real drop glue by the compiler. +LL | | drop_in_place(to_drop) +LL | | } + | |_^ error: aborting due to previous error diff --git a/src/test/ui/recursion/recursion.rs b/src/test/ui/recursion/recursion.rs index bf1eaef367d69..373cc17d0e0fe 100644 --- a/src/test/ui/recursion/recursion.rs +++ b/src/test/ui/recursion/recursion.rs @@ -12,11 +12,10 @@ impl Dot for Cons { self.head * other.head + self.tail.dot(other.tail) } } -fn test (n:isize, i:isize, first:T, second:T) ->isize { //~ ERROR recursion limit +fn test (n:isize, i:isize, first:T, second:T) ->isize { match n { 0 => {first.dot(second)} - // FIXME(#4287) Error message should be here. It should be - // a type error to instantiate `test` at a type other than T. _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} + //~^ ERROR recursion limit } } pub fn main() { diff --git a/src/test/ui/recursion/recursion.stderr b/src/test/ui/recursion/recursion.stderr index 1a65b0e84f6a3..0c0eba68c83b4 100644 --- a/src/test/ui/recursion/recursion.stderr +++ b/src/test/ui/recursion/recursion.stderr @@ -1,11 +1,16 @@ error: reached the recursion limit while instantiating `test::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/recursion.rs:17:11 + | +LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `test` defined here --> $DIR/recursion.rs:15:1 | LL | / fn test (n:isize, i:isize, first:T, second:T) ->isize { LL | | match n { 0 => {first.dot(second)} -LL | | // FIXME(#4287) Error message should be here. It should be -LL | | // a type error to instantiate `test` at a type other than T. LL | | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} +LL | | LL | | } LL | | } | |_^ diff --git a/src/test/ui/recursion/recursive-enum.stderr b/src/test/ui/recursion/recursive-enum.stderr index e4674b57a6d21..ab4709d8e709e 100644 --- a/src/test/ui/recursion/recursive-enum.stderr +++ b/src/test/ui/recursion/recursive-enum.stderr @@ -6,7 +6,10 @@ LL | enum List { Cons(T, List), Nil } | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `List` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `List` representable + | +LL | enum List { Cons(T, Box>), Nil } + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/recursion/recursive-requirements.stderr b/src/test/ui/recursion/recursive-requirements.stderr index 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-static-definition.stderr b/src/test/ui/recursion/recursive-static-definition.stderr index b724c261a7c3c..093606e100cb3 100644 --- a/src/test/ui/recursion/recursive-static-definition.stderr +++ b/src/test/ui/recursion/recursive-static-definition.stderr @@ -1,8 +1,8 @@ error[E0391]: cycle detected when const-evaluating `FOO` - --> $DIR/recursive-static-definition.rs:1:23 + --> $DIR/recursive-static-definition.rs:1:1 | LL | pub static FOO: u32 = FOO; - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: ...which requires const-evaluating `FOO`... --> $DIR/recursive-static-definition.rs:1:1 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 aa23aed4b425a..75e8ae264e79d 100644 --- a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr +++ b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr @@ -3,9 +3,15 @@ error[E0005]: refutable pattern in local binding: `Err(_)` not covered | LL | let Ok(x) = res; | ^^^^^ pattern `Err(_)` not covered + | + ::: $SRC_DIR/libcore/result.rs:LL:COL + | +LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), + | --- not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `std::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-fn-subtyping-return-static-fail.nll.stderr b/src/test/ui/regions-fn-subtyping-return-static-fail.nll.stderr new file mode 100644 index 0000000000000..d762f55f9d5f1 --- /dev/null +++ b/src/test/ui/regions-fn-subtyping-return-static-fail.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/regions-fn-subtyping-return-static-fail.rs:48:5 + | +LL | want_G(baz); + | ^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/regions-fn-subtyping-return-static-fail.rs b/src/test/ui/regions-fn-subtyping-return-static-fail.rs index 2dd0c9796e258..539221b5a046c 100644 --- a/src/test/ui/regions-fn-subtyping-return-static-fail.rs +++ b/src/test/ui/regions-fn-subtyping-return-static-fail.rs @@ -13,11 +13,11 @@ struct S; // Given 'cx, return 'cx type F = for<'cx> fn(&'cx S) -> &'cx S; -fn want_F(f: F) { } +fn want_F(f: F) {} // Given anything, return 'static type G = for<'cx> fn(&'cx S) -> &'static S; -fn want_G(f: G) { } +fn want_G(f: G) {} // Should meet both. fn foo(x: &S) -> &'static S { @@ -25,7 +25,7 @@ fn foo(x: &S) -> &'static S { } // Should meet both. -fn bar<'a,'b>(x: &'a S) -> &'b S { +fn bar<'a, 'b>(x: &'a S) -> &'b S { panic!() } @@ -37,7 +37,7 @@ fn baz(x: &S) -> &S { fn supply_F() { want_F(foo); - want_F(bar); //~ ERROR mismatched types + want_F(bar); want_F(baz); } @@ -48,5 +48,4 @@ fn supply_G() { want_G(baz); //~ ERROR mismatched types } -pub fn main() { -} +pub fn main() {} diff --git a/src/test/ui/regions-fn-subtyping-return-static-fail.stderr b/src/test/ui/regions-fn-subtyping-return-static-fail.stderr index 27704b3e0a8c7..c9ce936c7d43f 100644 --- a/src/test/ui/regions-fn-subtyping-return-static-fail.stderr +++ b/src/test/ui/regions-fn-subtyping-return-static-fail.stderr @@ -1,21 +1,12 @@ -error[E0308]: mismatched types - --> $DIR/regions-fn-subtyping-return-static-fail.rs:40:12 - | -LL | want_F(bar); - | ^^^ expected concrete lifetime, found bound lifetime parameter 'cx - | - = note: expected fn pointer `for<'cx> fn(&'cx S) -> &'cx S` - found fn item `for<'a> fn(&'a S) -> &S {bar::<'_>}` - error[E0308]: mismatched types --> $DIR/regions-fn-subtyping-return-static-fail.rs:48:12 | LL | want_G(baz); - | ^^^ expected concrete lifetime, found bound lifetime parameter 'cx + | ^^^ one type is more general than the other | = note: expected fn pointer `for<'cx> fn(&'cx S) -> &'static S` - found fn item `for<'r> fn(&'r S) -> &'r S {baz}` + found fn pointer `for<'r> fn(&'r S) -> &'r S` -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0308`. 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/region-lifetime-bounds-on-fns-where-clause.nll.stderr b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr index c3e8789a903b1..695f5506d5e38 100644 --- a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr +++ b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.nll.stderr @@ -1,14 +1,40 @@ -error[E0308]: mismatched types - --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:43 +error: lifetime may not live long enough + --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:8:5 + | +LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | // Illegal now because there is no `'b:'a` declaration. +LL | *x = *y; + | ^^^^^^^ assignment requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error: lifetime may not live long enough + --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:14:5 + | +LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | a(x, y); + | ^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error: higher-ranked subtype error + --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:12 | LL | let _: fn(&mut &isize, &mut &isize) = a; - | ---------------------------- ^ expected concrete lifetime, found bound lifetime parameter - | | - | expected due to this + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:12 | - = note: expected fn pointer `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)` - found fn item `for<'r, 's> fn(&'r mut &isize, &'s mut &isize) {a::<'_, '_>}` +LL | let _: fn(&mut &isize, &mut &isize) = a; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr index 159d32b50b03c..b83e07663faba 100644 --- a/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr +++ b/src/test/ui/regions/region-lifetime-bounds-on-fns-where-clause.stderr @@ -20,12 +20,10 @@ error[E0308]: mismatched types --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:43 | LL | let _: fn(&mut &isize, &mut &isize) = a; - | ---------------------------- ^ expected concrete lifetime, found bound lifetime parameter - | | - | expected due to this + | ^ one type is more general than the other | = note: expected fn pointer `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)` - found fn item `for<'r, 's> fn(&'r mut &isize, &'s mut &isize) {a::<'_, '_>}` + found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)` error: aborting due to 3 previous errors diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr index 2aadd8f4f8c7e..a28f7aa3e08ca 100644 --- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr +++ b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.nll.stderr @@ -1,14 +1,46 @@ -error[E0308]: mismatched types - --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:56 +error: lifetime may not live long enough + --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:9:5 + | +LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | // Illegal now because there is no `'b:'a` declaration. +LL | *x = *y; + | ^^^^^^^ assignment requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error: lifetime may not live long enough + --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:5 + | +LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | a(x, y, z); + | ^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error: higher-ranked subtype error + --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12 | LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; - | ----------------------------------------- ^ expected concrete lifetime, found bound lifetime parameter - | | - | expected due to this + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12 | - = note: expected fn pointer `for<'r, 's, 't0, 't1, 't2, 't3> fn(&'r mut &'s isize, &'t0 mut &'t1 isize, &'t2 mut &'t3 isize)` - found fn item `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize) {a::<'_, '_, '_>}` +LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12 + | +LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr index dda6129e19536..c93f2890f1110 100644 --- a/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr +++ b/src/test/ui/regions/region-multiple-lifetime-bounds-on-fns-where-clause.stderr @@ -31,12 +31,10 @@ error[E0308]: mismatched types --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:56 | LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; - | ----------------------------------------- ^ expected concrete lifetime, found bound lifetime parameter - | | - | expected due to this + | ^ one type is more general than the other | = note: expected fn pointer `for<'r, 's, 't0, 't1, 't2, 't3> fn(&'r mut &'s isize, &'t0 mut &'t1 isize, &'t2 mut &'t3 isize)` - found fn item `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize) {a::<'_, '_, '_>}` + found fn pointer `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize)` error: aborting due to 4 previous errors diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr index 767853d81480e..7e8f78067e08a 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr @@ -1,30 +1,30 @@ -error[E0621]: explicit lifetime required in the type of `v` +error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:8:12 | LL | fn a(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` + | - let's call the lifetime of this reference `'1` LL | let x: Box = Box::new(v); - | ^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required + | ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static` -error[E0621]: explicit lifetime required in the type of `v` - --> $DIR/region-object-lifetime-in-coercion.rs:14:5 +error: lifetime may not live long enough + --> $DIR/region-object-lifetime-in-coercion.rs:13:5 | LL | fn b(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` + | - let's call the lifetime of this reference `'1` LL | Box::new(v) - | ^^^^^^^^^^^ lifetime `'static` required + | ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` -error[E0621]: explicit lifetime required in the type of `v` - --> $DIR/region-object-lifetime-in-coercion.rs:21:5 +error: lifetime may not live long enough + --> $DIR/region-object-lifetime-in-coercion.rs:19:5 | LL | fn c(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` + | - let's call the lifetime of this reference `'1` ... LL | Box::new(v) - | ^^^^^^^^^^^ lifetime `'static` required + | ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` error: lifetime may not live long enough - --> $DIR/region-object-lifetime-in-coercion.rs:26:5 + --> $DIR/region-object-lifetime-in-coercion.rs:23:5 | LL | fn d<'a,'b>(v: &'a [u8]) -> Box { | -- -- lifetime `'b` defined here @@ -37,4 +37,3 @@ LL | Box::new(v) error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0621`. diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.rs b/src/test/ui/regions/region-object-lifetime-in-coercion.rs index 2dc67599913a6..5d199149c39b8 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.rs +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.rs @@ -5,26 +5,22 @@ trait Foo {} impl<'a> Foo for &'a [u8] {} fn a(v: &[u8]) -> Box { - let x: Box = Box::new(v); - //~^ ERROR explicit lifetime required in the type of `v` [E0621] + let x: Box = Box::new(v); //~ ERROR cannot infer an appropriate lifetime x } fn b(v: &[u8]) -> Box { - Box::new(v) - //~^ ERROR explicit lifetime required in the type of `v` [E0621] + Box::new(v) //~ ERROR cannot infer an appropriate lifetime } fn c(v: &[u8]) -> Box { // same as previous case due to RFC 599 - Box::new(v) - //~^ ERROR explicit lifetime required in the type of `v` [E0621] + Box::new(v) //~ ERROR cannot infer an appropriate lifetime } fn d<'a,'b>(v: &'a [u8]) -> Box { - Box::new(v) - //~^ ERROR cannot infer an appropriate lifetime due to conflicting + Box::new(v) //~ ERROR cannot infer an appropriate lifetime due to conflicting } fn e<'a:'b,'b>(v: &'a [u8]) -> Box { diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr index e889651647034..7f5a3a47976c7 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.stderr @@ -1,60 +1,83 @@ -error[E0621]: explicit lifetime required in the type of `v` - --> $DIR/region-object-lifetime-in-coercion.rs:8:37 +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/region-object-lifetime-in-coercion.rs:8:46 | LL | fn a(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` + | ----- this data with an anonymous lifetime `'_`... LL | let x: Box = Box::new(v); - | ^^^^^^^^^^^ lifetime `'static` required + | ^ ...is captured here, requiring it to live as long as `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn a(v: &[u8]) -> Box { + | ^^ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn a(v: &'static [u8]) -> Box { + | ^^^^^^^^^^^^^ -error[E0621]: explicit lifetime required in the type of `v` - --> $DIR/region-object-lifetime-in-coercion.rs:14:5 +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/region-object-lifetime-in-coercion.rs:13:14 | LL | fn b(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` + | ----- this data with an anonymous lifetime `'_`... LL | Box::new(v) - | ^^^^^^^^^^^ lifetime `'static` required + | ^ ...is captured here, requiring it to live as long as `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn b(v: &[u8]) -> Box { + | ^^ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn b(v: &'static [u8]) -> Box { + | ^^^^^^^^^^^^^ -error[E0621]: explicit lifetime required in the type of `v` - --> $DIR/region-object-lifetime-in-coercion.rs:21:5 +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/region-object-lifetime-in-coercion.rs:19:14 | LL | fn c(v: &[u8]) -> Box { - | ----- help: add explicit lifetime `'static` to the type of `v`: `&'static [u8]` + | ----- this data with an anonymous lifetime `'_`... ... LL | Box::new(v) - | ^^^^^^^^^^^ lifetime `'static` required + | ^ ...is captured here, requiring it to live as long as `'static` + | +help: to declare that the trait object captures data from argument `v`, you can add an explicit `'_` lifetime bound + | +LL | fn c(v: &[u8]) -> Box { + | ^^^^ error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements - --> $DIR/region-object-lifetime-in-coercion.rs:26:14 + --> $DIR/region-object-lifetime-in-coercion.rs:23:14 | LL | Box::new(v) | ^ | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 25:6... - --> $DIR/region-object-lifetime-in-coercion.rs:25:6 +note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 22:6... + --> $DIR/region-object-lifetime-in-coercion.rs:22:6 | LL | fn d<'a,'b>(v: &'a [u8]) -> Box { | ^^ note: ...so that the expression is assignable - --> $DIR/region-object-lifetime-in-coercion.rs:26:14 + --> $DIR/region-object-lifetime-in-coercion.rs:23:14 | LL | Box::new(v) | ^ - = note: expected `&[u8]` - found `&'a [u8]` -note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 25:9... - --> $DIR/region-object-lifetime-in-coercion.rs:25:9 + = note: expected `&[u8]` + found `&'a [u8]` +note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 22:9... + --> $DIR/region-object-lifetime-in-coercion.rs:22:9 | LL | fn d<'a,'b>(v: &'a [u8]) -> Box { | ^^ note: ...so that the expression is assignable - --> $DIR/region-object-lifetime-in-coercion.rs:26:5 + --> $DIR/region-object-lifetime-in-coercion.rs:23:5 | LL | Box::new(v) | ^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn Foo + 'b)>` - found `std::boxed::Box` + = note: expected `std::boxed::Box<(dyn Foo + 'b)>` + found `std::boxed::Box` error: aborting due to 4 previous errors -Some errors have detailed explanations: E0495, E0621. +Some errors have detailed explanations: E0495, E0759. For more information about an error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-assoc-type-region-bound-in-trait-not-met.stderr b/src/test/ui/regions/regions-assoc-type-region-bound-in-trait-not-met.stderr index 865e967fba32e..c134b3b3ed554 100644 --- a/src/test/ui/regions/regions-assoc-type-region-bound-in-trait-not-met.stderr +++ b/src/test/ui/regions/regions-assoc-type-region-bound-in-trait-not-met.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | impl<'a> Foo<'static> for &'a i32 { | ^^^^^^^^^^^^ - = note: expected `Foo<'static>` - found `Foo<'static>` + = note: expected `Foo<'static>` + found `Foo<'static>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that the type `&i32` will meet its required lifetime bounds --> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:14:10 @@ -39,8 +39,8 @@ note: ...so that the types are compatible | LL | impl<'a,'b> Foo<'b> for &'a i64 { | ^^^^^^^ - = note: expected `Foo<'b>` - found `Foo<'_>` + = note: expected `Foo<'b>` + found `Foo<'_>` note: but, the lifetime must be valid for the lifetime `'b` as defined on the impl at 19:9... --> $DIR/regions-assoc-type-region-bound-in-trait-not-met.rs:19:9 | diff --git a/src/test/ui/regions/regions-assoc-type-static-bound-in-trait-not-met.stderr b/src/test/ui/regions/regions-assoc-type-static-bound-in-trait-not-met.stderr index 6a34871c07efd..ac8c55ccc8fd4 100644 --- a/src/test/ui/regions/regions-assoc-type-static-bound-in-trait-not-met.stderr +++ b/src/test/ui/regions/regions-assoc-type-static-bound-in-trait-not-met.stderr @@ -14,8 +14,8 @@ note: ...so that the types are compatible | LL | impl<'a> Foo for &'a i32 { | ^^^ - = note: expected `Foo` - found `Foo` + = note: expected `Foo` + found `Foo` = note: but, the lifetime must be valid for the static lifetime... note: ...so that the type `&i32` will meet its required lifetime bounds --> $DIR/regions-assoc-type-static-bound-in-trait-not-met.rs:9:10 diff --git a/src/test/ui/regions/regions-close-associated-type-into-object.stderr b/src/test/ui/regions/regions-close-associated-type-into-object.stderr index 2401f549a5604..9303e0f8e6643 100644 --- a/src/test/ui/regions/regions-close-associated-type-into-object.stderr +++ b/src/test/ui/regions/regions-close-associated-type-into-object.stderr @@ -5,11 +5,7 @@ LL | Box::new(item) | ^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `::Item: 'static`... -note: ...so that the type `::Item` will meet its required lifetime bounds - --> $DIR/regions-close-associated-type-into-object.rs:15:5 - | -LL | Box::new(item) - | ^^^^^^^^^^^^^^ + = note: ...so that the type `::Item` will meet its required lifetime bounds error[E0310]: the associated type `::Item` may not live long enough --> $DIR/regions-close-associated-type-into-object.rs:22:5 @@ -18,11 +14,7 @@ LL | Box::new(item) | ^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `::Item: 'static`... -note: ...so that the type `std::boxed::Box<::Item>` will meet its required lifetime bounds - --> $DIR/regions-close-associated-type-into-object.rs:22:5 - | -LL | Box::new(item) - | ^^^^^^^^^^^^^^ + = note: ...so that the type `std::boxed::Box<::Item>` will meet its required lifetime bounds error[E0309]: the associated type `::Item` may not live long enough --> $DIR/regions-close-associated-type-into-object.rs:28:5 @@ -31,11 +23,7 @@ LL | Box::new(item) | ^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `::Item: 'a`... -note: ...so that the type `::Item` will meet its required lifetime bounds - --> $DIR/regions-close-associated-type-into-object.rs:28:5 - | -LL | Box::new(item) - | ^^^^^^^^^^^^^^ + = note: ...so that the type `::Item` will meet its required lifetime bounds error[E0309]: the associated type `::Item` may not live long enough --> $DIR/regions-close-associated-type-into-object.rs:35:5 @@ -44,11 +32,7 @@ LL | Box::new(item) | ^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `::Item: 'a`... -note: ...so that the type `std::boxed::Box<::Item>` will meet its required lifetime bounds - --> $DIR/regions-close-associated-type-into-object.rs:35:5 - | -LL | Box::new(item) - | ^^^^^^^^^^^^^^ + = note: ...so that the type `std::boxed::Box<::Item>` will meet its required lifetime bounds error: aborting due to 4 previous errors diff --git a/src/test/ui/regions/regions-close-object-into-object-2.stderr b/src/test/ui/regions/regions-close-object-into-object-2.stderr index 28873ab807f8d..114e4052aae09 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.stderr @@ -1,28 +1,20 @@ -error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements +error[E0759]: cannot infer an appropriate lifetime --> $DIR/regions-close-object-into-object-2.rs:10:11 | +LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { + | ------------------ this data with lifetime `'a`... LL | box B(&*v) as Box - | ^^^ - | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 9:6... - --> $DIR/regions-close-object-into-object-2.rs:9:6 + | ^^^ ...is captured here, requiring it to live as long as `'static` | -LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { - | ^^ -note: ...so that the type `(dyn A + 'a)` is not borrowed for too long - --> $DIR/regions-close-object-into-object-2.rs:10:11 +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | -LL | box B(&*v) as Box - | ^^^ - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that the expression is assignable - --> $DIR/regions-close-object-into-object-2.rs:10:5 +LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { + | ^^ +help: alternatively, add an explicit `'static` bound to this reference | -LL | box B(&*v) as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn X + 'static)>` - found `std::boxed::Box` +LL | fn g<'a, T: 'static>(v: std::boxed::Box<(dyn A + 'static)>) -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/regions/regions-close-object-into-object-4.stderr b/src/test/ui/regions/regions-close-object-into-object-4.stderr index 449a5b5fdd4d6..850d81940791f 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.stderr @@ -1,28 +1,20 @@ -error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements +error[E0759]: cannot infer an appropriate lifetime --> $DIR/regions-close-object-into-object-4.rs:10:11 | +LL | fn i<'a, T, U>(v: Box+'a>) -> Box { + | ---------------- this data with lifetime `'a`... LL | box B(&*v) as Box - | ^^^ - | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 9:6... - --> $DIR/regions-close-object-into-object-4.rs:9:6 + | ^^^ ...is captured here, requiring it to live as long as `'static` | -LL | fn i<'a, T, U>(v: Box+'a>) -> Box { - | ^^ -note: ...so that the type `(dyn A + 'a)` is not borrowed for too long - --> $DIR/regions-close-object-into-object-4.rs:10:11 +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | -LL | box B(&*v) as Box - | ^^^ - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that the expression is assignable - --> $DIR/regions-close-object-into-object-4.rs:10:5 +LL | fn i<'a, T, U>(v: Box+'a>) -> Box { + | ^^ +help: alternatively, add an explicit `'static` bound to this reference | -LL | box B(&*v) as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn X + 'static)>` - found `std::boxed::Box` +LL | fn i<'a, T, U>(v: std::boxed::Box<(dyn A + 'static)>) -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/regions/regions-close-object-into-object-5.rs b/src/test/ui/regions/regions-close-object-into-object-5.rs index 2921a2bb398c3..ff35b9ada45cd 100644 --- a/src/test/ui/regions/regions-close-object-into-object-5.rs +++ b/src/test/ui/regions/regions-close-object-into-object-5.rs @@ -6,22 +6,21 @@ trait A fn get(&self) -> T { panic!() } } -struct B<'a, T:'a>(&'a (A+'a)); +struct B<'a, T: 'a>(&'a (A + 'a)); trait X { fn foo(&self) {} } impl<'a, T> X for B<'a, T> {} -fn f<'a, T, U>(v: Box+'static>) -> Box { +fn f<'a, T, U>(v: Box + 'static>) -> Box { // oh dear! box B(&*v) as Box - //~^ ERROR the parameter type `T` may not live long enough - //~| ERROR the parameter type `T` may not live long enough - //~| ERROR the parameter type `T` may not live long enough - //~| ERROR the parameter type `T` may not live long enough - //~| ERROR the parameter type `T` may not live long enough - //~| ERROR the parameter type `T` may not live long enough - //~| ERROR the parameter type `T` may not live long enough + //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough } fn main() {} diff --git a/src/test/ui/regions/regions-close-object-into-object-5.stderr b/src/test/ui/regions/regions-close-object-into-object-5.stderr index 14727000b2c24..e5a80cbd54758 100644 --- a/src/test/ui/regions/regions-close-object-into-object-5.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-5.stderr @@ -1,108 +1,57 @@ error[E0310]: the parameter type `T` may not live long enough --> $DIR/regions-close-object-into-object-5.rs:17:5 | -LL | fn f<'a, T, U>(v: Box+'static>) -> Box { +LL | fn f<'a, T, U>(v: Box + 'static>) -> Box { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | // oh dear! LL | box B(&*v) as Box - | ^^^^^^^^^^ - | -note: ...so that the type `B<'_, T>` will meet its required lifetime bounds - --> $DIR/regions-close-object-into-object-5.rs:17:5 - | -LL | box B(&*v) as Box - | ^^^^^^^^^^ - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/regions-close-object-into-object-5.rs:17:5 - | -LL | fn f<'a, T, U>(v: Box+'static>) -> Box { - | - help: consider adding an explicit lifetime bound...: `T: 'static` -LL | // oh dear! -LL | box B(&*v) as Box - | ^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that it can be closed over into an object - --> $DIR/regions-close-object-into-object-5.rs:17:5 - | -LL | box B(&*v) as Box - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ ...so that the type `B<'_, T>` will meet its required lifetime bounds error[E0310]: the parameter type `T` may not live long enough --> $DIR/regions-close-object-into-object-5.rs:17:9 | -LL | fn f<'a, T, U>(v: Box+'static>) -> Box { +LL | fn f<'a, T, U>(v: Box + 'static>) -> Box { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | // oh dear! LL | box B(&*v) as Box - | ^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-close-object-into-object-5.rs:17:9 - | -LL | box B(&*v) as Box - | ^ + | ^ ...so that the type `T` will meet its required lifetime bounds error[E0310]: the parameter type `T` may not live long enough --> $DIR/regions-close-object-into-object-5.rs:17:9 | -LL | fn f<'a, T, U>(v: Box+'static>) -> Box { +LL | fn f<'a, T, U>(v: Box + 'static>) -> Box { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | // oh dear! LL | box B(&*v) as Box - | ^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-close-object-into-object-5.rs:17:9 - | -LL | box B(&*v) as Box - | ^^^^^^ + | ^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0310]: the parameter type `T` may not live long enough --> $DIR/regions-close-object-into-object-5.rs:17:11 | -LL | fn f<'a, T, U>(v: Box+'static>) -> Box { +LL | fn f<'a, T, U>(v: Box + 'static>) -> Box { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | // oh dear! LL | box B(&*v) as Box - | ^^^ - | -note: ...so that the reference type `&dyn A` does not outlive the data it points at - --> $DIR/regions-close-object-into-object-5.rs:17:11 - | -LL | box B(&*v) as Box - | ^^^ + | ^^^ ...so that the reference type `&dyn A` does not outlive the data it points at error[E0310]: the parameter type `T` may not live long enough --> $DIR/regions-close-object-into-object-5.rs:17:11 | -LL | fn f<'a, T, U>(v: Box+'static>) -> Box { +LL | fn f<'a, T, U>(v: Box + 'static>) -> Box { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | // oh dear! LL | box B(&*v) as Box - | ^^^ - | -note: ...so that the type `(dyn A + 'static)` is not borrowed for too long - --> $DIR/regions-close-object-into-object-5.rs:17:11 - | -LL | box B(&*v) as Box - | ^^^ + | ^^^ ...so that the type `(dyn A + 'static)` is not borrowed for too long error[E0310]: the parameter type `T` may not live long enough --> $DIR/regions-close-object-into-object-5.rs:17:11 | -LL | fn f<'a, T, U>(v: Box+'static>) -> Box { +LL | fn f<'a, T, U>(v: Box + 'static>) -> Box { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | // oh dear! LL | box B(&*v) as Box - | ^^^ - | -note: ...so that the type `(dyn A + 'static)` is not borrowed for too long - --> $DIR/regions-close-object-into-object-5.rs:17:11 - | -LL | box B(&*v) as Box - | ^^^ + | ^^^ ...so that the type `(dyn A + 'static)` is not borrowed for too long -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0310`. diff --git a/src/test/ui/regions/regions-close-over-type-parameter-1.nll.stderr b/src/test/ui/regions/regions-close-over-type-parameter-1.nll.stderr index 7d3d51bdb437e..3101d815881b1 100644 --- a/src/test/ui/regions/regions-close-over-type-parameter-1.nll.stderr +++ b/src/test/ui/regions/regions-close-over-type-parameter-1.nll.stderr @@ -1,5 +1,5 @@ error[E0310]: the parameter type `A` may not live long enough - --> $DIR/regions-close-over-type-parameter-1.rs:10:5 + --> $DIR/regions-close-over-type-parameter-1.rs:12:5 | LL | box v as Box | ^^^^^ @@ -7,7 +7,7 @@ LL | box v as Box = help: consider adding an explicit lifetime bound `A: 'static`... error[E0309]: the parameter type `A` may not live long enough - --> $DIR/regions-close-over-type-parameter-1.rs:20:5 + --> $DIR/regions-close-over-type-parameter-1.rs:21:5 | LL | box v as Box | ^^^^^ diff --git a/src/test/ui/regions/regions-close-over-type-parameter-1.rs b/src/test/ui/regions/regions-close-over-type-parameter-1.rs index 6a9aa66a446c3..6e708a5f70fbd 100644 --- a/src/test/ui/regions/regions-close-over-type-parameter-1.rs +++ b/src/test/ui/regions/regions-close-over-type-parameter-1.rs @@ -4,22 +4,22 @@ // an object. This should yield errors unless `A` (and the object) // both have suitable bounds. -trait SomeTrait { fn get(&self) -> isize; } +trait SomeTrait { + fn get(&self) -> isize; +} -fn make_object1(v: A) -> Box { +fn make_object1(v: A) -> Box { box v as Box - //~^ ERROR the parameter type `A` may not live long enough - //~| ERROR the parameter type `A` may not live long enough + //~^ ERROR the parameter type `A` may not live long enough } -fn make_object2<'a,A:SomeTrait+'a>(v: A) -> Box { +fn make_object2<'a, A: SomeTrait + 'a>(v: A) -> Box { box v as Box } -fn make_object3<'a,'b,A:SomeTrait+'a>(v: A) -> Box { +fn make_object3<'a, 'b, A: SomeTrait + 'a>(v: A) -> Box { box v as Box - //~^ ERROR the parameter type `A` may not live long enough - //~| ERROR the parameter type `A` may not live long enough + //~^ ERROR the parameter type `A` may not live long enough } -fn main() { } +fn main() {} diff --git a/src/test/ui/regions/regions-close-over-type-parameter-1.stderr b/src/test/ui/regions/regions-close-over-type-parameter-1.stderr index ed9a604e717dd..50274b066df60 100644 --- a/src/test/ui/regions/regions-close-over-type-parameter-1.stderr +++ b/src/test/ui/regions/regions-close-over-type-parameter-1.stderr @@ -1,60 +1,20 @@ error[E0310]: the parameter type `A` may not live long enough - --> $DIR/regions-close-over-type-parameter-1.rs:10:5 + --> $DIR/regions-close-over-type-parameter-1.rs:12:5 | -LL | fn make_object1(v: A) -> Box { +LL | fn make_object1(v: A) -> Box { | -- help: consider adding an explicit lifetime bound...: `A: 'static +` LL | box v as Box - | ^^^^^ - | -note: ...so that the type `A` will meet its required lifetime bounds - --> $DIR/regions-close-over-type-parameter-1.rs:10:5 - | -LL | box v as Box - | ^^^^^ - -error[E0310]: the parameter type `A` may not live long enough - --> $DIR/regions-close-over-type-parameter-1.rs:10:5 - | -LL | fn make_object1(v: A) -> Box { - | -- help: consider adding an explicit lifetime bound...: `A: 'static +` -LL | box v as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that it can be closed over into an object - --> $DIR/regions-close-over-type-parameter-1.rs:10:5 - | -LL | box v as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ ...so that the type `A` will meet its required lifetime bounds error[E0309]: the parameter type `A` may not live long enough - --> $DIR/regions-close-over-type-parameter-1.rs:20:5 - | -LL | fn make_object3<'a,'b,A:SomeTrait+'a>(v: A) -> Box { - | -- help: consider adding an explicit lifetime bound...: `A: 'b +` -LL | box v as Box - | ^^^^^ - | -note: ...so that the type `A` will meet its required lifetime bounds - --> $DIR/regions-close-over-type-parameter-1.rs:20:5 - | -LL | box v as Box - | ^^^^^ - -error[E0309]: the parameter type `A` may not live long enough - --> $DIR/regions-close-over-type-parameter-1.rs:20:5 - | -LL | fn make_object3<'a,'b,A:SomeTrait+'a>(v: A) -> Box { - | -- help: consider adding an explicit lifetime bound...: `A: 'b +` -LL | box v as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that it can be closed over into an object - --> $DIR/regions-close-over-type-parameter-1.rs:20:5 + --> $DIR/regions-close-over-type-parameter-1.rs:21:5 | +LL | fn make_object3<'a, 'b, A: SomeTrait + 'a>(v: A) -> Box { + | -- help: consider adding an explicit lifetime bound...: `A: 'b +` LL | box v as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ ...so that the type `A` will meet its required lifetime bounds -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0309, E0310. For more information about an error, try `rustc --explain E0309`. diff --git a/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr b/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr index b2a7afaf1b452..2070ce257b18d 100644 --- a/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr +++ b/src/test/ui/regions/regions-close-over-type-parameter-multiple.stderr @@ -24,8 +24,8 @@ note: ...so that the expression is assignable | LL | box v as Box | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn SomeTrait + 'c)>` - found `std::boxed::Box` + = note: expected `std::boxed::Box<(dyn SomeTrait + 'c)>` + found `std::boxed::Box` error: aborting due to previous error diff --git a/src/test/ui/regions/regions-close-param-into-object.stderr b/src/test/ui/regions/regions-close-param-into-object.stderr index 3b1a89d9ced77..705d21078ecd7 100644 --- a/src/test/ui/regions/regions-close-param-into-object.stderr +++ b/src/test/ui/regions/regions-close-param-into-object.stderr @@ -5,13 +5,7 @@ LL | fn p1(v: T) -> Box | - help: consider adding an explicit lifetime bound...: `T: 'static` ... LL | Box::new(v) - | ^^^^^^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-close-param-into-object.rs:6:5 - | -LL | Box::new(v) - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0310]: the parameter type `T` may not live long enough --> $DIR/regions-close-param-into-object.rs:12:5 @@ -20,13 +14,7 @@ LL | fn p2(v: Box) -> Box | - help: consider adding an explicit lifetime bound...: `T: 'static` ... LL | Box::new(v) - | ^^^^^^^^^^^ - | -note: ...so that the type `std::boxed::Box` will meet its required lifetime bounds - --> $DIR/regions-close-param-into-object.rs:12:5 - | -LL | Box::new(v) - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ ...so that the type `std::boxed::Box` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-close-param-into-object.rs:18:5 @@ -35,13 +23,7 @@ LL | fn p3<'a,T>(v: T) -> Box | - help: consider adding an explicit lifetime bound...: `T: 'a` ... LL | Box::new(v) - | ^^^^^^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-close-param-into-object.rs:18:5 - | -LL | Box::new(v) - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-close-param-into-object.rs:24:5 @@ -50,13 +32,7 @@ LL | fn p4<'a,T>(v: Box) -> Box | - help: consider adding an explicit lifetime bound...: `T: 'a` ... LL | Box::new(v) - | ^^^^^^^^^^^ - | -note: ...so that the type `std::boxed::Box` will meet its required lifetime bounds - --> $DIR/regions-close-param-into-object.rs:24:5 - | -LL | Box::new(v) - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ ...so that the type `std::boxed::Box` will meet its required lifetime bounds error: aborting due to 4 previous errors diff --git a/src/test/ui/regions/regions-creating-enums4.stderr b/src/test/ui/regions/regions-creating-enums4.stderr index 58f74e4ee142d..b24db1df18b0a 100644 --- a/src/test/ui/regions/regions-creating-enums4.stderr +++ b/src/test/ui/regions/regions-creating-enums4.stderr @@ -14,8 +14,8 @@ note: ...so that the expression is assignable | LL | Ast::Add(x, y) | ^ - = note: expected `&Ast<'_>` - found `&Ast<'a>` + = note: expected `&Ast<'_>` + found `&Ast<'a>` note: but, the lifetime must be valid for the lifetime `'b` as defined on the function body at 6:19... --> $DIR/regions-creating-enums4.rs:6:19 | @@ -26,8 +26,8 @@ note: ...so that the expression is assignable | LL | Ast::Add(x, y) | ^^^^^^^^^^^^^^ - = note: expected `Ast<'b>` - found `Ast<'_>` + = note: expected `Ast<'b>` + found `Ast<'_>` error: aborting due to previous error diff --git a/src/test/ui/regions/regions-enum-not-wf.stderr b/src/test/ui/regions/regions-enum-not-wf.stderr index 297fcb088d2bf..e32a36f72cd14 100644 --- a/src/test/ui/regions/regions-enum-not-wf.stderr +++ b/src/test/ui/regions/regions-enum-not-wf.stderr @@ -4,13 +4,7 @@ error[E0309]: the parameter type `T` may not live long enough LL | enum Ref1<'a, T> { | - help: consider adding an explicit lifetime bound...: `T: 'a` LL | Ref1Variant1(RequireOutlives<'a, T>) - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-enum-not-wf.rs:18:18 - | -LL | Ref1Variant1(RequireOutlives<'a, T>) - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-enum-not-wf.rs:23:25 @@ -19,13 +13,7 @@ LL | enum Ref2<'a, T> { | - help: consider adding an explicit lifetime bound...: `T: 'a` LL | Ref2Variant1, LL | Ref2Variant2(isize, RequireOutlives<'a, T>), - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-enum-not-wf.rs:23:25 - | -LL | Ref2Variant2(isize, RequireOutlives<'a, T>), - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-enum-not-wf.rs:35:1 @@ -37,16 +25,7 @@ LL | enum RefDouble<'a, 'b, T> { LL | | RefDoubleVariant1(&'a RequireOutlives<'b, T>) LL | | LL | | } - | |_^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-enum-not-wf.rs:35:1 - | -LL | / enum RefDouble<'a, 'b, T> { -LL | | RefDoubleVariant1(&'a RequireOutlives<'b, T>) -LL | | -LL | | } - | |_^ + | |_^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-enum-not-wf.rs:36:23 @@ -54,13 +33,7 @@ error[E0309]: the parameter type `T` may not live long enough LL | enum RefDouble<'a, 'b, T> { | - help: consider adding an explicit lifetime bound...: `T: 'b` LL | RefDoubleVariant1(&'a RequireOutlives<'b, T>) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-enum-not-wf.rs:36:23 - | -LL | RefDoubleVariant1(&'a RequireOutlives<'b, T>) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error: aborting due to 4 previous errors diff --git a/src/test/ui/regions/regions-escape-method.nll.stderr b/src/test/ui/regions/regions-escape-method.nll.stderr deleted file mode 100644 index 9f425125b9896..0000000000000 --- a/src/test/ui/regions/regions-escape-method.nll.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/regions-escape-method.rs:15:13 - | -LL | s.f(|p| p) - | -- ^ returning this value requires that `'1` must outlive `'2` - | || - | |return type of closure is &'2 i32 - | has type `&'1 i32` - -error: aborting due to previous error - diff --git a/src/test/ui/regions/regions-escape-method.rs b/src/test/ui/regions/regions-escape-method.rs index 5127d4d1ceb0f..69c01ae6906cb 100644 --- a/src/test/ui/regions/regions-escape-method.rs +++ b/src/test/ui/regions/regions-escape-method.rs @@ -12,5 +12,5 @@ impl S { fn main() { let s = S; - s.f(|p| p) //~ ERROR cannot infer + s.f(|p| p) //~ ERROR lifetime may not live long enough } diff --git a/src/test/ui/regions/regions-escape-method.stderr b/src/test/ui/regions/regions-escape-method.stderr index ffc2a259485aa..9f425125b9896 100644 --- a/src/test/ui/regions/regions-escape-method.stderr +++ b/src/test/ui/regions/regions-escape-method.stderr @@ -1,32 +1,11 @@ -error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements +error: lifetime may not live long enough --> $DIR/regions-escape-method.rs:15:13 | LL | s.f(|p| p) - | ^ - | -note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 15:9... - --> $DIR/regions-escape-method.rs:15:9 - | -LL | s.f(|p| p) - | ^^^^^ -note: ...so that the expression is assignable - --> $DIR/regions-escape-method.rs:15:13 - | -LL | s.f(|p| p) - | ^ - = note: expected `&i32` - found `&i32` -note: but, the lifetime must be valid for the method call at 15:5... - --> $DIR/regions-escape-method.rs:15:5 - | -LL | s.f(|p| p) - | ^^^^^^^^^^ -note: ...so that a type/lifetime parameter is in scope here - --> $DIR/regions-escape-method.rs:15:5 - | -LL | s.f(|p| p) - | ^^^^^^^^^^ + | -- ^ returning this value requires that `'1` must outlive `'2` + | || + | |return type of closure is &'2 i32 + | has type `&'1 i32` error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-escape-via-trait-or-not.nll.stderr b/src/test/ui/regions/regions-escape-via-trait-or-not.nll.stderr deleted file mode 100644 index cae6c33ac6e17..0000000000000 --- a/src/test/ui/regions/regions-escape-via-trait-or-not.nll.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/regions-escape-via-trait-or-not.rs:18:14 - | -LL | with(|o| o) - | -- ^ returning this value requires that `'1` must outlive `'2` - | || - | |return type of closure is &'2 isize - | has type `&'1 isize` - -error: aborting due to previous error - diff --git a/src/test/ui/regions/regions-escape-via-trait-or-not.rs b/src/test/ui/regions/regions-escape-via-trait-or-not.rs index 1e089616f5997..ac0e56de4a030 100644 --- a/src/test/ui/regions/regions-escape-via-trait-or-not.rs +++ b/src/test/ui/regions/regions-escape-via-trait-or-not.rs @@ -15,7 +15,7 @@ fn with(f: F) -> isize where F: FnOnce(&isize) -> R { } fn return_it() -> isize { - with(|o| o) //~ ERROR cannot infer + with(|o| o) //~ ERROR lifetime may not live long enough } fn main() { diff --git a/src/test/ui/regions/regions-escape-via-trait-or-not.stderr b/src/test/ui/regions/regions-escape-via-trait-or-not.stderr index 90823464c56d2..cae6c33ac6e17 100644 --- a/src/test/ui/regions/regions-escape-via-trait-or-not.stderr +++ b/src/test/ui/regions/regions-escape-via-trait-or-not.stderr @@ -1,32 +1,11 @@ -error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements +error: lifetime may not live long enough --> $DIR/regions-escape-via-trait-or-not.rs:18:14 | LL | with(|o| o) - | ^ - | -note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 18:10... - --> $DIR/regions-escape-via-trait-or-not.rs:18:10 - | -LL | with(|o| o) - | ^^^^^ -note: ...so that the expression is assignable - --> $DIR/regions-escape-via-trait-or-not.rs:18:14 - | -LL | with(|o| o) - | ^ - = note: expected `&isize` - found `&isize` -note: but, the lifetime must be valid for the expression at 18:5... - --> $DIR/regions-escape-via-trait-or-not.rs:18:5 - | -LL | with(|o| o) - | ^^^^ -note: ...so type `fn([closure@$DIR/regions-escape-via-trait-or-not.rs:18:10: 18:15]) -> isize {with::<&isize, [closure@$DIR/regions-escape-via-trait-or-not.rs:18:10: 18:15]>}` of expression is valid during the expression - --> $DIR/regions-escape-via-trait-or-not.rs:18:5 - | -LL | with(|o| o) - | ^^^^ + | -- ^ returning this value requires that `'1` must outlive `'2` + | || + | |return type of closure is &'2 isize + | has type `&'1 isize` error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-fn-subtyping-return-static.rs b/src/test/ui/regions/regions-fn-subtyping-return-static.rs index fa2cc37d05b2b..de14d5ba82a1b 100644 --- a/src/test/ui/regions/regions-fn-subtyping-return-static.rs +++ b/src/test/ui/regions/regions-fn-subtyping-return-static.rs @@ -5,6 +5,8 @@ // *ANY* lifetime and returns a reference with the 'static lifetime. // This can safely be considered to be an instance of `F` because all // lifetimes are sublifetimes of 'static. +// +// check-pass #![allow(dead_code)] #![allow(unused_variables)] @@ -14,11 +16,11 @@ struct S; // Given 'cx, return 'cx type F = for<'cx> fn(&'cx S) -> &'cx S; -fn want_F(f: F) { } +fn want_F(f: F) {} // Given anything, return 'static type G = for<'cx> fn(&'cx S) -> &'static S; -fn want_G(f: G) { } +fn want_G(f: G) {} // Should meet both. fn foo(x: &S) -> &'static S { @@ -26,7 +28,7 @@ fn foo(x: &S) -> &'static S { } // Should meet both. -fn bar<'a,'b>(x: &'a S) -> &'b S { +fn bar<'a, 'b>(x: &'a S) -> &'b S { panic!() } @@ -38,10 +40,9 @@ fn baz(x: &S) -> &S { fn supply_F() { want_F(foo); - want_F(bar); //~ ERROR mismatched types + want_F(bar); want_F(baz); } -pub fn main() { -} +pub fn main() {} diff --git a/src/test/ui/regions/regions-fn-subtyping-return-static.stderr b/src/test/ui/regions/regions-fn-subtyping-return-static.stderr deleted file mode 100644 index a8a7e97e6acf6..0000000000000 --- a/src/test/ui/regions/regions-fn-subtyping-return-static.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/regions-fn-subtyping-return-static.rs:41:12 - | -LL | want_F(bar); - | ^^^ expected concrete lifetime, found bound lifetime parameter 'cx - | - = note: expected fn pointer `for<'cx> fn(&'cx S) -> &'cx S` - found fn item `for<'a> fn(&'a S) -> &S {bar::<'_>}` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/regions/regions-free-region-ordering-caller.migrate.stderr b/src/test/ui/regions/regions-free-region-ordering-caller.migrate.stderr index a33d3583552dc..06e1b0f1ac262 100644 --- a/src/test/ui/regions/regions-free-region-ordering-caller.migrate.stderr +++ b/src/test/ui/regions/regions-free-region-ordering-caller.migrate.stderr @@ -1,32 +1,54 @@ -error[E0623]: lifetime mismatch +error[E0491]: in type `&'b &'a usize`, reference has a longer lifetime than the data it references --> $DIR/regions-free-region-ordering-caller.rs:11:12 | -LL | fn call2<'a, 'b>(a: &'a usize, b: &'b usize) { - | --------- --------- - | | - | these two types are declared with different lifetimes... LL | let z: Option<&'b &'a usize> = None; - | ^^^^^^^^^^^^^^^^^^^^^ ...but data from `a` flows into `b` here + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the pointer is valid for the lifetime `'b` as defined on the function body at 10:14 + --> $DIR/regions-free-region-ordering-caller.rs:10:14 + | +LL | fn call2<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ +note: but the referenced data is only valid for the lifetime `'a` as defined on the function body at 10:10 + --> $DIR/regions-free-region-ordering-caller.rs:10:10 + | +LL | fn call2<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ -error[E0623]: lifetime mismatch +error[E0491]: in type `&'b Paramd<'a>`, reference has a longer lifetime than the data it references --> $DIR/regions-free-region-ordering-caller.rs:17:12 | -LL | fn call3<'a, 'b>(a: &'a usize, b: &'b usize) { - | --------- --------- - | | - | these two types are declared with different lifetimes... -LL | let y: Paramd<'a> = Paramd { x: a }; LL | let z: Option<&'b Paramd<'a>> = None; - | ^^^^^^^^^^^^^^^^^^^^^^ ...but data from `a` flows into `b` here + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: the pointer is valid for the lifetime `'b` as defined on the function body at 15:14 + --> $DIR/regions-free-region-ordering-caller.rs:15:14 + | +LL | fn call3<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ +note: but the referenced data is only valid for the lifetime `'a` as defined on the function body at 15:10 + --> $DIR/regions-free-region-ordering-caller.rs:15:10 + | +LL | fn call3<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ -error[E0623]: lifetime mismatch +error[E0491]: in type `&'a &'b usize`, reference has a longer lifetime than the data it references --> $DIR/regions-free-region-ordering-caller.rs:22:12 | -LL | fn call4<'a, 'b>(a: &'a usize, b: &'b usize) { - | --------- --------- these two types are declared with different lifetimes... LL | let z: Option<&'a &'b usize> = None; - | ^^^^^^^^^^^^^^^^^^^^^ ...but data from `b` flows into `a` here + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the pointer is valid for the lifetime `'a` as defined on the function body at 21:10 + --> $DIR/regions-free-region-ordering-caller.rs:21:10 + | +LL | fn call4<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ +note: but the referenced data is only valid for the lifetime `'b` as defined on the function body at 21:14 + --> $DIR/regions-free-region-ordering-caller.rs:21:14 + | +LL | fn call4<'a, 'b>(a: &'a usize, b: &'b usize) { + | ^^ error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0623`. +For more information about this error, try `rustc --explain E0491`. diff --git a/src/test/ui/regions/regions-free-region-ordering-caller.rs b/src/test/ui/regions/regions-free-region-ordering-caller.rs index c0b12f23cdba7..2bf4734cf7380 100644 --- a/src/test/ui/regions/regions-free-region-ordering-caller.rs +++ b/src/test/ui/regions/regions-free-region-ordering-caller.rs @@ -8,18 +8,18 @@ struct Paramd<'a> { x: &'a usize } fn call2<'a, 'b>(a: &'a usize, b: &'b usize) { - let z: Option<&'b &'a usize> = None;//[migrate]~ ERROR E0623 + let z: Option<&'b &'a usize> = None;//[migrate]~ ERROR E0491 //[nll]~^ ERROR lifetime may not live long enough } fn call3<'a, 'b>(a: &'a usize, b: &'b usize) { let y: Paramd<'a> = Paramd { x: a }; - let z: Option<&'b Paramd<'a>> = None;//[migrate]~ ERROR E0623 + let z: Option<&'b Paramd<'a>> = None;//[migrate]~ ERROR E0491 //[nll]~^ ERROR lifetime may not live long enough } fn call4<'a, 'b>(a: &'a usize, b: &'b usize) { - let z: Option<&'a &'b usize> = None;//[migrate]~ ERROR E0623 + let z: Option<&'a &'b usize> = None;//[migrate]~ ERROR E0491 //[nll]~^ ERROR lifetime may not live long enough } diff --git a/src/test/ui/regions/regions-implied-bounds-projection-gap-1.stderr b/src/test/ui/regions/regions-implied-bounds-projection-gap-1.stderr index 2f1a4cea8e9ac..ea59ea11a143c 100644 --- a/src/test/ui/regions/regions-implied-bounds-projection-gap-1.stderr +++ b/src/test/ui/regions/regions-implied-bounds-projection-gap-1.stderr @@ -5,13 +5,7 @@ LL | fn func<'x, T:Trait1<'x>>(t: &'x T::Foo) | -- help: consider adding an explicit lifetime bound...: `T: 'x +` LL | { LL | wf::<&'x T>(); - | ^^^^^ - | -note: ...so that the reference type `&'x T` does not outlive the data it points at - --> $DIR/regions-implied-bounds-projection-gap-1.rs:16:10 - | -LL | wf::<&'x T>(); - | ^^^^^ + | ^^^^^ ...so that the reference type `&'x T` does not outlive the data it points at error: aborting due to previous error diff --git a/src/test/ui/regions/regions-infer-bound-from-trait-self.stderr b/src/test/ui/regions/regions-infer-bound-from-trait-self.stderr index bcdadd7a73d6c..4ca5ac291d5be 100644 --- a/src/test/ui/regions/regions-infer-bound-from-trait-self.stderr +++ b/src/test/ui/regions/regions-infer-bound-from-trait-self.stderr @@ -5,11 +5,7 @@ LL | check_bound(x, self) | ^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `Self: 'a`... -note: ...so that the type `Self` will meet its required lifetime bounds - --> $DIR/regions-infer-bound-from-trait-self.rs:46:9 - | -LL | check_bound(x, self) - | ^^^^^^^^^^^ + = note: ...so that the type `Self` will meet its required lifetime bounds error: aborting due to previous error diff --git a/src/test/ui/regions/regions-infer-bound-from-trait.stderr b/src/test/ui/regions/regions-infer-bound-from-trait.stderr index a5a0ff52fac12..196ee8ca7c0b5 100644 --- a/src/test/ui/regions/regions-infer-bound-from-trait.stderr +++ b/src/test/ui/regions/regions-infer-bound-from-trait.stderr @@ -4,13 +4,7 @@ error[E0309]: the parameter type `A` may not live long enough LL | fn bar1<'a,A>(x: Inv<'a>, a: A) { | - help: consider adding an explicit lifetime bound...: `A: 'a` LL | check_bound(x, a) - | ^^^^^^^^^^^ - | -note: ...so that the type `A` will meet its required lifetime bounds - --> $DIR/regions-infer-bound-from-trait.rs:33:5 - | -LL | check_bound(x, a) - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds error[E0309]: the parameter type `A` may not live long enough --> $DIR/regions-infer-bound-from-trait.rs:37:5 @@ -18,13 +12,7 @@ error[E0309]: the parameter type `A` may not live long enough LL | fn bar2<'a,'b,A:Is<'b>>(x: Inv<'a>, y: Inv<'b>, a: A) { | -- help: consider adding an explicit lifetime bound...: `A: 'a +` LL | check_bound(x, a) - | ^^^^^^^^^^^ - | -note: ...so that the type `A` will meet its required lifetime bounds - --> $DIR/regions-infer-bound-from-trait.rs:37:5 - | -LL | check_bound(x, a) - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds error: aborting due to 2 previous errors diff --git a/src/test/ui/regions/regions-infer-call-3.nll.stderr b/src/test/ui/regions/regions-infer-call-3.nll.stderr deleted file mode 100644 index ca51555a07749..0000000000000 --- a/src/test/ui/regions/regions-infer-call-3.nll.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/regions-infer-call-3.rs:8:24 - | -LL | let z = with(|y| { select(x, y) }); - | -- ^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` - | || - | |return type of closure is &'2 isize - | has type `&'1 isize` - -error: aborting due to previous error - diff --git a/src/test/ui/regions/regions-infer-call-3.rs b/src/test/ui/regions/regions-infer-call-3.rs index a76fccbdc5218..063ec84288d1f 100644 --- a/src/test/ui/regions/regions-infer-call-3.rs +++ b/src/test/ui/regions/regions-infer-call-3.rs @@ -6,7 +6,7 @@ fn with(f: F) -> T where F: FnOnce(&isize) -> T { fn manip<'a>(x: &'a isize) -> isize { let z = with(|y| { select(x, y) }); - //~^ ERROR cannot infer + //~^ ERROR lifetime may not live long enough *z } diff --git a/src/test/ui/regions/regions-infer-call-3.stderr b/src/test/ui/regions/regions-infer-call-3.stderr index 1d6dbdb2c7b57..ca51555a07749 100644 --- a/src/test/ui/regions/regions-infer-call-3.stderr +++ b/src/test/ui/regions/regions-infer-call-3.stderr @@ -1,30 +1,11 @@ -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'r in function call due to conflicting requirements +error: lifetime may not live long enough --> $DIR/regions-infer-call-3.rs:8:24 | LL | let z = with(|y| { select(x, y) }); - | ^^^^^^^^^^^^ - | -note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 8:18... - --> $DIR/regions-infer-call-3.rs:8:18 - | -LL | let z = with(|y| { select(x, y) }); - | ^^^^^^^^^^^^^^^^^^^^ -note: ...so that reference does not outlive borrowed content - --> $DIR/regions-infer-call-3.rs:8:34 - | -LL | let z = with(|y| { select(x, y) }); - | ^ -note: but, the lifetime must be valid for the call at 8:13... - --> $DIR/regions-infer-call-3.rs:8:13 - | -LL | let z = with(|y| { select(x, y) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...so type `&isize` of expression is valid during the expression - --> $DIR/regions-infer-call-3.rs:8:13 - | -LL | let z = with(|y| { select(x, y) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | -- ^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` + | || + | |return type of closure is &'2 isize + | has type `&'1 isize` error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr b/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr index 434a3e47b492a..37f7fcf2e331a 100644 --- a/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr +++ b/src/test/ui/regions/regions-lifetime-bounds-on-fns.nll.stderr @@ -1,14 +1,40 @@ -error[E0308]: mismatched types - --> $DIR/regions-lifetime-bounds-on-fns.rs:20:43 +error: lifetime may not live long enough + --> $DIR/regions-lifetime-bounds-on-fns.rs:8:5 + | +LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | // Illegal now because there is no `'b:'a` declaration. +LL | *x = *y; + | ^^^^^^^ assignment requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error: lifetime may not live long enough + --> $DIR/regions-lifetime-bounds-on-fns.rs:14:5 + | +LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | a(x, y); + | ^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + +error: higher-ranked subtype error + --> $DIR/regions-lifetime-bounds-on-fns.rs:20:12 | LL | let _: fn(&mut &isize, &mut &isize) = a; - | ---------------------------- ^ expected concrete lifetime, found bound lifetime parameter - | | - | expected due to this + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/regions-lifetime-bounds-on-fns.rs:20:12 | - = note: expected fn pointer `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)` - found fn item `for<'r, 's> fn(&'r mut &isize, &'s mut &isize) {a::<'_, '_>}` +LL | let _: fn(&mut &isize, &mut &isize) = a; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr b/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr index 01f43aeebaf7c..2b2dd0dbbf250 100644 --- a/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr +++ b/src/test/ui/regions/regions-lifetime-bounds-on-fns.stderr @@ -20,12 +20,10 @@ error[E0308]: mismatched types --> $DIR/regions-lifetime-bounds-on-fns.rs:20:43 | LL | let _: fn(&mut &isize, &mut &isize) = a; - | ---------------------------- ^ expected concrete lifetime, found bound lifetime parameter - | | - | expected due to this + | ^ one type is more general than the other | = note: expected fn pointer `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)` - found fn item `for<'r, 's> fn(&'r mut &isize, &'s mut &isize) {a::<'_, '_>}` + found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)` error: aborting due to 3 previous errors 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/regions/regions-nested-fns.stderr b/src/test/ui/regions/regions-nested-fns.stderr index 8fce1609d7830..9e405d83140d8 100644 --- a/src/test/ui/regions/regions-nested-fns.stderr +++ b/src/test/ui/regions/regions-nested-fns.stderr @@ -39,8 +39,8 @@ LL | | if false { return ay; } LL | | return z; LL | | })); | |_____^ - = note: expected `&isize` - found `&isize` + = note: expected `&isize` + found `&isize` error[E0312]: lifetime of reference outlives lifetime of borrowed content... --> $DIR/regions-nested-fns.rs:14:27 diff --git a/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr b/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr index c35516d2c0871..dc93d620ca637 100644 --- a/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr +++ b/src/test/ui/regions/regions-normalize-in-where-clause-list.stderr @@ -29,8 +29,8 @@ LL | | where <() as Project<'a, 'b>>::Item : Eq LL | | { LL | | } | |_^ - = note: expected `Project<'a, 'b>` - found `Project<'_, '_>` + = note: expected `Project<'a, 'b>` + found `Project<'_, '_>` error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> $DIR/regions-normalize-in-where-clause-list.rs:22:1 @@ -63,8 +63,8 @@ LL | | where <() as Project<'a, 'b>>::Item : Eq LL | | { LL | | } | |_^ - = note: expected `Project<'a, 'b>` - found `Project<'_, '_>` + = note: expected `Project<'a, 'b>` + found `Project<'_, '_>` error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> $DIR/regions-normalize-in-where-clause-list.rs:22:4 @@ -87,8 +87,8 @@ note: ...so that the types are compatible | LL | fn bar<'a, 'b>() | ^^^ - = note: expected `Project<'a, 'b>` - found `Project<'_, '_>` + = note: expected `Project<'a, 'b>` + found `Project<'_, '_>` error: aborting due to 3 previous errors diff --git a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr new file mode 100644 index 0000000000000..75890b8581537 --- /dev/null +++ b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/regions-proc-bound-capture.rs:9:5 + | +LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { + | - let's call the lifetime of this reference `'1` +LL | // This is illegal, because the region bound on `proc` is 'static. +LL | Box::new(move || { *x }) + | ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/regions/regions-proc-bound-capture.rs b/src/test/ui/regions/regions-proc-bound-capture.rs index 0c903b7384992..8617c0e9da8f7 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.rs +++ b/src/test/ui/regions/regions-proc-bound-capture.rs @@ -4,9 +4,9 @@ fn borrowed_proc<'a>(x: &'a isize) -> Box(isize) + 'a> { Box::new(move|| { *x }) } -fn static_proc(x: &isize) -> Box(isize) + 'static> { +fn static_proc(x: &isize) -> Box (isize) + 'static> { // This is illegal, because the region bound on `proc` is 'static. - Box::new(move|| { *x }) //~ ERROR explicit lifetime required in the type of `x` [E0621] + Box::new(move || { *x }) //~ ERROR cannot infer an appropriate lifetime } fn main() { } diff --git a/src/test/ui/regions/regions-proc-bound-capture.stderr b/src/test/ui/regions/regions-proc-bound-capture.stderr index c53af34456ef3..67eee3bb6e281 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.stderr @@ -1,12 +1,21 @@ -error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/regions-proc-bound-capture.rs:9:5 +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/regions-proc-bound-capture.rs:9:14 | -LL | fn static_proc(x: &isize) -> Box(isize) + 'static> { - | ------ help: add explicit lifetime `'static` to the type of `x`: `&'static isize` +LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { + | ------ this data with an anonymous lifetime `'_`... LL | // This is illegal, because the region bound on `proc` is 'static. -LL | Box::new(move|| { *x }) - | ^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required +LL | Box::new(move || { *x }) + | ^^^^^^^^^^^^^^ ...is captured here, requiring it to live as long as `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` + | +LL | fn static_proc(x: &isize) -> Box (isize) + '_> { + | ^^ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn static_proc(x: &'static isize) -> Box (isize) + 'static> { + | ^^^^^^^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0621`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/regions/regions-ret-borrowed-1.stderr b/src/test/ui/regions/regions-ret-borrowed-1.stderr index 2895a0ccdeec8..2c4769d8e3751 100644 --- a/src/test/ui/regions/regions-ret-borrowed-1.stderr +++ b/src/test/ui/regions/regions-ret-borrowed-1.stderr @@ -14,8 +14,8 @@ note: ...so that the expression is assignable | LL | with(|o| o) | ^ - = note: expected `&isize` - found `&isize` + = note: expected `&isize` + found `&isize` note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 9:14... --> $DIR/regions-ret-borrowed-1.rs:9:14 | diff --git a/src/test/ui/regions/regions-ret-borrowed.stderr b/src/test/ui/regions/regions-ret-borrowed.stderr index b74f10f5075eb..da560107cea99 100644 --- a/src/test/ui/regions/regions-ret-borrowed.stderr +++ b/src/test/ui/regions/regions-ret-borrowed.stderr @@ -14,8 +14,8 @@ note: ...so that the expression is assignable | LL | with(|o| o) | ^ - = note: expected `&isize` - found `&isize` + = note: expected `&isize` + found `&isize` note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 12:14... --> $DIR/regions-ret-borrowed.rs:12:14 | diff --git a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.nll.stderr b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.nll.stderr deleted file mode 100644 index 4c275b19492c6..0000000000000 --- a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.nll.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: captured variable cannot escape `FnMut` closure body - --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:7:24 - | -LL | let mut f = || &mut x; - | - ^^^^^^ returns a reference to a captured variable which escapes the closure body - | | - | inferred to be a `FnMut` closure - | - = note: `FnMut` closures only have access to their captured variables while they are executing... - = note: ...therefore, they cannot allow references to captured variables to escape - -error: aborting due to previous error - diff --git a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.rs b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.rs index afe87f47eadbe..86e759f088a54 100644 --- a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.rs +++ b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.rs @@ -4,7 +4,7 @@ fn main() { // Unboxed closure case { let mut x = 0; - let mut f = || &mut x; //~ ERROR cannot infer + let mut f = || &mut x; //~ ERROR captured variable cannot escape `FnMut` closure body let x = f(); let y = f(); } diff --git a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr index 946465bcb5f26..b087e03b464b3 100644 --- a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr +++ b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr @@ -1,30 +1,17 @@ -error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements +error: captured variable cannot escape `FnMut` closure body --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:7:24 | +LL | let mut x = 0; + | ----- variable defined here LL | let mut f = || &mut x; - | ^^^^^^ + | - ^^^^^- + | | | | + | | | variable captured here + | | returns a reference to a captured variable which escapes the closure body + | inferred to be a `FnMut` closure | -note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 7:21... - --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:7:21 - | -LL | let mut f = || &mut x; - | ^^^^^^^^^ -note: ...so that closure can access `x` - --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:7:24 - | -LL | let mut f = || &mut x; - | ^^^^^^ -note: but, the lifetime must be valid for the call at 9:17... - --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:9:17 - | -LL | let y = f(); - | ^^^ -note: ...so type `&mut i32` of expression is valid during the expression - --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:9:17 - | -LL | let y = f(); - | ^^^ + = note: `FnMut` closures only have access to their captured variables while they are executing... + = note: ...therefore, they cannot allow references to captured variables to escape error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/regions/regions-trait-object-subtyping.stderr b/src/test/ui/regions/regions-trait-object-subtyping.stderr index 58b79d212700c..7478b53bd3ccc 100644 --- a/src/test/ui/regions/regions-trait-object-subtyping.stderr +++ b/src/test/ui/regions/regions-trait-object-subtyping.stderr @@ -41,8 +41,8 @@ note: ...so that the expression is assignable | LL | x | ^ - = note: expected `&'b mut (dyn Dummy + 'b)` - found `&mut (dyn Dummy + 'b)` + = note: expected `&'b mut (dyn Dummy + 'b)` + found `&mut (dyn Dummy + 'b)` error[E0308]: mismatched types --> $DIR/regions-trait-object-subtyping.rs:22:5 diff --git a/src/test/ui/reify-intrinsic.stderr b/src/test/ui/reify-intrinsic.stderr index 4defe12b1b37b..c4eee0f466119 100644 --- a/src/test/ui/reify-intrinsic.stderr +++ b/src/test/ui/reify-intrinsic.stderr @@ -2,14 +2,16 @@ error[E0308]: cannot coerce intrinsics to function pointers --> $DIR/reify-intrinsic.rs:6:64 | LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute; - | ------------------------------------------------- ^^^^^^^^^^^^^^^^^^^ - | | | - | | cannot coerce intrinsics to function pointers - | | help: use parentheses to call this function: `std::mem::transmute(...)` + | ------------------------------------------------- ^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers + | | | expected due to this | = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize` found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` +help: use parentheses to call this function + | +LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute(...); + | ^^^^^ error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid --> $DIR/reify-intrinsic.rs:11:13 diff --git a/src/test/ui/reject-specialized-drops-8142.stderr b/src/test/ui/reject-specialized-drops-8142.stderr index c09418de51830..f819faa278995 100644 --- a/src/test/ui/reject-specialized-drops-8142.stderr +++ b/src/test/ui/reject-specialized-drops-8142.stderr @@ -118,8 +118,8 @@ note: ...so that the types are compatible | LL | impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `W<'l1, 'l2>` - found `W<'_, '_>` + = note: expected `W<'l1, 'l2>` + found `W<'_, '_>` error[E0367]: `Drop` impl requires `AddsBnd: Bound` but the enum it is implemented for does not --> $DIR/reject-specialized-drops-8142.rs:61:14 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.rs b/src/test/ui/repeat_count.rs index aca7af144a934..7e30491f0bdbc 100644 --- a/src/test/ui/repeat_count.rs +++ b/src/test/ui/repeat_count.rs @@ -22,6 +22,9 @@ fn main() { let f = [0_usize; -1_isize]; //~^ ERROR mismatched types //~| expected `usize`, found `isize` + let f = [0; 4u8]; + //~^ ERROR mismatched types + //~| expected `usize`, found `u8` struct G { g: (), } diff --git a/src/test/ui/repeat_count.stderr b/src/test/ui/repeat_count.stderr index efad00b272cdc..2eab3ebc7687d 100644 --- a/src/test/ui/repeat_count.stderr +++ b/src/test/ui/repeat_count.stderr @@ -28,16 +28,19 @@ error[E0308]: mismatched types LL | let e = [0; "foo"]; | ^^^^^ expected `usize`, found `&str` +error[E0308]: mismatched types + --> $DIR/repeat_count.rs:31:17 + | +LL | let g = [0; G { g: () }]; + | ^^^^^^^^^^^ expected `usize`, found struct `main::G` + error[E0308]: mismatched types --> $DIR/repeat_count.rs:19:17 | LL | let f = [0; -4_isize]; | ^^^^^^^^ expected `usize`, found `isize` | -help: you can convert an `isize` to `usize` and panic if the converted value wouldn't fit - | -LL | let f = [0; (-4_isize).try_into().unwrap()]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-4_isize` cannot fit into type `usize` error[E0308]: mismatched types --> $DIR/repeat_count.rs:22:23 @@ -45,18 +48,20 @@ error[E0308]: mismatched types LL | let f = [0_usize; -1_isize]; | ^^^^^^^^ expected `usize`, found `isize` | -help: you can convert an `isize` to `usize` and panic if the converted value wouldn't fit - | -LL | let f = [0_usize; (-1_isize).try_into().unwrap()]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-1_isize` cannot fit into type `usize` error[E0308]: mismatched types - --> $DIR/repeat_count.rs:28:17 + --> $DIR/repeat_count.rs:25:17 | -LL | let g = [0; G { g: () }]; - | ^^^^^^^^^^^ expected `usize`, found struct `main::G` +LL | let f = [0; 4u8]; + | ^^^ expected `usize`, found `u8` + | +help: change the type of the numeric literal from `u8` to `usize` + | +LL | let f = [0; 4usize]; + | ^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0308, E0435. For more information about an error, try `rustc --explain E0308`. 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..538eeead9fc9d 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,17 +19,14 @@ 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; | LL | use mul2::Mul; | -LL | use mul3::Mul; - | -LL | use mul4::Mul; +LL | use std::ops::Mul; | - and 2 other candidates error[E0405]: cannot find trait `ThisTraitReallyDoesntExistInAnyModuleReally` in this scope --> $DIR/issue-21221-1.rs:63:6 @@ -43,7 +40,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..d4fd7cb1257e0 100644 --- a/src/test/ui/resolve/issue-21221-2.stderr +++ b/src/test/ui/resolve/issue-21221-2.stderr @@ -4,7 +4,9 @@ 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 one of these items + | +LL | use baz::T; | 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-23305.stderr b/src/test/ui/resolve/issue-23305.stderr index 0ed3af81d5668..525b5cf7d8417 100644 --- a/src/test/ui/resolve/issue-23305.stderr +++ b/src/test/ui/resolve/issue-23305.stderr @@ -1,10 +1,10 @@ -error[E0391]: cycle detected when processing `` +error[E0391]: cycle detected when computing type of `` --> $DIR/issue-23305.rs:5:16 | LL | impl dyn ToNbt {} | ^^^^ | - = note: ...which again requires processing ``, completing the cycle + = note: ...which again requires computing type of ``, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/issue-23305.rs:1:1 | diff --git a/src/test/ui/resolve/issue-2356.stderr b/src/test/ui/resolve/issue-2356.stderr index 329543114a610..b687f0b0af0ad 100644 --- a/src/test/ui/resolve/issue-2356.stderr +++ b/src/test/ui/resolve/issue-2356.stderr @@ -14,7 +14,16 @@ error[E0425]: cannot find function `default` in this scope --> $DIR/issue-2356.rs:31:5 | LL | default(); - | ^^^^^^^ help: try: `Self::default` + | ^^^^^^^ + | +help: try + | +LL | Self::default(); + | ^^^^^^^^^^^^^ +help: consider importing this function + | +LL | use std::default::default; + | error[E0425]: cannot find value `whiskers` in this scope --> $DIR/issue-2356.rs:39:5 diff --git a/src/test/ui/resolve/issue-3907-2.stderr b/src/test/ui/resolve/issue-3907-2.stderr index d0c278d12d70a..bd6e9d5950272 100644 --- a/src/test/ui/resolve/issue-3907-2.stderr +++ b/src/test/ui/resolve/issue-3907-2.stderr @@ -3,8 +3,11 @@ error[E0038]: the trait `issue_3907::Foo` cannot be made into an object | LL | fn bar(_x: Foo) {} | ^^^ the trait `issue_3907::Foo` cannot be made into an object + | + ::: $DIR/auxiliary/issue-3907.rs:2:8 | - = note: the trait cannot be made into an object because associated function `bar` has no `self` parameter +LL | fn bar(); + | --- the trait cannot be made into an object because associated function `bar` has no `self` parameter error: aborting due to previous error 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 1674c166ddacb..41dff2fe54205 100644 --- a/src/test/ui/resolve/issue-5035.stderr +++ b/src/test/ui/resolve/issue-5035.stderr @@ -8,7 +8,7 @@ error[E0404]: expected trait, found type alias `K` --> $DIR/issue-5035.rs:3:6 | LL | trait I {} - | ---------- similarly named trait `I` defined here + | ------- similarly named trait `I` defined here LL | type K = dyn I; LL | impl K for isize {} | ^ @@ -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 63d3431ec9b2f..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,7 +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 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 82e2aa2db8e25..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,14 +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: 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 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 a622d6cb34948..68e0cf08ffacf 100644 --- a/src/test/ui/resolve/levenshtein.stderr +++ b/src/test/ui/resolve/levenshtein.stderr @@ -1,20 +1,20 @@ 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 + | -------- similarly named enum `Bar` defined here LL | 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,16 +40,16 @@ 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 + | ------------ similarly named function `foo_bar` defined here ... 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 08a1d790197a6..16baa6c9b6233 100644 --- a/src/test/ui/resolve/privacy-enum-ctor.stderr +++ b/src/test/ui/resolve/privacy-enum-ctor.stderr @@ -16,15 +16,11 @@ LL | m::Z::Unit; error[E0423]: expected value, found enum `Z` --> $DIR/privacy-enum-ctor.rs:25:9 | -LL | / fn f() { -LL | | n::Z; -LL | | -LL | | Z; - | | ^ -... | -LL | | // This is ok, it is equivalent to not having braces -LL | | } - | |_____- similarly named function `f` defined here +LL | fn f() { + | ------ similarly named function `f` defined here +... +LL | Z; + | ^ | help: a function with a similar name exists | @@ -53,17 +49,11 @@ LL | let _: Z = Z::Struct; error[E0423]: expected value, found enum `m::E` --> $DIR/privacy-enum-ctor.rs:41:16 | -LL | / fn f() { -LL | | n::Z; -LL | | -LL | | Z; -... | -LL | | // This is ok, it is equivalent to not having braces -LL | | } - | |_____- similarly named function `f` defined here +LL | fn f() { + | ------ similarly named function `f` defined here ... -LL | let _: E = m::E; - | ^^^^ +LL | let _: E = m::E; + | ^^^^ | help: a function with a similar name exists | @@ -77,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; | @@ -109,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,25 +120,19 @@ LL | let _: E = E::Struct; error[E0412]: cannot find type `Z` in this scope --> $DIR/privacy-enum-ctor.rs:57:12 | -LL | / pub enum E { -LL | | Fn(u8), -LL | | Struct { -LL | | s: u8, -LL | | }, -LL | | Unit, -LL | | } - | |_____- similarly named enum `E` defined here +LL | pub enum E { + | ---------- similarly named enum `E` defined here ... -LL | let _: Z = m::n::Z; - | ^ +LL | let _: Z = m::n::Z; + | ^ | 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; +LL | use m::Z; | error[E0423]: expected value, found enum `m::n::Z` @@ -169,49 +153,37 @@ LL | let _: Z = m::Z::Unit; error[E0412]: cannot find type `Z` in this scope --> $DIR/privacy-enum-ctor.rs:61:12 | -LL | / pub enum E { -LL | | Fn(u8), -LL | | Struct { -LL | | s: u8, -LL | | }, -LL | | Unit, -LL | | } - | |_____- similarly named enum `E` defined here +LL | pub enum E { + | ---------- similarly named enum `E` defined here ... -LL | let _: Z = m::n::Z::Fn; - | ^ +LL | let _: Z = m::n::Z::Fn; + | ^ | 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; +LL | use m::Z; | error[E0412]: cannot find type `Z` in this scope --> $DIR/privacy-enum-ctor.rs:64:12 | -LL | / pub enum E { -LL | | Fn(u8), -LL | | Struct { -LL | | s: u8, -LL | | }, -LL | | Unit, -LL | | } - | |_____- similarly named enum `E` defined here +LL | pub enum E { + | ---------- similarly named enum `E` defined here ... -LL | let _: Z = m::n::Z::Struct; - | ^ +LL | let _: Z = m::n::Z::Struct; + | ^ | 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; +LL | use m::Z; | error[E0423]: expected value, found struct variant `m::n::Z::Struct` @@ -228,32 +200,26 @@ LL | let _: Z = m::n::Z::Struct; error[E0412]: cannot find type `Z` in this scope --> $DIR/privacy-enum-ctor.rs:68:12 | -LL | / pub enum E { -LL | | Fn(u8), -LL | | Struct { -LL | | s: u8, -LL | | }, -LL | | Unit, -LL | | } - | |_____- similarly named enum `E` defined here +LL | pub enum E { + | ---------- similarly named enum `E` defined here ... -LL | let _: Z = m::n::Z::Unit {}; - | ^ +LL | let _: Z = m::n::Z::Unit {}; + | ^ | 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; +LL | use m::Z; | error[E0603]: enum `Z` is private --> $DIR/privacy-enum-ctor.rs:57:22 | LL | let _: Z = m::n::Z; - | ^ this enum is private + | ^ private enum | note: the enum `Z` is defined here --> $DIR/privacy-enum-ctor.rs:11:9 @@ -265,7 +231,7 @@ error[E0603]: enum `Z` is private --> $DIR/privacy-enum-ctor.rs:61:22 | LL | let _: Z = m::n::Z::Fn; - | ^ this enum is private + | ^ private enum | note: the enum `Z` is defined here --> $DIR/privacy-enum-ctor.rs:11:9 @@ -277,7 +243,7 @@ error[E0603]: enum `Z` is private --> $DIR/privacy-enum-ctor.rs:64:22 | LL | let _: Z = m::n::Z::Struct; - | ^ this enum is private + | ^ private enum | note: the enum `Z` is defined here --> $DIR/privacy-enum-ctor.rs:11:9 @@ -289,7 +255,7 @@ error[E0603]: enum `Z` is private --> $DIR/privacy-enum-ctor.rs:68:22 | LL | let _: Z = m::n::Z::Unit {}; - | ^ this enum is private + | ^ private enum | note: the enum `Z` is defined here --> $DIR/privacy-enum-ctor.rs:11:9 @@ -304,14 +270,16 @@ LL | Fn(u8), | ------ fn(u8) -> m::n::Z {m::n::Z::Fn} defined here ... LL | let _: Z = Z::Fn; - | - ^^^^^ - | | | - | | expected enum `m::n::Z`, found fn item - | | help: use parentheses to instantiate this tuple variant: `Z::Fn(_)` + | - ^^^^^ expected enum `m::n::Z`, found fn item + | | | expected due to this | = note: expected enum `m::n::Z` found fn item `fn(u8) -> m::n::Z {m::n::Z::Fn}` +help: use parentheses to instantiate this tuple variant + | +LL | let _: Z = Z::Fn(_); + | ^^^ error[E0618]: expected function, found enum variant `Z::Unit` --> $DIR/privacy-enum-ctor.rs:31:17 @@ -336,14 +304,16 @@ LL | Fn(u8), | ------ fn(u8) -> m::E {m::E::Fn} defined here ... LL | let _: E = m::E::Fn; - | - ^^^^^^^^ - | | | - | | expected enum `m::E`, found fn item - | | help: use parentheses to instantiate this tuple variant: `m::E::Fn(_)` + | - ^^^^^^^^ expected enum `m::E`, found fn item + | | | expected due to this | = note: expected enum `m::E` found fn item `fn(u8) -> m::E {m::E::Fn}` +help: use parentheses to instantiate this tuple variant + | +LL | let _: E = m::E::Fn(_); + | ^^^ error[E0618]: expected function, found enum variant `m::E::Unit` --> $DIR/privacy-enum-ctor.rs:47:16 @@ -368,14 +338,16 @@ LL | Fn(u8), | ------ fn(u8) -> m::E {m::E::Fn} defined here ... LL | let _: E = E::Fn; - | - ^^^^^ - | | | - | | expected enum `m::E`, found fn item - | | help: use parentheses to instantiate this tuple variant: `E::Fn(_)` + | - ^^^^^ expected enum `m::E`, found fn item + | | | expected due to this | = note: expected enum `m::E` found fn item `fn(u8) -> m::E {m::E::Fn}` +help: use parentheses to instantiate this tuple variant + | +LL | let _: E = E::Fn(_); + | ^^^ error[E0618]: expected function, found enum variant `E::Unit` --> $DIR/privacy-enum-ctor.rs:55:16 diff --git a/src/test/ui/resolve/privacy-struct-ctor.stderr b/src/test/ui/resolve/privacy-struct-ctor.stderr index 1673ec46ba488..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; | @@ -45,7 +45,7 @@ LL | pub(in m) struct Z(pub(in m::n) u8); | --------------- a constructor is private if any of the fields is private ... LL | n::Z; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `Z` is defined here --> $DIR/privacy-struct-ctor.rs:12:9 @@ -60,7 +60,7 @@ LL | pub struct S(u8); | -- a constructor is private if any of the fields is private ... LL | m::S; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `S` is defined here --> $DIR/privacy-struct-ctor.rs:6:5 @@ -75,7 +75,7 @@ LL | pub struct S(u8); | -- a constructor is private if any of the fields is private ... LL | let _: S = m::S(2); - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `S` is defined here --> $DIR/privacy-struct-ctor.rs:6:5 @@ -90,7 +90,7 @@ LL | pub(in m) struct Z(pub(in m::n) u8); | --------------- a constructor is private if any of the fields is private ... LL | m::n::Z; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | note: the tuple struct constructor `Z` is defined here --> $DIR/privacy-struct-ctor.rs:12:9 @@ -102,7 +102,7 @@ error[E0603]: tuple struct constructor `S` is private --> $DIR/privacy-struct-ctor.rs:41:16 | LL | xcrate::m::S; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy-struct-ctor.rs:2:18 | @@ -119,7 +119,7 @@ error[E0603]: tuple struct constructor `Z` is private --> $DIR/privacy-struct-ctor.rs:45:19 | LL | xcrate::m::n::Z; - | ^ this tuple struct constructor is private + | ^ private tuple struct constructor | ::: $DIR/auxiliary/privacy-struct-ctor.rs:5:28 | diff --git a/src/test/ui/resolve/raw-ident-in-path.rs b/src/test/ui/resolve/raw-ident-in-path.rs new file mode 100644 index 0000000000000..1bcbef5943741 --- /dev/null +++ b/src/test/ui/resolve/raw-ident-in-path.rs @@ -0,0 +1,5 @@ +// Regression test for issue #63882. + +type A = crate::r#break; //~ ERROR cannot find type `r#break` in module `crate` + +fn main() {} diff --git a/src/test/ui/resolve/raw-ident-in-path.stderr b/src/test/ui/resolve/raw-ident-in-path.stderr new file mode 100644 index 0000000000000..f2efcbc8e8586 --- /dev/null +++ b/src/test/ui/resolve/raw-ident-in-path.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `r#break` in module `crate` + --> $DIR/raw-ident-in-path.rs:3:17 + | +LL | type A = crate::r#break; + | ^^^^^^^ not found in `crate` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/resolve/resolve-hint-macro.stderr b/src/test/ui/resolve/resolve-hint-macro.stderr index ebe3c36f21eb1..361da4cc78e00 100644 --- a/src/test/ui/resolve/resolve-hint-macro.stderr +++ b/src/test/ui/resolve/resolve-hint-macro.stderr @@ -2,7 +2,12 @@ error[E0423]: expected function, found macro `assert` --> $DIR/resolve-hint-macro.rs:2:5 | LL | assert(true); - | ^^^^^^ help: use `!` to invoke the macro: `assert!` + | ^^^^^^ + | +help: use `!` to invoke the macro + | +LL | assert!(true); + | ^ error: aborting due to previous error 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/resolve-self-in-impl.stderr b/src/test/ui/resolve/resolve-self-in-impl.stderr index 58e851e5c12ca..5b5c1834cad19 100644 --- a/src/test/ui/resolve/resolve-self-in-impl.stderr +++ b/src/test/ui/resolve/resolve-self-in-impl.stderr @@ -1,10 +1,10 @@ -error[E0391]: cycle detected when processing `` +error[E0391]: cycle detected when computing type of `` --> $DIR/resolve-self-in-impl.rs:14:13 | LL | impl Tr for Self {} | ^^^^ | - = note: ...which again requires processing ``, completing the cycle + = note: ...which again requires computing type of ``, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/resolve-self-in-impl.rs:1:1 | @@ -17,13 +17,13 @@ LL | | LL | | fn main() {} | |____________^ -error[E0391]: cycle detected when processing `` +error[E0391]: cycle detected when computing type of `` --> $DIR/resolve-self-in-impl.rs:15:15 | LL | impl Tr for S {} | ^^^^ | - = note: ...which again requires processing ``, completing the cycle + = note: ...which again requires computing type of ``, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/resolve-self-in-impl.rs:1:1 | @@ -36,13 +36,13 @@ LL | | LL | | fn main() {} | |____________^ -error[E0391]: cycle detected when processing `` +error[E0391]: cycle detected when computing type of `` --> $DIR/resolve-self-in-impl.rs:16:6 | LL | impl Self {} | ^^^^ | - = note: ...which again requires processing ``, completing the cycle + = note: ...which again requires computing type of ``, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/resolve-self-in-impl.rs:1:1 | @@ -55,13 +55,13 @@ LL | | LL | | fn main() {} | |____________^ -error[E0391]: cycle detected when processing `` +error[E0391]: cycle detected when computing type of `` --> $DIR/resolve-self-in-impl.rs:17:8 | LL | impl S {} | ^^^^ | - = note: ...which again requires processing ``, completing the cycle + = note: ...which again requires computing type of ``, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/resolve-self-in-impl.rs:1:1 | @@ -74,13 +74,13 @@ LL | | LL | | fn main() {} | |____________^ -error[E0391]: cycle detected when processing `` +error[E0391]: cycle detected when computing trait implemented by `` --> $DIR/resolve-self-in-impl.rs:18:1 | LL | impl Tr for S {} | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: ...which again requires processing ``, completing the cycle + = note: ...which again requires computing trait implemented by ``, completing the cycle note: cycle used when collecting item types in top-level module --> $DIR/resolve-self-in-impl.rs:1:1 | 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.rs b/src/test/ui/resolve/use_suggestion.rs new file mode 100644 index 0000000000000..8c9bc6d76b8b2 --- /dev/null +++ b/src/test/ui/resolve/use_suggestion.rs @@ -0,0 +1,7 @@ +fn main() { + let x1 = HashMap::new(); //~ ERROR failed to resolve + let x2 = GooMap::new(); //~ ERROR failed to resolve + + let y1: HashMap; //~ ERROR cannot find type + let y2: GooMap; //~ ERROR cannot find type +} diff --git a/src/test/ui/resolve/use_suggestion.stderr b/src/test/ui/resolve/use_suggestion.stderr new file mode 100644 index 0000000000000..72dda94072962 --- /dev/null +++ b/src/test/ui/resolve/use_suggestion.stderr @@ -0,0 +1,38 @@ +error[E0433]: failed to resolve: use of undeclared type or module `GooMap` + --> $DIR/use_suggestion.rs:3:14 + | +LL | let x2 = GooMap::new(); + | ^^^^^^ use of undeclared type or module `GooMap` + +error[E0433]: failed to resolve: use of undeclared type or module `HashMap` + --> $DIR/use_suggestion.rs:2:14 + | +LL | let x1 = HashMap::new(); + | ^^^^^^^ not found in this scope + | +help: consider importing this struct + | +LL | use std::collections::HashMap; + | + +error[E0412]: cannot find type `HashMap` in this scope + --> $DIR/use_suggestion.rs:5:13 + | +LL | let y1: HashMap; + | ^^^^^^^ not found in this scope + | +help: consider importing this struct + | +LL | use std::collections::HashMap; + | + +error[E0412]: cannot find type `GooMap` in this scope + --> $DIR/use_suggestion.rs:6:13 + | +LL | let y2: GooMap; + | ^^^^^^ not found in this scope + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0412, E0433. +For more information about an error, try `rustc --explain E0412`. diff --git a/src/test/ui/resolve/use_suggestion_placement.stderr b/src/test/ui/resolve/use_suggestion_placement.stderr index ef451ea847a37..3f91760fe216b 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,12 +26,10 @@ 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 this struct | LL | use std::collections::HashMap; | -LL | use std::collections::hash_map::HashMap; - | error: aborting due to 3 previous errors 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 f992988c93fcc..3bc38830537cf 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/struct.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/struct.stderr @@ -14,7 +14,7 @@ error[E0603]: tuple struct constructor `TupleStruct` is private --> $DIR/struct.rs:23:32 | LL | let ts_explicit = structs::TupleStruct(640, 480); - | ^^^^^^^^^^^ this tuple struct constructor is private + | ^^^^^^^^^^^ private tuple struct constructor | ::: $DIR/auxiliary/structs.rs:11:24 | @@ -31,7 +31,7 @@ error[E0603]: unit struct `UnitStruct` is private --> $DIR/struct.rs:32:32 | LL | let us_explicit = structs::UnitStruct; - | ^^^^^^^^^^ this unit struct is private + | ^^^^^^^^^^ private unit struct | note: the unit struct `UnitStruct` is defined here --> $DIR/auxiliary/structs.rs:8:1 @@ -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 ccc25a184e946..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,14 +23,23 @@ 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 | LL | match x {} | ^ patterns `Tuple(_)` and `Struct { .. }` not covered + | + ::: $DIR/auxiliary/uninhabited.rs:17:23 + | +LL | #[non_exhaustive] Tuple(!), + | ----- not covered +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 a54885c96e5ee..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,14 +23,23 @@ 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 | LL | match x {} | ^ patterns `Tuple(_)` and `Struct { .. }` not covered + | + ::: $DIR/auxiliary/uninhabited.rs:17:23 + | +LL | #[non_exhaustive] Tuple(!), + | ----- not covered +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 2a438753a2c70..fbdbb0c9930a6 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/variant.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/variant.stderr @@ -2,7 +2,7 @@ error[E0603]: tuple variant `Tuple` is private --> $DIR/variant.rs:11:48 | LL | let variant_tuple = NonExhaustiveVariants::Tuple(640); - | ^^^^^ this tuple variant is private + | ^^^^^ private tuple variant | note: the tuple variant `Tuple` is defined here --> $DIR/auxiliary/variants.rs:5:23 @@ -14,7 +14,7 @@ error[E0603]: unit variant `Unit` is private --> $DIR/variant.rs:14:47 | LL | let variant_unit = NonExhaustiveVariants::Unit; - | ^^^^ this unit variant is private + | ^^^^ private unit variant | note: the unit variant `Unit` is defined here --> $DIR/auxiliary/variants.rs:4:23 @@ -26,7 +26,7 @@ error[E0603]: unit variant `Unit` is private --> $DIR/variant.rs:18:32 | LL | NonExhaustiveVariants::Unit => "", - | ^^^^ this unit variant is private + | ^^^^ private unit variant | note: the unit variant `Unit` is defined here --> $DIR/auxiliary/variants.rs:4:23 @@ -38,7 +38,7 @@ error[E0603]: tuple variant `Tuple` is private --> $DIR/variant.rs:20:32 | LL | NonExhaustiveVariants::Tuple(fe_tpl) => "", - | ^^^^^ this tuple variant is private + | ^^^^^ private tuple variant | note: the tuple variant `Tuple` is defined here --> $DIR/auxiliary/variants.rs:5:23 @@ -50,7 +50,7 @@ error[E0603]: tuple variant `Tuple` is private --> $DIR/variant.rs:26:35 | LL | if let NonExhaustiveVariants::Tuple(fe_tpl) = variant_struct { - | ^^^^^ this tuple variant is private + | ^^^^^ private tuple variant | note: the tuple variant `Tuple` is defined here --> $DIR/auxiliary/variants.rs:5:23 @@ -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/call-chain.rs b/src/test/ui/rfc-2091-track-caller/call-chain.rs new file mode 100644 index 0000000000000..3f1c8f7abe8b8 --- /dev/null +++ b/src/test/ui/rfc-2091-track-caller/call-chain.rs @@ -0,0 +1,28 @@ +// run-pass + +#![feature(track_caller)] + +use std::panic::Location; + +struct Foo; + +impl Foo { + #[track_caller] + fn check_loc(&self, line: u32, col: u32) -> &Self { + let loc = Location::caller(); + assert_eq!(loc.file(), file!(), "file mismatch"); + assert_eq!(loc.line(), line, "line mismatch"); + assert_eq!(loc.column(), col, "column mismatch"); + self + } +} + +fn main() { + // Tests that when `Location::caller` is used in a method chain, + // it points to the start of the correct call (the first character after the dot) + // instead of to the very first expression in the chain + let foo = Foo; + foo. + check_loc(line!(), 9).check_loc(line!(), 31) + .check_loc(line!(), 10); +} 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-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/error-with-trait-decl.rs b/src/test/ui/rfc-2091-track-caller/error-with-trait-decl.rs deleted file mode 100644 index ef037ab62aa3e..0000000000000 --- a/src/test/ui/rfc-2091-track-caller/error-with-trait-decl.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(track_caller)] - -trait Trait { - #[track_caller] //~ ERROR: `#[track_caller]` may not be used on trait methods - fn unwrap(&self); -} - -impl Trait for u64 { - fn unwrap(&self) {} -} - -fn main() {} diff --git a/src/test/ui/rfc-2091-track-caller/error-with-trait-decl.stderr b/src/test/ui/rfc-2091-track-caller/error-with-trait-decl.stderr deleted file mode 100644 index ded721d278253..0000000000000 --- a/src/test/ui/rfc-2091-track-caller/error-with-trait-decl.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0738]: `#[track_caller]` may not be used on trait methods - --> $DIR/error-with-trait-decl.rs:4: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-trait-default-impl.rs b/src/test/ui/rfc-2091-track-caller/error-with-trait-default-impl.rs deleted file mode 100644 index 17e4bf41ddb53..0000000000000 --- a/src/test/ui/rfc-2091-track-caller/error-with-trait-default-impl.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![feature(track_caller)] - -trait Trait { - #[track_caller] //~ ERROR: `#[track_caller]` may not be used on trait methods - fn unwrap(&self) {} -} - -fn main() {} diff --git a/src/test/ui/rfc-2091-track-caller/error-with-trait-default-impl.stderr b/src/test/ui/rfc-2091-track-caller/error-with-trait-default-impl.stderr deleted file mode 100644 index 867eb918b6e08..0000000000000 --- a/src/test/ui/rfc-2091-track-caller/error-with-trait-default-impl.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0738]: `#[track_caller]` may not be used on trait methods - --> $DIR/error-with-trait-default-impl.rs:4: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-trait-fn-impl.rs b/src/test/ui/rfc-2091-track-caller/error-with-trait-fn-impl.rs deleted file mode 100644 index 75f20f76e660d..0000000000000 --- a/src/test/ui/rfc-2091-track-caller/error-with-trait-fn-impl.rs +++ /dev/null @@ -1,21 +0,0 @@ -// check-fail - -#![feature(track_caller)] - -trait Trait { - fn unwrap(&self); -} - -impl Trait for u64 { - #[track_caller] //~ ERROR: `#[track_caller]` may not be used on trait methods - fn unwrap(&self) {} -} - -struct S; - -impl S { - #[track_caller] // ok - fn foo() {} -} - -fn main() {} diff --git a/src/test/ui/rfc-2091-track-caller/error-with-trait-fn-impl.stderr b/src/test/ui/rfc-2091-track-caller/error-with-trait-fn-impl.stderr deleted file mode 100644 index fafceefbfd839..0000000000000 --- a/src/test/ui/rfc-2091-track-caller/error-with-trait-fn-impl.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0738]: `#[track_caller]` may not be used on trait methods - --> $DIR/error-with-trait-fn-impl.rs:10: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/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-2091-track-caller/tracked-fn-ptr-with-arg.rs b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs index 0407eafbfd41c..b17c1efb3d38c 100644 --- a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs +++ b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs @@ -14,6 +14,49 @@ fn tracked_unit(_: ()) { assert_eq!(location.line(), expected_line, "call shims report location as fn definition"); } +trait Trait { + fn trait_tracked_unit(_: ()); +} + +impl Trait for () { + #[track_caller] + fn trait_tracked_unit(_: ()) { + let expected_line = line!() - 1; + let location = std::panic::Location::caller(); + assert_eq!(location.file(), file!()); + assert_eq!(location.line(), expected_line, "call shims report location as fn definition"); + } +} + +trait TrackedTrait { + #[track_caller] + fn trait_tracked_unit_default(_: ()) { + let expected_line = line!() - 1; + let location = std::panic::Location::caller(); + assert_eq!(location.file(), file!()); + assert_eq!(location.line(), expected_line, "call shims report location as fn definition"); + } +} + +impl TrackedTrait for () {} + +trait BlanketTrackedTrait { + #[track_caller] + fn tracked_blanket(_: ()); +} + +impl BlanketTrackedTrait for () { + fn tracked_blanket(_: ()) { + let expected_line = line!() - 1; + let location = std::panic::Location::caller(); + assert_eq!(location.file(), file!()); + assert_eq!(location.line(), expected_line, "call shims report location as fn definition"); + } +} + fn main() { pass_to_ptr_call(tracked_unit, ()); + pass_to_ptr_call(<() as Trait>::trait_tracked_unit, ()); + pass_to_ptr_call(<() as TrackedTrait>::trait_tracked_unit_default, ()); + pass_to_ptr_call(<() as BlanketTrackedTrait>::tracked_blanket, ()); } diff --git a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs index a4baaa26ced1e..8ee4d4fa16871 100644 --- a/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs +++ b/src/test/ui/rfc-2091-track-caller/tracked-fn-ptr.rs @@ -14,6 +14,49 @@ fn tracked() { assert_eq!(location.line(), expected_line, "call shims report location as fn definition"); } +trait Trait { + fn trait_tracked(); +} + +impl Trait for () { + #[track_caller] + fn trait_tracked() { + let expected_line = line!() - 1; + let location = std::panic::Location::caller(); + assert_eq!(location.file(), file!()); + assert_eq!(location.line(), expected_line, "call shims report location as fn definition"); + } +} + +trait TrackedTrait { + #[track_caller] + fn trait_tracked_default() { + let expected_line = line!() - 1; + let location = std::panic::Location::caller(); + assert_eq!(location.file(), file!()); + assert_eq!(location.line(), expected_line, "call shims report location as fn definition"); + } +} + +impl TrackedTrait for () {} + +trait TraitBlanketTracked { + #[track_caller] + fn tracked_blanket(); +} + +impl TraitBlanketTracked for () { + fn tracked_blanket() { + let expected_line = line!() - 1; + let location = std::panic::Location::caller(); + assert_eq!(location.file(), file!()); + assert_eq!(location.line(), expected_line, "call shims report location as fn definition"); + } +} + fn main() { ptr_call(tracked); + ptr_call(<() as Trait>::trait_tracked); + ptr_call(<() as TrackedTrait>::trait_tracked_default); + ptr_call(<() as TraitBlanketTracked>::tracked_blanket); } diff --git a/src/test/ui/rfc-2091-track-caller/tracked-trait-impls.rs b/src/test/ui/rfc-2091-track-caller/tracked-trait-impls.rs new file mode 100644 index 0000000000000..0a5f92bb635e5 --- /dev/null +++ b/src/test/ui/rfc-2091-track-caller/tracked-trait-impls.rs @@ -0,0 +1,79 @@ +// run-pass + +#![feature(track_caller)] + +macro_rules! assert_expansion_site_is_tracked { + () => {{ + let location = std::panic::Location::caller(); + assert_eq!(location.file(), file!()); + assert_ne!(location.line(), line!(), "line should be outside this fn"); + }} +} + +trait Tracked { + fn local_tracked(&self); + + #[track_caller] + fn blanket_tracked(&self); + + #[track_caller] + fn default_tracked(&self) { + assert_expansion_site_is_tracked!(); + } +} + +impl Tracked for () { + #[track_caller] + fn local_tracked(&self) { + assert_expansion_site_is_tracked!(); + } + + fn blanket_tracked(&self) { + assert_expansion_site_is_tracked!(); + } +} + +impl Tracked for bool { + #[track_caller] + fn local_tracked(&self) { + assert_expansion_site_is_tracked!(); + } + + fn blanket_tracked(&self) { + assert_expansion_site_is_tracked!(); + } + + fn default_tracked(&self) { + assert_expansion_site_is_tracked!(); + } +} + +impl Tracked for u8 { + #[track_caller] + fn local_tracked(&self) { + assert_expansion_site_is_tracked!(); + } + + fn blanket_tracked(&self) { + assert_expansion_site_is_tracked!(); + } + + #[track_caller] + fn default_tracked(&self) { + assert_expansion_site_is_tracked!(); + } +} + +fn main() { + ().local_tracked(); + ().default_tracked(); + ().blanket_tracked(); + + true.local_tracked(); + true.default_tracked(); + true.blanket_tracked(); + + 0u8.local_tracked(); + 0u8.default_tracked(); + 0u8.blanket_tracked(); +} diff --git a/src/test/ui/rfc-2093-infer-outlives/dont-infer-static.stderr b/src/test/ui/rfc-2093-infer-outlives/dont-infer-static.stderr index c3cfc5a4d97c8..2bb51731583a6 100644 --- a/src/test/ui/rfc-2093-infer-outlives/dont-infer-static.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/dont-infer-static.stderr @@ -4,13 +4,7 @@ error[E0310]: the parameter type `U` may not live long enough LL | struct Foo { | - help: consider adding an explicit lifetime bound...: `U: 'static` LL | bar: Bar - | ^^^^^^^^^^^ - | -note: ...so that the type `U` will meet its required lifetime bounds - --> $DIR/dont-infer-static.rs:8:5 - | -LL | bar: Bar - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ ...so that the type `U` will meet its required lifetime bounds error: aborting due to previous error diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr b/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr index 297fcb088d2bf..e32a36f72cd14 100644 --- a/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr @@ -4,13 +4,7 @@ error[E0309]: the parameter type `T` may not live long enough LL | enum Ref1<'a, T> { | - help: consider adding an explicit lifetime bound...: `T: 'a` LL | Ref1Variant1(RequireOutlives<'a, T>) - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-enum-not-wf.rs:18:18 - | -LL | Ref1Variant1(RequireOutlives<'a, T>) - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-enum-not-wf.rs:23:25 @@ -19,13 +13,7 @@ LL | enum Ref2<'a, T> { | - help: consider adding an explicit lifetime bound...: `T: 'a` LL | Ref2Variant1, LL | Ref2Variant2(isize, RequireOutlives<'a, T>), - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-enum-not-wf.rs:23:25 - | -LL | Ref2Variant2(isize, RequireOutlives<'a, T>), - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-enum-not-wf.rs:35:1 @@ -37,16 +25,7 @@ LL | enum RefDouble<'a, 'b, T> { LL | | RefDoubleVariant1(&'a RequireOutlives<'b, T>) LL | | LL | | } - | |_^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-enum-not-wf.rs:35:1 - | -LL | / enum RefDouble<'a, 'b, T> { -LL | | RefDoubleVariant1(&'a RequireOutlives<'b, T>) -LL | | -LL | | } - | |_^ + | |_^ ...so that the type `T` will meet its required lifetime bounds error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-enum-not-wf.rs:36:23 @@ -54,13 +33,7 @@ error[E0309]: the parameter type `T` may not live long enough LL | enum RefDouble<'a, 'b, T> { | - help: consider adding an explicit lifetime bound...: `T: 'b` LL | RefDoubleVariant1(&'a RequireOutlives<'b, T>) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-enum-not-wf.rs:36:23 - | -LL | RefDoubleVariant1(&'a RequireOutlives<'b, T>) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error: aborting due to 4 previous errors diff --git a/src/test/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr b/src/test/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr index f6658891fa622..44812a51778a7 100644 --- a/src/test/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr +++ b/src/test/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr @@ -4,13 +4,7 @@ error[E0309]: the parameter type `T` may not live long enough LL | impl<'a, T> Trait<'a, T> for usize { | - help: consider adding an explicit lifetime bound...: `T: 'a` LL | type Out = &'a T; - | ^^^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'a T` does not outlive the data it points at - --> $DIR/regions-struct-not-wf.rs:13:5 - | -LL | type Out = &'a T; - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at error[E0309]: the parameter type `T` may not live long enough --> $DIR/regions-struct-not-wf.rs:21:5 @@ -18,13 +12,7 @@ error[E0309]: the parameter type `T` may not live long enough LL | impl<'a, T> Trait<'a, T> for u32 { | - help: consider adding an explicit lifetime bound...: `T: 'a` LL | type Out = RefOk<'a, T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/regions-struct-not-wf.rs:21:5 - | -LL | type Out = RefOk<'a, T>; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds error[E0491]: in type `&'a &'b T`, reference has a longer lifetime than the data it references --> $DIR/regions-struct-not-wf.rs:25:5 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/crate_name_nonascii_forbidden-1.rs b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs new file mode 100644 index 0000000000000..3fb1cf9f557b2 --- /dev/null +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs @@ -0,0 +1,6 @@ +#![feature(non_ascii_idents)] + +extern crate ьаг; //~ ERROR cannot load a crate with a non-ascii name `ьаг` +//~| ERROR can't find crate for `ьаг` + +fn main() {} diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr new file mode 100644 index 0000000000000..1e424237fd238 --- /dev/null +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr @@ -0,0 +1,15 @@ +error: cannot load a crate with a non-ascii name `ьаг` + --> $DIR/crate_name_nonascii_forbidden-1.rs:3:1 + | +LL | extern crate ьаг; + | ^^^^^^^^^^^^^^^^^ + +error[E0463]: can't find crate for `ьаг` + --> $DIR/crate_name_nonascii_forbidden-1.rs:3:1 + | +LL | extern crate ьаг; + | ^^^^^^^^^^^^^^^^^ can't find crate + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0463`. diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs new file mode 100644 index 0000000000000..e1acdbff06189 --- /dev/null +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs @@ -0,0 +1,9 @@ +// compile-flags:--extern му_сгате +// edition:2018 +#![feature(non_ascii_idents)] + +use му_сгате::baz; //~ ERROR cannot load a crate with a non-ascii name `му_сгате` + //~| can't find crate for `му_сгате` + + +fn main() {} diff --git a/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr new file mode 100644 index 0000000000000..c06405ebb37ec --- /dev/null +++ b/src/test/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr @@ -0,0 +1,15 @@ +error: cannot load a crate with a non-ascii name `му_сгате` + --> $DIR/crate_name_nonascii_forbidden-2.rs:5:5 + | +LL | use му_сгате::baz; + | ^^^^^^^^ + +error[E0463]: can't find crate for `му_сгате` + --> $DIR/crate_name_nonascii_forbidden-2.rs:5:5 + | +LL | use му_сгате::baz; + | ^^^^^^^^ can't find crate + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0463`. 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-2565-param-attrs/param-attrs-cfg.stderr b/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr index 82099066a89d2..1ced8d8a14a53 100644 --- a/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr +++ b/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr @@ -2,7 +2,7 @@ error: unused variable: `a` --> $DIR/param-attrs-cfg.rs:24:23 | LL | #[cfg(something)] a: i32, - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` | note: the lint level is defined here --> $DIR/param-attrs-cfg.rs:5:9 @@ -14,109 +14,109 @@ error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:30:23 | LL | #[cfg(something)] b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `c` --> $DIR/param-attrs-cfg.rs:32:40 | LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, - | ^ help: consider prefixing with an underscore: `_c` + | ^ help: if this is intentional, prefix it with an underscore: `_c` error: unused variable: `a` --> $DIR/param-attrs-cfg.rs:107:27 | LL | #[cfg(something)] a: i32, - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:113:27 | LL | #[cfg(something)] b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `c` --> $DIR/param-attrs-cfg.rs:115:44 | LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, - | ^ help: consider prefixing with an underscore: `_c` + | ^ help: if this is intentional, prefix it with an underscore: `_c` error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:67:27 | LL | #[cfg(something)] b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `c` --> $DIR/param-attrs-cfg.rs:69:44 | LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, - | ^ help: consider prefixing with an underscore: `_c` + | ^ help: if this is intentional, prefix it with an underscore: `_c` error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:75:27 | LL | #[cfg(something)] b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `c` --> $DIR/param-attrs-cfg.rs:77:44 | LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, - | ^ help: consider prefixing with an underscore: `_c` + | ^ help: if this is intentional, prefix it with an underscore: `_c` error: unused variable: `a` --> $DIR/param-attrs-cfg.rs:41:27 | LL | #[cfg(something)] a: i32, - | ^ help: consider prefixing with an underscore: `_a` + | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:48:27 | LL | #[cfg(something)] b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `c` --> $DIR/param-attrs-cfg.rs:50:44 | LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, - | ^ help: consider prefixing with an underscore: `_c` + | ^ help: if this is intentional, prefix it with an underscore: `_c` error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:56:27 | LL | #[cfg(something)] b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `c` --> $DIR/param-attrs-cfg.rs:58:44 | LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, - | ^ help: consider prefixing with an underscore: `_c` + | ^ help: if this is intentional, prefix it with an underscore: `_c` error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:86:27 | LL | #[cfg(something)] b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `c` --> $DIR/param-attrs-cfg.rs:88:44 | LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, - | ^ help: consider prefixing with an underscore: `_c` + | ^ help: if this is intentional, prefix it with an underscore: `_c` error: unused variable: `b` --> $DIR/param-attrs-cfg.rs:94:27 | LL | #[cfg(something)] b: i32, - | ^ help: consider prefixing with an underscore: `_b` + | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `c` --> $DIR/param-attrs-cfg.rs:96:44 | LL | #[cfg_attr(nothing, cfg(nothing))] c: i32, - | ^ help: consider prefixing with an underscore: `_c` + | ^ help: if this is intentional, prefix it with an underscore: `_c` error: aborting due to 19 previous errors 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/rfc1623.nll.stderr b/src/test/ui/rfc1623.nll.stderr new file mode 100644 index 0000000000000..848d4fef1abfc --- /dev/null +++ b/src/test/ui/rfc1623.nll.stderr @@ -0,0 +1,68 @@ +error[E0277]: `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` cannot be shared between threads safely + --> $DIR/rfc1623.rs:21:1 + | +LL | / static SOME_STRUCT: &SomeStruct = &SomeStruct { +LL | | foo: &Foo { bools: &[false, true] }, +LL | | bar: &Bar { bools: &[true, true] }, +LL | | f: &id, +LL | | +LL | | }; + | |__^ `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` cannot be shared between threads safely + | + = help: within `&SomeStruct`, the trait `std::marker::Sync` is not implemented for `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` + = note: required because it appears within the type `&dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` + = note: required because it appears within the type `SomeStruct` + = note: required because it appears within the type `&SomeStruct` + = note: shared static variables must have a type that implements `Sync` + +error: higher-ranked subtype error + --> $DIR/rfc1623.rs:21:35 + | +LL | static SOME_STRUCT: &SomeStruct = &SomeStruct { + | ___________________________________^ +LL | | foo: &Foo { bools: &[false, true] }, +LL | | bar: &Bar { bools: &[true, true] }, +LL | | f: &id, +LL | | +LL | | }; + | |_^ + +error: higher-ranked subtype error + --> $DIR/rfc1623.rs:21:35 + | +LL | static SOME_STRUCT: &SomeStruct = &SomeStruct { + | ___________________________________^ +LL | | foo: &Foo { bools: &[false, true] }, +LL | | bar: &Bar { bools: &[true, true] }, +LL | | f: &id, +LL | | +LL | | }; + | |_^ + +error: higher-ranked subtype error + --> $DIR/rfc1623.rs:21:35 + | +LL | static SOME_STRUCT: &SomeStruct = &SomeStruct { + | ___________________________________^ +LL | | foo: &Foo { bools: &[false, true] }, +LL | | bar: &Bar { bools: &[true, true] }, +LL | | f: &id, +LL | | +LL | | }; + | |_^ + +error: higher-ranked subtype error + --> $DIR/rfc1623.rs:21:35 + | +LL | static SOME_STRUCT: &SomeStruct = &SomeStruct { + | ___________________________________^ +LL | | foo: &Foo { bools: &[false, true] }, +LL | | bar: &Bar { bools: &[true, true] }, +LL | | f: &id, +LL | | +LL | | }; + | |_^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc1623.rs b/src/test/ui/rfc1623.rs index 55f5d0b94dcb0..aa6b1c0012c93 100644 --- a/src/test/ui/rfc1623.rs +++ b/src/test/ui/rfc1623.rs @@ -8,23 +8,21 @@ fn non_elidable<'a, 'b>(a: &'a u8, b: &'b u8) -> &'a u8 { static NON_ELIDABLE_FN: &for<'a> fn(&'a u8, &'a u8) -> &'a u8 = &(non_elidable as for<'a> fn(&'a u8, &'a u8) -> &'a u8); - struct SomeStruct<'x, 'y, 'z: 'x> { foo: &'x Foo<'z>, bar: &'x Bar<'z>, - f: &'y dyn for<'a, 'b> Fn(&'a Foo<'b>) -> &'a Bar<'b>, + f: &'y dyn for<'a, 'b> Fn(&'a Foo<'b>) -> &'a Foo<'b>, } fn id(t: T) -> T { t } -static SOME_STRUCT: &SomeStruct = SomeStruct { //~ ERROR mismatched types +static SOME_STRUCT: &SomeStruct = &SomeStruct { foo: &Foo { bools: &[false, true] }, bar: &Bar { bools: &[true, true] }, f: &id, - //~^ ERROR type mismatch in function arguments - //~| ERROR type mismatch resolving + //~^ ERROR mismatched types }; // very simple test for a 'static static with default lifetime diff --git a/src/test/ui/rfc1623.stderr b/src/test/ui/rfc1623.stderr index ca956004ef76f..2efc58ac3819c 100644 --- a/src/test/ui/rfc1623.stderr +++ b/src/test/ui/rfc1623.stderr @@ -1,46 +1,12 @@ error[E0308]: mismatched types - --> $DIR/rfc1623.rs:22:35 - | -LL | static SOME_STRUCT: &SomeStruct = SomeStruct { - | ___________________________________^ -LL | | foo: &Foo { bools: &[false, true] }, -LL | | bar: &Bar { bools: &[true, true] }, -LL | | f: &id, -LL | | -LL | | -LL | | }; - | |_^ expected `&SomeStruct<'static, 'static, 'static>`, found struct `SomeStruct` - | -help: consider borrowing here - | -LL | static SOME_STRUCT: &SomeStruct = &SomeStruct { -LL | foo: &Foo { bools: &[false, true] }, -LL | bar: &Bar { bools: &[true, true] }, -LL | f: &id, -LL | -LL | - ... - -error[E0631]: type mismatch in function arguments - --> $DIR/rfc1623.rs:25:8 - | -LL | fn id(t: T) -> T { - | ------------------- found signature of `fn(_) -> _` -... -LL | f: &id, - | ^^^ expected signature of `for<'a, 'b> fn(&'a Foo<'b>) -> _` - | - = note: required for the cast to the object type `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` - -error[E0271]: type mismatch resolving `for<'a, 'b> _ {id::<_>} as std::ops::FnOnce<(&'a Foo<'b>,)>>::Output == &'a Foo<'b>` - --> $DIR/rfc1623.rs:25:8 + --> $DIR/rfc1623.rs:24:8 | LL | f: &id, - | ^^^ expected bound lifetime parameter 'a, found concrete lifetime + | ^^^ one type is more general than the other | - = note: required for the cast to the object type `dyn for<'a, 'b> std::ops::Fn(&'a Foo<'b>) -> &'a Foo<'b>` + = note: expected type `std::ops::FnOnce<(&'a Foo<'b>,)>` + found type `std::ops::FnOnce<(&Foo<'_>,)>` -error: aborting due to 3 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0271, E0308, E0631. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0308`. 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/address.rs b/src/test/ui/sanitize/address.rs index f8650cd86d51e..cee73b0425ad5 100644 --- a/src/test/ui/sanitize/address.rs +++ b/src/test/ui/sanitize/address.rs @@ -1,5 +1,5 @@ // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-address // // compile-flags: -Z sanitizer=address -O -g // diff --git a/src/test/ui/sanitize/badfree.rs b/src/test/ui/sanitize/badfree.rs index 1ca082c8b4704..095a6f4697b1c 100644 --- a/src/test/ui/sanitize/badfree.rs +++ b/src/test/ui/sanitize/badfree.rs @@ -1,5 +1,5 @@ // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-address // // compile-flags: -Z sanitizer=address -O // diff --git a/src/test/ui/sanitize/cfg.rs b/src/test/ui/sanitize/cfg.rs index 9c198543a8664..79dfe58f04d0b 100644 --- a/src/test/ui/sanitize/cfg.rs +++ b/src/test/ui/sanitize/cfg.rs @@ -2,8 +2,10 @@ // the `#[cfg(sanitize = "option")]` attribute is configured. // needs-sanitizer-support -// only-linux -// only-x86_64 +// needs-sanitizer-address +// needs-sanitizer-leak +// needs-sanitizer-memory +// needs-sanitizer-thread // check-pass // revisions: address leak memory thread //[address]compile-flags: -Zsanitizer=address --cfg address diff --git a/src/test/ui/sanitize/incompatible.rs b/src/test/ui/sanitize/incompatible.rs new file mode 100644 index 0000000000000..4947f3b3d8b13 --- /dev/null +++ b/src/test/ui/sanitize/incompatible.rs @@ -0,0 +1,6 @@ +// compile-flags: -Z sanitizer=address -Z sanitizer=memory --target x86_64-unknown-linux-gnu +// error-pattern: error: `-Zsanitizer=address` is incompatible with `-Zsanitizer=memory` + +#![feature(no_core)] +#![no_core] +#![no_main] diff --git a/src/test/ui/sanitize/incompatible.stderr b/src/test/ui/sanitize/incompatible.stderr new file mode 100644 index 0000000000000..f86db41bac717 --- /dev/null +++ b/src/test/ui/sanitize/incompatible.stderr @@ -0,0 +1,4 @@ +error: `-Zsanitizer=address` is incompatible with `-Zsanitizer=memory` + +error: aborting due to previous error + 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..b2e182238ce28 --- /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 +// needs-sanitizer-address +// +// 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/leak.rs b/src/test/ui/sanitize/leak.rs index 5c2f2cb4e868b..c9f10fe4f467e 100644 --- a/src/test/ui/sanitize/leak.rs +++ b/src/test/ui/sanitize/leak.rs @@ -1,5 +1,5 @@ // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-leak // // compile-flags: -Z sanitizer=leak -O // diff --git a/src/test/ui/sanitize/memory.rs b/src/test/ui/sanitize/memory.rs index 3e1cf4509a31f..a26649a580013 100644 --- a/src/test/ui/sanitize/memory.rs +++ b/src/test/ui/sanitize/memory.rs @@ -1,6 +1,5 @@ // needs-sanitizer-support -// only-linux -// only-x86_64 +// needs-sanitizer-memory // // compile-flags: -Z sanitizer=memory -Zsanitizer-memory-track-origins -O // diff --git a/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs index d0984bbe65fd5..64d6ccf340916 100644 --- a/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs +++ b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs @@ -4,7 +4,7 @@ // // min-llvm-version 9.0 // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-address // // no-prefer-dynamic // revisions: opt0 opt1 diff --git a/src/test/ui/sanitize/thread.rs b/src/test/ui/sanitize/thread.rs new file mode 100644 index 0000000000000..c70cf5accc077 --- /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 +// needs-sanitizer-thread +// +// 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/sanitize/unsupported-target.rs b/src/test/ui/sanitize/unsupported-target.rs index 444333c3f01e2..6ccc9988cdecc 100644 --- a/src/test/ui/sanitize/unsupported-target.rs +++ b/src/test/ui/sanitize/unsupported-target.rs @@ -1,6 +1,5 @@ -// ignore-tidy-linelength // compile-flags: -Z sanitizer=leak --target i686-unknown-linux-gnu -// error-pattern: error: LeakSanitizer only works with the `x86_64-unknown-linux-gnu` or `x86_64-apple-darwin` target +// error-pattern: error: `-Zsanitizer=leak` only works with targets: #![feature(no_core)] #![no_core] diff --git a/src/test/ui/sanitize/unsupported-target.stderr b/src/test/ui/sanitize/unsupported-target.stderr index 38be58dd4b365..f5961a11b1f1c 100644 --- a/src/test/ui/sanitize/unsupported-target.stderr +++ b/src/test/ui/sanitize/unsupported-target.stderr @@ -1,4 +1,4 @@ -error: LeakSanitizer only works with the `x86_64-unknown-linux-gnu` or `x86_64-apple-darwin` target +error: `-Zsanitizer=leak` only works with targets: aarch64-unknown-linux-gnu, x86_64-apple-darwin, x86_64-unknown-linux-gnu error: aborting due to previous error diff --git a/src/test/ui/sanitize/use-after-scope.rs b/src/test/ui/sanitize/use-after-scope.rs index 6a2067e157af5..30be2ae6f0906 100644 --- a/src/test/ui/sanitize/use-after-scope.rs +++ b/src/test/ui/sanitize/use-after-scope.rs @@ -1,5 +1,5 @@ // needs-sanitizer-support -// only-x86_64 +// needs-sanitizer-address // // compile-flags: -Zsanitizer=address // run-fail diff --git a/src/test/ui/save-analysis/issue-68621.stderr b/src/test/ui/save-analysis/issue-68621.stderr index 2c5bbd7782b35..3af6d0a3e076e 100644 --- a/src/test/ui/save-analysis/issue-68621.stderr +++ b/src/test/ui/save-analysis/issue-68621.stderr @@ -1,8 +1,8 @@ error: could not find defining uses - --> $DIR/issue-68621.rs:14:5 + --> $DIR/issue-68621.rs:14:19 | LL | type Future = impl Trait; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ error: aborting due to previous error 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/save-analysis/issue-73020.rs b/src/test/ui/save-analysis/issue-73020.rs new file mode 100644 index 0000000000000..87ce0933681c5 --- /dev/null +++ b/src/test/ui/save-analysis/issue-73020.rs @@ -0,0 +1,5 @@ +// compile-flags: -Zsave-analysis +use {self}; //~ ERROR E0431 + +fn main () { +} diff --git a/src/test/ui/save-analysis/issue-73020.stderr b/src/test/ui/save-analysis/issue-73020.stderr new file mode 100644 index 0000000000000..5bb3aae99975c --- /dev/null +++ b/src/test/ui/save-analysis/issue-73020.stderr @@ -0,0 +1,9 @@ +error[E0431]: `self` import can only appear in an import list with a non-empty prefix + --> $DIR/issue-73020.rs:2:6 + | +LL | use {self}; + | ^^^^ can only appear in an import list with a non-empty prefix + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0431`. diff --git a/src/test/ui/save-analysis/issue-73022.rs b/src/test/ui/save-analysis/issue-73022.rs new file mode 100644 index 0000000000000..9ad89a319ba3b --- /dev/null +++ b/src/test/ui/save-analysis/issue-73022.rs @@ -0,0 +1,13 @@ +// build-pass +// compile-flags: -Zsave-analysis +enum Enum2 { + Variant8 { _field: bool }, +} + +impl Enum2 { + fn new_variant8() -> Enum2 { + Self::Variant8 { _field: true } + } +} + +fn main() {} diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index 91075ffbdb605..f2fbb0ba7d755 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -1,16 +1,17 @@ -error: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:16 | LL | async fn f(self: Pin<&Self>) -> impl Clone { self } - | ^^^^ ---------- this return type evaluates to the `'static` lifetime... - | | - | ...but this borrow... + | ^^^^ ---------- ---------- ...and is required to live as long as `'static` here + | | | + | | this data with an anonymous lifetime `'_`... + | ...is captured here... | -note: ...can't outlive the lifetime `'_` as defined on the method body at 8:26 - --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:26 +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound | -LL | async fn f(self: Pin<&Self>) -> impl Clone { self } - | ^ +LL | async fn f(self: Pin<&Self>) -> impl Clone + '_ { self } + | ^^^^ error: aborting due to previous error +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr index 47ab6fff83878..2e10ab3d3f9b8 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -1,20 +1,21 @@ -error: cannot infer an appropriate lifetime +error[E0759]: cannot infer an appropriate lifetime --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:6:44 | LL | fn f(self: Pin<&Self>) -> impl Clone { self } - | ---------- ^^^^ ...but this borrow... - | | - | this return type evaluates to the `'static` lifetime... + | ---------- ^^^^ ...is captured here... + | | + | this data with an anonymous lifetime `'_`... | -note: ...can't outlive the anonymous lifetime #1 defined on the method body at 6:5 - --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:6:5 +note: ...and is required to live as long as `'static` here + --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:6:31 | LL | fn f(self: Pin<&Self>) -> impl Clone { self } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: you can add a bound to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the method body at 6:5 + | ^^^^^^^^^^ +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound | LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self } - | ^^^^^^^^^^^^^^^ + | ^^^^ error: aborting due to previous error +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/self/elision/ref-self-async.nll.stderr b/src/test/ui/self/elision/ref-self-async.nll.stderr index 541e49543221e..bd1f80811b542 100644 --- a/src/test/ui/self/elision/ref-self-async.nll.stderr +++ b/src/test/ui/self/elision/ref-self-async.nll.stderr @@ -1,13 +1,72 @@ -error[E0658]: `Wrap<&Struct, Struct>` cannot be used as the type of `self` without the `arbitrary_self_types` feature - --> $DIR/ref-self-async.rs:47:39 +error: lifetime may not live long enough + --> $DIR/ref-self-async.rs:23:9 | -LL | async fn wrap_ref_Self_Self(self: Wrap<&Self, Self>, f: &u8) -> &u8 { - | ^^^^^^^^^^^^^^^^^ +LL | async fn ref_self(&self, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self-async.rs:29:9 + | +LL | async fn ref_Self(self: &Self, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self-async.rs:33:9 | - = note: see issue #44874 for more information - = help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable - = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) +LL | async fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self-async.rs:37:9 + | +LL | async fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self-async.rs:41:9 + | +LL | async fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self-async.rs:45:9 + | +LL | async fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self-async.rs:49:9 + | +LL | async fn wrap_ref_Self_Self(self: Wrap<&Self, Self>, f: &u8) -> &u8 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` -error: aborting due to previous error +error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/self/elision/ref-self-async.rs b/src/test/ui/self/elision/ref-self-async.rs index 6a98b79cb3bba..0fbbd95c975d6 100644 --- a/src/test/ui/self/elision/ref-self-async.rs +++ b/src/test/ui/self/elision/ref-self-async.rs @@ -1,6 +1,7 @@ // edition:2018 #![allow(non_snake_case)] +#![feature(arbitrary_self_types)] use std::marker::PhantomData; use std::ops::Deref; diff --git a/src/test/ui/self/elision/ref-self-async.stderr b/src/test/ui/self/elision/ref-self-async.stderr index b73290b024f8f..bda958241b67b 100644 --- a/src/test/ui/self/elision/ref-self-async.stderr +++ b/src/test/ui/self/elision/ref-self-async.stderr @@ -1,5 +1,5 @@ error[E0623]: lifetime mismatch - --> $DIR/ref-self-async.rs:22:9 + --> $DIR/ref-self-async.rs:23:9 | LL | async fn ref_self(&self, f: &u32) -> &u32 { | ----- ---- @@ -9,7 +9,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self-async.rs:28:9 + --> $DIR/ref-self-async.rs:29:9 | LL | async fn ref_Self(self: &Self, f: &u32) -> &u32 { | ----- ---- @@ -19,7 +19,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self-async.rs:32:9 + --> $DIR/ref-self-async.rs:33:9 | LL | async fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { | ----- ---- @@ -29,7 +29,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self-async.rs:36:9 + --> $DIR/ref-self-async.rs:37:9 | LL | async fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { | ----- ---- @@ -39,7 +39,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self-async.rs:40:9 + --> $DIR/ref-self-async.rs:41:9 | LL | async fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | ----- ---- @@ -49,7 +49,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self-async.rs:44:9 + --> $DIR/ref-self-async.rs:45:9 | LL | async fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { | ----- ---- @@ -59,7 +59,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self-async.rs:48:9 + --> $DIR/ref-self-async.rs:49:9 | LL | async fn wrap_ref_Self_Self(self: Wrap<&Self, Self>, f: &u8) -> &u8 { | ----- --- 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/shadowed/shadowed-use-visibility.stderr b/src/test/ui/shadowed/shadowed-use-visibility.stderr index cd8ec13794c6f..1a642ae6e8ed8 100644 --- a/src/test/ui/shadowed/shadowed-use-visibility.stderr +++ b/src/test/ui/shadowed/shadowed-use-visibility.stderr @@ -2,25 +2,35 @@ error[E0603]: module import `bar` is private --> $DIR/shadowed-use-visibility.rs:9:14 | LL | use foo::bar::f as g; - | ^^^ this module import is private + | ^^^ private module import | -note: the module import `bar` is defined here +note: the module import `bar` is defined here... --> $DIR/shadowed-use-visibility.rs:4:9 | LL | use foo as bar; | ^^^^^^^^^^ +note: ...and refers to the module `foo` which is defined here + --> $DIR/shadowed-use-visibility.rs:1:1 + | +LL | mod foo { + | ^^^^^^^ error[E0603]: module import `f` is private --> $DIR/shadowed-use-visibility.rs:15:10 | LL | use bar::f::f; - | ^ this module import is private + | ^ private module import | -note: the module import `f` is defined here +note: the module import `f` is defined here... --> $DIR/shadowed-use-visibility.rs:11:9 | LL | use foo as f; | ^^^^^^^^ +note: ...and refers to the module `foo` which is defined here + --> $DIR/shadowed-use-visibility.rs:1:1 + | +LL | mod foo { + | ^^^^^^^ error: aborting due to 2 previous errors 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..b7b3ec997810e 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)] @@ -21,7 +20,7 @@ extern "platform-intrinsic" { fn main() { // unsigned { - const M: u32 = u32::max_value(); + const M: u32 = u32::MAX; let a = u32x4(1, 2, 3, 4); let b = u32x4(2, 4, 6, 8); @@ -49,8 +48,8 @@ fn main() { // signed { - const MIN: i32 = i32::min_value(); - const MAX: i32 = i32::max_value(); + const MIN: i32 = i32::MIN; + const MAX: i32 = i32::MAX; let a = i32x4(1, 2, 3, 4); let b = i32x4(2, 4, 6, 8); diff --git a/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs b/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs index b28f742a92e94..a323bd9e82b4a 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs @@ -39,7 +39,7 @@ fn main() { let e = 0b_1101; // Check usize / isize - let msize: Tx4 = Tx4(usize::max_value(), 0, usize::max_value(), usize::max_value()); + let msize: Tx4 = Tx4(usize::MAX, 0, usize::MAX, usize::MAX); unsafe { let r: u8 = simd_bitmask(z); diff --git a/src/test/ui/simd/simd-intrinsic-generic-reduction.rs b/src/test/ui/simd/simd-intrinsic-generic-reduction.rs index 4195444a73f67..8b5afeac0bc2d 100644 --- a/src/test/ui/simd/simd-intrinsic-generic-reduction.rs +++ b/src/test/ui/simd/simd-intrinsic-generic-reduction.rs @@ -100,7 +100,7 @@ fn main() { let r: u32 = simd_reduce_max(x); assert_eq!(r, 4_u32); - let t = u32::max_value(); + let t = u32::MAX; let x = u32x4(t, t, t, t); let r: u32 = simd_reduce_and(x); assert_eq!(r, t); diff --git a/src/test/ui/sized-cycle-note.stderr b/src/test/ui/sized-cycle-note.stderr index 95bdc34942645..45062c2ea6c72 100644 --- a/src/test/ui/sized-cycle-note.stderr +++ b/src/test/ui/sized-cycle-note.stderr @@ -2,21 +2,27 @@ error[E0072]: recursive type `Baz` has infinite size --> $DIR/sized-cycle-note.rs:9:1 | LL | struct Baz { q: Option } - | ^^^^^^^^^^ -------------- recursive without indirection + | ^^^^^^^^^^ ----------- recursive without indirection | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Baz` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Baz` representable + | +LL | struct Baz { q: Box> } + | ^^^^ ^ error[E0072]: recursive type `Foo` has infinite size --> $DIR/sized-cycle-note.rs:11:1 | LL | struct Foo { q: Option } - | ^^^^^^^^^^ -------------- recursive without indirection + | ^^^^^^^^^^ ----------- recursive without indirection | | | recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable + | +LL | struct Foo { q: Box> } + | ^^^^ ^ error: aborting due to 2 previous errors diff --git a/src/test/ui/span/E0072.stderr b/src/test/ui/span/E0072.stderr index d4a5e7400d2a4..06493f05142e6 100644 --- a/src/test/ui/span/E0072.stderr +++ b/src/test/ui/span/E0072.stderr @@ -5,9 +5,12 @@ LL | struct ListNode { | ^^^^^^^^^^^^^^^ recursive type has infinite size LL | head: u8, LL | tail: Option, - | ---------------------- recursive without indirection + | ---------------- recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `ListNode` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `ListNode` representable + | +LL | tail: Box>, + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/span/E0493.stderr b/src/test/ui/span/E0493.stderr index d05e89e257f45..29d1b00094321 100644 --- a/src/test/ui/span/E0493.stderr +++ b/src/test/ui/span/E0493.stderr @@ -2,7 +2,9 @@ error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/E0493.rs:17:17 | LL | const F : Foo = (Foo { a : 0 }, Foo { a : 1 }).1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constants cannot evaluate destructors + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - value is dropped here + | | + | constants cannot evaluate destructors error: aborting due to previous error diff --git a/src/test/ui/span/auxiliary/transitive_dep_three.rs b/src/test/ui/span/auxiliary/transitive_dep_three.rs new file mode 100644 index 0000000000000..99b51625ac3ec --- /dev/null +++ b/src/test/ui/span/auxiliary/transitive_dep_three.rs @@ -0,0 +1,9 @@ +#[macro_export] +macro_rules! define_parse_error { + () => { + #[macro_export] + macro_rules! parse_error { + () => { parse error } + } + } +} diff --git a/src/test/ui/span/auxiliary/transitive_dep_two.rs b/src/test/ui/span/auxiliary/transitive_dep_two.rs new file mode 100644 index 0000000000000..5110c42765b6d --- /dev/null +++ b/src/test/ui/span/auxiliary/transitive_dep_two.rs @@ -0,0 +1,3 @@ +extern crate transitive_dep_three; + +transitive_dep_three::define_parse_error!(); 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 69d1150abba47..73e166e6403e4 100644 --- a/src/test/ui/span/issue-24690.stderr +++ b/src/test/ui/span/issue-24690.stderr @@ -2,7 +2,7 @@ warning: unused variable: `theOtherTwo` --> $DIR/issue-24690.rs:13:9 | LL | let theOtherTwo = 2; - | ^^^^^^^^^^^ help: consider prefixing with an underscore: `_theOtherTwo` + | ^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_theOtherTwo` | note: the lint level is defined here --> $DIR/issue-24690.rs:8:9 @@ -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-39698.stderr b/src/test/ui/span/issue-39698.stderr index 7fa5d24c41b6d..445df90d395ee 100644 --- a/src/test/ui/span/issue-39698.stderr +++ b/src/test/ui/span/issue-39698.stderr @@ -8,16 +8,6 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | pattern doesn't bind `a` | variable not in all patterns -error[E0408]: variable `d` is not bound in all patterns - --> $DIR/issue-39698.rs:10:37 - | -LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } - | - - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `d` - | | | | - | | | pattern doesn't bind `d` - | | variable not in all patterns - | variable not in all patterns - error[E0408]: variable `b` is not bound in all patterns --> $DIR/issue-39698.rs:10:9 | @@ -38,6 +28,16 @@ LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?} | | pattern doesn't bind `c` | pattern doesn't bind `c` +error[E0408]: variable `d` is not bound in all patterns + --> $DIR/issue-39698.rs:10:37 + | +LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); } + | - - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `d` + | | | | + | | | pattern doesn't bind `d` + | | variable not in all patterns + | variable not in all patterns + error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0408`. 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/multiline-span-E0072.stderr b/src/test/ui/span/multiline-span-E0072.stderr index dd322fe833b49..55128347f7404 100644 --- a/src/test/ui/span/multiline-span-E0072.stderr +++ b/src/test/ui/span/multiline-span-E0072.stderr @@ -6,11 +6,14 @@ LL | | ListNode LL | | { LL | | head: u8, LL | | tail: Option, - | | ---------------------- recursive without indirection + | | ---------------- recursive without indirection LL | | } | |_^ recursive type has infinite size | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `ListNode` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `ListNode` representable + | +LL | tail: Box>, + | ^^^^ ^ error: aborting due to previous error 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/recursive-type-field.stderr b/src/test/ui/span/recursive-type-field.stderr index d240872647e50..fb1d98b58dfbe 100644 --- a/src/test/ui/span/recursive-type-field.stderr +++ b/src/test/ui/span/recursive-type-field.stderr @@ -4,9 +4,12 @@ error[E0072]: recursive type `Foo` has infinite size LL | struct Foo<'a> { | ^^^^^^^^^^^^^^ recursive type has infinite size LL | bar: Bar<'a>, - | ------------ recursive without indirection + | ------- recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Foo` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Foo` representable + | +LL | bar: Box>, + | ^^^^ ^ error[E0072]: recursive type `Bar` has infinite size --> $DIR/recursive-type-field.rs:8:1 @@ -14,18 +17,18 @@ error[E0072]: recursive type `Bar` has infinite size LL | struct Bar<'a> { | ^^^^^^^^^^^^^^ recursive type has infinite size LL | y: (Foo<'a>, Foo<'a>), - | --------------------- recursive without indirection + | ------------------ recursive without indirection LL | z: Option>, - | ------------------ recursive without indirection + | --------------- recursive without indirection ... LL | d: [Bar<'a>; 1], - | --------------- recursive without indirection + | ------------ recursive without indirection LL | e: Foo<'a>, - | ---------- recursive without indirection + | ------- recursive without indirection LL | x: Bar<'a>, - | ---------- recursive without indirection + | ------- recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `Bar` representable + = help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Bar` representable error: aborting due to 2 previous errors diff --git a/src/test/ui/span/transitive-dep-span.rs b/src/test/ui/span/transitive-dep-span.rs new file mode 100644 index 0000000000000..2d46f74ad9bc9 --- /dev/null +++ b/src/test/ui/span/transitive-dep-span.rs @@ -0,0 +1,15 @@ +// Tests that we properly serialize/deserialize spans from transitive dependencies +// (e.g. imported SourceFiles) +// +// The order of these next lines is important, since we need +// transitive_dep_two.rs to be able to reference transitive_dep_three.rs +// +// aux-build: transitive_dep_three.rs +// aux-build: transitive_dep_two.rs +// compile-flags: -Z macro-backtrace + +extern crate transitive_dep_two; + +transitive_dep_two::parse_error!(); //~ ERROR expected one of + +fn main() {} diff --git a/src/test/ui/span/transitive-dep-span.stderr b/src/test/ui/span/transitive-dep-span.stderr new file mode 100644 index 0000000000000..68d8911a4351c --- /dev/null +++ b/src/test/ui/span/transitive-dep-span.stderr @@ -0,0 +1,19 @@ +error: expected one of `!` or `::`, found `error` + --> $DIR/auxiliary/transitive_dep_three.rs:6:27 + | +LL | / macro_rules! parse_error { +LL | | () => { parse error } + | | ^^^^^ expected one of `!` or `::` +LL | | } + | |_________- in this expansion of `transitive_dep_two::parse_error!` + | + ::: $DIR/transitive-dep-span.rs:13:1 + | +LL | transitive_dep_two::parse_error!(); + | ----------------------------------- + | | + | in this macro invocation + | in this macro invocation + +error: aborting due to previous error + diff --git a/src/test/ui/span/type-annotations-needed-expr.stderr b/src/test/ui/span/type-annotations-needed-expr.stderr index 35d994e194f3f..3e6d350b36c33 100644 --- a/src/test/ui/span/type-annotations-needed-expr.stderr +++ b/src/test/ui/span/type-annotations-needed-expr.stderr @@ -2,12 +2,13 @@ error[E0282]: type annotations needed --> $DIR/type-annotations-needed-expr.rs:2:39 | LL | let _ = (vec![1,2,3]).into_iter().sum() as f64; - | ^^^ - | | - | cannot infer type for type parameter `S` declared on the associated function `sum` - | help: consider specifying the type argument in the method call: `sum::` + | ^^^ cannot infer type for type parameter `S` declared on the associated function `sum` | = note: type must be known at this point +help: consider specifying the type argument in the method call + | +LL | let _ = (vec![1,2,3]).into_iter().sum::() as f64; + | ^^^^^ error: aborting due to previous error 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/assoc-ty-graph-cycle.rs b/src/test/ui/specialization/assoc-ty-graph-cycle.rs index 54d51492ab349..fc39b553a61ac 100644 --- a/src/test/ui/specialization/assoc-ty-graph-cycle.rs +++ b/src/test/ui/specialization/assoc-ty-graph-cycle.rs @@ -2,7 +2,7 @@ // Make sure we don't crash with a cycle error during coherence. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Trait { type Assoc; diff --git a/src/test/ui/specialization/assoc-ty-graph-cycle.stderr b/src/test/ui/specialization/assoc-ty-graph-cycle.stderr new file mode 100644 index 0000000000000..250f48f8e5932 --- /dev/null +++ b/src/test/ui/specialization/assoc-ty-graph-cycle.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/assoc-ty-graph-cycle.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/cross-crate-defaults.rs b/src/test/ui/specialization/cross-crate-defaults.rs index 79cb659439721..fc28d0c815eb3 100644 --- a/src/test/ui/specialization/cross-crate-defaults.rs +++ b/src/test/ui/specialization/cross-crate-defaults.rs @@ -2,7 +2,7 @@ // aux-build:cross_crates_defaults.rs -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete extern crate cross_crates_defaults; diff --git a/src/test/ui/specialization/cross-crate-defaults.stderr b/src/test/ui/specialization/cross-crate-defaults.stderr new file mode 100644 index 0000000000000..f18bc99d73916 --- /dev/null +++ b/src/test/ui/specialization/cross-crate-defaults.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/cross-crate-defaults.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/deafult-associated-type-bound-1.rs b/src/test/ui/specialization/deafult-associated-type-bound-1.rs new file mode 100644 index 0000000000000..272a5e3fe10c6 --- /dev/null +++ b/src/test/ui/specialization/deafult-associated-type-bound-1.rs @@ -0,0 +1,24 @@ +// Check that we check that default associated types satisfy the required +// bounds on them. + +#![feature(specialization)] +//~^ WARNING `specialization` is incomplete + +trait X { + type U: Clone; + fn unsafe_clone(&self, x: Option<&Self::U>) { + x.cloned(); + } +} + +// We cannot normalize `::U` to `str` here, because the default could +// be overridden. The error here must therefore be found by a method other than +// normalization. +impl X for T { + default type U = str; + //~^ ERROR the trait bound `str: std::clone::Clone` is not satisfied +} + +pub fn main() { + 1.unsafe_clone(None); +} diff --git a/src/test/ui/specialization/deafult-associated-type-bound-1.stderr b/src/test/ui/specialization/deafult-associated-type-bound-1.stderr new file mode 100644 index 0000000000000..90ad5d4c1559b --- /dev/null +++ b/src/test/ui/specialization/deafult-associated-type-bound-1.stderr @@ -0,0 +1,21 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/deafult-associated-type-bound-1.rs:4:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +error[E0277]: the trait bound `str: std::clone::Clone` is not satisfied + --> $DIR/deafult-associated-type-bound-1.rs:18:5 + | +LL | type U: Clone; + | -------------- required by `X::U` +... +LL | default type U = str; + | ^^^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` is not implemented for `str` + +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/specialization/deafult-associated-type-bound-2.rs b/src/test/ui/specialization/deafult-associated-type-bound-2.rs new file mode 100644 index 0000000000000..0a21b1f09106b --- /dev/null +++ b/src/test/ui/specialization/deafult-associated-type-bound-2.rs @@ -0,0 +1,22 @@ +// Check that generic predicates are also checked for default associated types. +#![feature(specialization)] +//~^ WARNING `specialization` is incomplete + +trait X { + type U: PartialEq; + fn unsafe_compare(x: Option, y: Option) { + match (x, y) { + (Some(a), Some(b)) => a == b, + _ => false, + }; + } +} + +impl X for T { + default type U = &'static B; + //~^ ERROR can't compare `&'static B` with `B` +} + +pub fn main() { + >::unsafe_compare(None, None); +} diff --git a/src/test/ui/specialization/deafult-associated-type-bound-2.stderr b/src/test/ui/specialization/deafult-associated-type-bound-2.stderr new file mode 100644 index 0000000000000..ea40f846e3665 --- /dev/null +++ b/src/test/ui/specialization/deafult-associated-type-bound-2.stderr @@ -0,0 +1,23 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/deafult-associated-type-bound-2.rs:2:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +error[E0277]: can't compare `&'static B` with `B` + --> $DIR/deafult-associated-type-bound-2.rs:16:5 + | +LL | type U: PartialEq; + | --------------------- required by `X::U` +... +LL | default type U = &'static B; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `&'static B == B` + | + = help: the trait `std::cmp::PartialEq` is not implemented for `&'static B` + +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/specialization/deafult-generic-associated-type-bound.rs b/src/test/ui/specialization/deafult-generic-associated-type-bound.rs new file mode 100644 index 0000000000000..8a94ea658d2d0 --- /dev/null +++ b/src/test/ui/specialization/deafult-generic-associated-type-bound.rs @@ -0,0 +1,27 @@ +// Check that default generics associated types are validated. + +#![feature(specialization)] +#![feature(generic_associated_types)] +//~^^ WARNING `specialization` is incomplete +//~^^ WARNING the feature `generic_associated_types` is incomplete + +trait X { + type U<'a>: PartialEq<&'a Self>; + fn unsafe_compare<'b>(x: Option>, y: Option<&'b Self>) { + match (x, y) { + (Some(a), Some(b)) => a == b, + _ => false, + }; + } +} + +impl X for T { + default type U<'a> = &'a T; + //~^ ERROR can't compare `T` with `T` +} + +struct NotComparable; + +pub fn main() { + ::unsafe_compare(None, None); +} diff --git a/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr b/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr new file mode 100644 index 0000000000000..7f3c49e753fd7 --- /dev/null +++ b/src/test/ui/specialization/deafult-generic-associated-type-bound.stderr @@ -0,0 +1,36 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/deafult-generic-associated-type-bound.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: the feature `generic_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/deafult-generic-associated-type-bound.rs:4:12 + | +LL | #![feature(generic_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #44265 for more information + +error[E0277]: can't compare `T` with `T` + --> $DIR/deafult-generic-associated-type-bound.rs:19:5 + | +LL | type U<'a>: PartialEq<&'a Self>; + | -------------------------------- required by `X::U` +... +LL | default type U<'a> = &'a T; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `T == T` + | + = help: the trait `std::cmp::PartialEq` is not implemented for `T` + = note: required because of the requirements on the impl of `std::cmp::PartialEq` for `&'a T` +help: consider further restricting this bound + | +LL | impl X for T { + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/specialization/defaultimpl/allowed-cross-crate.rs b/src/test/ui/specialization/defaultimpl/allowed-cross-crate.rs index 15550bcce2a8a..5d67160eb96ad 100644 --- a/src/test/ui/specialization/defaultimpl/allowed-cross-crate.rs +++ b/src/test/ui/specialization/defaultimpl/allowed-cross-crate.rs @@ -5,7 +5,7 @@ // aux-build:go_trait.rs -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete extern crate go_trait; diff --git a/src/test/ui/specialization/defaultimpl/allowed-cross-crate.stderr b/src/test/ui/specialization/defaultimpl/allowed-cross-crate.stderr new file mode 100644 index 0000000000000..1b50329719d01 --- /dev/null +++ b/src/test/ui/specialization/defaultimpl/allowed-cross-crate.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/allowed-cross-crate.rs:8:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/defaultimpl/out-of-order.rs b/src/test/ui/specialization/defaultimpl/out-of-order.rs index f9c73a19cfa46..13258ac8c9fe6 100644 --- a/src/test/ui/specialization/defaultimpl/out-of-order.rs +++ b/src/test/ui/specialization/defaultimpl/out-of-order.rs @@ -2,7 +2,7 @@ // Test that you can list the more specific impl before the more general one. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { type Out; diff --git a/src/test/ui/specialization/defaultimpl/out-of-order.stderr b/src/test/ui/specialization/defaultimpl/out-of-order.stderr new file mode 100644 index 0000000000000..deae021a8914d --- /dev/null +++ b/src/test/ui/specialization/defaultimpl/out-of-order.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/out-of-order.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/defaultimpl/overlap-projection.rs b/src/test/ui/specialization/defaultimpl/overlap-projection.rs index ed38bb3fc3a12..0add4d5516c7b 100644 --- a/src/test/ui/specialization/defaultimpl/overlap-projection.rs +++ b/src/test/ui/specialization/defaultimpl/overlap-projection.rs @@ -4,7 +4,7 @@ // projections involve specialization, so long as the associated type is // provided by the most specialized impl. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Assoc { type Output; diff --git a/src/test/ui/specialization/defaultimpl/overlap-projection.stderr b/src/test/ui/specialization/defaultimpl/overlap-projection.stderr new file mode 100644 index 0000000000000..46899ca995490 --- /dev/null +++ b/src/test/ui/specialization/defaultimpl/overlap-projection.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/overlap-projection.rs:7:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/defaultimpl/projection.rs b/src/test/ui/specialization/defaultimpl/projection.rs index 897a7aade2fe1..4a9140969324d 100644 --- a/src/test/ui/specialization/defaultimpl/projection.rs +++ b/src/test/ui/specialization/defaultimpl/projection.rs @@ -1,7 +1,7 @@ // run-pass #![allow(dead_code)] -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Make sure we *can* project non-defaulted associated types // cf compile-fail/specialization-default-projection.rs diff --git a/src/test/ui/specialization/defaultimpl/projection.stderr b/src/test/ui/specialization/defaultimpl/projection.stderr new file mode 100644 index 0000000000000..8629c6c52d4a7 --- /dev/null +++ b/src/test/ui/specialization/defaultimpl/projection.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/projection.rs:4:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/defaultimpl/specialization-no-default.rs b/src/test/ui/specialization/defaultimpl/specialization-no-default.rs index 37005f839d488..661724eef8a43 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-no-default.rs +++ b/src/test/ui/specialization/defaultimpl/specialization-no-default.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Check a number of scenarios in which one impl tries to override another, // without correctly using `default`. diff --git a/src/test/ui/specialization/defaultimpl/specialization-no-default.stderr b/src/test/ui/specialization/defaultimpl/specialization-no-default.stderr index 13636b28b126c..7958eddbeba25 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-no-default.stderr +++ b/src/test/ui/specialization/defaultimpl/specialization-no-default.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-no-default.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0520]: `foo` specializes an item from a parent `impl`, but that item is not marked `default` --> $DIR/specialization-no-default.rs:20:5 | @@ -65,6 +74,6 @@ LL | fn redundant(&self) {} | = note: to specialize, `redundant` in the parent `impl` must be marked `default` -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 E0520`. diff --git a/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented-rpass.rs b/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented-rpass.rs index 2b8ca6bb1ddbb..89fef5b5ef969 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented-rpass.rs +++ b/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented-rpass.rs @@ -3,7 +3,7 @@ // Tests that we can combine a default impl that supplies one method with a // full impl that supplies the other, and they can invoke one another. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { fn foo_one(&self) -> &'static str; diff --git a/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented-rpass.stderr b/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented-rpass.stderr new file mode 100644 index 0000000000000..dc377dd10c868 --- /dev/null +++ b/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented-rpass.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-trait-item-not-implemented-rpass.rs:6:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented.rs b/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented.rs index 2a121e61aaa97..3c5414469fac2 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented.rs +++ b/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented.rs @@ -1,6 +1,6 @@ // Tests that default impls do not have to supply all items but regular impls do. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { fn foo_one(&self) -> &'static str; diff --git a/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented.stderr b/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented.stderr index b862a937066d4..9d1eca1d6af76 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented.stderr +++ b/src/test/ui/specialization/defaultimpl/specialization-trait-item-not-implemented.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-trait-item-not-implemented.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0046]: not all trait items implemented, missing: `foo_two` --> $DIR/specialization-trait-item-not-implemented.rs:18:1 | @@ -7,6 +16,6 @@ LL | fn foo_two(&self) -> &'static str; LL | impl Foo for MyStruct {} | ^^^^^^^^^^^^^^^^^^^^^ missing `foo_two` in implementation -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0046`. diff --git a/src/test/ui/specialization/defaultimpl/specialization-trait-not-implemented.rs b/src/test/ui/specialization/defaultimpl/specialization-trait-not-implemented.rs index 5c104449fe9c0..35e3b8725a82a 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-trait-not-implemented.rs +++ b/src/test/ui/specialization/defaultimpl/specialization-trait-not-implemented.rs @@ -2,7 +2,7 @@ // - default impls do not have to supply all items and // - a default impl does not count as an impl (in this case, an incomplete default impl). -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { fn foo_one(&self) -> &'static str; diff --git a/src/test/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr b/src/test/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr index a55d79ee03534..6b8e559bc3634 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr +++ b/src/test/ui/specialization/defaultimpl/specialization-trait-not-implemented.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-trait-not-implemented.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0599]: no method named `foo_one` found for struct `MyStruct` in the current scope --> $DIR/specialization-trait-not-implemented.rs:22:29 | @@ -19,6 +28,6 @@ note: `Foo` defines an item `foo_one`, perhaps you need to implement it LL | trait 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 E0599`. diff --git a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.rs b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.rs index 232338d9d4d99..afd634725e365 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.rs +++ b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.rs @@ -1,6 +1,6 @@ // Tests that a default impl still has to have a WF trait ref. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo<'a, T: Eq + 'a> { } diff --git a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr index ee7c002b16db1..d45825651a8e2 100644 --- a/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr +++ b/src/test/ui/specialization/defaultimpl/specialization-wfcheck.stderr @@ -1,15 +1,26 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-wfcheck.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0277]: the trait bound `U: std::cmp::Eq` is not satisfied --> $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 +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/specialization/defaultimpl/validation.rs b/src/test/ui/specialization/defaultimpl/validation.rs index 26b3f1ec41491..8558a1efb82f3 100644 --- a/src/test/ui/specialization/defaultimpl/validation.rs +++ b/src/test/ui/specialization/defaultimpl/validation.rs @@ -1,5 +1,5 @@ -#![feature(optin_builtin_traits)] -#![feature(specialization)] +#![feature(negative_impls)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete struct S; struct Z; @@ -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 negative impls are only allowed for auto traits +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 6e19d79e48f6b..2449849725f38 100644 --- a/src/test/ui/specialization/defaultimpl/validation.stderr +++ b/src/test/ui/specialization/defaultimpl/validation.stderr @@ -8,24 +8,43 @@ LL | default impl S {} | = note: only trait implementations may be annotated with `default` +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/validation.rs:2:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error: impls of auto traits cannot be default - --> $DIR/validation.rs:9:1 + --> $DIR/validation.rs:9:21 | LL | default unsafe impl Send for S {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ------- ^^^^ auto trait + | | + | default because of this error: impls of auto traits cannot be default + --> $DIR/validation.rs:10:15 + | +LL | default impl !Send for Z {} + | ------- ^^^^ auto trait + | | + | default because of this + +error[E0750]: negative impls cannot be default impls --> $DIR/validation.rs:10:1 | LL | default impl !Send for Z {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ ^ -error[E0192]: negative impls are only allowed for auto traits (e.g., `Send` and `Sync`) - --> $DIR/validation.rs:13:1 +error[E0750]: negative impls cannot be default impls + --> $DIR/validation.rs:14:1 | LL | default impl !Tr for S {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ ^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors; 1 warning emitted -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-36804.rs b/src/test/ui/specialization/issue-36804.rs index 9546a5dd5f516..89350602f3652 100644 --- a/src/test/ui/specialization/issue-36804.rs +++ b/src/test/ui/specialization/issue-36804.rs @@ -1,5 +1,5 @@ // check-pass -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete pub struct Cloned(I); diff --git a/src/test/ui/specialization/issue-36804.stderr b/src/test/ui/specialization/issue-36804.stderr new file mode 100644 index 0000000000000..744d88204247b --- /dev/null +++ b/src/test/ui/specialization/issue-36804.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-36804.rs:2:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/issue-39448.rs b/src/test/ui/specialization/issue-39448.rs index 8ac6d8e9311fc..9dd47a4a17e43 100644 --- a/src/test/ui/specialization/issue-39448.rs +++ b/src/test/ui/specialization/issue-39448.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Regression test for a specialization-related ICE (#39448). diff --git a/src/test/ui/specialization/issue-39448.stderr b/src/test/ui/specialization/issue-39448.stderr index 861a1d9e8fc66..f3bb69b8f712a 100644 --- a/src/test/ui/specialization/issue-39448.stderr +++ b/src/test/ui/specialization/issue-39448.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-39448.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0275]: overflow evaluating the requirement `T: FromA` --> $DIR/issue-39448.rs:45:13 | @@ -7,6 +16,6 @@ LL | x.foo(y.to()).to() = note: required because of the requirements on the impl of `FromA` for `T` = note: required because of the requirements on the impl of `ToA` for `U` -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/specialization/issue-39618.rs b/src/test/ui/specialization/issue-39618.rs index 20e81e4359bac..72630ee9c7055 100644 --- a/src/test/ui/specialization/issue-39618.rs +++ b/src/test/ui/specialization/issue-39618.rs @@ -4,7 +4,7 @@ // check-pass -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { fn foo(&self); diff --git a/src/test/ui/specialization/issue-39618.stderr b/src/test/ui/specialization/issue-39618.stderr new file mode 100644 index 0000000000000..d40d17d8f71ca --- /dev/null +++ b/src/test/ui/specialization/issue-39618.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-39618.rs:7:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/issue-44861.rs b/src/test/ui/specialization/issue-44861.rs new file mode 100644 index 0000000000000..c37a6273de366 --- /dev/null +++ b/src/test/ui/specialization/issue-44861.rs @@ -0,0 +1,40 @@ +#![crate_type = "lib"] +#![feature(specialization)] +#![feature(unsize, coerce_unsized)] +#![allow(incomplete_features)] + +use std::ops::CoerceUnsized; + +pub struct SmartassPtr(A::Data); + +pub trait Smartass { + type Data; + type Data2: CoerceUnsized<*const [u8]>; +} + +pub trait MaybeObjectSafe {} + +impl MaybeObjectSafe for () {} + +impl Smartass for T { + type Data = ::Data2; + default type Data2 = (); + //~^ ERROR: the trait bound `(): std::ops::CoerceUnsized<*const [u8]>` is not satisfied +} + +impl Smartass for () { + type Data2 = *const [u8; 1]; +} + +impl Smartass for dyn MaybeObjectSafe { + type Data = *const [u8]; + type Data2 = *const [u8; 0]; +} + +impl CoerceUnsized> for SmartassPtr + where ::Data: std::ops::CoerceUnsized<::Data> +{} + +pub fn conv(s: SmartassPtr<()>) -> SmartassPtr { + s +} diff --git a/src/test/ui/specialization/issue-44861.stderr b/src/test/ui/specialization/issue-44861.stderr new file mode 100644 index 0000000000000..b41b17e76a6ab --- /dev/null +++ b/src/test/ui/specialization/issue-44861.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `(): std::ops::CoerceUnsized<*const [u8]>` is not satisfied + --> $DIR/issue-44861.rs:21:5 + | +LL | type Data2: CoerceUnsized<*const [u8]>; + | --------------------------------------- required by `Smartass::Data2` +... +LL | default type Data2 = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::ops::CoerceUnsized<*const [u8]>` is not implemented for `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/specialization/issue-50452.rs b/src/test/ui/specialization/issue-50452.rs index 93f081d955819..29fc12066e875 100644 --- a/src/test/ui/specialization/issue-50452.rs +++ b/src/test/ui/specialization/issue-50452.rs @@ -1,6 +1,6 @@ // run-pass -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete pub trait Foo { fn foo(); diff --git a/src/test/ui/specialization/issue-50452.stderr b/src/test/ui/specialization/issue-50452.stderr new file mode 100644 index 0000000000000..c01817e0b2793 --- /dev/null +++ b/src/test/ui/specialization/issue-50452.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-50452.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/issue-52050.rs b/src/test/ui/specialization/issue-52050.rs index 1e1bfe9cf0755..8046587020661 100644 --- a/src/test/ui/specialization/issue-52050.rs +++ b/src/test/ui/specialization/issue-52050.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Regression test for #52050: when inserting the blanket impl `I` // into the tree, we had to replace the child node for `Foo`, which diff --git a/src/test/ui/specialization/issue-52050.stderr b/src/test/ui/specialization/issue-52050.stderr index 36f96b011983f..a7564ced055d5 100644 --- a/src/test/ui/specialization/issue-52050.stderr +++ b/src/test/ui/specialization/issue-52050.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-52050.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0119]: conflicting implementations of trait `IntoPyDictPointer` for type `()`: --> $DIR/issue-52050.rs:28:1 | @@ -13,6 +22,6 @@ LL | impl IntoPyDictPointer for () | = note: upstream crates may add a new impl of trait `std::iter::Iterator` for type `()` in future versions -error: aborting due to previous error +error: aborting due to previous error; 1 warning emitted For more information about this error, try `rustc --explain E0119`. diff --git a/src/test/ui/specialization/issue-59435.rs b/src/test/ui/specialization/issue-59435.rs new file mode 100644 index 0000000000000..47323d3096f3d --- /dev/null +++ b/src/test/ui/specialization/issue-59435.rs @@ -0,0 +1,17 @@ +#![feature(specialization)] +#![allow(incomplete_features)] + +struct MyStruct {} + +trait MyTrait { + type MyType: Default; +} + +impl MyTrait for i32 { + default type MyType = MyStruct; + //~^ ERROR: the trait bound `MyStruct: std::default::Default` is not satisfied +} + +fn main() { + let _x: ::MyType = ::MyType::default(); +} diff --git a/src/test/ui/specialization/issue-59435.stderr b/src/test/ui/specialization/issue-59435.stderr new file mode 100644 index 0000000000000..fd512a539a3ee --- /dev/null +++ b/src/test/ui/specialization/issue-59435.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `MyStruct: std::default::Default` is not satisfied + --> $DIR/issue-59435.rs:11:5 + | +LL | type MyType: Default; + | --------------------- required by `MyTrait::MyType` +... +LL | default type MyType = MyStruct; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `MyStruct` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/specialization/issue-63716-parse-async.rs b/src/test/ui/specialization/issue-63716-parse-async.rs index c3764ffaab83f..10f185c335144 100644 --- a/src/test/ui/specialization/issue-63716-parse-async.rs +++ b/src/test/ui/specialization/issue-63716-parse-async.rs @@ -4,7 +4,7 @@ // check-pass // edition:2018 -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete fn main() {} diff --git a/src/test/ui/specialization/issue-63716-parse-async.stderr b/src/test/ui/specialization/issue-63716-parse-async.stderr new file mode 100644 index 0000000000000..43620e1ba51e1 --- /dev/null +++ b/src/test/ui/specialization/issue-63716-parse-async.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-63716-parse-async.rs:7:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/issue-70442.rs b/src/test/ui/specialization/issue-70442.rs new file mode 100644 index 0000000000000..d41b5355c2cde --- /dev/null +++ b/src/test/ui/specialization/issue-70442.rs @@ -0,0 +1,23 @@ +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete + +// 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/issue-70442.stderr b/src/test/ui/specialization/issue-70442.stderr new file mode 100644 index 0000000000000..f71e4c7dd1cef --- /dev/null +++ b/src/test/ui/specialization/issue-70442.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-70442.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs b/src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs new file mode 100644 index 0000000000000..6ec0d261d518a --- /dev/null +++ b/src/test/ui/specialization/min_specialization/auxiliary/specialization-trait.rs @@ -0,0 +1,6 @@ +#![feature(rustc_attrs)] + +#[rustc_specialization_trait] +pub trait SpecTrait { + fn method(&self); +} diff --git a/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.rs b/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.rs new file mode 100644 index 0000000000000..03cab00b0fb96 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.rs @@ -0,0 +1,32 @@ +// Test that associated types in trait objects are not considered to be +// constrained. + +#![feature(min_specialization)] + +trait Specializable { + fn f(); +} + +trait B { + type Y; +} + +trait C { + type Y; +} + +impl Specializable for A { + default fn f() {} +} + +impl<'a, T> Specializable for dyn B + 'a { + //~^ ERROR specializing impl repeats parameter `T` + fn f() {} +} + +impl<'a, T> Specializable for dyn C + 'a { + //~^ ERROR specializing impl repeats parameter `T` + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.stderr b/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.stderr new file mode 100644 index 0000000000000..6345cee2c3781 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/dyn-trait-assoc-types.stderr @@ -0,0 +1,20 @@ +error: specializing impl repeats parameter `T` + --> $DIR/dyn-trait-assoc-types.rs:22:1 + | +LL | / impl<'a, T> Specializable for dyn B + 'a { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: specializing impl repeats parameter `T` + --> $DIR/dyn-trait-assoc-types.rs:27:1 + | +LL | / impl<'a, T> Specializable for dyn C + 'a { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/specialization/min_specialization/impl_specialization_trait.rs b/src/test/ui/specialization/min_specialization/impl_specialization_trait.rs new file mode 100644 index 0000000000000..723ed71c3e95d --- /dev/null +++ b/src/test/ui/specialization/min_specialization/impl_specialization_trait.rs @@ -0,0 +1,16 @@ +// Check that specialization traits can't be implemented without a feature. + +// gate-test-min_specialization + +// aux-build:specialization-trait.rs + +extern crate specialization_trait; + +struct A {} + +impl specialization_trait::SpecTrait for A { + //~^ ERROR implementing `rustc_specialization_trait` traits is unstable + fn method(&self) {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr b/src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr new file mode 100644 index 0000000000000..934103d49dc0e --- /dev/null +++ b/src/test/ui/specialization/min_specialization/impl_specialization_trait.stderr @@ -0,0 +1,10 @@ +error: implementing `rustc_specialization_trait` traits is unstable + --> $DIR/impl_specialization_trait.rs:11:1 + | +LL | impl specialization_trait::SpecTrait for A { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(min_specialization)]` to the crate attributes to enable + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs b/src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs new file mode 100644 index 0000000000000..98d7f9194351c --- /dev/null +++ b/src/test/ui/specialization/min_specialization/implcit-well-formed-bounds.rs @@ -0,0 +1,30 @@ +// Test that specializing on the well-formed predicates of the trait and +// self-type of an impl is allowed. + +// check-pass + +#![feature(min_specialization)] + +struct OrdOnly(T); + +trait SpecTrait { + fn f(); +} + +impl SpecTrait for T { + default fn f() {} +} + +impl SpecTrait<()> for OrdOnly { + fn f() {} +} + +impl SpecTrait> for () { + fn f() {} +} + +impl SpecTrait<(OrdOnly, OrdOnly)> for &[OrdOnly] { + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/repeated_projection_type.rs b/src/test/ui/specialization/min_specialization/repeated_projection_type.rs new file mode 100644 index 0000000000000..f21f39f066981 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeated_projection_type.rs @@ -0,0 +1,24 @@ +// Test that projection bounds can't be specialized on. + +#![feature(min_specialization)] + +trait X { + fn f(); +} +trait Id { + type This; +} +impl Id for T { + type This = T; +} + +impl X for T { + default fn f() {} +} + +impl> X for V { + //~^ ERROR cannot specialize on + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr new file mode 100644 index 0000000000000..7cc4357a704c0 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr @@ -0,0 +1,11 @@ +error: cannot specialize on `Binder(ProjectionPredicate(ProjectionTy { substs: [V], item_def_id: DefId(0:6 ~ repeated_projection_type[317d]::Id[0]::This[0]) }, (I,)))` + --> $DIR/repeated_projection_type.rs:19:1 + | +LL | / impl> X for V { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/repeating_lifetimes.rs b/src/test/ui/specialization/min_specialization/repeating_lifetimes.rs new file mode 100644 index 0000000000000..49bfacec0ae12 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeating_lifetimes.rs @@ -0,0 +1,19 @@ +// Test that directly specializing on repeated lifetime parameters is not +// allowed. + +#![feature(min_specialization)] + +trait X { + fn f(); +} + +impl X for T { + default fn f() {} +} + +impl<'a> X for (&'a u8, &'a u8) { + //~^ ERROR specializing impl repeats parameter `'a` + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/repeating_lifetimes.stderr b/src/test/ui/specialization/min_specialization/repeating_lifetimes.stderr new file mode 100644 index 0000000000000..ce9309f70122b --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeating_lifetimes.stderr @@ -0,0 +1,11 @@ +error: specializing impl repeats parameter `'a` + --> $DIR/repeating_lifetimes.rs:14:1 + | +LL | / impl<'a> X for (&'a u8, &'a u8) { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/repeating_param.rs b/src/test/ui/specialization/min_specialization/repeating_param.rs new file mode 100644 index 0000000000000..5a1c97fd321a9 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeating_param.rs @@ -0,0 +1,17 @@ +// Test that specializing on two type parameters being equal is not allowed. + +#![feature(min_specialization)] + +trait X { + fn f(); +} + +impl X for T { + default fn f() {} +} +impl X for (T, T) { + //~^ ERROR specializing impl repeats parameter `T` + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/repeating_param.stderr b/src/test/ui/specialization/min_specialization/repeating_param.stderr new file mode 100644 index 0000000000000..8b4be1c499537 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/repeating_param.stderr @@ -0,0 +1,11 @@ +error: specializing impl repeats parameter `T` + --> $DIR/repeating_param.rs:12:1 + | +LL | / impl X for (T, T) { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/spec-iter.rs b/src/test/ui/specialization/min_specialization/spec-iter.rs new file mode 100644 index 0000000000000..e17e9dd5f133c --- /dev/null +++ b/src/test/ui/specialization/min_specialization/spec-iter.rs @@ -0,0 +1,20 @@ +// Check that we can specialize on a concrete iterator type. This requires us +// to consider which parameters in the parent impl are constrained. + +// check-pass + +#![feature(min_specialization)] + +trait SpecFromIter { + fn f(&self); +} + +impl<'a, T: 'a, I: Iterator> SpecFromIter for I { + default fn f(&self) {} +} + +impl<'a, T> SpecFromIter for std::slice::Iter<'a, T> { + fn f(&self) {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/spec-reference.rs b/src/test/ui/specialization/min_specialization/spec-reference.rs new file mode 100644 index 0000000000000..377889e2ccad2 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/spec-reference.rs @@ -0,0 +1,19 @@ +// Check that lifetime parameters are allowed in specializing impls. + +// check-pass + +#![feature(min_specialization)] + +trait MySpecTrait { + fn f(); +} + +impl MySpecTrait for T { + default fn f() {} +} + +impl<'a, T: ?Sized> MySpecTrait for &'a T { + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_marker.rs b/src/test/ui/specialization/min_specialization/specialization_marker.rs new file mode 100644 index 0000000000000..93462d02ea578 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_marker.rs @@ -0,0 +1,17 @@ +// Test that `rustc_unsafe_specialization_marker` is only allowed on marker traits. + +#![feature(rustc_attrs)] + +#[rustc_unsafe_specialization_marker] +trait SpecMarker { + fn f(); + //~^ ERROR marker traits +} + +#[rustc_unsafe_specialization_marker] +trait SpecMarker2 { + type X; + //~^ ERROR marker traits +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_marker.stderr b/src/test/ui/specialization/min_specialization/specialization_marker.stderr new file mode 100644 index 0000000000000..ffeced198211f --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_marker.stderr @@ -0,0 +1,15 @@ +error[E0714]: marker traits cannot have associated items + --> $DIR/specialization_marker.rs:7:5 + | +LL | fn f(); + | ^^^^^^^ + +error[E0714]: marker traits cannot have associated items + --> $DIR/specialization_marker.rs:13:5 + | +LL | type X; + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0714`. diff --git a/src/test/ui/specialization/min_specialization/specialization_super_trait.rs b/src/test/ui/specialization/min_specialization/specialization_super_trait.rs new file mode 100644 index 0000000000000..145f376edf938 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_super_trait.rs @@ -0,0 +1,18 @@ +// Test that supertraits can't be assumed in impls of +// `rustc_specialization_trait`, as such impls would +// allow specializing on the supertrait. + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +#[rustc_specialization_trait] +trait SpecMarker: Default { + fn f(); +} + +impl SpecMarker for T { + //~^ ERROR cannot specialize + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr new file mode 100644 index 0000000000000..154c839c6da6e --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr @@ -0,0 +1,11 @@ +error: cannot specialize on trait `std::default::Default` + --> $DIR/specialization_super_trait.rs:13:1 + | +LL | / impl SpecMarker for T { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/specialization_trait.rs b/src/test/ui/specialization/min_specialization/specialization_trait.rs new file mode 100644 index 0000000000000..d597278d29693 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_trait.rs @@ -0,0 +1,26 @@ +// Test that `rustc_specialization_trait` requires always applicable impls. + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +#[rustc_specialization_trait] +trait SpecMarker { + fn f(); +} + +impl SpecMarker for &'static u8 { + //~^ ERROR cannot specialize + fn f() {} +} + +impl SpecMarker for (T, T) { + //~^ ERROR specializing impl + fn f() {} +} + +impl SpecMarker for [T] { + //~^ ERROR cannot specialize + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialization_trait.stderr b/src/test/ui/specialization/min_specialization/specialization_trait.stderr new file mode 100644 index 0000000000000..4357d2318fc5d --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialization_trait.stderr @@ -0,0 +1,29 @@ +error: cannot specialize on `'static` lifetime + --> $DIR/specialization_trait.rs:11:1 + | +LL | / impl SpecMarker for &'static u8 { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: specializing impl repeats parameter `T` + --> $DIR/specialization_trait.rs:16:1 + | +LL | / impl SpecMarker for (T, T) { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: cannot specialize on trait `std::clone::Clone` + --> $DIR/specialization_trait.rs:21:1 + | +LL | / impl SpecMarker for [T] { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/specialization/min_specialization/specialize_on_marker.rs b/src/test/ui/specialization/min_specialization/specialize_on_marker.rs new file mode 100644 index 0000000000000..4219bd13b1816 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_marker.rs @@ -0,0 +1,24 @@ +// Test that specializing on a `rustc_unsafe_specialization_marker` trait is +// allowed. + +// check-pass + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +#[rustc_unsafe_specialization_marker] +trait SpecMarker {} + +trait X { + fn f(); +} + +impl X for T { + default fn f() {} +} + +impl X for T { + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialize_on_spec_trait.rs b/src/test/ui/specialization/min_specialization/specialize_on_spec_trait.rs new file mode 100644 index 0000000000000..abbab5c23dbb7 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_spec_trait.rs @@ -0,0 +1,27 @@ +// Test that specializing on a `rustc_specialization_trait` trait is allowed. + +// check-pass + +#![feature(min_specialization)] +#![feature(rustc_attrs)] + +#[rustc_specialization_trait] +trait SpecTrait { + fn g(&self); +} + +trait X { + fn f(&self); +} + +impl X for T { + default fn f(&self) {} +} + +impl X for T { + fn f(&self) { + self.g(); + } +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialize_on_static.rs b/src/test/ui/specialization/min_specialization/specialize_on_static.rs new file mode 100644 index 0000000000000..dd1b05401e6e7 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_static.rs @@ -0,0 +1,18 @@ +// Test that directly specializing on `'static` is not allowed. + +#![feature(min_specialization)] + +trait X { + fn f(); +} + +impl X for &'_ T { + default fn f() {} +} + +impl X for &'static u8 { + //~^ ERROR cannot specialize on `'static` lifetime + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialize_on_static.stderr b/src/test/ui/specialization/min_specialization/specialize_on_static.stderr new file mode 100644 index 0000000000000..d1809d6dfbb52 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_static.stderr @@ -0,0 +1,11 @@ +error: cannot specialize on `'static` lifetime + --> $DIR/specialize_on_static.rs:13:1 + | +LL | / impl X for &'static u8 { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/min_specialization/specialize_on_trait.rs b/src/test/ui/specialization/min_specialization/specialize_on_trait.rs new file mode 100644 index 0000000000000..0588442c32080 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_trait.rs @@ -0,0 +1,20 @@ +// Test that specializing on a trait is not allowed in general. + +#![feature(min_specialization)] + +trait SpecMarker {} + +trait X { + fn f(); +} + +impl X for T { + default fn f() {} +} + +impl X for T { + //~^ ERROR cannot specialize on trait `SpecMarker` + fn f() {} +} + +fn main() {} diff --git a/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr b/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr new file mode 100644 index 0000000000000..35445fd09b949 --- /dev/null +++ b/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr @@ -0,0 +1,11 @@ +error: cannot specialize on trait `SpecMarker` + --> $DIR/specialize_on_trait.rs:15:1 + | +LL | / impl X for T { +LL | | +LL | | fn f() {} +LL | | } + | |_^ + +error: aborting due to previous error + diff --git a/src/test/ui/specialization/non-defaulted-item-fail.rs b/src/test/ui/specialization/non-defaulted-item-fail.rs index 403f718d7dd9f..b7d6ac829dd14 100644 --- a/src/test/ui/specialization/non-defaulted-item-fail.rs +++ b/src/test/ui/specialization/non-defaulted-item-fail.rs @@ -1,4 +1,5 @@ #![feature(specialization, associated_type_defaults)] +//~^ WARN the feature `specialization` is incomplete // Test that attempting to override a non-default method or one not in the // parent impl causes an error. diff --git a/src/test/ui/specialization/non-defaulted-item-fail.stderr b/src/test/ui/specialization/non-defaulted-item-fail.stderr index e6c5fc1441b2f..eae045b92c04d 100644 --- a/src/test/ui/specialization/non-defaulted-item-fail.stderr +++ b/src/test/ui/specialization/non-defaulted-item-fail.stderr @@ -1,5 +1,14 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/non-defaulted-item-fail.rs:1:12 + | +LL | #![feature(specialization, associated_type_defaults)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0520]: `Ty` specializes an item from a parent `impl`, but that item is not marked `default` - --> $DIR/non-defaulted-item-fail.rs:29:5 + --> $DIR/non-defaulted-item-fail.rs:30:5 | LL | / impl Foo for Box { LL | | type Ty = bool; @@ -14,7 +23,7 @@ LL | type Ty = Vec<()>; = note: to specialize, `Ty` in the parent `impl` must be marked `default` error[E0520]: `CONST` specializes an item from a parent `impl`, but that item is not marked `default` - --> $DIR/non-defaulted-item-fail.rs:31:5 + --> $DIR/non-defaulted-item-fail.rs:32:5 | LL | / impl Foo for Box { LL | | type Ty = bool; @@ -29,7 +38,7 @@ LL | const CONST: u8 = 42; = note: to specialize, `CONST` in the parent `impl` must be marked `default` error[E0520]: `foo` specializes an item from a parent `impl`, but that item is not marked `default` - --> $DIR/non-defaulted-item-fail.rs:33:5 + --> $DIR/non-defaulted-item-fail.rs:34:5 | LL | / impl Foo for Box { LL | | type Ty = bool; @@ -44,7 +53,7 @@ LL | fn foo(&self) -> bool { true } = note: to specialize, `foo` in the parent `impl` must be marked `default` error[E0520]: `Ty` specializes an item from a parent `impl`, but that item is not marked `default` - --> $DIR/non-defaulted-item-fail.rs:45:5 + --> $DIR/non-defaulted-item-fail.rs:46:5 | LL | impl Foo for Vec {} | ------------------------- parent `impl` is here @@ -55,7 +64,7 @@ LL | type Ty = Vec<()>; = note: to specialize, `Ty` in the parent `impl` must be marked `default` error[E0520]: `CONST` specializes an item from a parent `impl`, but that item is not marked `default` - --> $DIR/non-defaulted-item-fail.rs:47:5 + --> $DIR/non-defaulted-item-fail.rs:48:5 | LL | impl Foo for Vec {} | ------------------------- parent `impl` is here @@ -66,7 +75,7 @@ LL | const CONST: u8 = 42; = note: to specialize, `CONST` in the parent `impl` must be marked `default` error[E0520]: `foo` specializes an item from a parent `impl`, but that item is not marked `default` - --> $DIR/non-defaulted-item-fail.rs:49:5 + --> $DIR/non-defaulted-item-fail.rs:50:5 | LL | impl Foo for Vec {} | ------------------------- parent `impl` is here @@ -76,6 +85,6 @@ LL | fn foo(&self) -> bool { true } | = note: to specialize, `foo` in the parent `impl` must be marked `default` -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 E0520`. diff --git a/src/test/ui/specialization/specialization-allowed-cross-crate.rs b/src/test/ui/specialization/specialization-allowed-cross-crate.rs index 15550bcce2a8a..5d67160eb96ad 100644 --- a/src/test/ui/specialization/specialization-allowed-cross-crate.rs +++ b/src/test/ui/specialization/specialization-allowed-cross-crate.rs @@ -5,7 +5,7 @@ // aux-build:go_trait.rs -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete extern crate go_trait; diff --git a/src/test/ui/specialization/specialization-allowed-cross-crate.stderr b/src/test/ui/specialization/specialization-allowed-cross-crate.stderr new file mode 100644 index 0000000000000..7d087545725be --- /dev/null +++ b/src/test/ui/specialization/specialization-allowed-cross-crate.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-allowed-cross-crate.rs:8:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-assoc-fns.rs b/src/test/ui/specialization/specialization-assoc-fns.rs index b6a7a48972ac2..cbfcb4719f6a4 100644 --- a/src/test/ui/specialization/specialization-assoc-fns.rs +++ b/src/test/ui/specialization/specialization-assoc-fns.rs @@ -2,7 +2,7 @@ // Test that non-method associated functions can be specialized -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { fn mk() -> Self; diff --git a/src/test/ui/specialization/specialization-assoc-fns.stderr b/src/test/ui/specialization/specialization-assoc-fns.stderr new file mode 100644 index 0000000000000..b12738604ea83 --- /dev/null +++ b/src/test/ui/specialization/specialization-assoc-fns.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-assoc-fns.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-basics.rs b/src/test/ui/specialization/specialization-basics.rs index 6c359e51bc2e4..721c934dbfab9 100644 --- a/src/test/ui/specialization/specialization-basics.rs +++ b/src/test/ui/specialization/specialization-basics.rs @@ -1,6 +1,6 @@ // run-pass -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Tests a variety of basic specialization scenarios and method // dispatch for them. diff --git a/src/test/ui/specialization/specialization-basics.stderr b/src/test/ui/specialization/specialization-basics.stderr new file mode 100644 index 0000000000000..ad00cd81df13c --- /dev/null +++ b/src/test/ui/specialization/specialization-basics.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-basics.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-cross-crate.rs b/src/test/ui/specialization/specialization-cross-crate.rs index fa63c86632985..4171505aa374c 100644 --- a/src/test/ui/specialization/specialization-cross-crate.rs +++ b/src/test/ui/specialization/specialization-cross-crate.rs @@ -2,7 +2,7 @@ // aux-build:specialization_cross_crate.rs -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete extern crate specialization_cross_crate; diff --git a/src/test/ui/specialization/specialization-cross-crate.stderr b/src/test/ui/specialization/specialization-cross-crate.stderr new file mode 100644 index 0000000000000..7481eed796d96 --- /dev/null +++ b/src/test/ui/specialization/specialization-cross-crate.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-cross-crate.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-default-methods.rs b/src/test/ui/specialization/specialization-default-methods.rs index 9ae3d1e9f3931..dcf68afa945bf 100644 --- a/src/test/ui/specialization/specialization-default-methods.rs +++ b/src/test/ui/specialization/specialization-default-methods.rs @@ -1,6 +1,6 @@ // run-pass -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Test that default methods are cascaded correctly diff --git a/src/test/ui/specialization/specialization-default-methods.stderr b/src/test/ui/specialization/specialization-default-methods.stderr new file mode 100644 index 0000000000000..4fa19adad066e --- /dev/null +++ b/src/test/ui/specialization/specialization-default-methods.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-methods.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-default-projection.rs b/src/test/ui/specialization/specialization-default-projection.rs index e9343f2360170..7f3ae951287ca 100644 --- a/src/test/ui/specialization/specialization-default-projection.rs +++ b/src/test/ui/specialization/specialization-default-projection.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Make sure we can't project defaulted associated types diff --git a/src/test/ui/specialization/specialization-default-projection.stderr b/src/test/ui/specialization/specialization-default-projection.stderr index d03aec7ab30d4..456eb6d5ca553 100644 --- a/src/test/ui/specialization/specialization-default-projection.stderr +++ b/src/test/ui/specialization/specialization-default-projection.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-projection.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0308]: mismatched types --> $DIR/specialization-default-projection.rs:21:5 | @@ -9,7 +18,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,9 +34,9 @@ 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 +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/specialization/specialization-default-types.rs b/src/test/ui/specialization/specialization-default-types.rs index acb86d889d43c..346471f11e4a8 100644 --- a/src/test/ui/specialization/specialization-default-types.rs +++ b/src/test/ui/specialization/specialization-default-types.rs @@ -2,7 +2,7 @@ // associated type in the impl defining it -- otherwise, what happens // if it's overridden? -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Example { type Output; diff --git a/src/test/ui/specialization/specialization-default-types.stderr b/src/test/ui/specialization/specialization-default-types.stderr index 257c114252cfc..5e0221f07882e 100644 --- a/src/test/ui/specialization/specialization-default-types.stderr +++ b/src/test/ui/specialization/specialization-default-types.stderr @@ -1,6 +1,17 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-types.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + 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 +19,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,9 +30,9 @@ 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 +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/specialization/specialization-no-default.rs b/src/test/ui/specialization/specialization-no-default.rs index 57346b26d24ec..ae739b2358d58 100644 --- a/src/test/ui/specialization/specialization-no-default.rs +++ b/src/test/ui/specialization/specialization-no-default.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Check a number of scenarios in which one impl tries to override another, // without correctly using `default`. diff --git a/src/test/ui/specialization/specialization-no-default.stderr b/src/test/ui/specialization/specialization-no-default.stderr index 992e9abbd4ce2..bb8b2a6c98e09 100644 --- a/src/test/ui/specialization/specialization-no-default.stderr +++ b/src/test/ui/specialization/specialization-no-default.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-no-default.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0520]: `foo` specializes an item from a parent `impl`, but that item is not marked `default` --> $DIR/specialization-no-default.rs:20:5 | @@ -65,6 +74,6 @@ LL | default fn redundant(&self) {} | = note: to specialize, `redundant` in the parent `impl` must be marked `default` -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 E0520`. diff --git a/src/test/ui/specialization/specialization-on-projection.rs b/src/test/ui/specialization/specialization-on-projection.rs index 5606eaea3073d..be8dcc4232e7b 100644 --- a/src/test/ui/specialization/specialization-on-projection.rs +++ b/src/test/ui/specialization/specialization-on-projection.rs @@ -1,7 +1,7 @@ // run-pass #![allow(dead_code)] -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Ensure that specialization works for impls defined directly on a projection diff --git a/src/test/ui/specialization/specialization-on-projection.stderr b/src/test/ui/specialization/specialization-on-projection.stderr new file mode 100644 index 0000000000000..d91668d10c5f3 --- /dev/null +++ b/src/test/ui/specialization/specialization-on-projection.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-on-projection.rs:4:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-out-of-order.rs b/src/test/ui/specialization/specialization-out-of-order.rs index 94e764f76366e..cb7563e2760c2 100644 --- a/src/test/ui/specialization/specialization-out-of-order.rs +++ b/src/test/ui/specialization/specialization-out-of-order.rs @@ -2,7 +2,7 @@ // Test that you can list the more specific impl before the more general one. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { type Out; diff --git a/src/test/ui/specialization/specialization-out-of-order.stderr b/src/test/ui/specialization/specialization-out-of-order.stderr new file mode 100644 index 0000000000000..a17f9f11a3f31 --- /dev/null +++ b/src/test/ui/specialization/specialization-out-of-order.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-out-of-order.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-overlap-negative.rs b/src/test/ui/specialization/specialization-overlap-negative.rs index af80d6686e3a1..550d370829539 100644 --- a/src/test/ui/specialization/specialization-overlap-negative.rs +++ b/src/test/ui/specialization/specialization-overlap-negative.rs @@ -1,11 +1,11 @@ -#![feature(optin_builtin_traits)] -#![feature(specialization)] +#![feature(negative_impls)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete 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..6141174ba8c03 100644 --- a/src/test/ui/specialization/specialization-overlap-negative.stderr +++ b/src/test/ui/specialization/specialization-overlap-negative.stderr @@ -1,11 +1,20 @@ -error[E0119]: conflicting implementations of trait `std::marker::Send` for type `TestType<_>`: +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-overlap-negative.rs:2:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +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 +error: aborting due to previous error; 1 warning emitted -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-overlap-projection.rs b/src/test/ui/specialization/specialization-overlap-projection.rs index 00b83c7e7a1b8..b07efb2a5c1cd 100644 --- a/src/test/ui/specialization/specialization-overlap-projection.rs +++ b/src/test/ui/specialization/specialization-overlap-projection.rs @@ -4,7 +4,7 @@ // projections involve specialization, so long as the associated type is // provided by the most specialized impl. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Assoc { type Output; diff --git a/src/test/ui/specialization/specialization-overlap-projection.stderr b/src/test/ui/specialization/specialization-overlap-projection.stderr new file mode 100644 index 0000000000000..6f1a594bacb3a --- /dev/null +++ b/src/test/ui/specialization/specialization-overlap-projection.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-overlap-projection.rs:7:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-overlap.rs b/src/test/ui/specialization/specialization-overlap.rs index c8ef8d61c1e8f..6bee22ceb8b64 100644 --- a/src/test/ui/specialization/specialization-overlap.rs +++ b/src/test/ui/specialization/specialization-overlap.rs @@ -1,4 +1,4 @@ -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Foo { fn foo() {} } impl Foo for T {} diff --git a/src/test/ui/specialization/specialization-overlap.stderr b/src/test/ui/specialization/specialization-overlap.stderr index 4275e7bdd85e2..cf0f186a18337 100644 --- a/src/test/ui/specialization/specialization-overlap.stderr +++ b/src/test/ui/specialization/specialization-overlap.stderr @@ -1,3 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-overlap.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0119]: conflicting implementations of trait `Foo` for type `std::vec::Vec<_>`: --> $DIR/specialization-overlap.rs:5:1 | @@ -30,6 +39,6 @@ LL | impl Qux for T {} LL | impl Qux for T {} | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation -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 E0119`. diff --git a/src/test/ui/specialization/specialization-polarity.rs b/src/test/ui/specialization/specialization-polarity.rs index d574480674571..17897d8b803d9 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(specialization)] +#![feature(negative_impls)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete 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..c44af22b8e63b 100644 --- a/src/test/ui/specialization/specialization-polarity.stderr +++ b/src/test/ui/specialization/specialization-polarity.stderr @@ -1,19 +1,28 @@ -error[E0119]: conflicting implementations of trait `Foo` for type `u8`: - --> $DIR/specialization-polarity.rs:9:1 +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-polarity.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +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 +error: aborting due to 2 previous errors; 1 warning emitted -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-projection-alias.rs b/src/test/ui/specialization/specialization-projection-alias.rs index 0081ed455c960..f1f0b47bb6503 100644 --- a/src/test/ui/specialization/specialization-projection-alias.rs +++ b/src/test/ui/specialization/specialization-projection-alias.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] #![allow(unused_variables)] -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Regression test for ICE when combining specialized associated types and type // aliases diff --git a/src/test/ui/specialization/specialization-projection-alias.stderr b/src/test/ui/specialization/specialization-projection-alias.stderr new file mode 100644 index 0000000000000..0c3659a8f7a06 --- /dev/null +++ b/src/test/ui/specialization/specialization-projection-alias.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-projection-alias.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-projection.rs b/src/test/ui/specialization/specialization-projection.rs index 86cdccf131e22..700975e3b828f 100644 --- a/src/test/ui/specialization/specialization-projection.rs +++ b/src/test/ui/specialization/specialization-projection.rs @@ -1,7 +1,7 @@ // run-pass #![allow(dead_code)] -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Make sure we *can* project non-defaulted associated types // cf compile-fail/specialization-default-projection.rs diff --git a/src/test/ui/specialization/specialization-projection.stderr b/src/test/ui/specialization/specialization-projection.stderr new file mode 100644 index 0000000000000..c5c86f5108e6e --- /dev/null +++ b/src/test/ui/specialization/specialization-projection.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-projection.rs:4:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-super-traits.rs b/src/test/ui/specialization/specialization-super-traits.rs index a0f71d876931e..fb85d8019218a 100644 --- a/src/test/ui/specialization/specialization-super-traits.rs +++ b/src/test/ui/specialization/specialization-super-traits.rs @@ -1,6 +1,6 @@ // run-pass -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Test that you can specialize via an explicit trait hierarchy diff --git a/src/test/ui/specialization/specialization-super-traits.stderr b/src/test/ui/specialization/specialization-super-traits.stderr new file mode 100644 index 0000000000000..05bdfd40136a4 --- /dev/null +++ b/src/test/ui/specialization/specialization-super-traits.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-super-traits.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-translate-projections-with-lifetimes.rs b/src/test/ui/specialization/specialization-translate-projections-with-lifetimes.rs index 2e32e3ff02d3e..5c2781a9c63a6 100644 --- a/src/test/ui/specialization/specialization-translate-projections-with-lifetimes.rs +++ b/src/test/ui/specialization/specialization-translate-projections-with-lifetimes.rs @@ -1,6 +1,6 @@ // run-pass -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Iterator { fn next(&self); diff --git a/src/test/ui/specialization/specialization-translate-projections-with-lifetimes.stderr b/src/test/ui/specialization/specialization-translate-projections-with-lifetimes.stderr new file mode 100644 index 0000000000000..6284dd8f3f7d7 --- /dev/null +++ b/src/test/ui/specialization/specialization-translate-projections-with-lifetimes.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-translate-projections-with-lifetimes.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-translate-projections-with-params.rs b/src/test/ui/specialization/specialization-translate-projections-with-params.rs index bdc6501df44b8..62d63590a6688 100644 --- a/src/test/ui/specialization/specialization-translate-projections-with-params.rs +++ b/src/test/ui/specialization/specialization-translate-projections-with-params.rs @@ -4,7 +4,7 @@ // type parameters *and* rely on projections, and the type parameters are input // types on the trait. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Trait { fn convert(&self) -> T; diff --git a/src/test/ui/specialization/specialization-translate-projections-with-params.stderr b/src/test/ui/specialization/specialization-translate-projections-with-params.stderr new file mode 100644 index 0000000000000..b17794173c570 --- /dev/null +++ b/src/test/ui/specialization/specialization-translate-projections-with-params.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-translate-projections-with-params.rs:7:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/specialization/specialization-translate-projections.rs b/src/test/ui/specialization/specialization-translate-projections.rs index fcccb67902e58..92ea9e2b85d32 100644 --- a/src/test/ui/specialization/specialization-translate-projections.rs +++ b/src/test/ui/specialization/specialization-translate-projections.rs @@ -3,7 +3,7 @@ // Ensure that provided items are inherited properly even when impls vary in // type parameters *and* rely on projections. -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete use std::convert::Into; diff --git a/src/test/ui/specialization/specialization-translate-projections.stderr b/src/test/ui/specialization/specialization-translate-projections.stderr new file mode 100644 index 0000000000000..fbb28e6064088 --- /dev/null +++ b/src/test/ui/specialization/specialization-translate-projections.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-translate-projections.rs:6:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + 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 3a974164f9473..8a7588c80d71e 100644 --- a/src/test/ui/stability-in-private-module.stderr +++ b/src/test/ui/stability-in-private-module.stderr @@ -1,8 +1,8 @@ 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(); - | ^^^^^^^^^^^ this module is private + | ^^^^^^^^^^^ private module | note: the module `thread_info` is defined here --> $SRC_DIR/libstd/thread/mod.rs:LL:COL 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-drop-scope.stderr b/src/test/ui/static/static-drop-scope.stderr index bc08f33f82093..ed81734f6ebd7 100644 --- a/src/test/ui/static/static-drop-scope.stderr +++ b/src/test/ui/static/static-drop-scope.stderr @@ -2,7 +2,9 @@ error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:9:60 | LL | static PROMOTION_FAIL_S: Option<&'static WithDtor> = Some(&WithDtor); - | ^^^^^^^^ statics cannot evaluate destructors + | ^^^^^^^^- value is dropped here + | | + | statics cannot evaluate destructors error[E0716]: temporary value dropped while borrowed --> $DIR/static-drop-scope.rs:9:60 @@ -18,7 +20,9 @@ error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:13:59 | LL | const PROMOTION_FAIL_C: Option<&'static WithDtor> = Some(&WithDtor); - | ^^^^^^^^ constants cannot evaluate destructors + | ^^^^^^^^- value is dropped here + | | + | constants cannot evaluate destructors error[E0716]: temporary value dropped while borrowed --> $DIR/static-drop-scope.rs:13:59 @@ -34,37 +38,50 @@ error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:17:28 | LL | static EARLY_DROP_S: i32 = (WithDtor, 0).1; - | ^^^^^^^^^^^^^ statics cannot evaluate destructors + | ^^^^^^^^^^^^^ - value is dropped here + | | + | statics cannot evaluate destructors error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:20:27 | LL | const EARLY_DROP_C: i32 = (WithDtor, 0).1; - | ^^^^^^^^^^^^^ constants cannot evaluate destructors + | ^^^^^^^^^^^^^ - value is dropped here + | | + | constants cannot evaluate destructors error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:23:24 | LL | const fn const_drop(_: T) {} - | ^ constant functions cannot evaluate destructors + | ^ - value is dropped here + | | + | constant functions cannot evaluate destructors error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:27:5 | LL | (x, ()).1 | ^^^^^^^ constant functions cannot evaluate destructors +LL | +LL | } + | - value is dropped here error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:31:34 | LL | const EARLY_DROP_C_OPTION: i32 = (Some(WithDtor), 0).1; - | ^^^^^^^^^^^^^^^^^^^ constants cannot evaluate destructors + | ^^^^^^^^^^^^^^^^^^^ - value is dropped here + | | + | constants cannot evaluate destructors error[E0493]: destructors cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:36:43 | LL | const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1; - | ^^^^^^^^^^^ constants cannot evaluate destructors + | ^^^^^^^^^^^ - value is dropped here + | | + | constants cannot evaluate destructors error: aborting due to 10 previous errors 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-method-privacy.stderr b/src/test/ui/static/static-method-privacy.stderr index 78d211438cc92..569608780def9 100644 --- a/src/test/ui/static/static-method-privacy.stderr +++ b/src/test/ui/static/static-method-privacy.stderr @@ -1,8 +1,8 @@ error[E0624]: associated function `new` is private - --> $DIR/static-method-privacy.rs:9:13 + --> $DIR/static-method-privacy.rs:9:19 | LL | let _ = a::S::new(); - | ^^^^^^^^^ + | ^^^ private associated function error: aborting due to previous error 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/static/static-priv-by-default2.stderr b/src/test/ui/static/static-priv-by-default2.stderr index f6cd40412dd84..d731da79246ee 100644 --- a/src/test/ui/static/static-priv-by-default2.stderr +++ b/src/test/ui/static/static-priv-by-default2.stderr @@ -2,7 +2,7 @@ error[E0603]: static `private` is private --> $DIR/static-priv-by-default2.rs:15:30 | LL | use child::childs_child::private; - | ^^^^^^^ this static is private + | ^^^^^^^ private static | note: the static `private` is defined here --> $DIR/static-priv-by-default2.rs:7:9 @@ -14,7 +14,7 @@ error[E0603]: static `private` is private --> $DIR/static-priv-by-default2.rs:23:33 | LL | use static_priv_by_default::private; - | ^^^^^^^ this static is private + | ^^^^^^^ private static | note: the static `private` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:3:1 diff --git a/src/test/ui/str/str-mut-idx.stderr b/src/test/ui/str/str-mut-idx.stderr index a9ec6b9c02fe8..2fd805e646991 100644 --- a/src/test/ui/str/str-mut-idx.stderr +++ b/src/test/ui/str/str-mut-idx.stderr @@ -2,15 +2,17 @@ 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 {} } - | --- -- help: consider relaxing the implicit `Sized` restriction: `: ?Sized` - | | - | 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 | = help: the trait `std::marker::Sized` is not implemented for `str` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | fn bot() -> T { loop {} } + | ^^^^^^^^ error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/str-mut-idx.rs:4:5 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/structs/struct-field-privacy.stderr b/src/test/ui/structs/struct-field-privacy.stderr index 91d000b8672e0..f8b16ec0d01b3 100644 --- a/src/test/ui/structs/struct-field-privacy.stderr +++ b/src/test/ui/structs/struct-field-privacy.stderr @@ -1,32 +1,32 @@ error[E0616]: field `a` of struct `inner::A` is private - --> $DIR/struct-field-privacy.rs:23:5 + --> $DIR/struct-field-privacy.rs:23:7 | LL | b.a; - | ^^^ + | ^ private field error[E0616]: field `b` of struct `inner::B` is private - --> $DIR/struct-field-privacy.rs:26:5 + --> $DIR/struct-field-privacy.rs:26:7 | LL | c.b; - | ^^^ + | ^ private field error[E0616]: field `a` of struct `xc::A` is private - --> $DIR/struct-field-privacy.rs:28:5 + --> $DIR/struct-field-privacy.rs:28:7 | LL | d.a; - | ^^^ + | ^ private field error[E0616]: field `b` of struct `xc::B` is private - --> $DIR/struct-field-privacy.rs:32:5 + --> $DIR/struct-field-privacy.rs:32:7 | LL | e.b; - | ^^^ + | ^ private field error[E0616]: field `1` of struct `inner::Z` is private - --> $DIR/struct-field-privacy.rs:35:5 + --> $DIR/struct-field-privacy.rs:35:7 | LL | z.1; - | ^^^ + | ^ private field error: aborting due to 5 previous errors diff --git a/src/test/ui/structs/struct-variant-privacy-xc.stderr b/src/test/ui/structs/struct-variant-privacy-xc.stderr index 0203b7b5242e5..4e022cef1b2d3 100644 --- a/src/test/ui/structs/struct-variant-privacy-xc.stderr +++ b/src/test/ui/structs/struct-variant-privacy-xc.stderr @@ -2,7 +2,7 @@ error[E0603]: enum `Bar` is private --> $DIR/struct-variant-privacy-xc.rs:4:33 | LL | fn f(b: struct_variant_privacy::Bar) { - | ^^^ this enum is private + | ^^^ private enum | note: the enum `Bar` is defined here --> $DIR/auxiliary/struct_variant_privacy.rs:1:1 @@ -14,7 +14,7 @@ error[E0603]: enum `Bar` is private --> $DIR/struct-variant-privacy-xc.rs:6:33 | LL | struct_variant_privacy::Bar::Baz { a: _a } => {} - | ^^^ this enum is private + | ^^^ private enum | note: the enum `Bar` is defined here --> $DIR/auxiliary/struct_variant_privacy.rs:1:1 diff --git a/src/test/ui/structs/struct-variant-privacy.stderr b/src/test/ui/structs/struct-variant-privacy.stderr index d1b603f9d46fc..a6bc381ff6b38 100644 --- a/src/test/ui/structs/struct-variant-privacy.stderr +++ b/src/test/ui/structs/struct-variant-privacy.stderr @@ -2,7 +2,7 @@ error[E0603]: enum `Bar` is private --> $DIR/struct-variant-privacy.rs:7:14 | LL | fn f(b: foo::Bar) { - | ^^^ this enum is private + | ^^^ private enum | note: the enum `Bar` is defined here --> $DIR/struct-variant-privacy.rs:2:5 @@ -14,7 +14,7 @@ error[E0603]: enum `Bar` is private --> $DIR/struct-variant-privacy.rs:9:14 | LL | foo::Bar::Baz { a: _a } => {} - | ^^^ this enum is private + | ^^^ private enum | note: the enum `Bar` is defined here --> $DIR/struct-variant-privacy.rs:2:5 diff --git a/src/test/ui/substs-ppaux.normal.stderr b/src/test/ui/substs-ppaux.normal.stderr index 4423f3c130e2a..bcdeed262ecba 100644 --- a/src/test/ui/substs-ppaux.normal.stderr +++ b/src/test/ui/substs-ppaux.normal.stderr @@ -5,14 +5,16 @@ LL | fn bar<'a, T>() where T: 'a {} | --------------------------- fn() {>::bar::<'static, char>} defined here ... LL | let x: () = >::bar::<'static, char>; - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | expected `()`, found fn item - | | help: use parentheses to call this function: `>::bar::<'static, char>()` + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item + | | | expected due to this | = note: expected unit type `()` found fn item `fn() {>::bar::<'static, char>}` +help: use parentheses to call this function + | +LL | let x: () = >::bar::<'static, char>(); + | ^^ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:25:17 @@ -21,14 +23,16 @@ LL | fn bar<'a, T>() where T: 'a {} | --------------------------- fn() {>::bar::<'static, char>} defined here ... LL | let x: () = >::bar::<'static, char>; - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | expected `()`, found fn item - | | help: use parentheses to call this function: `>::bar::<'static, char>()` + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item + | | | expected due to this | = note: expected unit type `()` found fn item `fn() {>::bar::<'static, char>}` +help: use parentheses to call this function + | +LL | let x: () = >::bar::<'static, char>(); + | ^^ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:33:17 @@ -37,14 +41,16 @@ LL | fn baz() {} | -------- fn() {>::baz} defined here ... LL | let x: () = >::baz; - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | expected `()`, found fn item - | | help: use parentheses to call this function: `>::baz()` + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item + | | | expected due to this | = note: expected unit type `()` found fn item `fn() {>::baz}` +help: use parentheses to call this function + | +LL | let x: () = >::baz(); + | ^^ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:41:17 @@ -53,20 +59,22 @@ LL | fn foo<'z>() where &'z (): Sized { | -------------------------------- fn() {foo::<'static>} defined here ... LL | let x: () = foo::<'static>; - | -- ^^^^^^^^^^^^^^ - | | | - | | expected `()`, found fn item - | | help: use parentheses to call this function: `foo::<'static>()` + | -- ^^^^^^^^^^^^^^ expected `()`, found fn item + | | | expected due to this | = note: expected unit type `()` found fn item `fn() {foo::<'static>}` +help: use parentheses to call this function + | +LL | let x: () = foo::<'static>(); + | ^^ error[E0277]: the size for values of type `str` cannot be known at compilation time --> $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 2aebdebee72c1..fb5e6fbcfe712 100644 --- a/src/test/ui/substs-ppaux.verbose.stderr +++ b/src/test/ui/substs-ppaux.verbose.stderr @@ -5,14 +5,16 @@ LL | fn bar<'a, T>() where T: 'a {} | --------------------------- fn() {>::bar::} defined here ... LL | let x: () = >::bar::<'static, char>; - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | expected `()`, found fn item - | | help: use parentheses to call this function: `>::bar::<'static, char>()` + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item + | | | expected due to this | = note: expected unit type `()` found fn item `fn() {>::bar::}` +help: use parentheses to call this function + | +LL | let x: () = >::bar::<'static, char>(); + | ^^ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:25:17 @@ -21,14 +23,16 @@ LL | fn bar<'a, T>() where T: 'a {} | --------------------------- fn() {>::bar::} defined here ... LL | let x: () = >::bar::<'static, char>; - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | expected `()`, found fn item - | | help: use parentheses to call this function: `>::bar::<'static, char>()` + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item + | | | expected due to this | = note: expected unit type `()` found fn item `fn() {>::bar::}` +help: use parentheses to call this function + | +LL | let x: () = >::bar::<'static, char>(); + | ^^ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:33:17 @@ -37,14 +41,16 @@ LL | fn baz() {} | -------- fn() {>::baz} defined here ... LL | let x: () = >::baz; - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | | - | | expected `()`, found fn item - | | help: use parentheses to call this function: `>::baz()` + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item + | | | expected due to this | = note: expected unit type `()` found fn item `fn() {>::baz}` +help: use parentheses to call this function + | +LL | let x: () = >::baz(); + | ^^ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:41:17 @@ -53,20 +59,22 @@ LL | fn foo<'z>() where &'z (): Sized { | -------------------------------- fn() {foo::} defined here ... LL | let x: () = foo::<'static>; - | -- ^^^^^^^^^^^^^^ - | | | - | | expected `()`, found fn item - | | help: use parentheses to call this function: `foo::<'static>()` + | -- ^^^^^^^^^^^^^^ expected `()`, found fn item + | | | expected due to this | = note: expected unit type `()` found fn item `fn() {foo::}` +help: use parentheses to call this function + | +LL | let x: () = foo::<'static>(); + | ^^ error[E0277]: the size for values of type `str` cannot be known at compilation time --> $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/adt-param-with-implicit-sized-bound.rs b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs new file mode 100644 index 0000000000000..ef64d799b65cf --- /dev/null +++ b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs @@ -0,0 +1,28 @@ +trait Trait { + fn func1() -> Struct1; //~ ERROR E0277 + fn func2<'a>() -> Struct2<'a, Self>; //~ ERROR E0277 + fn func3() -> Struct3; //~ ERROR E0277 + fn func4() -> Struct4; //~ ERROR E0277 +} + +struct Struct1{ + _t: std::marker::PhantomData<*const T>, +} +struct Struct2<'a, T>{ + _t: &'a T, +} +struct Struct3{ + _t: T, +} + +struct X(T); + +struct Struct4{ + _t: X, +} + +struct Struct5{ + _t: X, //~ ERROR E0277 +} + +fn main() {} diff --git a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr new file mode 100644 index 0000000000000..ee08f51f80270 --- /dev/null +++ b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr @@ -0,0 +1,107 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:25:5 + | +LL | struct X(T); + | - required by this bound in `X` +... +LL | struct Struct5{ + | - this type parameter needs to be `std::marker::Sized` +LL | _t: X, + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `T` + = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/adt-param-with-implicit-sized-bound.rs:18:10 + | +LL | struct X(T); + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:2:19 + | +LL | fn func1() -> Struct1; + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Struct1{ + | - required by this bound in `Struct1` + | + = help: the trait `std::marker::Sized` is not implemented for `Self` + = note: to learn more, visit +help: consider further restricting `Self` + | +LL | fn func1() -> Struct1 where Self: std::marker::Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider relaxing the implicit `Sized` restriction + | +LL | struct Struct1{ + | ^^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:3:23 + | +LL | fn func2<'a>() -> Struct2<'a, Self>; + | ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Struct2<'a, T>{ + | - required by this bound in `Struct2` + | + = help: the trait `std::marker::Sized` is not implemented for `Self` + = note: to learn more, visit +help: consider further restricting `Self` + | +LL | fn func2<'a>() -> Struct2<'a, Self> where Self: std::marker::Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider relaxing the implicit `Sized` restriction + | +LL | struct Struct2<'a, T: ?Sized>{ + | ^^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:4:19 + | +LL | fn func3() -> Struct3; + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Struct3{ + | - required by this bound in `Struct3` + | + = help: the trait `std::marker::Sized` is not implemented for `Self` + = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/adt-param-with-implicit-sized-bound.rs:14:16 + | +LL | struct Struct3{ + | ^ this could be changed to `T: ?Sized`... +LL | _t: T, + | - ...if indirection was used here: `Box` +help: consider further restricting `Self` + | +LL | fn func3() -> Struct3 where Self: std::marker::Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/adt-param-with-implicit-sized-bound.rs:5:19 + | +LL | fn func4() -> Struct4; + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time +... +LL | struct Struct4{ + | - required by this bound in `Struct4` + | + = help: the trait `std::marker::Sized` is not implemented for `Self` + = note: to learn more, visit +help: consider further restricting `Self` + | +LL | fn func4() -> Struct4 where Self: std::marker::Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider relaxing the implicit `Sized` restriction + | +LL | struct Struct4{ + | ^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. 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 05583876a066c..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,27 +5,31 @@ 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}` - | help: use parentheses to call the function: `foo()` + | ^^^ the trait `std::future::Future` is not implemented for `fn() -> impl std::future::Future {foo}` + | +help: use parentheses to call the function + | +LL | bar(foo()); + | ^^ error[E0277]: the trait bound `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:36]: std::future::Future` is not satisfied --> $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 LL | bar(async_closure); - | ^^^^^^^^^^^^^ - | | - | the trait `std::future::Future` is not implemented for `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:36]` - | help: use parentheses to call the closure: `async_closure()` + | ^^^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:36]` + | +help: use parentheses to call the closure + | +LL | bar(async_closure()); + | ^^ error: aborting due to 2 previous errors 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-in-struct-pat.stderr b/src/test/ui/suggestions/const-in-struct-pat.stderr index 0a010dcab4c26..ab336b14d2948 100644 --- a/src/test/ui/suggestions/const-in-struct-pat.stderr +++ b/src/test/ui/suggestions/const-in-struct-pat.stderr @@ -9,7 +9,11 @@ LL | let Thing { foo } = t; | | | expected struct `std::string::String`, found struct `foo` | `foo` is interpreted as a unit struct, not a new binding - | help: bind the struct field to a different name instead: `foo: other_foo` + | +help: bind the struct field to a different name instead + | +LL | let Thing { foo: other_foo } = t; + | ^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/suggestions/const-no-type.rs b/src/test/ui/suggestions/const-no-type.rs index 99200a965dd21..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 @@ -43,4 +48,4 @@ static S = Vec::::new(); static mut SM = "abc"; //~^ ERROR missing type for `static mut` item //~| HELP provide a type for the item -//~| SUGGESTION &'static str +//~| SUGGESTION &str diff --git a/src/test/ui/suggestions/const-no-type.stderr b/src/test/ui/suggestions/const-no-type.stderr index c4f17109dc5c7..874c1bac10bd5 100644 --- a/src/test/ui/suggestions/const-no-type.stderr +++ b/src/test/ui/suggestions/const-no-type.stderr @@ -4,17 +4,23 @@ 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: &'static str` + | ^^ help: provide a type for the item: `SM: &str` error: missing type for `const` item --> $DIR/const-no-type.rs:14:7 @@ -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.rs b/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.rs index 2a11871db8e67..21fb8d4a2e68d 100644 --- a/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.rs +++ b/src/test/ui/suggestions/const-pat-non-exaustive-let-new-var.rs @@ -1,6 +1,6 @@ fn main() { let A = 3; - //~^ ERROR refutable pattern in local binding: `std::i32::MIN..=1i32` and + //~^ ERROR refutable pattern in local binding: `i32::MIN..=1i32` and //~| interpreted as a constant pattern, not a new variable //~| HELP introduce a variable instead //~| SUGGESTION a_var 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..7a6269da07f32 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 @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `std::i32::MIN..=1i32` and `3i32..=std::i32::MAX` not covered +error[E0005]: refutable pattern in local binding: `i32::MIN..=1i32` and `3i32..=i32::MAX` not covered --> $DIR/const-pat-non-exaustive-let-new-var.rs:2:9 | LL | let A = 3; @@ -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 91f60e8f426c4..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,27 +5,31 @@ 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}` - | help: use parentheses to call the function: `foo()` + | ^^^ the trait `T` is not implemented for `fn() -> impl T {foo}` + | +help: use parentheses to call the function + | +LL | bar(foo()); + | ^^ error[E0277]: the trait bound `[closure@$DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:18:19: 18:23]: T` is not satisfied --> $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 LL | bar(closure); - | ^^^^^^^ - | | - | the trait `T` is not implemented for `[closure@$DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:18:19: 18:23]` - | help: use parentheses to call the closure: `closure()` + | ^^^^^^^ the trait `T` is not implemented for `[closure@$DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:18:19: 18:23]` + | +help: use parentheses to call the closure + | +LL | bar(closure()); + | ^^ error: aborting due to 2 previous errors 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/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr index 232e54b5d37b2..b03bea1eddbf0 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -19,14 +19,16 @@ LL | fn foo(a: usize, b: usize) -> usize { a } | ----------------------------------- fn(usize, usize) -> usize {foo} defined here ... LL | let _: usize = foo; - | ----- ^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `foo(a, b)` + | ----- ^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `fn(usize, usize) -> usize {foo}` +help: use parentheses to call this function + | +LL | let _: usize = foo(a, b); + | ^^^^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:30:16 @@ -35,14 +37,16 @@ LL | struct S(usize, usize); | ----------------------- fn(usize, usize) -> S {S} defined here ... LL | let _: S = S; - | - ^ - | | | - | | expected struct `S`, found fn item - | | help: use parentheses to instantiate this tuple struct: `S(_, _)` + | - ^ expected struct `S`, found fn item + | | | expected due to this | = note: expected struct `S` found fn item `fn(usize, usize) -> S {S}` +help: use parentheses to instantiate this tuple struct + | +LL | let _: S = S(_, _); + | ^^^^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:31:20 @@ -51,14 +55,16 @@ LL | fn bar() -> usize { 42 } | ----------------- fn() -> usize {bar} defined here ... LL | let _: usize = bar; - | ----- ^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `bar()` + | ----- ^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `fn() -> usize {bar}` +help: use parentheses to call this function + | +LL | let _: usize = bar(); + | ^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:32:16 @@ -67,14 +73,16 @@ LL | struct V(); | ----------- fn() -> V {V} defined here ... LL | let _: V = V; - | - ^ - | | | - | | expected struct `V`, found fn item - | | help: use parentheses to instantiate this tuple struct: `V()` + | - ^ expected struct `V`, found fn item + | | | expected due to this | = note: expected struct `V` found fn item `fn() -> V {V}` +help: use parentheses to instantiate this tuple struct + | +LL | let _: V = V(); + | ^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:33:20 @@ -83,14 +91,16 @@ LL | fn baz(x: usize, y: usize) -> usize { x } | ----------------------------------- fn(usize, usize) -> usize {<_ as T>::baz} defined here ... LL | let _: usize = T::baz; - | ----- ^^^^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `T::baz(x, y)` + | ----- ^^^^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `fn(usize, usize) -> usize {<_ as T>::baz}` +help: use parentheses to call this function + | +LL | let _: usize = T::baz(x, y); + | ^^^^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:34:20 @@ -99,14 +109,16 @@ LL | fn bat(x: usize) -> usize { 42 } | ------------------------- fn(usize) -> usize {<_ as T>::bat} defined here ... LL | let _: usize = T::bat; - | ----- ^^^^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `T::bat(x)` + | ----- ^^^^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `fn(usize) -> usize {<_ as T>::bat}` +help: use parentheses to call this function + | +LL | let _: usize = T::bat(x); + | ^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:35:16 @@ -115,14 +127,16 @@ LL | A(usize), | -------- fn(usize) -> E {E::A} defined here ... LL | let _: E = E::A; - | - ^^^^ - | | | - | | expected enum `E`, found fn item - | | help: use parentheses to instantiate this tuple variant: `E::A(_)` + | - ^^^^ expected enum `E`, found fn item + | | | expected due to this | = note: expected enum `E` found fn item `fn(usize) -> E {E::A}` +help: use parentheses to instantiate this tuple variant + | +LL | let _: E = E::A(_); + | ^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:37:20 @@ -131,14 +145,16 @@ LL | fn baz(x: usize, y: usize) -> usize { x } | ----------------------------------- fn(usize, usize) -> usize {::baz} defined here ... LL | let _: usize = X::baz; - | ----- ^^^^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `X::baz(x, y)` + | ----- ^^^^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `fn(usize, usize) -> usize {::baz}` +help: use parentheses to call this function + | +LL | let _: usize = X::baz(x, y); + | ^^^^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:38:20 @@ -147,14 +163,16 @@ LL | fn bat(x: usize) -> usize { 42 } | ------------------------- fn(usize) -> usize {::bat} defined here ... LL | let _: usize = X::bat; - | ----- ^^^^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `X::bat(x)` + | ----- ^^^^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `fn(usize) -> usize {::bat}` +help: use parentheses to call this function + | +LL | let _: usize = X::bat(x); + | ^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:39:20 @@ -163,14 +181,16 @@ LL | fn bax(x: usize) -> usize { 42 } | ------------------------- fn(usize) -> usize {::bax} defined here ... LL | let _: usize = X::bax; - | ----- ^^^^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `X::bax(x)` + | ----- ^^^^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `fn(usize) -> usize {::bax}` +help: use parentheses to call this function + | +LL | let _: usize = X::bax(x); + | ^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:40:20 @@ -179,14 +199,16 @@ LL | fn bach(x: usize) -> usize; | --------------------------- fn(usize) -> usize {::bach} defined here ... LL | let _: usize = X::bach; - | ----- ^^^^^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `X::bach(x)` + | ----- ^^^^^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `fn(usize) -> usize {::bach}` +help: use parentheses to call this function + | +LL | let _: usize = X::bach(x); + | ^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:41:20 @@ -195,14 +217,16 @@ LL | fn ban(&self) -> usize { 42 } | ---------------------- for<'r> fn(&'r X) -> usize {::ban} defined here ... LL | let _: usize = X::ban; - | ----- ^^^^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `X::ban(_)` + | ----- ^^^^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `for<'r> fn(&'r X) -> usize {::ban}` +help: use parentheses to call this function + | +LL | let _: usize = X::ban(_); + | ^^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:42:20 @@ -211,26 +235,38 @@ LL | fn bal(&self) -> usize; | ----------------------- for<'r> fn(&'r X) -> usize {::bal} defined here ... LL | let _: usize = X::bal; - | ----- ^^^^^^ - | | | - | | expected `usize`, found fn item - | | help: use parentheses to call this function: `X::bal(_)` + | ----- ^^^^^^ expected `usize`, found fn item + | | | expected due to this | = note: expected type `usize` found fn item `for<'r> fn(&'r X) -> usize {::bal}` +help: use parentheses to call this function + | +LL | let _: usize = X::bal(_); + | ^^^ error[E0615]: attempted to take value of method `ban` on type `X` --> $DIR/fn-or-tuple-struct-without-args.rs:43:22 | LL | let _: usize = X.ban; - | ^^^ help: use parentheses to call the method: `ban()` + | ^^^ method, not a field + | +help: use parentheses to call the method + | +LL | let _: usize = X.ban(); + | ^^ error[E0615]: attempted to take value of method `bal` on type `X` --> $DIR/fn-or-tuple-struct-without-args.rs:44:22 | LL | let _: usize = X.bal; - | ^^^ help: use parentheses to call the method: `bal()` + | ^^^ method, not a field + | +help: use parentheses to call the method + | +LL | let _: usize = X.bal(); + | ^^ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:46:20 @@ -238,14 +274,16 @@ error[E0308]: mismatched types LL | let closure = || 42; | ----- the found closure LL | let _: usize = closure; - | ----- ^^^^^^^ - | | | - | | expected `usize`, found closure - | | help: use parentheses to call this closure: `closure()` + | ----- ^^^^^^^ expected `usize`, found closure + | | | expected due to this | = note: expected type `usize` found closure `[closure@$DIR/fn-or-tuple-struct-without-args.rs:45:19: 45:24]` +help: use parentheses to call this closure + | +LL | let _: usize = closure(); + | ^^ error: aborting due to 17 previous errors diff --git a/src/test/ui/suggestions/fn-trait-notation.fixed b/src/test/ui/suggestions/fn-trait-notation.fixed new file mode 100644 index 0000000000000..cf940f4e9267a --- /dev/null +++ b/src/test/ui/suggestions/fn-trait-notation.fixed @@ -0,0 +1,19 @@ +// run-rustfix +fn e0658(f: F, g: G, h: H) -> i32 +where + F: Fn(i32) -> i32, //~ ERROR E0658 + G: Fn(i32, i32) -> (i32, i32), //~ ERROR E0658 + H: Fn(i32) -> i32, //~ ERROR E0658 +{ + f(3); + g(3, 4); + h(3) +} + +fn main() { + e0658( + |a| a, + |a, b| (b, a), + |a| a, + ); +} diff --git a/src/test/ui/suggestions/fn-trait-notation.rs b/src/test/ui/suggestions/fn-trait-notation.rs new file mode 100644 index 0000000000000..f0bb03315d987 --- /dev/null +++ b/src/test/ui/suggestions/fn-trait-notation.rs @@ -0,0 +1,19 @@ +// run-rustfix +fn e0658(f: F, g: G, h: H) -> i32 +where + F: Fn, //~ ERROR E0658 + G: Fn<(i32, i32, ), Output = (i32, i32)>, //~ ERROR E0658 + H: Fn<(i32,), Output = i32>, //~ ERROR E0658 +{ + f(3); + g(3, 4); + h(3) +} + +fn main() { + e0658( + |a| a, + |a, b| (b, a), + |a| a, + ); +} diff --git a/src/test/ui/suggestions/fn-trait-notation.stderr b/src/test/ui/suggestions/fn-trait-notation.stderr new file mode 100644 index 0000000000000..3e3b541744017 --- /dev/null +++ b/src/test/ui/suggestions/fn-trait-notation.stderr @@ -0,0 +1,30 @@ +error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change + --> $DIR/fn-trait-notation.rs:4:8 + | +LL | F: Fn, + | ^^^^^^^^^^^^^^^^^^^^^ help: use parenthetical notation instead: `Fn(i32) -> i32` + | + = note: see issue #29625 for more information + = 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/fn-trait-notation.rs:5:8 + | +LL | G: Fn<(i32, i32, ), Output = (i32, i32)>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use parenthetical notation instead: `Fn(i32, i32) -> (i32, i32)` + | + = note: see issue #29625 for more information + = 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/fn-trait-notation.rs:6:8 + | +LL | H: Fn<(i32,), Output = i32>, + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use parenthetical notation instead: `Fn(i32) -> i32` + | + = note: see issue #29625 for more information + = 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/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 ccaceefacd739..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,22 +2,23 @@ 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` - | help: consider changing this borrow's mutability: `&mut` + | ^^ the trait `Trait` is not implemented for `&S` | = help: the following implementations were found: <&'a mut S as Trait> +help: consider changing this borrow's mutability + | +LL | foo(&mut s); + | ^^^^ 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..f7affdbf1b408 100644 --- a/src/test/ui/suggestions/into-str.stderr +++ b/src/test/ui/suggestions/into-str.stderr @@ -2,13 +2,12 @@ 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` | = note: to coerce a `std::string::String` into a `&str`, use `&*` as a prefix - = note: `std::convert::From` is implemented for `&mut str`, but not for `&str` = note: required because of the requirements on the impl of `std::convert::Into<&str>` for `std::string::String` error: aborting due to previous error diff --git a/src/test/ui/suggestions/issue-61963.rs b/src/test/ui/suggestions/issue-61963.rs index c9d738f5a283e..666fc965f02f5 100644 --- a/src/test/ui/suggestions/issue-61963.rs +++ b/src/test/ui/suggestions/issue-61963.rs @@ -16,6 +16,7 @@ pub struct Qux(T); #[dom_struct] pub struct Foo { + //~^ ERROR trait objects without an explicit `dyn` are deprecated [bare_trait_objects] qux: Qux>, bar: Box, //~^ ERROR trait objects without an explicit `dyn` are deprecated [bare_trait_objects] diff --git a/src/test/ui/suggestions/issue-61963.stderr b/src/test/ui/suggestions/issue-61963.stderr index 0e2eb7616cf9d..62ae5fa3fe54f 100644 --- a/src/test/ui/suggestions/issue-61963.stderr +++ b/src/test/ui/suggestions/issue-61963.stderr @@ -1,5 +1,5 @@ error: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-61963.rs:20:14 + --> $DIR/issue-61963.rs:21:14 | LL | bar: Box, | ^^^ help: use `dyn`: `dyn Bar` @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(bare_trait_objects)] | ^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/issue-61963.rs:18:1 + | +LL | pub struct Foo { + | ^^^ help: use `dyn`: `dyn pub` + +error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/issue-71394-no-from-impl.rs b/src/test/ui/suggestions/issue-71394-no-from-impl.rs new file mode 100644 index 0000000000000..9ffcc3f7bc1c1 --- /dev/null +++ b/src/test/ui/suggestions/issue-71394-no-from-impl.rs @@ -0,0 +1,5 @@ +fn main() { + let data: &[u8] = &[0; 10]; + let _: &[i8] = data.into(); + //~^ ERROR the trait bound `&[i8]: std::convert::From<&[u8]>` is not satisfied +} diff --git a/src/test/ui/suggestions/issue-71394-no-from-impl.stderr b/src/test/ui/suggestions/issue-71394-no-from-impl.stderr new file mode 100644 index 0000000000000..84c73c2f67e70 --- /dev/null +++ b/src/test/ui/suggestions/issue-71394-no-from-impl.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `&[i8]: std::convert::From<&[u8]>` is not satisfied + --> $DIR/issue-71394-no-from-impl.rs:3:25 + | +LL | let _: &[i8] = data.into(); + | ^^^^ the trait `std::convert::From<&[u8]>` is not implemented for `&[i8]` + | + = note: required because of the requirements on the impl of `std::convert::Into<&[i8]>` for `&[u8]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/issue-72766.rs b/src/test/ui/suggestions/issue-72766.rs new file mode 100644 index 0000000000000..0448f0719589d --- /dev/null +++ b/src/test/ui/suggestions/issue-72766.rs @@ -0,0 +1,20 @@ +// edition:2018 +// compile-flags: -Cincremental=tmp/issue-72766 + +pub struct SadGirl; + +impl SadGirl { + pub async fn call(&self) -> Result<(), ()> { + Ok(()) + } +} + +async fn async_main() -> Result<(), ()> { + // should be `.call().await?` + SadGirl {}.call()?; //~ ERROR: the `?` operator can only be applied to values + Ok(()) +} + +fn main() { + let _ = async_main(); +} diff --git a/src/test/ui/suggestions/issue-72766.stderr b/src/test/ui/suggestions/issue-72766.stderr new file mode 100644 index 0000000000000..4290f3b4bf1aa --- /dev/null +++ b/src/test/ui/suggestions/issue-72766.stderr @@ -0,0 +1,15 @@ +error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` + --> $DIR/issue-72766.rs:14:5 + | +LL | SadGirl {}.call()?; + | ^^^^^^^^^^^^^^^^^^ + | | + | the `?` operator cannot be applied to type `impl std::future::Future` + | help: consider using `.await` here: `SadGirl {}.call().await?` + | + = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` + = note: required by `std::ops::Try::into_result` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr new file mode 100644 index 0000000000000..2072b00f7b2cb --- /dev/null +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.nll.stderr @@ -0,0 +1,115 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/missing-lifetimes-in-signature.rs:37:11 + | +LL | fn baz(g: G, dest: &mut T) -> impl FnOnce() + '_ + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `'a,` + +error: lifetime may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:15:37 + | +LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + | - ^^^^^^^^^^^^^ opaque type requires that `'1` must outlive `'static` + | | + | let's call the lifetime of this reference `'1` + | +help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as a bound + | +LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + '_ + | ^^^^^^^^^^^^^^^^^^ + +error[E0311]: the parameter type `G` may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:25:37 + | +LL | fn bar(g: G, dest: &mut T) -> impl FnOnce() + '_ + | ^^^^^^^^^^^^^^^^^^ + | +note: the parameter type `G` must be valid for the anonymous lifetime #1 defined on the function body at 25:1... + --> $DIR/missing-lifetimes-in-signature.rs:25:1 + | +LL | / fn bar(g: G, dest: &mut T) -> impl FnOnce() + '_ +LL | | +LL | | where +LL | | G: Get +... | +LL | | } +LL | | } + | |_^ + +error[E0311]: the parameter type `G` may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:47:45 + | +LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + | ^^^^^^^^^^^^^^^^^^ + | +note: the parameter type `G` must be valid for the anonymous lifetime #1 defined on the function body at 47:1... + --> $DIR/missing-lifetimes-in-signature.rs:47:1 + | +LL | / fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ +LL | | +LL | | where +LL | | G: Get +... | +LL | | } +LL | | } + | |_^ + +error[E0311]: the parameter type `G` may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:59:58 + | +LL | fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { + | ^^^^^^^^^^^^^^^^^^ + | +note: the parameter type `G` must be valid for the anonymous lifetime #1 defined on the method body at 59:5... + --> $DIR/missing-lifetimes-in-signature.rs:59:5 + | +LL | / fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { +LL | | +LL | | move || { +LL | | *dest = g.get(); +LL | | } +LL | | } + | |_____^ + +error[E0311]: the parameter type `G` may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:68:45 + | +LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the parameter type `G` must be valid for the anonymous lifetime #1 defined on the function body at 68:1... + --> $DIR/missing-lifetimes-in-signature.rs:68:1 + | +LL | / fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a +LL | | +LL | | where +LL | | G: Get +... | +LL | | } +LL | | } + | |_^ + +error[E0621]: explicit lifetime required in the type of `dest` + --> $DIR/missing-lifetimes-in-signature.rs:73:5 + | +LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a + | ------ help: add explicit lifetime `'a` to the type of `dest`: `&'a mut T` +... +LL | / move || { +LL | | *dest = g.get(); +LL | | } + | |_____^ lifetime `'a` required + +error[E0309]: the parameter type `G` may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:79:44 + | +LL | fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `G: 'a`... + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0261, E0309, E0621. +For more information about an error, try `rustc --explain E0261`. diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs new file mode 100644 index 0000000000000..d3853445dfdfe --- /dev/null +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.rs @@ -0,0 +1,110 @@ +pub trait Get { + fn get(self) -> T; +} + +struct Foo { + x: usize, +} + +impl Get for Foo { + fn get(self) -> usize { + self.x + } +} + +fn foo(g: G, dest: &mut T) -> impl FnOnce() +where + G: Get +{ + move || { //~ ERROR cannot infer an appropriate lifetime + *dest = g.get(); + } +} + +// After applying suggestion for `foo`: +fn bar(g: G, dest: &mut T) -> impl FnOnce() + '_ +//~^ ERROR the parameter type `G` may not live long enough +where + G: Get +{ + move || { + *dest = g.get(); + } +} + + +// After applying suggestion for `bar`: +fn baz(g: G, dest: &mut T) -> impl FnOnce() + '_ //~ ERROR undeclared lifetime +where + G: Get +{ + move || { + *dest = g.get(); + } +} + +// After applying suggestion for `baz`: +fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ +//~^ ERROR the parameter type `G` may not live long enough +where + G: Get +{ + move || { + *dest = g.get(); + } +} + +// Same as above, but show that we pay attention to lifetime names from parent item +impl<'a> Foo { + fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { + //~^ ERROR the parameter type `G` may not live long enough + move || { + *dest = g.get(); + } + } +} + +// After applying suggestion for `qux`: +fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a +//~^ ERROR explicit lifetime required in the type of `dest` +where + G: Get +{ + move || { + *dest = g.get(); + } +} + +// Potential incorrect attempt: +fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a +//~^ ERROR the parameter type `G` may not live long enough +where + G: Get +{ + move || { + *dest = g.get(); + } +} + + +// We need to tie the lifetime of `G` with the lifetime of `&mut T` and the returned closure: +fn ok<'a, G: 'a, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a +where + G: Get +{ + move || { + *dest = g.get(); + } +} + +// This also works. The `'_` isn't necessary but it's where we arrive to following the suggestions: +fn ok2<'a, G: 'a, T>(g: G, dest: &'a mut T) -> impl FnOnce() + '_ + 'a +where + G: Get +{ + move || { + *dest = g.get(); + } +} + +fn main() {} diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr new file mode 100644 index 0000000000000..9ab060328537b --- /dev/null +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -0,0 +1,129 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/missing-lifetimes-in-signature.rs:37:11 + | +LL | fn baz(g: G, dest: &mut T) -> impl FnOnce() + '_ + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `'a,` + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/missing-lifetimes-in-signature.rs:19:5 + | +LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + | ------ this data with an anonymous lifetime `'_`... +... +LL | / move || { +LL | | *dest = g.get(); +LL | | } + | |_____^ ...is captured here... + | +note: ...and is required to live as long as `'static` here + --> $DIR/missing-lifetimes-in-signature.rs:15:37 + | +LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + | ^^^^^^^^^^^^^ +help: to declare that the `impl Trait` captures data from argument `dest`, you can add an explicit `'_` lifetime bound + | +LL | fn foo(g: G, dest: &mut T) -> impl FnOnce() + '_ + | ^^^^ + +error[E0311]: the parameter type `G` may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:25:37 + | +LL | fn bar(g: G, dest: &mut T) -> impl FnOnce() + '_ + | ^^^^^^^^^^^^^^^^^^ + | +note: the parameter type `G` must be valid for the anonymous lifetime #1 defined on the function body at 25:1... + --> $DIR/missing-lifetimes-in-signature.rs:25:1 + | +LL | / fn bar(g: G, dest: &mut T) -> impl FnOnce() + '_ +LL | | +LL | | where +LL | | G: Get +... | +LL | | } +LL | | } + | |_^ +note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:30:5: 32:6 g:G, dest:&mut T]` will meet its required lifetime bounds + --> $DIR/missing-lifetimes-in-signature.rs:25:37 + | +LL | fn bar(g: G, dest: &mut T) -> impl FnOnce() + '_ + | ^^^^^^^^^^^^^^^^^^ +help: consider introducing an explicit lifetime bound + | +LL | fn bar<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a + | ^^^^^ ^^^^ + +error[E0311]: the parameter type `G` may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:47:45 + | +LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + | ^^^^^^^^^^^^^^^^^^ + | +note: the parameter type `G` must be valid for the anonymous lifetime #1 defined on the function body at 47:1... + --> $DIR/missing-lifetimes-in-signature.rs:47:1 + | +LL | / fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ +LL | | +LL | | where +LL | | G: Get +... | +LL | | } +LL | | } + | |_^ +note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:52:5: 54:6 g:G, dest:&mut T]` will meet its required lifetime bounds + --> $DIR/missing-lifetimes-in-signature.rs:47:45 + | +LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + | ^^^^^^^^^^^^^^^^^^ +help: consider introducing an explicit lifetime bound + | +LL | fn qux<'b, 'a, G: 'b + 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'b + | ^^^ ^^^^^^^ ^^^^ + +error[E0311]: the parameter type `G` may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:59:58 + | +LL | fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { + | ^^^^^^^^^^^^^^^^^^ + | +note: the parameter type `G` must be valid for the anonymous lifetime #1 defined on the method body at 59:5... + --> $DIR/missing-lifetimes-in-signature.rs:59:5 + | +LL | / fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { +LL | | +LL | | move || { +LL | | *dest = g.get(); +LL | | } +LL | | } + | |_____^ +note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:61:9: 63:10 g:G, dest:&mut T]` will meet its required lifetime bounds + --> $DIR/missing-lifetimes-in-signature.rs:59:58 + | +LL | fn qux<'b, G: Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ { + | ^^^^^^^^^^^^^^^^^^ +help: consider introducing an explicit lifetime bound + | +LL | fn qux<'c, 'b, G: 'c + Get + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'c { + | ^^^ ^^^^^^^ ^^^^ + +error[E0621]: explicit lifetime required in the type of `dest` + --> $DIR/missing-lifetimes-in-signature.rs:68:45 + | +LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a + | ------ ^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required + | | + | help: add explicit lifetime `'a` to the type of `dest`: `&'a mut T` + +error[E0309]: the parameter type `G` may not live long enough + --> $DIR/missing-lifetimes-in-signature.rs:79:44 + | +LL | fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a + | - ^^^^^^^^^^^^^^^^^^ ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:84:5: 86:6 g:G, dest:&mut T]` will meet its required lifetime bounds + | | + | help: consider adding an explicit lifetime bound...: `G: 'a` + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0261, E0309, E0621, E0759. +For more information about an error, try `rustc --explain E0261`. diff --git a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr new file mode 100644 index 0000000000000..2407d13714a2a --- /dev/null +++ b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr @@ -0,0 +1,53 @@ +error: lifetime may not live long enough + --> $DIR/trait-object-nested-in-impl-trait.rs:27:23 + | +LL | fn iter(&self) -> impl Iterator> { + | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ opaque type requires that `'1` must outlive `'static` + | | + | let's call the lifetime of this reference `'1` + | +help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as a bound + | +LL | fn iter(&self) -> impl Iterator> + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lifetime may not live long enough + --> $DIR/trait-object-nested-in-impl-trait.rs:39:9 + | +LL | fn iter(&self) -> impl Iterator> + '_ { + | - let's call the lifetime of this reference `'1` +LL | / Iter { +LL | | current: None, +LL | | remaining: self.0.iter(), +LL | | } + | |_________^ returning this value requires that `'1` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/trait-object-nested-in-impl-trait.rs:50:9 + | +LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { + | -- lifetime `'a` defined here +LL | / Iter { +LL | | current: None, +LL | | remaining: self.0.iter(), +LL | | } + | |_________^ returning this value requires that `'a` must outlive `'static` + | + = help: consider replacing `'a` with `'static` + +error: lifetime may not live long enough + --> $DIR/trait-object-nested-in-impl-trait.rs:60:30 + | +LL | fn iter<'a>(&'a self) -> impl Iterator> { + | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ opaque type requires that `'a` must outlive `'static` + | | + | lifetime `'a` defined here + | + = help: consider replacing `'a` with `'static` +help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a bound + | +LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.rs b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.rs new file mode 100644 index 0000000000000..f78edb1c83a4c --- /dev/null +++ b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.rs @@ -0,0 +1,68 @@ +trait Foo {} +impl<'a, T: Foo> Foo for &'a T {} +impl Foo for Box {} + +struct Iter<'a, T> { + current: Option>, + remaining: T, +} + +impl<'a, T> Iterator for Iter<'a, T> +where + T: Iterator, + T::Item: Foo + 'a, +{ + type Item = Box; + + fn next(&mut self) -> Option { + let result = self.current.take(); + self.current = Box::new(self.remaining.next()).map(|f| Box::new(f) as _); + result + } +} + +struct Bar(Vec>); + +impl Bar { + fn iter(&self) -> impl Iterator> { + Iter { + current: None, + remaining: self.0.iter(), //~ ERROR cannot infer an appropriate lifetime + } + } +} + +struct Baz(Vec>); + +impl Baz { + fn iter(&self) -> impl Iterator> + '_ { + Iter { + current: None, + remaining: self.0.iter(), //~ ERROR cannot infer an appropriate lifetime + } + } +} + +struct Bat(Vec>); + +impl Bat { + fn iter<'a>(&'a self) -> impl Iterator> + 'a { + Iter { + current: None, + remaining: self.0.iter(), //~ ERROR cannot infer an appropriate lifetime + } + } +} + +struct Ban(Vec>); + +impl Ban { + fn iter<'a>(&'a self) -> impl Iterator> { + Iter { + current: None, + remaining: self.0.iter(), //~ ERROR cannot infer an appropriate lifetime + } + } +} + +fn main() {} diff --git a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr new file mode 100644 index 0000000000000..1257e9b172cf7 --- /dev/null +++ b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.stderr @@ -0,0 +1,95 @@ +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/trait-object-nested-in-impl-trait.rs:30:31 + | +LL | fn iter(&self) -> impl Iterator> { + | ----- this data with an anonymous lifetime `'_`... +... +LL | remaining: self.0.iter(), + | ------ ^^^^ + | | + | ...is captured here... + | +note: ...and is required to live as long as `'static` here + --> $DIR/trait-object-nested-in-impl-trait.rs:27:23 + | +LL | fn iter(&self) -> impl Iterator> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound + | +LL | fn iter(&self) -> impl Iterator> + '_ { + | ^^^^ +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound + | +LL | fn iter(&self) -> impl Iterator> { + | ^^^^ + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/trait-object-nested-in-impl-trait.rs:41:31 + | +LL | fn iter(&self) -> impl Iterator> + '_ { + | ----- this data with an anonymous lifetime `'_`... +... +LL | remaining: self.0.iter(), + | ------ ^^^^ + | | + | ...is captured here... + | +note: ...and is required to live as long as `'static` here + --> $DIR/trait-object-nested-in-impl-trait.rs:38:23 + | +LL | fn iter(&self) -> impl Iterator> + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound + | +LL | fn iter(&self) -> impl Iterator> + '_ { + | ^^^^ + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/trait-object-nested-in-impl-trait.rs:52:31 + | +LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { + | -------- this data with lifetime `'a`... +... +LL | remaining: self.0.iter(), + | ------ ^^^^ + | | + | ...is captured here... + | +note: ...and is required to live as long as `'static` here + --> $DIR/trait-object-nested-in-impl-trait.rs:49:30 + | +LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound + | +LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { + | ^^^^ + +error[E0759]: cannot infer an appropriate lifetime + --> $DIR/trait-object-nested-in-impl-trait.rs:63:31 + | +LL | fn iter<'a>(&'a self) -> impl Iterator> { + | -------- this data with lifetime `'a`... +... +LL | remaining: self.0.iter(), + | ------ ^^^^ + | | + | ...is captured here... + | +note: ...and is required to live as long as `'static` here + --> $DIR/trait-object-nested-in-impl-trait.rs:60:30 + | +LL | fn iter<'a>(&'a self) -> impl Iterator> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'a` lifetime bound + | +LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { + | ^^^^ +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound + | +LL | fn iter<'a>(&'a self) -> impl Iterator> { + | ^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/suggestions/method-missing-parentheses.stderr b/src/test/ui/suggestions/method-missing-parentheses.stderr index 6e4f7a84724bf..04c67ec6a06b6 100644 --- a/src/test/ui/suggestions/method-missing-parentheses.stderr +++ b/src/test/ui/suggestions/method-missing-parentheses.stderr @@ -8,9 +8,12 @@ error[E0615]: attempted to take value of method `collect` on type `std::vec::Int --> $DIR/method-missing-parentheses.rs:2:32 | LL | let _ = vec![].into_iter().collect::; - | ^^^^^^^--------- - | | - | help: use parentheses to call the method: `collect::()` + | ^^^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | let _ = vec![].into_iter().collect::(); + | ^^ error: aborting due to 2 previous errors 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-impl-trait-lifetime.stderr b/src/test/ui/suggestions/suggest-impl-trait-lifetime.stderr index b6e6c0bbf32df..643dac2572497 100644 --- a/src/test/ui/suggestions/suggest-impl-trait-lifetime.stderr +++ b/src/test/ui/suggestions/suggest-impl-trait-lifetime.stderr @@ -5,13 +5,7 @@ LL | fn foo(d: impl Debug) { | ---------- help: consider adding an explicit lifetime bound...: `impl Debug + 'static` LL | LL | bar(d); - | ^^^ - | -note: ...so that the type `impl Debug` will meet its required lifetime bounds - --> $DIR/suggest-impl-trait-lifetime.rs:7:5 - | -LL | bar(d); - | ^^^ + | ^^^ ...so that the type `impl Debug` will meet its required lifetime bounds error: aborting due to previous error 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..3c2226574ee9e 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,20 +119,22 @@ 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> { | ^^ | - = note: type arguments must be provided before lifetime arguments + = note: lifetime arguments must be provided before type arguments + = help: reorder the arguments: lifetimes, then types: `<'a, 'b, 'c, T, U, V>` error[E0747]: lifetime provided when a type was expected - --> $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> { | ^^ | - = note: type arguments must be provided before lifetime arguments + = note: lifetime arguments must be provided before type arguments + = help: reorder the arguments: lifetimes, then types: `<'a, 'b, 'c, T, U, V>` error: aborting due to 12 previous errors 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..0d90e449523a3 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs @@ -0,0 +1,44 @@ +// 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 funq(&self) -> Self::A {} //~ ERROR mismatched types +} + +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..e629f8f970d32 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr @@ -0,0 +1,120 @@ +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:10:31 + | +LL | fn funq(&self) -> Self::A {} + | ^^ expected associated type, found `()` + | + = note: expected associated type `>::A` + found unit type `()` +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` + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:14: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:18: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:22: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:26:12 + | +LL | x.funk(3); + | ^ expected associated type, found integer + | + = note: expected associated type `>::A` + found type `{integer}` +help: some methods are available that return `>::A` + --> $DIR/trait-with-missing-associated-type-restriction.rs:8:5 + | +LL | fn func(&self) -> Self::A; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ consider calling `Trait::func` +LL | fn funk(&self, _: Self::A); +LL | fn funq(&self) -> Self::A {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ consider calling `Trait::funq` +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:27: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:31: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:35:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found `()` + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:39: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 9 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/type-not-found-in-adt-field.rs b/src/test/ui/suggestions/type-not-found-in-adt-field.rs new file mode 100644 index 0000000000000..4cbfe58d35703 --- /dev/null +++ b/src/test/ui/suggestions/type-not-found-in-adt-field.rs @@ -0,0 +1,9 @@ +struct Struct { + m: Vec>, //~ ERROR cannot find type `Someunknownname` in this scope + //~^ NOTE not found in this scope +} +struct OtherStruct { //~ HELP you might be missing a type parameter + m: K, //~ ERROR cannot find type `K` in this scope + //~^ NOTE not found in this scope +} +fn main() {} diff --git a/src/test/ui/suggestions/type-not-found-in-adt-field.stderr b/src/test/ui/suggestions/type-not-found-in-adt-field.stderr new file mode 100644 index 0000000000000..e990fb5ba1210 --- /dev/null +++ b/src/test/ui/suggestions/type-not-found-in-adt-field.stderr @@ -0,0 +1,17 @@ +error[E0412]: cannot find type `Someunknownname` in this scope + --> $DIR/type-not-found-in-adt-field.rs:2:12 + | +LL | m: Vec>, + | ^^^^^^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `K` in this scope + --> $DIR/type-not-found-in-adt-field.rs:6:8 + | +LL | struct OtherStruct { + | - help: you might be missing a type parameter: `` +LL | m: K, + | ^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/suggestions/unused-closure-argument.stderr b/src/test/ui/suggestions/unused-closure-argument.stderr index d83ab08e71efc..55195ce50a13e 100644 --- a/src/test/ui/suggestions/unused-closure-argument.stderr +++ b/src/test/ui/suggestions/unused-closure-argument.stderr @@ -14,7 +14,7 @@ error: unused variable: `x` --> $DIR/unused-closure-argument.rs:17:15 | LL | .map(|x| 4) - | ^ help: consider prefixing with an underscore: `_x` + | ^ help: if this is intentional, prefix it with an underscore: `_x` error: aborting due to 2 previous errors diff --git a/src/test/ui/symbol-names/basic.legacy.stderr b/src/test/ui/symbol-names/basic.legacy.stderr index 895ff5ae54fde..dec57e06ea24b 100644 --- a/src/test/ui/symbol-names/basic.legacy.stderr +++ b/src/test/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h81759b0695851718E) +error: symbol-name(_ZN5basic4main17h4272b3de5e868f5aE) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h81759b0695851718) +error: demangling(basic::main::h4272b3de5e868f5a) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] diff --git a/src/test/ui/symbol-names/impl1.legacy.stderr b/src/test/ui/symbol-names/impl1.legacy.stderr index 33cacaf212855..52ee3452a48f7 100644 --- a/src/test/ui/symbol-names/impl1.legacy.stderr +++ b/src/test/ui/symbol-names/impl1.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5impl13foo3Foo3bar17h92cf46db76791039E) +error: symbol-name(_ZN5impl13foo3Foo3bar17ha318160f105e638cE) --> $DIR/impl1.rs:16:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(impl1::foo::Foo::bar::h92cf46db76791039) +error: demangling(impl1::foo::Foo::bar::ha318160f105e638c) --> $DIR/impl1.rs:16:9 | LL | #[rustc_symbol_name] @@ -22,13 +22,13 @@ error: def-path(foo::Foo::bar) LL | #[rustc_def_path] | ^^^^^^^^^^^^^^^^^ -error: symbol-name(_ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz17h90c4a800b1aa0df0E) +error: symbol-name(_ZN5impl13bar33_$LT$impl$u20$impl1..foo..Foo$GT$3baz17h6c2dbab6e66f9fa3E) --> $DIR/impl1.rs:34:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(impl1::bar::::baz::h90c4a800b1aa0df0) +error: demangling(impl1::bar::::baz::h6c2dbab6e66f9fa3) --> $DIR/impl1.rs:34:9 | LL | #[rustc_symbol_name] diff --git a/src/test/ui/symbol-names/impl1.rs b/src/test/ui/symbol-names/impl1.rs index 1f689a5bd2541..380d20d0b12c6 100644 --- a/src/test/ui/symbol-names/impl1.rs +++ b/src/test/ui/symbol-names/impl1.rs @@ -3,8 +3,8 @@ // revisions: legacy v0 //[legacy]compile-flags: -Z symbol-mangling-version=legacy //[v0]compile-flags: -Z symbol-mangling-version=v0 -//[legacy]normalize-stderr-32bit: "hdb62078998ce7ea8" -> "SYMBOL_HASH" -//[legacy]normalize-stderr-64bit: "h62e540f14f879d56" -> "SYMBOL_HASH" +//[legacy]normalize-stderr-32bit: "hee444285569b39c2" -> "SYMBOL_HASH" +//[legacy]normalize-stderr-64bit: "h310ea0259fc3d32d" -> "SYMBOL_HASH" #![feature(optin_builtin_traits, rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/ui/symbol-names/issue-60925.legacy.stderr b/src/test/ui/symbol-names/issue-60925.legacy.stderr index 0e3a34adbc7cf..f5699052795b8 100644 --- a/src/test/ui/symbol-names/issue-60925.legacy.stderr +++ b/src/test/ui/symbol-names/issue-60925.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17hc86312d25b60f6eeE) +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h79d9aaa05f4b26d6E) --> $DIR/issue-60925.rs:22:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo::foo::hc86312d25b60f6ee) +error: demangling(issue_60925::foo::Foo::foo::h79d9aaa05f4b26d6) --> $DIR/issue-60925.rs:22:9 | LL | #[rustc_symbol_name] 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 1b7fc1587e6d2..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 negative impls are only allowed for auto traits 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 negative impls are only allowed for auto traits fn main() {} diff --git a/src/test/ui/syntax-trait-polarity.stderr b/src/test/ui/syntax-trait-polarity.stderr index 5777e0ade908e..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,19 +32,6 @@ LL | unsafe impl !Send for TestType2 {} | | negative because of this | unsafe because of this -error[E0192]: negative impls are only allowed for auto traits (e.g., `Send` and `Sync`) - --> $DIR/syntax-trait-polarity.rs:14:1 - | -LL | impl !TestTrait for TestType {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0192]: negative impls are only allowed for auto traits (e.g., `Send` and `Sync`) - --> $DIR/syntax-trait-polarity.rs:24:1 - | -LL | impl !TestTrait for TestType2 {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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/tag-variant-cast-non-nullary.fixed b/src/test/ui/tag-variant-cast-non-nullary.fixed new file mode 100644 index 0000000000000..53e68c2ac6af6 --- /dev/null +++ b/src/test/ui/tag-variant-cast-non-nullary.fixed @@ -0,0 +1,20 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] +enum NonNullary { + Nullary, + Other(isize), +} + +impl From for isize { + fn from(val: NonNullary) -> isize { + match val { + NonNullary::Nullary => 0, + NonNullary::Other(i) => i, + } + } +} + +fn main() { + let v = NonNullary::Nullary; + let val = isize::from(v); //~ ERROR non-primitive cast: `NonNullary` as `isize` [E0605] +} diff --git a/src/test/ui/tag-variant-cast-non-nullary.rs b/src/test/ui/tag-variant-cast-non-nullary.rs index bb34e82cdca37..0d0c6188ad114 100644 --- a/src/test/ui/tag-variant-cast-non-nullary.rs +++ b/src/test/ui/tag-variant-cast-non-nullary.rs @@ -1,8 +1,19 @@ +// run-rustfix +#![allow(dead_code, unused_variables)] enum NonNullary { Nullary, Other(isize), } +impl From for isize { + fn from(val: NonNullary) -> isize { + match val { + NonNullary::Nullary => 0, + NonNullary::Other(i) => i, + } + } +} + fn main() { let v = NonNullary::Nullary; let val = v as isize; //~ ERROR non-primitive cast: `NonNullary` as `isize` [E0605] diff --git a/src/test/ui/tag-variant-cast-non-nullary.stderr b/src/test/ui/tag-variant-cast-non-nullary.stderr index 87ec20f20d789..ae2f5a7aead55 100644 --- a/src/test/ui/tag-variant-cast-non-nullary.stderr +++ b/src/test/ui/tag-variant-cast-non-nullary.stderr @@ -1,10 +1,10 @@ error[E0605]: non-primitive cast: `NonNullary` as `isize` - --> $DIR/tag-variant-cast-non-nullary.rs:8:15 + --> $DIR/tag-variant-cast-non-nullary.rs:19:15 | LL | let val = v as isize; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(v)` | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error diff --git a/src/test/ui/target-feature/gate.rs b/src/test/ui/target-feature/gate.rs index 2d51cab675e00..10fbba36d3f9d 100644 --- a/src/test/ui/target-feature/gate.rs +++ b/src/test/ui/target-feature/gate.rs @@ -7,6 +7,7 @@ // ignore-powerpc // ignore-powerpc64 // ignore-powerpc64le +// ignore-riscv64 // ignore-sparc // ignore-sparc64 // ignore-s390x @@ -25,6 +26,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..2d6abcc0a0150 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:31: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..98afded6712d7 100644 --- a/src/test/ui/target-feature/invalid-attribute.rs +++ b/src/test/ui/target-feature/invalid-attribute.rs @@ -7,6 +7,7 @@ // ignore-powerpc // ignore-powerpc64 // ignore-powerpc64le +// ignore-riscv64 // ignore-s390x // ignore-sparc // ignore-sparc64 @@ -26,7 +27,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 +66,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..f3995f118d3e9 100644 --- a/src/test/ui/target-feature/invalid-attribute.stderr +++ b/src/test/ui/target-feature/invalid-attribute.stderr @@ -1,38 +1,41 @@ error: malformed `target_feature` attribute input - --> $DIR/invalid-attribute.rs:16:1 + --> $DIR/invalid-attribute.rs:17:1 | LL | #[target_feature = "+sse2"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[target_feature(enable = "name")]` error: the feature named `foo` is not valid for this target - --> $DIR/invalid-attribute.rs:18:18 + --> $DIR/invalid-attribute.rs:19:18 | LL | #[target_feature(enable = "foo")] | ^^^^^^^^^^^^^^ `foo` is not valid for this target error: malformed `target_feature` attribute input - --> $DIR/invalid-attribute.rs:21:18 + --> $DIR/invalid-attribute.rs:22:18 | LL | #[target_feature(bar)] | ^^^ help: must be of the form: `enable = ".."` error: malformed `target_feature` attribute input - --> $DIR/invalid-attribute.rs:23:18 + --> $DIR/invalid-attribute.rs:24:18 | LL | #[target_feature(disable = "baz")] | ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."` -error: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/invalid-attribute.rs:27:1 +error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions + --> $DIR/invalid-attribute.rs:28: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 + --> $DIR/invalid-attribute.rs:34:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +44,7 @@ LL | mod another {} | -------------- not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:38:1 + --> $DIR/invalid-attribute.rs:39:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +53,7 @@ LL | const FOO: usize = 7; | --------------------- not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:43:1 + --> $DIR/invalid-attribute.rs:44:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,7 +62,7 @@ LL | struct Foo; | ----------- not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:48:1 + --> $DIR/invalid-attribute.rs:49:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +71,7 @@ LL | enum Bar { } | ------------ not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:53:1 + --> $DIR/invalid-attribute.rs:54:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +80,7 @@ LL | union Qux { f1: u16, f2: u16 } | ------------------------------ not a function error: attribute should be applied to a function - --> $DIR/invalid-attribute.rs:58:1 + --> $DIR/invalid-attribute.rs:59:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -86,10 +89,35 @@ LL | trait Baz { } | ------------- not a function error: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/invalid-attribute.rs:63:1 + --> $DIR/invalid-attribute.rs:64:1 | 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:86: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:74: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-attr-non-associated-functions.rs b/src/test/ui/test-attrs/test-attr-non-associated-functions.rs index e475f5b4a75a5..31e567c396067 100644 --- a/src/test/ui/test-attrs/test-attr-non-associated-functions.rs +++ b/src/test/ui/test-attrs/test-attr-non-associated-functions.rs @@ -6,7 +6,13 @@ struct A {} impl A { #[test] - fn new() -> A { //~ ERROR `#[test]` attribute is only allowed on non associated functions + fn new() -> A { + //~^ ERROR `#[test]` attribute is only allowed on non associated functions + A {} + } + #[test] + fn recovery_witness() -> A { + //~^ ERROR `#[test]` attribute is only allowed on non associated functions A {} } } diff --git a/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr b/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr index cb3ae51823e45..a81b8f3980c37 100644 --- a/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr +++ b/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr @@ -2,9 +2,19 @@ error: `#[test]` attribute is only allowed on non associated functions --> $DIR/test-attr-non-associated-functions.rs:9:5 | LL | / fn new() -> A { +LL | | LL | | A {} LL | | } | |_____^ -error: aborting due to previous error +error: `#[test]` attribute is only allowed on non associated functions + --> $DIR/test-attr-non-associated-functions.rs:14:5 + | +LL | / fn recovery_witness() -> A { +LL | | +LL | | A {} +LL | | } + | |_____^ + +error: aborting due to 2 previous errors 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/ui/test-panic-while-printing.rs b/src/test/ui/test-panic-while-printing.rs new file mode 100644 index 0000000000000..23f45407c1af3 --- /dev/null +++ b/src/test/ui/test-panic-while-printing.rs @@ -0,0 +1,24 @@ +// compile-flags:--test +// run-pass +// ignore-emscripten no subprocess support + +use std::fmt; +use std::fmt::{Display, Formatter}; + +pub struct A(Vec); + +impl Display for A { + fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result { + self.0[0]; + Ok(()) + } +} + +#[test] +fn main() { + let result = std::panic::catch_unwind(|| { + let a = A(vec![]); + eprintln!("{}", a); + }); + assert!(result.is_err()); +} 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/tls.rs b/src/test/ui/tls.rs new file mode 100644 index 0000000000000..fbd3413885f8f --- /dev/null +++ b/src/test/ui/tls.rs @@ -0,0 +1,14 @@ +// run-pass +// ignore-emscripten no threads support +// compile-flags: -O + +#![feature(thread_local)] + +#[thread_local] +static S: u32 = 222; + +fn main() { + let local = &S as *const u32 as usize; + let foreign = std::thread::spawn(|| &S as *const u32 as usize).join().unwrap(); + assert_ne!(local, foreign); +} 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/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..c68bca432fa86 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-default-impls.rs @@ -0,0 +1,11 @@ +#![feature(negative_impls)] +#![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete + +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..50e74373b53bb --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-default-impls.stderr @@ -0,0 +1,18 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/negative-default-impls.rs:2:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +error[E0750]: negative impls cannot be default impls + --> $DIR/negative-default-impls.rs:9:1 + | +LL | default impl !MyTrait for u32 {} + | ^^^^^^^ ^ + +error: aborting due to previous error; 1 warning emitted + +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..35297ab124ed0 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-negative.rs @@ -0,0 +1,13 @@ +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete +#![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-negative.stderr b/src/test/ui/traits/negative-impls/negative-specializes-negative.stderr new file mode 100644 index 0000000000000..8b536de378630 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-negative.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/negative-specializes-negative.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + 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..4281eedaf631c --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-positive-item.rs @@ -0,0 +1,13 @@ +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete +#![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..89ef15e89ac96 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-positive-item.stderr @@ -0,0 +1,21 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/negative-specializes-positive-item.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +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; 1 warning emitted + +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..0e227691e0404 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-positive.rs @@ -0,0 +1,14 @@ +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete +#![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..e45d5a251ab26 --- /dev/null +++ b/src/test/ui/traits/negative-impls/negative-specializes-positive.stderr @@ -0,0 +1,20 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/negative-specializes-positive.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +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; 1 warning emitted + +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..a06b357654068 --- /dev/null +++ b/src/test/ui/traits/negative-impls/positive-specializes-negative.rs @@ -0,0 +1,9 @@ +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete +#![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..49c16d474040e --- /dev/null +++ b/src/test/ui/traits/negative-impls/positive-specializes-negative.stderr @@ -0,0 +1,20 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/positive-specializes-negative.rs:1:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +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; 1 warning emitted + +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/self-without-lifetime-constraint.rs b/src/test/ui/traits/self-without-lifetime-constraint.rs new file mode 100644 index 0000000000000..99013d32ab8d0 --- /dev/null +++ b/src/test/ui/traits/self-without-lifetime-constraint.rs @@ -0,0 +1,53 @@ +use std::error::Error; +use std::fmt; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ValueRef<'a> { + Null, + Integer(i64), + Real(f64), + Text(&'a [u8]), + Blob(&'a [u8]), +} + +impl<'a> ValueRef<'a> { + pub fn as_str(&self) -> FromSqlResult<&'a str, &'a &'a str> { + match *self { + ValueRef::Text(t) => { + std::str::from_utf8(t).map_err(|_| FromSqlError::InvalidType).map(|x| (x, &x)) + } + _ => Err(FromSqlError::InvalidType), + } + } +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum FromSqlError { + InvalidType +} + +impl fmt::Display for FromSqlError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "InvalidType") + } +} + +impl Error for FromSqlError {} + +pub type FromSqlResult = Result<(T, K), FromSqlError>; + +pub trait FromSql: Sized { + fn column_result(value: ValueRef<'_>) -> FromSqlResult; +} + +impl FromSql for &str { + fn column_result(value: ValueRef<'_>) -> FromSqlResult<&str, &&str> { + //~^ ERROR `impl` item signature doesn't match `trait` item signature + value.as_str() + } +} + +pub fn main() { + println!("{}", "Hello World"); +} diff --git a/src/test/ui/traits/self-without-lifetime-constraint.stderr b/src/test/ui/traits/self-without-lifetime-constraint.stderr new file mode 100644 index 0000000000000..6c7abe753e2bf --- /dev/null +++ b/src/test/ui/traits/self-without-lifetime-constraint.stderr @@ -0,0 +1,19 @@ +error: `impl` item signature doesn't match `trait` item signature + --> $DIR/self-without-lifetime-constraint.rs:45:5 + | +LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult; + | -------------------------------------------------------------------- expected `fn(ValueRef<'_>) -> std::result::Result<(&str, &&str), FromSqlError>` +... +LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult<&str, &&str> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(ValueRef<'_>) -> std::result::Result<(&str, &&str), FromSqlError>` + | + = note: expected `fn(ValueRef<'_>) -> std::result::Result<(&str, &&str), _>` + found `fn(ValueRef<'_>) -> std::result::Result<(&str, &&str), _>` +help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + --> $DIR/self-without-lifetime-constraint.rs:41:60 + | +LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult; + | ^^^^ consider borrowing this type parameter in the trait + +error: aborting due to previous error + 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/issue-60021-assoc-method-resolve.rs b/src/test/ui/traits/trait-alias/issue-60021-assoc-method-resolve.rs new file mode 100644 index 0000000000000..5e27ed3c6460e --- /dev/null +++ b/src/test/ui/traits/trait-alias/issue-60021-assoc-method-resolve.rs @@ -0,0 +1,19 @@ +// check-pass + +#![feature(trait_alias)] + +trait SomeTrait { + fn map(&self) {} +} + +impl SomeTrait for Option {} + +trait SomeAlias = SomeTrait; + +fn main() { + let x = Some(123); + // This should resolve to the trait impl for Option + Option::map(x, |z| z); + // This should resolve to the trait impl for SomeTrait + SomeTrait::map(&x); +} diff --git a/src/test/ui/traits/trait-alias/issue-72415-assoc-const-resolve.rs b/src/test/ui/traits/trait-alias/issue-72415-assoc-const-resolve.rs new file mode 100644 index 0000000000000..e49125d10249d --- /dev/null +++ b/src/test/ui/traits/trait-alias/issue-72415-assoc-const-resolve.rs @@ -0,0 +1,14 @@ +// check-pass + +#![feature(trait_alias)] + +trait Bounded { const MAX: Self; } + +impl Bounded for u32 { + // This should correctly resolve to the associated const in the inherent impl of u32. + const MAX: Self = u32::MAX; +} + +trait Num = Bounded + Copy; + +fn main() {} 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-only-maybe-bound.stderr b/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.stderr index 6de79fa917b16..594115d980c8b 100644 --- a/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.stderr +++ b/src/test/ui/traits/trait-alias/trait-alias-only-maybe-bound.stderr @@ -12,3 +12,4 @@ LL | type _T1 = dyn _2; error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0224`. 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-impl-for-module.stderr b/src/test/ui/traits/trait-impl-for-module.stderr index 4b3c930dccd45..cd2713a5bd50c 100644 --- a/src/test/ui/traits/trait-impl-for-module.stderr +++ b/src/test/ui/traits/trait-impl-for-module.stderr @@ -1,12 +1,11 @@ error[E0573]: expected type, found module `a` --> $DIR/trait-impl-for-module.rs:7:12 | -LL | / trait A { -LL | | } - | |_- similarly named trait `A` defined here -LL | -LL | impl A for a { - | ^ help: a trait with a similar name exists: `A` +LL | trait A { + | ------- similarly named trait `A` defined here +... +LL | impl A for a { + | ^ help: a trait with a similar name exists: `A` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-impl-of-supertrait-has-wrong-lifetime-parameters.stderr b/src/test/ui/traits/trait-impl-of-supertrait-has-wrong-lifetime-parameters.stderr index 9fdcd4de495c0..46aa7db967ad4 100644 --- a/src/test/ui/traits/trait-impl-of-supertrait-has-wrong-lifetime-parameters.stderr +++ b/src/test/ui/traits/trait-impl-of-supertrait-has-wrong-lifetime-parameters.stderr @@ -19,8 +19,8 @@ note: ...so that the types are compatible | LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> { | ^^^^^^^^^^ - = note: expected `T1<'a>` - found `T1<'_>` + = note: expected `T1<'a>` + found `T1<'_>` error: aborting due to previous error diff --git a/src/test/ui/traits/trait-item-privacy.stderr b/src/test/ui/traits/trait-item-privacy.stderr index 4b40c6405c47b..3be4f11097311 100644 --- a/src/test/ui/traits/trait-item-privacy.stderr +++ b/src/test/ui/traits/trait-item-privacy.stderr @@ -20,13 +20,6 @@ error[E0599]: no method named `b` found for struct `S` in the current scope LL | struct S; | --------- method `b` not found for this ... -LL | fn b(&self) { } - | - - | | - | the method is available for `std::boxed::Box` here - | the method is available for `std::sync::Arc` here - | the method is available for `std::rc::Rc` here -... LL | S.b(); | ^ method not found in `S` | @@ -40,7 +33,7 @@ error[E0624]: associated function `a` is private --> $DIR/trait-item-privacy.rs:72:7 | LL | c.a(); - | ^ + | ^ private associated function error[E0599]: no function or associated item named `a` found for struct `S` in the current scope --> $DIR/trait-item-privacy.rs:78:8 @@ -74,10 +67,10 @@ LL | use method::B; | error[E0624]: associated function `a` is private - --> $DIR/trait-item-privacy.rs:84:5 + --> $DIR/trait-item-privacy.rs:84:8 | LL | C::a(&S); - | ^^^^ + | ^ private associated function error[E0599]: no associated item named `A` found for struct `S` in the current scope --> $DIR/trait-item-privacy.rs:97:8 @@ -111,10 +104,10 @@ LL | use assoc_const::B; | error[E0624]: associated constant `A` is private - --> $DIR/trait-item-privacy.rs:101:5 + --> $DIR/trait-item-privacy.rs:101:8 | LL | C::A; - | ^^^^ + | ^ private associated constant error[E0038]: the trait `assoc_const::C` cannot be made into an object --> $DIR/trait-item-privacy.rs:101:5 @@ -159,13 +152,13 @@ error: associated type `A` is private --> $DIR/trait-item-privacy.rs:119:12 | LL | let _: T::A; - | ^^^^ + | ^^^^ private associated type error: associated type `A` is private --> $DIR/trait-item-privacy.rs:128:9 | LL | A = u8, - | ^^^^^^ + | ^^^^^^ private associated type error: aborting due to 15 previous errors diff --git a/src/test/ui/traits/trait-method-private.stderr b/src/test/ui/traits/trait-method-private.stderr index 035c1ea092b29..c33673aea4d79 100644 --- a/src/test/ui/traits/trait-method-private.stderr +++ b/src/test/ui/traits/trait-method-private.stderr @@ -2,7 +2,7 @@ error[E0624]: associated function `method` is private --> $DIR/trait-method-private.rs:19:9 | LL | foo.method(); - | ^^^^^^ + | ^^^^^^ private associated function | = help: items from traits can only be used if the trait is in scope help: the following trait is implemented but not in scope; perhaps add a `use` for it: diff --git a/src/test/ui/traits/trait-object-macro-matcher.stderr b/src/test/ui/traits/trait-object-macro-matcher.stderr index a8511f63c16a5..cb48bd1258ea2 100644 --- a/src/test/ui/traits/trait-object-macro-matcher.stderr +++ b/src/test/ui/traits/trait-object-macro-matcher.stderr @@ -14,4 +14,5 @@ LL | m!(dyn Copy + Send + 'static); error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0038`. +Some errors have detailed explanations: E0038, E0224. +For more information about an error, try `rustc --explain E0038`. diff --git a/src/test/ui/traits/trait-object-vs-lifetime-2.stderr b/src/test/ui/traits/trait-object-vs-lifetime-2.stderr index 014d380b63ca5..28b8e11f1330c 100644 --- a/src/test/ui/traits/trait-object-vs-lifetime-2.stderr +++ b/src/test/ui/traits/trait-object-vs-lifetime-2.stderr @@ -6,3 +6,4 @@ LL | dyn 'static +: 'static + Copy, error: aborting due to previous error +For more information about this error, try `rustc --explain E0224`. diff --git a/src/test/ui/traits/trait-object-vs-lifetime.stderr b/src/test/ui/traits/trait-object-vs-lifetime.stderr index 04529fb8cfab3..ff3fc2a197c66 100644 --- a/src/test/ui/traits/trait-object-vs-lifetime.stderr +++ b/src/test/ui/traits/trait-object-vs-lifetime.stderr @@ -32,5 +32,5 @@ LL | let _: S; error: aborting due to 5 previous errors -Some errors have detailed explanations: E0107, E0747. +Some errors have detailed explanations: E0107, E0224, E0747. For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/traits/trait-param-without-lifetime-constraint.rs b/src/test/ui/traits/trait-param-without-lifetime-constraint.rs new file mode 100644 index 0000000000000..a79b74dcddead --- /dev/null +++ b/src/test/ui/traits/trait-param-without-lifetime-constraint.rs @@ -0,0 +1,20 @@ +struct Article { + proof_reader: ProofReader, +} + +struct ProofReader { + name: String, +} + +pub trait HaveRelationship { + fn get_relation(&self) -> To; +} + +impl HaveRelationship<&ProofReader> for Article { + fn get_relation(&self) -> &ProofReader { + //~^ ERROR `impl` item signature doesn't match `trait` item signature + &self.proof_reader + } +} + +fn main() {} diff --git a/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr new file mode 100644 index 0000000000000..4942dbe480ba3 --- /dev/null +++ b/src/test/ui/traits/trait-param-without-lifetime-constraint.stderr @@ -0,0 +1,19 @@ +error: `impl` item signature doesn't match `trait` item signature + --> $DIR/trait-param-without-lifetime-constraint.rs:14:5 + | +LL | fn get_relation(&self) -> To; + | ----------------------------- expected `fn(&Article) -> &ProofReader` +... +LL | fn get_relation(&self) -> &ProofReader { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&Article) -> &ProofReader` + | + = note: expected `fn(&Article) -> &ProofReader` + found `fn(&Article) -> &ProofReader` +help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + --> $DIR/trait-param-without-lifetime-constraint.rs:10:31 + | +LL | fn get_relation(&self) -> To; + | ^^ consider borrowing this type parameter in the trait + +error: aborting due to previous error + 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-deferences-issue-39029.fixed b/src/test/ui/traits/trait-suggest-deferences-issue-39029.fixed new file mode 100644 index 0000000000000..2bb34b0ebee6f --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-issue-39029.fixed @@ -0,0 +1,18 @@ +// run-rustfix +use std::net::TcpListener; + +struct NoToSocketAddrs(String); + +impl std::ops::Deref for NoToSocketAddrs { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn main() { + let _works = TcpListener::bind("some string"); + let bad = NoToSocketAddrs("bad".to_owned()); + let _errors = TcpListener::bind(&*bad); + //~^ ERROR the trait bound `NoToSocketAddrs: std::net::ToSocketAddrs` is not satisfied +} diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-39029.rs b/src/test/ui/traits/trait-suggest-deferences-issue-39029.rs new file mode 100644 index 0000000000000..33d524608a058 --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-issue-39029.rs @@ -0,0 +1,18 @@ +// run-rustfix +use std::net::TcpListener; + +struct NoToSocketAddrs(String); + +impl std::ops::Deref for NoToSocketAddrs { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn main() { + let _works = TcpListener::bind("some string"); + let bad = NoToSocketAddrs("bad".to_owned()); + let _errors = TcpListener::bind(&bad); + //~^ ERROR the trait bound `NoToSocketAddrs: std::net::ToSocketAddrs` is not satisfied +} diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-39029.stderr b/src/test/ui/traits/trait-suggest-deferences-issue-39029.stderr new file mode 100644 index 0000000000000..0bf9794a744c9 --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-issue-39029.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `NoToSocketAddrs: std::net::ToSocketAddrs` is not satisfied + --> $DIR/trait-suggest-deferences-issue-39029.rs:16:37 + | +LL | let _errors = TcpListener::bind(&bad); + | ^^^^ + | | + | the trait `std::net::ToSocketAddrs` is not implemented for `NoToSocketAddrs` + | help: consider adding dereference here: `&*bad` + | + ::: $SRC_DIR/libstd/net/tcp.rs:LL:COL + | +LL | pub fn bind(addr: A) -> io::Result { + | ------------- required by this bound in `std::net::TcpListener::bind` + | + = note: required because of the requirements on the impl of `std::net::ToSocketAddrs` for `&NoToSocketAddrs` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-62530.fixed b/src/test/ui/traits/trait-suggest-deferences-issue-62530.fixed new file mode 100644 index 0000000000000..fa7b9167d8d7f --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-issue-62530.fixed @@ -0,0 +1,15 @@ +// run-rustfix +fn takes_str(_x: &str) {} + +fn takes_type_parameter(_x: T) where T: SomeTrait {} + +trait SomeTrait {} +impl SomeTrait for &'_ str {} +impl SomeTrait for char {} + +fn main() { + let string = String::new(); + takes_str(&string); // Ok + takes_type_parameter(&*string); // Error + //~^ ERROR the trait bound `&std::string::String: SomeTrait` is not satisfied +} diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-62530.rs b/src/test/ui/traits/trait-suggest-deferences-issue-62530.rs new file mode 100644 index 0000000000000..e785f01217735 --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-issue-62530.rs @@ -0,0 +1,15 @@ +// run-rustfix +fn takes_str(_x: &str) {} + +fn takes_type_parameter(_x: T) where T: SomeTrait {} + +trait SomeTrait {} +impl SomeTrait for &'_ str {} +impl SomeTrait for char {} + +fn main() { + let string = String::new(); + takes_str(&string); // Ok + takes_type_parameter(&string); // Error + //~^ ERROR the trait bound `&std::string::String: SomeTrait` is not satisfied +} diff --git a/src/test/ui/traits/trait-suggest-deferences-issue-62530.stderr b/src/test/ui/traits/trait-suggest-deferences-issue-62530.stderr new file mode 100644 index 0000000000000..9c2a582638ecb --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-issue-62530.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `&std::string::String: SomeTrait` is not satisfied + --> $DIR/trait-suggest-deferences-issue-62530.rs:13:26 + | +LL | fn takes_type_parameter(_x: T) where T: SomeTrait {} + | --------- required by this bound in `takes_type_parameter` +... +LL | takes_type_parameter(&string); // Error + | ^^^^^^^ + | | + | the trait `SomeTrait` is not implemented for `&std::string::String` + | help: consider adding dereference here: `&*string` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/trait-suggest-deferences-multiple-0.fixed b/src/test/ui/traits/trait-suggest-deferences-multiple-0.fixed new file mode 100644 index 0000000000000..b7160b75c605e --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-multiple-0.fixed @@ -0,0 +1,36 @@ +// run-rustfix +use std::ops::Deref; + +trait Happy {} +struct LDM; +impl Happy for &LDM {} + +struct Foo(LDM); +struct Bar(Foo); +struct Baz(Bar); +impl Deref for Foo { + type Target = LDM; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Bar { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Baz { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn foo(_: T) where T: Happy {} + +fn main() { + let baz = Baz(Bar(Foo(LDM))); + foo(&***baz); + //~^ ERROR the trait bound `&Baz: Happy` is not satisfied +} diff --git a/src/test/ui/traits/trait-suggest-deferences-multiple-0.rs b/src/test/ui/traits/trait-suggest-deferences-multiple-0.rs new file mode 100644 index 0000000000000..9ac55177ffadd --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-multiple-0.rs @@ -0,0 +1,36 @@ +// run-rustfix +use std::ops::Deref; + +trait Happy {} +struct LDM; +impl Happy for &LDM {} + +struct Foo(LDM); +struct Bar(Foo); +struct Baz(Bar); +impl Deref for Foo { + type Target = LDM; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Bar { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Baz { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn foo(_: T) where T: Happy {} + +fn main() { + let baz = Baz(Bar(Foo(LDM))); + foo(&baz); + //~^ ERROR the trait bound `&Baz: Happy` is not satisfied +} diff --git a/src/test/ui/traits/trait-suggest-deferences-multiple-0.stderr b/src/test/ui/traits/trait-suggest-deferences-multiple-0.stderr new file mode 100644 index 0000000000000..add34a553bc9f --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-multiple-0.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `&Baz: Happy` is not satisfied + --> $DIR/trait-suggest-deferences-multiple-0.rs:34:9 + | +LL | fn foo(_: T) where T: Happy {} + | ----- required by this bound in `foo` +... +LL | foo(&baz); + | ^^^^ + | | + | the trait `Happy` is not implemented for `&Baz` + | help: consider adding dereference here: `&***baz` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/trait-suggest-deferences-multiple-1.rs b/src/test/ui/traits/trait-suggest-deferences-multiple-1.rs new file mode 100644 index 0000000000000..91c6c7924a408 --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-multiple-1.rs @@ -0,0 +1,54 @@ +use std::ops::{Deref, DerefMut}; + +trait Happy {} +struct LDM; +impl Happy for &mut LDM {} + +struct Foo(LDM); +struct Bar(Foo); +struct Baz(Bar); +impl Deref for Foo { + type Target = LDM; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Bar { + type Target = Foo; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Deref for Baz { + type Target = Bar; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl DerefMut for Bar { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl DerefMut for Baz { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + + +fn foo(_: T) where T: Happy {} + +fn main() { + // Currently the compiler doesn't try to suggest dereferences for situations + // where DerefMut involves. So this test is meant to ensure compiler doesn't + // generate incorrect help message. + let mut baz = Baz(Bar(Foo(LDM))); + foo(&mut baz); + //~^ ERROR the trait bound `&mut Baz: Happy` is not satisfied +} diff --git a/src/test/ui/traits/trait-suggest-deferences-multiple-1.stderr b/src/test/ui/traits/trait-suggest-deferences-multiple-1.stderr new file mode 100644 index 0000000000000..e90278fa16f0e --- /dev/null +++ b/src/test/ui/traits/trait-suggest-deferences-multiple-1.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `&mut Baz: Happy` is not satisfied + --> $DIR/trait-suggest-deferences-multiple-1.rs:52:9 + | +LL | fn foo(_: T) where T: Happy {} + | ----- required by this bound in `foo` +... +LL | foo(&mut baz); + | ^^^^^^^^ the trait `Happy` is not implemented for `&mut Baz` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. 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-lifetime.rs b/src/test/ui/traits/traits-inductive-overflow-lifetime.rs new file mode 100644 index 0000000000000..205d50a2ed9ce --- /dev/null +++ b/src/test/ui/traits/traits-inductive-overflow-lifetime.rs @@ -0,0 +1,30 @@ +// Test that we don't hit the recursion limit for short cycles involving lifetimes. + +// Shouldn't hit this, we should realize that we're in a cycle sooner. +#![recursion_limit="20"] + +trait NotAuto {} +trait Y { + type P; +} + +impl<'a> Y for C<'a> { + type P = Box>>; +} + +struct C<'a>(&'a ()); +struct X(T::P); + +impl NotAuto for Box {} +impl NotAuto for X where T::P: NotAuto {} +impl<'a> NotAuto for C<'a> {} + +fn is_send() {} +//~^ NOTE: required + +fn main() { + // Should only be a few notes. + is_send::>>(); + //~^ ERROR overflow evaluating + //~| NOTE: required +} diff --git a/src/test/ui/traits/traits-inductive-overflow-lifetime.stderr b/src/test/ui/traits/traits-inductive-overflow-lifetime.stderr new file mode 100644 index 0000000000000..9a227229ea4c2 --- /dev/null +++ b/src/test/ui/traits/traits-inductive-overflow-lifetime.stderr @@ -0,0 +1,14 @@ +error[E0275]: overflow evaluating the requirement `std::boxed::Box>>: NotAuto` + --> $DIR/traits-inductive-overflow-lifetime.rs:27:5 + | +LL | fn is_send() {} + | ------- required by this bound in `is_send` +... +LL | is_send::>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: required because of the requirements on the impl of `NotAuto` for `X>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0275`. 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-issue-71136.rs b/src/test/ui/traits/traits-issue-71136.rs new file mode 100644 index 0000000000000..b21756e2b637f --- /dev/null +++ b/src/test/ui/traits/traits-issue-71136.rs @@ -0,0 +1,8 @@ +struct Foo(u8); + +#[derive(Clone)] +struct FooHolster { + the_foos: Vec, //~ERROR Clone +} + +fn main() {} diff --git a/src/test/ui/traits/traits-issue-71136.stderr b/src/test/ui/traits/traits-issue-71136.stderr new file mode 100644 index 0000000000000..4c0a43062f60d --- /dev/null +++ b/src/test/ui/traits/traits-issue-71136.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `Foo: std::clone::Clone` is not satisfied + --> $DIR/traits-issue-71136.rs:5:5 + | +LL | the_foos: Vec, + | ^^^^^^^^^^^^^^^^^^ expected an implementor of trait `std::clone::Clone` + | + = note: required because of the requirements on the impl of `std::clone::Clone` for `std::vec::Vec` + = note: required by `std::clone::Clone::clone` + = note: 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 E0277`. 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/traits/wf-trait-object-only-maybe-bound.stderr b/src/test/ui/traits/wf-trait-object-only-maybe-bound.stderr index 8cc97addc7dd4..482410886329e 100644 --- a/src/test/ui/traits/wf-trait-object-only-maybe-bound.stderr +++ b/src/test/ui/traits/wf-trait-object-only-maybe-bound.stderr @@ -12,3 +12,4 @@ LL | type _0 = 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/transmute-specialization.rs b/src/test/ui/transmute-specialization.rs index 002fba9ce8101..499334d983b1f 100644 --- a/src/test/ui/transmute-specialization.rs +++ b/src/test/ui/transmute-specialization.rs @@ -1,6 +1,6 @@ // run-pass -#![feature(specialization)] +#![feature(specialization)] //~ WARN the feature `specialization` is incomplete trait Specializable { type Output; } diff --git a/src/test/ui/transmute-specialization.stderr b/src/test/ui/transmute-specialization.stderr new file mode 100644 index 0000000000000..02315051d30ec --- /dev/null +++ b/src/test/ui/transmute-specialization.stderr @@ -0,0 +1,11 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/transmute-specialization.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/transmute/main.stderr b/src/test/ui/transmute/main.stderr index c72876e050f05..4e781318329bf 100644 --- a/src/test/ui/transmute/main.stderr +++ b/src/test/ui/transmute/main.stderr @@ -4,8 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | transmute(x) | ^^^^^^^^^ | - = note: source type: `>::T` (size can vary because of ::T) - = note: target type: `>::T` (size can vary because of ::T) + = note: `::T` does not have a fixed size error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/main.rs:20:17 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-edition2015.stderr b/src/test/ui/try-block/try-block-in-edition2015.stderr index 9b4fe2c1205db..fe870ab737cc8 100644 --- a/src/test/ui/try-block/try-block-in-edition2015.stderr +++ b/src/test/ui/try-block/try-block-in-edition2015.stderr @@ -11,9 +11,13 @@ error[E0574]: expected struct, variant or union type, found macro `try` --> $DIR/try-block-in-edition2015.rs:4:33 | LL | let try_result: Option<_> = try { - | ^^^ help: use `!` to invoke the macro: `try!` + | ^^^ | = note: if you want the `try` keyword, you need to be in the 2018 edition +help: use `!` to invoke the macro + | +LL | let try_result: Option<_> = try! { + | ^ error: aborting due to 2 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..9ebf63468e773 100644 --- a/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr +++ b/src/test/ui/type-alias-impl-trait/bound_reduction2.stderr @@ -1,29 +1,14 @@ error[E0277]: the trait bound `T: TraitWithAssoc` is not satisfied - --> $DIR/bound_reduction2.rs:10:1 + --> $DIR/bound_reduction2.rs:10:15 | LL | type Foo = impl Trait; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TraitWithAssoc` is not implemented for `T` + | ^^^^^^^^^^^^^ 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/declared_but_never_defined.stderr b/src/test/ui/type-alias-impl-trait/declared_but_never_defined.stderr index ae0fee4333b5b..21c2e8a9db618 100644 --- a/src/test/ui/type-alias-impl-trait/declared_but_never_defined.stderr +++ b/src/test/ui/type-alias-impl-trait/declared_but_never_defined.stderr @@ -1,8 +1,8 @@ error: could not find defining uses - --> $DIR/declared_but_never_defined.rs:6:1 + --> $DIR/declared_but_never_defined.rs:6:12 | LL | type Bar = impl std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr b/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr index 0642407aba3cd..c0cb94b15d033 100644 --- a/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr +++ b/src/test/ui/type-alias-impl-trait/declared_but_not_defined_in_scope.stderr @@ -1,8 +1,8 @@ error: could not find defining uses - --> $DIR/declared_but_not_defined_in_scope.rs:7:5 + --> $DIR/declared_but_not_defined_in_scope.rs:7:20 | LL | pub type Boo = impl ::std::fmt::Debug; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error 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_type_does_not_live_long_enough.nll.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr index 9549074d4bf78..76654d7a718b8 100644 --- a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.nll.stderr @@ -13,10 +13,10 @@ LL | let z: i32 = x; | expected due to this ... LL | type WrongGeneric = impl 'static; - | ------------------------------------ the found opaque type + | ------------ the found opaque type | = note: expected type `i32` - found opaque type `WrongGeneric::<&{integer}>` + found opaque type `impl Sized` error: aborting due to 2 previous errors diff --git a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr index 22e2391f8380b..18d8daa05e63d 100644 --- a/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr @@ -13,25 +13,19 @@ LL | let z: i32 = x; | expected due to this ... LL | type WrongGeneric = impl 'static; - | ------------------------------------ the found opaque type + | ------------ the found opaque type | = note: expected type `i32` - found opaque type `WrongGeneric::<&{integer}>` + found opaque type `impl Sized` error[E0310]: the parameter type `T` may not live long enough - --> $DIR/generic_type_does_not_live_long_enough.rs:9:1 + --> $DIR/generic_type_does_not_live_long_enough.rs:9:24 | LL | type WrongGeneric = impl 'static; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds ... LL | fn wrong_generic(t: T) -> WrongGeneric { | - help: consider adding an explicit lifetime bound...: `T: 'static` - | -note: ...so that the type `T` will meet its required lifetime bounds - --> $DIR/generic_type_does_not_live_long_enough.rs:9:1 - | -LL | type WrongGeneric = impl 'static; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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..911f592f73f27 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained.stderr @@ -5,17 +5,16 @@ LL | type Underconstrained = impl 'static; | ^^^^^^^^^^^^ error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/generic_underconstrained.rs:6:1 + --> $DIR/generic_underconstrained.rs:6:35 | 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 + | ^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` | -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..247d68ef2a1f0 100644 --- a/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ b/src/test/ui/type-alias-impl-trait/generic_underconstrained2.stderr @@ -11,38 +11,36 @@ LL | type Underconstrained2 = impl 'static; | ^^^^^^^^^^^^ error[E0277]: `U` doesn't implement `std::fmt::Debug` - --> $DIR/generic_underconstrained2.rs:5:1 + --> $DIR/generic_underconstrained2.rs:5:45 | LL | type Underconstrained = impl 'static; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` ... 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 + --> $DIR/generic_underconstrained2.rs:14:46 | LL | type Underconstrained2 = impl 'static; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | ^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` ... 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/impl-with-unconstrained-param.rs b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs new file mode 100644 index 0000000000000..bc6543a9229db --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs @@ -0,0 +1,18 @@ +// Ensure that we don't ICE if associated type impl trait is used in an impl +// with an unconstrained type parameter. + +#![feature(type_alias_impl_trait)] + +trait X { + type I; + fn f() -> Self::I; +} + +impl X for () { + type I = impl Sized; + //~^ ERROR could not find defining uses + fn f() -> Self::I {} + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr new file mode 100644 index 0000000000000..e8b677113dba7 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr @@ -0,0 +1,15 @@ +error[E0282]: type annotations needed + --> $DIR/impl-with-unconstrained-param.rs:14:23 + | +LL | fn f() -> Self::I {} + | ^^ cannot infer type for type parameter `T` + +error: could not find defining uses + --> $DIR/impl-with-unconstrained-param.rs:12:14 + | +LL | type I = impl Sized; + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs b/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs new file mode 100644 index 0000000000000..8e8508cdd6f30 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-55099-lifetime-inference.rs @@ -0,0 +1,28 @@ +// check-pass +// Regression test for issue #55099 +// Tests that we don't incorrectly consider a lifetime to part +// of the concrete type + +#![feature(type_alias_impl_trait)] + +trait Future { +} + +struct AndThen(F); + +impl Future for AndThen { +} + +struct Foo<'a> { + x: &'a mut (), +} + +type F = impl Future; + +impl<'a> Foo<'a> { + fn reply(&mut self) -> F { + AndThen(|| ()) + } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs b/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs new file mode 100644 index 0000000000000..3a7a5da075f11 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57188-associate-impl-capture.rs @@ -0,0 +1,24 @@ +// Regression test for #57188 + +// check-pass + +#![feature(type_alias_impl_trait)] + +struct Baz<'a> { + source: &'a str, +} + +trait Foo<'a> { + type T: Iterator> + 'a; + fn foo(source: &'a str) -> Self::T; +} + +struct Bar; +impl<'a> Foo<'a> for Bar { + type T = impl Iterator> + 'a; + fn foo(source: &'a str) -> Self::T { + std::iter::once(Baz { source }) + } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.nll.stderr b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.nll.stderr new file mode 100644 index 0000000000000..8c9cb742fac91 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.nll.stderr @@ -0,0 +1,14 @@ +error: higher-ranked subtype error + --> $DIR/issue-57611-trait-alias.rs:21:9 + | +LL | |x| x + | ^^^^^ + +error: higher-ranked subtype error + --> $DIR/issue-57611-trait-alias.rs:21:9 + | +LL | |x| x + | ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs index 1c2051e7eaeeb..41e019247c942 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs @@ -14,8 +14,8 @@ trait Foo { struct X; impl Foo for X { - type Bar = impl Baz; //~ ERROR type mismatch in closure arguments - //~^ ERROR type mismatch resolving + type Bar = impl Baz; + //~^ ERROR mismatched types fn bar(&self) -> Self::Bar { |x| x diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr index f648b7bfc991d..cd637056c94ad 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.stderr @@ -1,23 +1,12 @@ -error[E0631]: type mismatch in closure arguments - --> $DIR/issue-57611-trait-alias.rs:17:5 +error[E0308]: mismatched types + --> $DIR/issue-57611-trait-alias.rs:17:16 | LL | type Bar = impl Baz; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected signature of `for<'r> fn(&'r X) -> _` -... -LL | |x| x - | ----- found signature of `fn(_) -> _` + | ^^^^^^^^^^^^^^^^^^^^ one type is more general than the other | - = note: the return type of a function must have a statically known size + = note: expected type `std::ops::FnOnce<(&X,)>` + found type `std::ops::FnOnce<(&X,)>` -error[E0271]: type mismatch resolving `for<'r> <[closure@$DIR/issue-57611-trait-alias.rs:21:9: 21:14] as std::ops::FnOnce<(&'r X,)>>::Output == &'r X` - --> $DIR/issue-57611-trait-alias.rs:17:5 - | -LL | type Bar = impl Baz; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime - | - = note: the return type of a function must have a statically known size - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0271, E0631. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/issue-60371.stderr b/src/test/ui/type-alias-impl-trait/issue-60371.stderr index 2796c77baa1c4..bf2d612fcdb41 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60371.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-60371.stderr @@ -8,20 +8,20 @@ LL | type Item = impl Bug; = help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable error[E0277]: the trait bound `(): Bug` is not satisfied - --> $DIR/issue-60371.rs:8:5 + --> $DIR/issue-60371.rs:8:17 | LL | type Item = impl Bug; - | ^^^^^^^^^^^^^^^^^^^^^ the trait `Bug` is not implemented for `()` + | ^^^^^^^^ the trait `Bug` is not implemented for `()` | = help: the following implementations were found: <&() as Bug> = note: the return type of a function must have a statically known size error: could not find defining uses - --> $DIR/issue-60371.rs:8:5 + --> $DIR/issue-60371.rs:8:17 | LL | type Item = impl Bug; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ error: aborting due to 3 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..78def0d1136de 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,10 +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 - (0u8..n) - .rev() - .map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) + //~^ 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..66fa862ef9d7a 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,14 @@ -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 | | } - | |_____^ - -error: could not find defining uses - --> $DIR/issue-60564.rs:8:1 +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 +error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.rs b/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.rs new file mode 100644 index 0000000000000..36779a0ce89c3 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-62000-associate-impl-trait-lifetimes.rs @@ -0,0 +1,38 @@ +// Regression test for #62988 + +// check-pass + +#![feature(type_alias_impl_trait)] + +trait MyTrait { + type AssocType: Send; + fn ret(&self) -> Self::AssocType; +} + +impl MyTrait for () { + type AssocType = impl Send; + fn ret(&self) -> Self::AssocType { + () + } +} + +impl<'a> MyTrait for &'a () { + type AssocType = impl Send; + fn ret(&self) -> Self::AssocType { + () + } +} + +trait MyLifetimeTrait<'a> { + type AssocType: Send + 'a; + fn ret(&self) -> Self::AssocType; +} + +impl<'a> MyLifetimeTrait<'a> for &'a () { + type AssocType = impl Send + 'a; + fn ret(&self) -> Self::AssocType { + *self + } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-63279.stderr b/src/test/ui/type-alias-impl-trait/issue-63279.stderr index bef4d01093c62..d07f64c3312d3 100644 --- a/src/test/ui/type-alias-impl-trait/issue-63279.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-63279.stderr @@ -1,10 +1,10 @@ error[E0271]: type mismatch resolving `<[closure@$DIR/issue-63279.rs:8:5: 8:28] as std::ops::FnOnce<()>>::Output == ()` - --> $DIR/issue-63279.rs:5:1 + --> $DIR/issue-63279.rs:5:16 | LL | type Closure = impl FnOnce(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found `()` + | ^^^^^^^^^^^^^ expected opaque type, found `()` | - = note: expected opaque type `Closure` + = note: expected opaque type `impl std::ops::FnOnce<()>` found unit type `()` = note: the return type of a function must have a statically known size diff --git a/src/test/ui/type-alias-impl-trait/issue-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/issue-69136-inner-lifetime-resolve-error.rs b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs new file mode 100644 index 0000000000000..6732902c09a50 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.rs @@ -0,0 +1,22 @@ +// Regression test for #69136 + +#![feature(type_alias_impl_trait)] + +trait SomeTrait {} + +impl SomeTrait for () {} + +trait WithAssoc { + type AssocType; +} + +impl WithAssoc for () { + type AssocType = (); +} + +type Return = impl WithAssoc; +//~^ ERROR use of undeclared lifetime name `'a` + +fn my_fun() -> Return<()> {} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.stderr b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.stderr new file mode 100644 index 0000000000000..fe45e39d938f0 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-error.stderr @@ -0,0 +1,11 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/issue-69136-inner-lifetime-resolve-error.rs:17:65 + | +LL | type Return = impl WithAssoc; + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `'a,` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0261`. diff --git a/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs new file mode 100644 index 0000000000000..a6916eda8b093 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-69136-inner-lifetime-resolve-ok.rs @@ -0,0 +1,23 @@ +// Test-pass variant of #69136 + +// check-pass + +#![feature(type_alias_impl_trait)] + +trait SomeTrait {} + +impl SomeTrait for () {} + +trait WithAssoc { + type AssocType; +} + +impl WithAssoc for () { + type AssocType = (); +} + +type Return<'a> = impl WithAssoc; + +fn my_fun<'a>() -> Return<'a> {} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-70121.rs b/src/test/ui/type-alias-impl-trait/issue-70121.rs new file mode 100644 index 0000000000000..dff0d89d465dd --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-70121.rs @@ -0,0 +1,23 @@ +// check-pass + +#![feature(type_alias_impl_trait)] + +pub type Successors<'a> = impl Iterator; + +pub fn f<'a>() -> Successors<'a> { + None.into_iter() +} + +pub trait Tr { + type Item; +} + +impl<'a> Tr for &'a () { + type Item = Successors<'a>; +} + +pub fn kazusa<'a>() -> <&'a () as Tr>::Item { + None.into_iter() +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr index 70c99c944d654..a6b7e35b488b1 100644 --- a/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr +++ b/src/test/ui/type-alias-impl-trait/never_reveal_concrete_type.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/never_reveal_concrete_type.rs:13:27 | LL | type NoReveal = impl std::fmt::Debug; - | ------------------------------------- the found opaque type + | -------------------- the found opaque type ... LL | let _: &'static str = x; | ------------ ^ expected `&str`, found opaque type @@ -10,15 +10,13 @@ LL | let _: &'static str = x; | expected due to this | = note: expected reference `&'static str` - found opaque type `NoReveal` + found opaque type `impl std::fmt::Debug` -error[E0605]: non-primitive cast: `NoReveal` as `&'static str` +error[E0605]: non-primitive cast: `impl std::fmt::Debug` as `&'static str` --> $DIR/never_reveal_concrete_type.rs:14:13 | LL | let _ = x as &'static str; - | ^^^^^^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to 2 previous errors diff --git a/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr b/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr index 444e6e8214ff2..61025e846921e 100644 --- a/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr +++ b/src/test/ui/type-alias-impl-trait/no_inferrable_concrete_type.stderr @@ -1,8 +1,8 @@ error: could not find defining uses - --> $DIR/no_inferrable_concrete_type.rs:6:1 + --> $DIR/no_inferrable_concrete_type.rs:6:12 | LL | type Foo = impl Copy; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr index 375c0bc7fe2ed..d237cc6238ae1 100644 --- a/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr +++ b/src/test/ui/type-alias-impl-trait/no_revealing_outside_defining_module.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/no_revealing_outside_defining_module.rs:15:19 | LL | pub type Boo = impl ::std::fmt::Debug; - | -------------------------------------- the found opaque type + | ---------------------- the found opaque type ... LL | let _: &str = bomp(); | ---- ^^^^^^ expected `&str`, found opaque type @@ -10,20 +10,20 @@ LL | let _: &str = bomp(); | expected due to this | = note: expected reference `&str` - found opaque type `Boo` + found opaque type `impl std::fmt::Debug` error[E0308]: mismatched types --> $DIR/no_revealing_outside_defining_module.rs:19:5 | LL | pub type Boo = impl ::std::fmt::Debug; - | -------------------------------------- the expected opaque type + | ---------------------- the expected opaque type ... LL | fn bomp() -> boo::Boo { - | -------- expected `Boo` because of return type + | -------- expected `impl std::fmt::Debug` because of return type LL | "" | ^^ expected opaque type, found `&str` | - = note: expected opaque type `Boo` + = note: expected opaque type `impl std::fmt::Debug` found reference `&'static str` error: aborting due to 2 previous errors diff --git a/src/test/ui/type-alias-impl-trait/not_a_defining_use.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/structural-match-no-leak.rs b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.rs new file mode 100644 index 0000000000000..479d6cd9af765 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.rs @@ -0,0 +1,20 @@ +#![feature(const_fn, type_alias_impl_trait)] + +type Bar = impl Send; + +// While i32 is structural-match, we do not want to leak this information. +// (See https://github.com/rust-lang/rust/issues/72156) +const fn leak_free() -> Bar { + 7i32 +} +const LEAK_FREE: Bar = leak_free(); + +fn leak_free_test() { + match todo!() { + LEAK_FREE => (), + //~^ opaque types cannot be used in patterns + _ => (), + } +} + +fn main() { } diff --git a/src/test/ui/type-alias-impl-trait/structural-match-no-leak.stderr b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.stderr new file mode 100644 index 0000000000000..ae0d8e8d4239c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/structural-match-no-leak.stderr @@ -0,0 +1,8 @@ +error: opaque types cannot be used in patterns + --> $DIR/structural-match-no-leak.rs:14:9 + | +LL | LEAK_FREE => (), + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/structural-match.rs b/src/test/ui/type-alias-impl-trait/structural-match.rs new file mode 100644 index 0000000000000..481448d64b1aa --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/structural-match.rs @@ -0,0 +1,21 @@ +#![feature(const_fn, type_alias_impl_trait)] + +type Foo = impl Send; + +// This is not structural-match +struct A; + +const fn value() -> Foo { + A +} +const VALUE: Foo = value(); + +fn test() { + match todo!() { + VALUE => (), + //~^ opaque types cannot be used in patterns + _ => (), + } +} + +fn main() { } diff --git a/src/test/ui/type-alias-impl-trait/structural-match.stderr b/src/test/ui/type-alias-impl-trait/structural-match.stderr new file mode 100644 index 0000000000000..ad9036a87d1d9 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/structural-match.stderr @@ -0,0 +1,8 @@ +error: opaque types cannot be used in patterns + --> $DIR/structural-match.rs:15:9 + | +LL | VALUE => (), + | ^^^^^ + +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-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr index 02ab3399ea6fa..726f4ea6e00f7 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error.stderr @@ -1,8 +1,8 @@ error: could not find defining uses - --> $DIR/type-alias-impl-trait-with-cycle-error.rs:3:1 + --> $DIR/type-alias-impl-trait-with-cycle-error.rs:3:12 | LL | type Foo = impl Fn() -> Foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr index e9abb79588683..3947cc4d27055 100644 --- a/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr +++ b/src/test/ui/type-alias-impl-trait/type-alias-impl-trait-with-cycle-error2.stderr @@ -1,8 +1,8 @@ error: could not find defining uses - --> $DIR/type-alias-impl-trait-with-cycle-error2.rs:7:1 + --> $DIR/type-alias-impl-trait-with-cycle-error2.rs:7:12 | LL | type Foo = impl Bar; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs b/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs new file mode 100644 index 0000000000000..fd954801dc047 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/type-alias-nested-impl-trait.rs @@ -0,0 +1,14 @@ +// run-pass + +#![feature(type_alias_impl_trait)] + +use std::iter::{once, Chain}; + +type I = Chain>; +fn test2>(x: A) -> I { + x.chain(once("5")) +} + +fn main() { + assert_eq!(vec!["1", "3", "5"], test2(["1", "3"].iter().cloned()).collect::>()); +} diff --git a/src/test/ui/type-inference/or_else-multiple-type-params.stderr b/src/test/ui/type-inference/or_else-multiple-type-params.stderr index 24122e658679a..047728dc1ea4e 100644 --- a/src/test/ui/type-inference/or_else-multiple-type-params.stderr +++ b/src/test/ui/type-inference/or_else-multiple-type-params.stderr @@ -2,10 +2,12 @@ error[E0282]: type annotations needed --> $DIR/or_else-multiple-type-params.rs:7:10 | LL | .or_else(|err| { - | ^^^^^^^ - | | - | cannot infer type for type parameter `F` declared on the associated function `or_else` - | help: consider specifying the type arguments in the method call: `or_else::` + | ^^^^^^^ cannot infer type for type parameter `F` declared on the associated function `or_else` + | +help: consider specifying the type arguments in the method call + | +LL | .or_else::(|err| { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type-inference/sort_by_key.stderr b/src/test/ui/type-inference/sort_by_key.stderr index bb108adcd64af..0b6630ec89423 100644 --- a/src/test/ui/type-inference/sort_by_key.stderr +++ b/src/test/ui/type-inference/sort_by_key.stderr @@ -2,9 +2,12 @@ error[E0282]: type annotations needed --> $DIR/sort_by_key.rs:3:9 | LL | lst.sort_by_key(|&(v, _)| v.iter().sum()); - | ^^^^^^^^^^^ --- help: consider specifying the type argument in the method call: `sum::` - | | - | cannot infer type for type parameter `K` declared on the associated function `sort_by_key` + | ^^^^^^^^^^^ cannot infer type for type parameter `K` declared on the associated function `sort_by_key` + | +help: consider specifying the type argument in the method call + | +LL | lst.sort_by_key(|&(v, _)| v.iter().sum::()); + | ^^^^^ error: aborting due to previous error 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/ascription/issue-47666.rs b/src/test/ui/type/ascription/issue-47666.rs index ceb1dd89daea9..8035de4a48a92 100644 --- a/src/test/ui/type/ascription/issue-47666.rs +++ b/src/test/ui/type/ascription/issue-47666.rs @@ -1,5 +1,7 @@ fn main() { let _ = Option:Some(vec![0, 1]); //~ ERROR expected type, found + //~^ ERROR expected value, found enum `Option` + //~| ERROR expected type, found variant `Some` } // This case isn't currently being handled gracefully due to the macro invocation. diff --git a/src/test/ui/type/ascription/issue-47666.stderr b/src/test/ui/type/ascription/issue-47666.stderr index f4c9240ab5372..3cd3be70aa75b 100644 --- a/src/test/ui/type/ascription/issue-47666.stderr +++ b/src/test/ui/type/ascription/issue-47666.stderr @@ -13,5 +13,35 @@ LL | let _ = Option:Some(vec![0, 1]); = note: see issue #23416 for more information = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error[E0423]: expected value, found enum `Option` + --> $DIR/issue-47666.rs:2:13 + | +LL | let _ = Option:Some(vec![0, 1]); + | ^^^^^^ + | +help: try using one of the enum's variants + | +LL | let _ = std::option::Option::None:Some(vec![0, 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = std::option::Option::Some:Some(vec![0, 1]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0573]: expected type, found variant `Some` + --> $DIR/issue-47666.rs:2:20 + | +LL | let _ = Option:Some(vec![0, 1]); + | ^^^^^^^^^^^^^^^^ not a type + | +help: try using the variant's enum + | +LL | let _ = Option:std::option::Option; + | ^^^^^^^^^^^^^^^^^^^ +help: maybe you meant to write a path separator here + | +LL | let _ = Option::Some(vec![0, 1]); + | ^^ + +error: aborting due to 3 previous errors +Some errors have detailed explanations: E0423, E0573. +For more information about an error, try `rustc --explain E0423`. 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 c6a811e836342..927cc507265fa 100644 --- a/src/test/ui/type/type-annotation-needed.stderr +++ b/src/test/ui/type/type-annotation-needed.stderr @@ -1,16 +1,17 @@ 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` - | help: consider specifying the type argument in the function call: `foo::` + | ^^^ 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); + | ^^^^^ error: aborting due to previous error 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/type-recursive.stderr b/src/test/ui/type/type-recursive.stderr index 72bf372e561d6..d6d32cc5d6f39 100644 --- a/src/test/ui/type/type-recursive.stderr +++ b/src/test/ui/type/type-recursive.stderr @@ -5,9 +5,12 @@ LL | struct T1 { | ^^^^^^^^^ recursive type has infinite size LL | foo: isize, LL | foolish: T1 - | ----------- recursive without indirection + | -- recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `T1` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `T1` representable + | +LL | foolish: Box + | ^^^^ ^ error: aborting due to previous error 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/issue-68590-reborrow-through-derefmut.rs b/src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs new file mode 100644 index 0000000000000..e4436260e70a0 --- /dev/null +++ b/src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs @@ -0,0 +1,25 @@ +// check-pass + +// rust-lang/rust#68590: confusing diagnostics when reborrowing through DerefMut. + +use std::cell::RefCell; + +struct A; + +struct S<'a> { + a: &'a mut A, +} + +fn take_a(_: &mut A) {} + +fn test<'a>(s: &RefCell>) { + let mut guard = s.borrow_mut(); + take_a(guard.a); + let _s2 = S { a: guard.a }; +} + +fn main() { + let a = &mut A; + let s = RefCell::new(S { a }); + test(&s); +} diff --git a/src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs b/src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs new file mode 100644 index 0000000000000..3ea05389f04a0 --- /dev/null +++ b/src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs @@ -0,0 +1,21 @@ +// check-pass + +// rust-lang/rust#72225: confusing diagnostics when calling FnMut through DerefMut. + +use std::cell::RefCell; + +struct S { + f: Box +} + +fn test(s: &RefCell) { + let mut guard = s.borrow_mut(); + (guard.f)(); +} + +fn main() { + let s = RefCell::new(S { + f: Box::new(|| ()) + }); + test(&s); +} diff --git a/src/test/ui/typeck/issue-73592-borrow_mut-through-deref.rs b/src/test/ui/typeck/issue-73592-borrow_mut-through-deref.rs new file mode 100644 index 0000000000000..0cf77da559470 --- /dev/null +++ b/src/test/ui/typeck/issue-73592-borrow_mut-through-deref.rs @@ -0,0 +1,58 @@ +// check-pass +// +// rust-lang/rust#73592: borrow_mut through Deref should work. +// +// Before #72280, when we see something like `&mut *rcvr.method()`, we +// incorrectly requires `rcvr` to be type-checked as a mut place. While this +// requirement is usually correct for smart pointers, it is overly restrictive +// for types like `Mutex` or `RefCell` which can produce a guard that +// implements `DerefMut` from `&self`. +// +// Making it more confusing, because we use Deref as the fallback when DerefMut +// is implemented, we won't see an issue when the smart pointer does not +// implement `DerefMut`. It only causes an issue when `rcvr` is obtained via a +// type that implements both `Deref` or `DerefMut`. +// +// This bug is only discovered in #73592 after it is already fixed as a side-effect +// of a refactoring made in #72280. + +#![warn(unused_mut)] + +use std::pin::Pin; +use std::cell::RefCell; + +struct S(RefCell<()>); + +fn test_pin(s: Pin<&S>) { + // This works before #72280. + let _ = &mut *s.0.borrow_mut(); +} + +fn test_pin_mut(s: Pin<&mut S>) { + // This should compile but didn't before #72280. + let _ = &mut *s.0.borrow_mut(); +} + +fn test_vec(s: &Vec>) { + // This should compile but didn't before #72280. + let _ = &mut *s[0].borrow_mut(); +} + +fn test_mut_pin(mut s: Pin<&S>) { + //~^ WARN variable does not need to be mutable + let _ = &mut *s.0.borrow_mut(); +} + +fn test_mut_pin_mut(mut s: Pin<&mut S>) { + //~^ WARN variable does not need to be mutable + let _ = &mut *s.0.borrow_mut(); +} + +fn main() { + let mut s = S(RefCell::new(())); + test_pin(Pin::new(&s)); + test_pin_mut(Pin::new(&mut s)); + test_mut_pin(Pin::new(&s)); + test_mut_pin_mut(Pin::new(&mut s)); + test_vec(&vec![s.0]); +} diff --git a/src/test/ui/typeck/issue-73592-borrow_mut-through-deref.stderr b/src/test/ui/typeck/issue-73592-borrow_mut-through-deref.stderr new file mode 100644 index 0000000000000..51303adc9e533 --- /dev/null +++ b/src/test/ui/typeck/issue-73592-borrow_mut-through-deref.stderr @@ -0,0 +1,24 @@ +warning: variable does not need to be mutable + --> $DIR/issue-73592-borrow_mut-through-deref.rs:41:17 + | +LL | fn test_mut_pin(mut s: Pin<&S>) { + | ----^ + | | + | help: remove this `mut` + | +note: the lint level is defined here + --> $DIR/issue-73592-borrow_mut-through-deref.rs:19:9 + | +LL | #![warn(unused_mut)] + | ^^^^^^^^^^ + +warning: variable does not need to be mutable + --> $DIR/issue-73592-borrow_mut-through-deref.rs:46:21 + | +LL | fn test_mut_pin_mut(mut s: Pin<&mut S>) { + | ----^ + | | + | help: remove this `mut` + +warning: 2 warnings emitted + 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-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-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 7bdd1035a1bf2..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 negative impls are only allowed for auto traits (e.g., `Send` and `Sync`) - -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 4e3d054ff6fad..0000000000000 --- a/src/test/ui/typeck/typeck-negative-impls-builtin.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0192]: negative impls are only allowed for auto traits (e.g., `Send` and `Sync`) - --> $DIR/typeck-negative-impls-builtin.rs:9:1 - | -LL | impl !TestTrait for TestType {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 6cd2b8c75b639..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; @@ -158,12 +156,9 @@ trait BadTrait<_> {} //~^ ERROR expected identifier, found reserved identifier `_` impl BadTrait<_> for BadStruct<_> {} //~^ ERROR the type placeholder `_` is not allowed within types on item signatures -//~| ERROR the type placeholder `_` is not allowed within types on item signatures -//~| ERROR the type placeholder `_` is not allowed within types on item signatures fn impl_trait() -> impl BadTrait<_> { //~^ ERROR the type placeholder `_` is not allowed within types on item signatures -//~| ERROR the type placeholder `_` is not allowed within types on item signatures unimplemented!() } @@ -178,14 +173,12 @@ struct BadStruct2<_, T>(_, T); type X = Box<_>; //~^ 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 Struct; trait Trait {} impl Trait for Struct {} type Y = impl Trait<_>; //~^ ERROR the type placeholder `_` is not allowed within types on item signatures -//~| ERROR the type placeholder `_` is not allowed within types on item signatures fn foo() -> Y { Struct } diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr index f2d02f70f4a66..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:170: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:170: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:175: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:208: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:170:22 + --> $DIR/typeck_type_placeholder_item.rs:165:22 | LL | struct BadStruct1<_, _>(_); | - ^ already used @@ -70,7 +70,7 @@ LL | static TEST3: _ = "test"; | ^ | | | not allowed in type signatures - | help: replace `_` with the correct type: `&'static str` + | 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:15:15 @@ -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,16 +220,16 @@ 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"; | ^ | | | not allowed in type signatures - | help: replace `_` with the correct type: `&'static str` + | 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,19 +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:32 - | -LL | impl BadTrait<_> for BadStruct<_> {} - | ^ not allowed in type signatures - -error[E0121]: the type placeholder `_` is not allowed within types on item signatures - --> $DIR/typeck_type_placeholder_item.rs:159:15 - | -LL | impl BadTrait<_> for BadStruct<_> {} - | ^ not allowed in type signatures - -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 @@ -377,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:164: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:170:25 + --> $DIR/typeck_type_placeholder_item.rs:165:25 | LL | struct BadStruct1<_, _>(_); | ^ not allowed in type signatures @@ -394,30 +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:175: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:179: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:179: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 @@ -428,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 @@ -439,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 @@ -452,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 @@ -463,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 @@ -474,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 @@ -487,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 @@ -498,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 @@ -509,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 @@ -520,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 @@ -531,37 +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:164: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:186: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:186: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:195: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:197: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:199:14 + --> $DIR/typeck_type_placeholder_item.rs:192:14 | LL | const D: _ = 42; | ^ @@ -570,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) -> _ { () } | ^ @@ -579,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 } | ^ @@ -588,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) -> _ { () } | ^ @@ -597,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 } | ^ @@ -606,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:204: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:206: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:208: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:211:14 + --> $DIR/typeck_type_placeholder_item.rs:204:14 | LL | const D: _ = 42; | ^ @@ -632,7 +590,7 @@ LL | const D: _ = 42; | not allowed in type signatures | help: replace `_` with the correct type: `i32` -error: aborting due to 71 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/ufcs/ufcs-partially-resolved.stderr b/src/test/ui/ufcs/ufcs-partially-resolved.stderr index 7177ca4908545..3950dc9877cd8 100644 --- a/src/test/ui/ufcs/ufcs-partially-resolved.stderr +++ b/src/test/ui/ufcs/ufcs-partially-resolved.stderr @@ -35,7 +35,7 @@ error[E0576]: cannot find method or associated constant `N` in trait `Tr` --> $DIR/ufcs-partially-resolved.rs:22:17 | LL | fn Y() {} - | --------- similarly named associated function `Y` defined here + | ------ similarly named associated function `Y` defined here ... LL | ::N; | ^ help: an associated function with a similar name exists: `Y` @@ -181,7 +181,7 @@ error[E0575]: expected method or associated constant, found associated type `Dr: --> $DIR/ufcs-partially-resolved.rs:53:5 | LL | fn Z() {} - | --------- similarly named associated function `Z` defined here + | ------ similarly named associated function `Z` defined here ... LL | ::X; | ^^^^^^^^^^^^- diff --git a/src/test/ui/ui-testing-optout.rs b/src/test/ui/ui-testing-optout.rs index 901263c5bf8d2..88e811583161c 100644 --- a/src/test/ui/ui-testing-optout.rs +++ b/src/test/ui/ui-testing-optout.rs @@ -3,9 +3,6 @@ // Line number < 10 type A = B; //~ ERROR -// http://rust-lang.org/COPYRIGHT. -// - // Line number >=10, <100 type C = D; //~ ERROR diff --git a/src/test/ui/ui-testing-optout.stderr b/src/test/ui/ui-testing-optout.stderr index ff5bf6238e20b..f562bb74c1173 100644 --- a/src/test/ui/ui-testing-optout.stderr +++ b/src/test/ui/ui-testing-optout.stderr @@ -8,21 +8,21 @@ error[E0412]: cannot find type `B` in this scope | similarly named type alias `A` defined here error[E0412]: cannot find type `D` in this scope - --> $DIR/ui-testing-optout.rs:10:10 - | -4 | type A = B; - | ----------- similarly named type alias `A` defined here + --> $DIR/ui-testing-optout.rs:7:10 + | +4 | type A = B; + | ----------- similarly named type alias `A` defined here ... -10 | type C = D; - | ^ help: a type alias with a similar name exists: `A` +7 | type C = D; + | ^ help: a type alias with a similar name exists: `A` error[E0412]: cannot find type `F` in this scope - --> $DIR/ui-testing-optout.rs:95:10 + --> $DIR/ui-testing-optout.rs:92:10 | 4 | type A = B; | ----------- similarly named type alias `A` defined here ... -95 | type E = F; +92 | type E = F; | ^ help: a type alias with a similar name exists: `A` error: aborting due to 3 previous errors 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-counter-not-moved.rs b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs index fb24df3c24e87..390386e57fa72 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs @@ -1,5 +1,4 @@ // run-pass -#![allow(unused_variables)] // Test that we mutate a counter on the stack only when we expect to. fn call(f: F) where F : FnOnce() { @@ -13,7 +12,7 @@ fn main() { call(|| { // Move `y`, but do not move `counter`, even though it is read // by value (note that it is also mutated). - for item in y { + for item in y { //~ WARN unused variable: `item` let v = counter; counter += v; } @@ -22,7 +21,8 @@ fn main() { call(move || { // this mutates a moved copy, and hence doesn't affect original - counter += 1; + counter += 1; //~ WARN value assigned to `counter` is never read + //~| WARN unused variable: `counter` }); assert_eq!(counter, 88); } diff --git a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr new file mode 100644 index 0000000000000..ba4b3dac6705e --- /dev/null +++ b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr @@ -0,0 +1,27 @@ +warning: unused variable: `item` + --> $DIR/unboxed-closures-counter-not-moved.rs:15:13 + | +LL | for item in y { + | ^^^^ help: if this is intentional, prefix it with an underscore: `_item` + | + = note: `#[warn(unused_variables)]` on by default + +warning: value assigned to `counter` is never read + --> $DIR/unboxed-closures-counter-not-moved.rs:24:9 + | +LL | counter += 1; + | ^^^^^^^ + | + = note: `#[warn(unused_assignments)]` on by default + = help: maybe it is overwritten before being read? + +warning: unused variable: `counter` + --> $DIR/unboxed-closures-counter-not-moved.rs:24:9 + | +LL | counter += 1; + | ^^^^^^^ + | + = help: did you mean to capture by reference instead? + +warning: 3 warnings emitted + 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-move-mutable.rs b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs index 9b519e63a95cc..e5b19db782231 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs @@ -13,11 +13,11 @@ fn set(x: &mut usize) { *x = 42; } fn main() { { let mut x = 0_usize; - move || x += 1; + move || x += 1; //~ WARN unused variable: `x` } { let mut x = 0_usize; - move || x += 1; + move || x += 1; //~ WARN unused variable: `x` } { let mut x = 0_usize; diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr new file mode 100644 index 0000000000000..4dfd1bb307574 --- /dev/null +++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr @@ -0,0 +1,19 @@ +warning: unused variable: `x` + --> $DIR/unboxed-closures-move-mutable.rs:16:17 + | +LL | move || x += 1; + | ^ + | + = note: `#[warn(unused_variables)]` on by default + = help: did you mean to capture by reference instead? + +warning: unused variable: `x` + --> $DIR/unboxed-closures-move-mutable.rs:20:17 + | +LL | move || x += 1; + | ^ + | + = help: did you mean to capture by reference instead? + +warning: 2 warnings emitted + diff --git a/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.rs b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.rs index a6e26614a6a50..e2082d4f78e70 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.rs @@ -1,23 +1,29 @@ // Tests that unsafe extern fn pointers do not implement any Fn traits. -use std::ops::{Fn,FnMut,FnOnce}; +use std::ops::{Fn, FnMut, FnOnce}; -unsafe fn square(x: &isize) -> isize { (*x) * (*x) } +unsafe fn square(x: &isize) -> isize { + (*x) * (*x) +} -fn call_itisize>(_: &F, _: isize) -> isize { 0 } -fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } -fn call_it_onceisize>(_: F, _: isize) -> isize { 0 } +fn call_it isize>(_: &F, _: isize) -> isize { + 0 +} +fn call_it_mut isize>(_: &mut F, _: isize) -> isize { + 0 +} +fn call_it_once isize>(_: F, _: isize) -> isize { + 0 +} fn a() { let x = call_it(&square, 22); //~^ ERROR E0277 - //~| ERROR expected } fn b() { let y = call_it_mut(&mut square, 22); //~^ ERROR E0277 - //~| ERROR expected } fn c() { @@ -25,4 +31,4 @@ fn c() { //~^ ERROR E0277 } -fn main() { } +fn main() {} 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..b06f745e7c1f1 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr @@ -1,30 +1,19 @@ error[E0277]: expected a `std::ops::Fn<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` - --> $DIR/unboxed-closures-unsafe-extern-fn.rs:12:21 + --> $DIR/unboxed-closures-unsafe-extern-fn.rs:20:21 | -LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----------------- required by this bound in `call_it` +LL | fn call_it isize>(_: &F, _: isize) -> isize { + | ------------------- 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}` | = help: the trait `for<'r> std::ops::Fn<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` -error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` - --> $DIR/unboxed-closures-unsafe-extern-fn.rs:12:21 - | -LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----- 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}` - | - = help: the trait `std::ops::FnOnce<(&isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` - error[E0277]: expected a `std::ops::FnMut<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` - --> $DIR/unboxed-closures-unsafe-extern-fn.rs:18:25 + --> $DIR/unboxed-closures-unsafe-extern-fn.rs:25:25 | -LL | fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } - | ----------- -------------------- required by this bound in `call_it_mut` +LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { + | ---------------------- 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}` @@ -32,27 +21,16 @@ LL | let y = call_it_mut(&mut square, 22); = help: the trait `for<'r> std::ops::FnMut<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` - --> $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` -... -LL | let y = call_it_mut(&mut square, 22); - | ^^^^^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` - | - = help: the trait `std::ops::FnOnce<(&isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` - -error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}` - --> $DIR/unboxed-closures-unsafe-extern-fn.rs:24:26 + --> $DIR/unboxed-closures-unsafe-extern-fn.rs:30:26 | -LL | fn call_it_onceisize>(_: F, _: isize) -> isize { 0 } - | ------------ ----- required by this bound in `call_it_once` +LL | fn call_it_once isize>(_: F, _: isize) -> isize { + | ----------------------- 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}` | - = help: the trait `std::ops::FnOnce<(&isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` + = help: the trait `for<'r> std::ops::FnOnce<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}` -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.rs b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.rs index dd3b1afc39f31..dd76c597d28ad 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.rs @@ -1,23 +1,29 @@ // Tests that unsafe extern fn pointers do not implement any Fn traits. -use std::ops::{Fn,FnMut,FnOnce}; +use std::ops::{Fn, FnMut, FnOnce}; -extern "C" fn square(x: &isize) -> isize { (*x) * (*x) } +extern "C" fn square(x: &isize) -> isize { + (*x) * (*x) +} -fn call_itisize>(_: &F, _: isize) -> isize { 0 } -fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } -fn call_it_onceisize>(_: F, _: isize) -> isize { 0 } +fn call_it isize>(_: &F, _: isize) -> isize { + 0 +} +fn call_it_mut isize>(_: &mut F, _: isize) -> isize { + 0 +} +fn call_it_once isize>(_: F, _: isize) -> isize { + 0 +} fn a() { let x = call_it(&square, 22); //~^ ERROR E0277 - //~| ERROR expected } fn b() { let y = call_it_mut(&mut square, 22); //~^ ERROR E0277 - //~| ERROR expected } fn c() { @@ -25,4 +31,4 @@ fn c() { //~^ ERROR E0277 } -fn main() { } +fn main() {} 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..8f6945cda806c 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr @@ -1,30 +1,19 @@ error[E0277]: expected a `std::ops::Fn<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-abi.rs:12:21 + --> $DIR/unboxed-closures-wrong-abi.rs:20:21 | -LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----------------- required by this bound in `call_it` +LL | fn call_it isize>(_: &F, _: isize) -> isize { + | ------------------- 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}` | = help: the trait `for<'r> std::ops::Fn<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` -error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-abi.rs:12:21 - | -LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----- 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}` - | - = help: the trait `std::ops::FnOnce<(&isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` - error[E0277]: expected a `std::ops::FnMut<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-abi.rs:18:25 + --> $DIR/unboxed-closures-wrong-abi.rs:25:25 | -LL | fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } - | ----------- -------------------- required by this bound in `call_it_mut` +LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { + | ---------------------- 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}` @@ -32,27 +21,16 @@ LL | let y = call_it_mut(&mut square, 22); = help: the trait `for<'r> std::ops::FnMut<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` - --> $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` -... -LL | let y = call_it_mut(&mut square, 22); - | ^^^^^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` - | - = help: the trait `std::ops::FnOnce<(&isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` - -error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-abi.rs:24:26 + --> $DIR/unboxed-closures-wrong-abi.rs:30:26 | -LL | fn call_it_onceisize>(_: F, _: isize) -> isize { 0 } - | ------------ ----- required by this bound in `call_it_once` +LL | fn call_it_once isize>(_: F, _: isize) -> isize { + | ----------------------- 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}` | - = help: the trait `std::ops::FnOnce<(&isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` + = help: the trait `for<'r> std::ops::FnOnce<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}` -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.rs b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.rs index c689d79266187..02e8b7b47ae19 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.rs +++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.rs @@ -1,24 +1,30 @@ // Tests that unsafe extern fn pointers do not implement any Fn traits. -use std::ops::{Fn,FnMut,FnOnce}; +use std::ops::{Fn, FnMut, FnOnce}; -unsafe fn square(x: isize) -> isize { x * x } +unsafe fn square(x: isize) -> isize { + x * x +} // note: argument type here is `isize`, not `&isize` -fn call_itisize>(_: &F, _: isize) -> isize { 0 } -fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } -fn call_it_onceisize>(_: F, _: isize) -> isize { 0 } +fn call_it isize>(_: &F, _: isize) -> isize { + 0 +} +fn call_it_mut isize>(_: &mut F, _: isize) -> isize { + 0 +} +fn call_it_once isize>(_: F, _: isize) -> isize { + 0 +} fn a() { let x = call_it(&square, 22); //~^ ERROR E0277 - //~| ERROR expected } fn b() { let y = call_it_mut(&mut square, 22); //~^ ERROR E0277 - //~| ERROR expected } fn c() { @@ -26,4 +32,4 @@ fn c() { //~^ ERROR E0277 } -fn main() { } +fn main() {} 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..93a645b485ef0 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr @@ -1,30 +1,19 @@ error[E0277]: expected a `std::ops::Fn<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:13:21 + --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:21:21 | -LL | fn call_itisize>(_: &F, _: isize) -> isize { 0 } - | ------- ----------------- required by this bound in `call_it` +LL | fn call_it isize>(_: &F, _: isize) -> isize { + | ------------------- 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}` | = help: the trait `for<'r> std::ops::Fn<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` -error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` - --> $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` -... -LL | let x = call_it(&square, 22); - | ^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` - | - = help: the trait `std::ops::FnOnce<(&isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` - error[E0277]: expected a `std::ops::FnMut<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:19:25 + --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:26:25 | -LL | fn call_it_mutisize>(_: &mut F, _: isize) -> isize { 0 } - | ----------- -------------------- required by this bound in `call_it_mut` +LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { + | ---------------------- 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}` @@ -32,27 +21,16 @@ LL | let y = call_it_mut(&mut square, 22); = help: the trait `for<'r> std::ops::FnMut<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` - --> $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` -... -LL | let y = call_it_mut(&mut square, 22); - | ^^^^^^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` - | - = help: the trait `std::ops::FnOnce<(&isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` - -error[E0277]: expected a `std::ops::FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}` - --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:25:26 + --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:31:26 | -LL | fn call_it_onceisize>(_: F, _: isize) -> isize { 0 } - | ------------ ----- required by this bound in `call_it_once` +LL | fn call_it_once isize>(_: F, _: isize) -> isize { + | ----------------------- 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}` | - = help: the trait `std::ops::FnOnce<(&isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` + = help: the trait `for<'r> std::ops::FnOnce<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}` -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. 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/dyn-trait-underscore.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr index e6029e0d4623a..dda5de431d309 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.stderr @@ -1,31 +1,17 @@ -error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements +error[E0759]: cannot infer an appropriate lifetime --> $DIR/dyn-trait-underscore.rs:8:20 | +LL | fn a(items: &[T]) -> Box> { + | ---- this data with an anonymous lifetime `'_`... +LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` LL | Box::new(items.iter()) - | ^^^^ + | ---------------^^^^--- ...is captured and required to live as long as `'static` here | -note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the function body at 6:1... - --> $DIR/dyn-trait-underscore.rs:6:1 +help: to declare that the trait object captures data from argument `items`, you can add an explicit `'_` lifetime bound | -LL | / fn a(items: &[T]) -> Box> { -LL | | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` -LL | | Box::new(items.iter()) -LL | | } - | |_^ -note: ...so that reference does not outlive borrowed content - --> $DIR/dyn-trait-underscore.rs:8:14 - | -LL | Box::new(items.iter()) - | ^^^^^ - = note: but, the lifetime must be valid for the static lifetime... -note: ...so that the expression is assignable - --> $DIR/dyn-trait-underscore.rs:8:5 - | -LL | Box::new(items.iter()) - | ^^^^^^^^^^^^^^^^^^^^^^ - = note: expected `std::boxed::Box<(dyn std::iter::Iterator + 'static)>` - found `std::boxed::Box>` +LL | fn a(items: &[T]) -> Box + '_> { + | ^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. +For more information about this error, try `rustc --explain E0759`. diff --git a/src/test/ui/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-enum-cast.stderr b/src/test/ui/uninhabited/uninhabited-enum-cast.stderr index a39af7832f8c9..a9f10dfec994a 100644 --- a/src/test/ui/uninhabited/uninhabited-enum-cast.stderr +++ b/src/test/ui/uninhabited/uninhabited-enum-cast.stderr @@ -2,9 +2,7 @@ error[E0605]: non-primitive cast: `E` as `isize` --> $DIR/uninhabited-enum-cast.rs:4:20 | LL | println!("{}", (e as isize).to_string()); - | ^^^^^^^^^^^^ - | - = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait + | ^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error: aborting due to previous error 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.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index a667e1fe2da3a..c7bf6710d06bf 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -3,8 +3,14 @@ error[E0004]: non-exhaustive patterns: `Err(_)` not covered | LL | let _ = match x { | ^ pattern `Err(_)` not covered + | + ::: $SRC_DIR/libcore/result.rs:LL:COL + | +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:15:19 @@ -16,6 +22,7 @@ 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:18:19 @@ -24,6 +31,7 @@ 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:21:19 @@ -32,6 +40,7 @@ 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:24:19 @@ -40,23 +49,36 @@ 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:32:19 | LL | let _ = match x { | ^ pattern `Err(_)` not covered + | + ::: $SRC_DIR/libcore/result.rs:LL:COL + | +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:37:9 | LL | let Ok(x) = x; | ^^^^^ pattern `Err(_)` not covered + | + ::: $SRC_DIR/libcore/result.rs:LL:COL + | +LL | Err(#[stable(feature = "rust1", since = "1.0.0")] E), + | --- not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `std::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.stderr b/src/test/ui/union/union-derive-clone.stderr index 01c8e8471aac2..b536325810a3b 100644 --- a/src/test/ui/union/union-derive-clone.stderr +++ b/src/test/ui/union/union-derive-clone.stderr @@ -3,8 +3,12 @@ error[E0277]: the trait bound `U1: std::marker::Copy` is not satisfied | 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 @@ -21,6 +25,14 @@ LL | struct CloneNoCopy; ... LL | let w = u.clone(); | ^^^^^ method not found in `U5` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `std::sync::Arc>` here + | the method is available for `std::rc::Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: `CloneNoCopy: std::marker::Copy` 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/union/union-nonrepresentable.stderr b/src/test/ui/union/union-nonrepresentable.stderr index 746c1033ea348..c54d04de12c50 100644 --- a/src/test/ui/union/union-nonrepresentable.stderr +++ b/src/test/ui/union/union-nonrepresentable.stderr @@ -5,9 +5,12 @@ LL | union U { | ^^^^^^^ recursive type has infinite size LL | a: u8, LL | b: U, - | ---- recursive without indirection + | - recursive without indirection | - = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `U` representable +help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `U` representable + | +LL | b: Box, + | ^^^^ ^ error: aborting due to previous error diff --git a/src/test/ui/union/union-suggest-field.rs b/src/test/ui/union/union-suggest-field.rs index d84a22cee5ab2..71b93e873c220 100644 --- a/src/test/ui/union/union-suggest-field.rs +++ b/src/test/ui/union/union-suggest-field.rs @@ -17,5 +17,5 @@ fn main() { let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U` //~| HELP use parentheses to call the method - //~| SUGGESTION calculate() + //~| SUGGESTION () } diff --git a/src/test/ui/union/union-suggest-field.stderr b/src/test/ui/union/union-suggest-field.stderr index 5050e4a986499..461db1712064f 100644 --- a/src/test/ui/union/union-suggest-field.stderr +++ b/src/test/ui/union/union-suggest-field.stderr @@ -14,7 +14,12 @@ error[E0615]: attempted to take value of method `calculate` on type `U` --> $DIR/union-suggest-field.rs:18:15 | LL | let y = u.calculate; - | ^^^^^^^^^ help: use parentheses to call the method: `calculate()` + | ^^^^^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | let y = u.calculate(); + | ^^ error: aborting due to 3 previous errors 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 fb78095224b90..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 { | --------- @@ -14,6 +14,14 @@ LL | let _z = y.clone(); | LL | pub struct Box(Unique); | ------------------------------------- doesn't satisfy `std::boxed::Box: std::clone::Clone` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `std::sync::Arc>` here + | the method is available for `std::rc::Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: `dyn Foo: std::marker::Sized` 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 ea6575d1d85dc..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` @@ -11,6 +11,14 @@ LL | let _j = i.clone(); | LL | pub struct Box(Unique); | ------------------------------------- doesn't satisfy `std::boxed::Box: std::clone::Clone` + | + ::: $SRC_DIR/libcore/clone.rs:LL:COL + | +LL | fn clone(&self) -> Self; + | ----- + | | + | the method is available for `std::sync::Arc>` here + | the method is available for `std::rc::Rc>` here | = note: the method `clone` exists but the following trait bounds were not satisfied: `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/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs new file mode 100644 index 0000000000000..540612a7dce05 --- /dev/null +++ b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs @@ -0,0 +1,67 @@ +#![feature(unsafe_block_in_unsafe_fn)] + +#[repr(packed)] +pub struct Packed { + data: &'static u32, +} + +const PACKED: Packed = Packed { data: &0 }; + +#[allow(safe_packed_borrows)] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn allow_allow() { + &PACKED.data; // allowed +} + +#[allow(safe_packed_borrows)] +#[warn(unsafe_op_in_unsafe_fn)] +unsafe fn allow_warn() { + &PACKED.data; // allowed +} + +#[allow(safe_packed_borrows)] +#[deny(unsafe_op_in_unsafe_fn)] +unsafe fn allow_deny() { + &PACKED.data; // allowed +} + +#[warn(safe_packed_borrows)] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn warn_allow() { + &PACKED.data; // allowed +} + +#[warn(safe_packed_borrows)] +#[warn(unsafe_op_in_unsafe_fn)] +unsafe fn warn_warn() { + &PACKED.data; //~ WARN + //~| WARNING this was previously accepted by the compiler but is being phased out +} + +#[warn(safe_packed_borrows)] +#[deny(unsafe_op_in_unsafe_fn)] +unsafe fn warn_deny() { + &PACKED.data; //~ WARN + //~| WARNING this was previously accepted by the compiler but is being phased out +} + +#[deny(safe_packed_borrows)] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn deny_allow() { + &PACKED.data; // allowed +} + +#[deny(safe_packed_borrows)] +#[warn(unsafe_op_in_unsafe_fn)] +unsafe fn deny_warn() { + &PACKED.data; //~ WARN +} + +#[deny(safe_packed_borrows)] +#[deny(unsafe_op_in_unsafe_fn)] +unsafe fn deny_deny() { + &PACKED.data; //~ ERROR + //~| WARNING this was previously accepted by the compiler but is being phased out +} + +fn main() {} diff --git a/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr new file mode 100644 index 0000000000000..fda15159643b6 --- /dev/null +++ b/src/test/ui/unsafe/rfc-2585-safe_packed_borrows-in-unsafe-fn.stderr @@ -0,0 +1,60 @@ +warning: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:37:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:34:8 + | +LL | #[warn(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = 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 + +warning: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:44:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:41:8 + | +LL | #[warn(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = 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 + +warning: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:57:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:55:8 + | +LL | #[warn(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior + +error: borrow of packed field is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:63:5 + | +LL | &PACKED.data; + | ^^^^^^^^^^^^ borrow of packed field + | +note: the lint level is defined here + --> $DIR/rfc-2585-safe_packed_borrows-in-unsafe-fn.rs:60:8 + | +LL | #[deny(safe_packed_borrows)] + | ^^^^^^^^^^^^^^^^^^^ + = 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: aborting due to previous error; 3 warnings emitted + diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs new file mode 100644 index 0000000000000..1e57b03ced48b --- /dev/null +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.rs @@ -0,0 +1,76 @@ +#![feature(unsafe_block_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] +#![deny(unused_unsafe)] + +unsafe fn unsf() {} +const PTR: *const () = std::ptr::null(); +static mut VOID: () = (); + +unsafe fn deny_level() { + unsf(); + //~^ ERROR call to unsafe function is unsafe and requires unsafe block + *PTR; + //~^ ERROR dereference of raw pointer is unsafe and requires unsafe block + VOID = (); + //~^ ERROR use of mutable static is unsafe and requires unsafe block +} + +// Check that `unsafe_op_in_unsafe_fn` works starting from the `warn` level. +#[warn(unsafe_op_in_unsafe_fn)] +#[deny(warnings)] +unsafe fn warning_level() { + unsf(); + //~^ ERROR call to unsafe function is unsafe and requires unsafe block + *PTR; + //~^ ERROR dereference of raw pointer is unsafe and requires unsafe block + VOID = (); + //~^ ERROR use of mutable static is unsafe and requires unsafe block +} + +unsafe fn explicit_block() { + // no error + unsafe { + unsf(); + *PTR; + VOID = (); + } +} + +unsafe fn two_explicit_blocks() { + unsafe { unsafe { unsf() } } + //~^ ERROR unnecessary `unsafe` block +} + +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn allow_level() { + // lint allowed -> no error + unsf(); + *PTR; + VOID = (); + + unsafe { unsf() } + //~^ ERROR unnecessary `unsafe` block +} + +unsafe fn nested_allow_level() { + #[allow(unsafe_op_in_unsafe_fn)] + { + // lint allowed -> no error + unsf(); + *PTR; + VOID = (); + + unsafe { unsf() } + //~^ ERROR unnecessary `unsafe` block + } +} + +fn main() { + unsf(); + //~^ ERROR call to unsafe function is unsafe and requires unsafe block + #[allow(unsafe_op_in_unsafe_fn)] + { + unsf(); + //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block + } +} diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr new file mode 100644 index 0000000000000..cc595df12cc44 --- /dev/null +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.stderr @@ -0,0 +1,104 @@ +error: call to unsafe function is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:10:5 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | +note: the lint level is defined here + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:2:9 + | +LL | #![deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:12:5 + | +LL | *PTR; + | ^^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: use of mutable static is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:14:5 + | +LL | VOID = (); + | ^^^^^^^^^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error: call to unsafe function is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:22:5 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | +note: the lint level is defined here + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:20:8 + | +LL | #[deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(unsafe_op_in_unsafe_fn)]` implied by `#[deny(warnings)]` + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:24:5 + | +LL | *PTR; + | ^^^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: use of mutable static is unsafe and requires unsafe block (error E0133) + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:26:5 + | +LL | VOID = (); + | ^^^^^^^^^ use of mutable static + | + = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior + +error: unnecessary `unsafe` block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:40:14 + | +LL | unsafe { unsafe { unsf() } } + | ------ ^^^^^^ unnecessary `unsafe` block + | | + | because it's nested under this `unsafe` block + | +note: the lint level is defined here + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:3:9 + | +LL | #![deny(unused_unsafe)] + | ^^^^^^^^^^^^^ + +error: unnecessary `unsafe` block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:51:5 + | +LL | unsafe { unsf() } + | ^^^^^^ unnecessary `unsafe` block + +error: unnecessary `unsafe` block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:63:9 + | +LL | unsafe { unsf() } + | ^^^^^^ unnecessary `unsafe` block + +error[E0133]: call to unsafe function is unsafe and requires unsafe block + --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:69:5 + | +LL | unsf(); + | ^^^^^^ 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/rfc-2585-unsafe_op_in_unsafe_fn.rs:73:9 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.rs b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.rs index 64bdca5245719..91264e790c8db 100644 --- a/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.rs +++ b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.rs @@ -1,7 +1,3 @@ -// http://rust-lang.org/COPYRIGHT. -// - - fn f(p: *mut u8) { *p = 0; //~ ERROR dereference of raw pointer is unsafe return; diff --git a/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.stderr b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.stderr index 8f621d6ed11ca..28db83db92ac8 100644 --- a/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.stderr +++ b/src/test/ui/unsafe/unsafe-fn-assign-deref-ptr.stderr @@ -1,5 +1,5 @@ error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block - --> $DIR/unsafe-fn-assign-deref-ptr.rs:6:5 + --> $DIR/unsafe-fn-assign-deref-ptr.rs:2:5 | LL | *p = 0; | ^^^^^^ dereference of raw pointer diff --git a/src/test/ui/unsafe/unsafe-unstable-const-fn.rs b/src/test/ui/unsafe/unsafe-unstable-const-fn.rs new file mode 100644 index 0000000000000..963d892931a75 --- /dev/null +++ b/src/test/ui/unsafe/unsafe-unstable-const-fn.rs @@ -0,0 +1,13 @@ +#![stable(feature = "foo", since = "1.33.0")] +#![feature(staged_api)] +#![feature(const_raw_ptr_deref)] +#![feature(const_fn)] + +#[stable(feature = "foo", since = "1.33.0")] +#[rustc_const_unstable(feature = "const_foo", issue = "none")] +const fn unstable(a: *const i32, b: i32) -> bool { + *a == b + //~^ dereference of raw pointer is unsafe +} + +fn main() {} diff --git a/src/test/ui/unsafe/unsafe-unstable-const-fn.stderr b/src/test/ui/unsafe/unsafe-unstable-const-fn.stderr new file mode 100644 index 0000000000000..4642a7a5fc9f8 --- /dev/null +++ b/src/test/ui/unsafe/unsafe-unstable-const-fn.stderr @@ -0,0 +1,11 @@ +error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block + --> $DIR/unsafe-unstable-const-fn.rs:9:5 + | +LL | *a == b + | ^^ dereference of raw pointer + | + = note: raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0133`. 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..1908aee25ea7b 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 @@ -11,6 +11,13 @@ LL | fn foo2() { not_sized::>() } | = help: the trait `std::marker::Sized` is not implemented for `T` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `U` if it were used through indirection like `&U` or `Box` + --> $DIR/unsized-enum.rs:4:10 + | +LL | enum Foo { FooSome(U), FooNone } + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `U: ?Sized`... error: aborting due to previous error 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..e0f077d66f99c 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 @@ -11,6 +11,13 @@ LL | impl S5 { | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box` + --> $DIR/unsized-inherent-impl-self-type.rs:5:11 + | +LL | struct S5(Y); + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `Y: ?Sized`... error: aborting due to previous error diff --git a/src/test/ui/unsized/unsized-struct.stderr b/src/test/ui/unsized/unsized-struct.stderr index 653fb5c1ae8dc..d92d1d9113e5c 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 @@ -11,12 +11,19 @@ LL | fn foo2() { not_sized::>() } | = help: the trait `std::marker::Sized` is not implemented for `T` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/unsized-struct.rs:4:12 + | +LL | struct Foo { data: T } + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `T: ?Sized`... error[E0277]: the size for values of type `T` cannot be known at compilation time --> $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..73c5439da53b6 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 @@ -11,6 +11,13 @@ LL | impl T3 for S5 { | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit +help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box` + --> $DIR/unsized-trait-impl-self-type.rs:8:11 + | +LL | struct S5(Y); + | ^ - ...if indirection was used here: `Box` + | | + | this could be changed to `Y: ?Sized`... error: aborting due to previous error 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..e423a9bdeab6f 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 | | @@ -8,6 +11,10 @@ LL | impl T2 for S4 { | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | trait T2 { + | ^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/unsized3.stderr b/src/test/ui/unsized3.stderr index e97d00fc4741d..e0a0389dc4690 100644 --- a/src/test/ui/unsized3.stderr +++ b/src/test/ui/unsized3.stderr @@ -7,12 +7,14 @@ LL | f2::(x); | ^ doesn't have a size known at compile-time ... LL | fn f2(x: &X) { - | -- -- help: consider relaxing the implicit `Sized` restriction: `: ?Sized` - | | - | 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 +help: consider relaxing the implicit `Sized` restriction + | +LL | fn f2(x: &X) { + | ^^^^^^^^ error[E0277]: the size for values of type `X` cannot be known at compilation time --> $DIR/unsized3.rs:18:13 @@ -23,18 +25,20 @@ LL | f4::(x); | ^ doesn't have a size known at compile-time ... LL | fn f4(x: &X) { - | -- - - help: consider relaxing the implicit `Sized` restriction: `+ ?Sized` - | | - | 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) { + | ^^^^^^^^ 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` @@ -44,6 +48,10 @@ LL | f5(x1); = help: within `S`, the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit = note: required because it appears within the type `S` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn f5(x: &Y) {} + | ^^^^^^^^ error[E0277]: the size for values of type `X` cannot be known at compilation time --> $DIR/unsized3.rs:40:8 @@ -76,7 +84,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` @@ -87,6 +95,10 @@ LL | f5(&(32, *x1)); = note: to learn more, visit = note: required because it appears within the type `S` = note: required because it appears within the type `({integer}, S)` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn f5(x: &Y) {} + | ^^^^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/unsized7.stderr b/src/test/ui/unsized7.stderr index 0f71c5f6f8fe6..e616a5cf0f9c2 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 | | @@ -8,6 +11,10 @@ LL | impl T1 for S3 { | = help: the trait `std::marker::Sized` is not implemented for `X` = note: to learn more, visit +help: consider relaxing the implicit `Sized` restriction + | +LL | trait T1 { + | ^^^^^^^^ error: aborting due to previous error 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/unterminated-comment.rs b/src/test/ui/unterminated-comment.rs new file mode 100644 index 0000000000000..1cfdfb1fb4575 --- /dev/null +++ b/src/test/ui/unterminated-comment.rs @@ -0,0 +1 @@ +/* //~ ERROR E0758 diff --git a/src/test/ui/unterminated-comment.stderr b/src/test/ui/unterminated-comment.stderr new file mode 100644 index 0000000000000..c513fafeeb35c --- /dev/null +++ b/src/test/ui/unterminated-comment.stderr @@ -0,0 +1,9 @@ +error[E0758]: unterminated block comment + --> $DIR/unterminated-comment.rs:1:1 + | +LL | /* + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0758`. diff --git a/src/test/ui/unterminated-doc-comment.rs b/src/test/ui/unterminated-doc-comment.rs new file mode 100644 index 0000000000000..82546fe73da4f --- /dev/null +++ b/src/test/ui/unterminated-doc-comment.rs @@ -0,0 +1 @@ +/*! //~ ERROR E0758 diff --git a/src/test/ui/unterminated-doc-comment.stderr b/src/test/ui/unterminated-doc-comment.stderr new file mode 100644 index 0000000000000..2d5e537973ea8 --- /dev/null +++ b/src/test/ui/unterminated-doc-comment.stderr @@ -0,0 +1,9 @@ +error[E0758]: unterminated block doc-comment + --> $DIR/unterminated-doc-comment.rs:1:1 + | +LL | /*! + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0758`. diff --git a/src/test/ui/unused-crate-deps/auxiliary/bar.rs b/src/test/ui/unused-crate-deps/auxiliary/bar.rs new file mode 100644 index 0000000000000..1d3824e7a44f6 --- /dev/null +++ b/src/test/ui/unused-crate-deps/auxiliary/bar.rs @@ -0,0 +1 @@ +pub const BAR: &str = "bar"; diff --git a/src/test/ui/unused-crate-deps/auxiliary/foo.rs b/src/test/ui/unused-crate-deps/auxiliary/foo.rs new file mode 100644 index 0000000000000..0ef03eb9edf0f --- /dev/null +++ b/src/test/ui/unused-crate-deps/auxiliary/foo.rs @@ -0,0 +1,5 @@ +// edition:2018 +// aux-crate:bar=bar.rs + +pub const FOO: &str = "foo"; +pub use bar::BAR; diff --git a/src/test/ui/unused-crate-deps/ignore-pathless-extern.rs b/src/test/ui/unused-crate-deps/ignore-pathless-extern.rs new file mode 100644 index 0000000000000..8c273cb534d74 --- /dev/null +++ b/src/test/ui/unused-crate-deps/ignore-pathless-extern.rs @@ -0,0 +1,12 @@ +// Pathless --extern references don't count + +// edition:2018 +// check-pass +// aux-crate:bar=bar.rs +// compile-flags:--extern proc_macro + +#![warn(unused_crate_dependencies)] + +use bar as _; + +fn main() {} diff --git a/src/test/ui/unused-crate-deps/libfib.rs b/src/test/ui/unused-crate-deps/libfib.rs new file mode 100644 index 0000000000000..c1545dca99f57 --- /dev/null +++ b/src/test/ui/unused-crate-deps/libfib.rs @@ -0,0 +1,21 @@ +// Test warnings for a library crate + +// check-pass +// aux-crate:bar=bar.rs +// compile-flags:--crate-type lib -Wunused-crate-dependencies + +pub fn fib(n: u32) -> Vec { +//~^ WARNING external crate `bar` unused in +let mut prev = 0; + let mut cur = 1; + let mut v = vec![]; + + for _ in 0..n { + v.push(prev); + let n = prev + cur; + prev = cur; + cur = n; + } + + v +} diff --git a/src/test/ui/unused-crate-deps/libfib.stderr b/src/test/ui/unused-crate-deps/libfib.stderr new file mode 100644 index 0000000000000..15833126bd620 --- /dev/null +++ b/src/test/ui/unused-crate-deps/libfib.stderr @@ -0,0 +1,10 @@ +warning: external crate `bar` unused in `libfib`: remove the dependency or add `use bar as _;` + --> $DIR/libfib.rs:7:1 + | +LL | pub fn fib(n: u32) -> Vec { + | ^ + | + = note: requested on the command line with `-W unused-crate-dependencies` + +warning: 1 warning emitted + diff --git a/src/test/ui/unused-crate-deps/lint-group.rs b/src/test/ui/unused-crate-deps/lint-group.rs new file mode 100644 index 0000000000000..e21ffb5dec2de --- /dev/null +++ b/src/test/ui/unused-crate-deps/lint-group.rs @@ -0,0 +1,9 @@ +// `unused_crate_dependencies` is not currently in the `unused` group +// due to false positives from Cargo. + +// check-pass +// aux-crate:bar=bar.rs + +#![deny(unused)] + +fn main() {} diff --git a/src/test/ui/unused-crate-deps/suppress.rs b/src/test/ui/unused-crate-deps/suppress.rs new file mode 100644 index 0000000000000..8904d04bc14f7 --- /dev/null +++ b/src/test/ui/unused-crate-deps/suppress.rs @@ -0,0 +1,11 @@ +// Suppress by using crate + +// edition:2018 +// check-pass +// aux-crate:bar=bar.rs + +#![warn(unused_crate_dependencies)] + +use bar as _; + +fn main() {} diff --git a/src/test/ui/unused-crate-deps/test-use-ok.rs b/src/test/ui/unused-crate-deps/test-use-ok.rs new file mode 100644 index 0000000000000..66d6440c9cb9f --- /dev/null +++ b/src/test/ui/unused-crate-deps/test-use-ok.rs @@ -0,0 +1,15 @@ +// Test-only use OK + +// edition:2018 +// check-pass +// aux-crate:bar=bar.rs +// compile-flags:--test + +#![deny(unused_crate_dependencies)] + +fn main() {} + +#[test] +fn test_bar() { + assert_eq!(bar::BAR, "bar"); +} diff --git a/src/test/ui/unused-crate-deps/unused-aliases.rs b/src/test/ui/unused-crate-deps/unused-aliases.rs new file mode 100644 index 0000000000000..1b7cb9b970e49 --- /dev/null +++ b/src/test/ui/unused-crate-deps/unused-aliases.rs @@ -0,0 +1,13 @@ +// Warn about unused aliased for the crate + +// edition:2018 +// check-pass +// aux-crate:bar=bar.rs +// aux-crate:barbar=bar.rs + +#![warn(unused_crate_dependencies)] +//~^ WARNING external crate `barbar` unused in + +use bar as _; + +fn main() {} diff --git a/src/test/ui/unused-crate-deps/unused-aliases.stderr b/src/test/ui/unused-crate-deps/unused-aliases.stderr new file mode 100644 index 0000000000000..c8c6c4507b0c5 --- /dev/null +++ b/src/test/ui/unused-crate-deps/unused-aliases.stderr @@ -0,0 +1,14 @@ +warning: external crate `barbar` unused in `unused_aliases`: remove the dependency or add `use barbar as _;` + --> $DIR/unused-aliases.rs:8:1 + | +LL | #![warn(unused_crate_dependencies)] + | ^ + | +note: the lint level is defined here + --> $DIR/unused-aliases.rs:8:9 + | +LL | #![warn(unused_crate_dependencies)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/src/test/ui/unused-crate-deps/use_extern_crate_2015.rs b/src/test/ui/unused-crate-deps/use_extern_crate_2015.rs new file mode 100644 index 0000000000000..f15c87fa0b249 --- /dev/null +++ b/src/test/ui/unused-crate-deps/use_extern_crate_2015.rs @@ -0,0 +1,13 @@ +// Suppress by using crate + +// edition:2015 +// check-pass +// aux-crate:bar=bar.rs + +#![warn(unused_crate_dependencies)] + +extern crate bar; + +fn main() { + println!("bar {}", bar::BAR); +} diff --git a/src/test/ui/unused-crate-deps/warn-attr.rs b/src/test/ui/unused-crate-deps/warn-attr.rs new file mode 100644 index 0000000000000..1acb307ab21b3 --- /dev/null +++ b/src/test/ui/unused-crate-deps/warn-attr.rs @@ -0,0 +1,10 @@ +// Check for unused crate dep, no path + +// edition:2018 +// check-pass +// aux-crate:bar=bar.rs + +#![warn(unused_crate_dependencies)] +//~^ WARNING external crate `bar` unused in + +fn main() {} diff --git a/src/test/ui/unused-crate-deps/warn-attr.stderr b/src/test/ui/unused-crate-deps/warn-attr.stderr new file mode 100644 index 0000000000000..0d38315704b11 --- /dev/null +++ b/src/test/ui/unused-crate-deps/warn-attr.stderr @@ -0,0 +1,14 @@ +warning: external crate `bar` unused in `warn_attr`: remove the dependency or add `use bar as _;` + --> $DIR/warn-attr.rs:7:1 + | +LL | #![warn(unused_crate_dependencies)] + | ^ + | +note: the lint level is defined here + --> $DIR/warn-attr.rs:7:9 + | +LL | #![warn(unused_crate_dependencies)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/src/test/ui/unused-crate-deps/warn-cmdline-static.rs b/src/test/ui/unused-crate-deps/warn-cmdline-static.rs new file mode 100644 index 0000000000000..c609529a6c6fb --- /dev/null +++ b/src/test/ui/unused-crate-deps/warn-cmdline-static.rs @@ -0,0 +1,10 @@ +// Check for unused crate dep, no path + +// edition:2018 +// check-pass +// compile-flags: -Wunused-crate-dependencies +// aux-crate:bar=bar.rs +// no-prefer-dynamic + +fn main() {} +//~^ WARNING external crate `bar` unused in diff --git a/src/test/ui/unused-crate-deps/warn-cmdline-static.stderr b/src/test/ui/unused-crate-deps/warn-cmdline-static.stderr new file mode 100644 index 0000000000000..65956461d6439 --- /dev/null +++ b/src/test/ui/unused-crate-deps/warn-cmdline-static.stderr @@ -0,0 +1,10 @@ +warning: external crate `bar` unused in `warn_cmdline_static`: remove the dependency or add `use bar as _;` + --> $DIR/warn-cmdline-static.rs:9:1 + | +LL | fn main() {} + | ^ + | + = note: requested on the command line with `-W unused-crate-dependencies` + +warning: 1 warning emitted + diff --git a/src/test/ui/unused-crate-deps/warn-cmdline.rs b/src/test/ui/unused-crate-deps/warn-cmdline.rs new file mode 100644 index 0000000000000..3bae61c3ea2cc --- /dev/null +++ b/src/test/ui/unused-crate-deps/warn-cmdline.rs @@ -0,0 +1,9 @@ +// Check for unused crate dep, no path + +// edition:2018 +// check-pass +// compile-flags: -Wunused-crate-dependencies +// aux-crate:bar=bar.rs + +fn main() {} +//~^ WARNING external crate `bar` unused in diff --git a/src/test/ui/unused-crate-deps/warn-cmdline.stderr b/src/test/ui/unused-crate-deps/warn-cmdline.stderr new file mode 100644 index 0000000000000..ea675ba9a1eb1 --- /dev/null +++ b/src/test/ui/unused-crate-deps/warn-cmdline.stderr @@ -0,0 +1,10 @@ +warning: external crate `bar` unused in `warn_cmdline`: remove the dependency or add `use bar as _;` + --> $DIR/warn-cmdline.rs:8:1 + | +LL | fn main() {} + | ^ + | + = note: requested on the command line with `-W unused-crate-dependencies` + +warning: 1 warning emitted + diff --git a/src/test/ui/use/use-from-trait-xc.stderr b/src/test/ui/use/use-from-trait-xc.stderr index 3f38a6cae7b81..37b4e61c8085e 100644 --- a/src/test/ui/use/use-from-trait-xc.stderr +++ b/src/test/ui/use/use-from-trait-xc.stderr @@ -44,7 +44,7 @@ error[E0603]: struct `Foo` is private --> $DIR/use-from-trait-xc.rs:14:24 | LL | use use_from_trait_xc::Foo::new; - | ^^^ this struct is private + | ^^^ private struct | note: the struct `Foo` is defined here --> $DIR/auxiliary/use-from-trait-xc.rs:9:1 @@ -56,7 +56,7 @@ error[E0603]: struct `Foo` is private --> $DIR/use-from-trait-xc.rs:17:24 | LL | use use_from_trait_xc::Foo::C; - | ^^^ this struct is private + | ^^^ private struct | note: the struct `Foo` is defined here --> $DIR/auxiliary/use-from-trait-xc.rs:9:1 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-3.stderr b/src/test/ui/use/use-mod/use-mod-3.stderr index 4852759286ae6..1b12b3c6fa09a 100644 --- a/src/test/ui/use/use-mod/use-mod-3.stderr +++ b/src/test/ui/use/use-mod/use-mod-3.stderr @@ -2,7 +2,7 @@ error[E0603]: module `bar` is private --> $DIR/use-mod-3.rs:1:10 | LL | use foo::bar::{ - | ^^^ this module is private + | ^^^ private module | note: the module `bar` is defined here --> $DIR/use-mod-3.rs:9:5 @@ -14,7 +14,7 @@ error[E0603]: module `bar` is private --> $DIR/use-mod-3.rs:4:10 | LL | use foo::bar::{ - | ^^^ this module is private + | ^^^ private module | note: the module `bar` is defined here --> $DIR/use-mod-3.rs:9:5 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/ui/variance-iterators-in-libcore.rs b/src/test/ui/variance-iterators-in-libcore.rs index 2ab3a8ab5c129..a542e44d517a7 100644 --- a/src/test/ui/variance-iterators-in-libcore.rs +++ b/src/test/ui/variance-iterators-in-libcore.rs @@ -1,9 +1,10 @@ // run-pass -#![allow(warnings)] +#![allow(dead_code)] -use std::iter::Zip; +use std::iter::{Fuse, Zip}; +fn fuse_covariant<'a, I>(iter: Fuse<&'static I>) -> Fuse<&'a I> { iter } fn zip_covariant<'a, A, B>(iter: Zip<&'static A, &'static B>) -> Zip<&'a A, &'a B> { iter } fn main() { } diff --git a/src/test/ui/variance/variance-associated-types2.nll.stderr b/src/test/ui/variance/variance-associated-types2.nll.stderr new file mode 100644 index 0000000000000..27d1e1844167e --- /dev/null +++ b/src/test/ui/variance/variance-associated-types2.nll.stderr @@ -0,0 +1,12 @@ +error: lifetime may not live long enough + --> $DIR/variance-associated-types2.rs:13:12 + | +LL | fn take<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +LL | let _: Box> = make(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | + = help: consider replacing `'a` with `'static` + +error: aborting due to previous error + diff --git a/src/test/ui/variance/variance-associated-types2.rs b/src/test/ui/variance/variance-associated-types2.rs new file mode 100644 index 0000000000000..6a095fce7abfa --- /dev/null +++ b/src/test/ui/variance/variance-associated-types2.rs @@ -0,0 +1,17 @@ +// Test that dyn Foo is invariant with respect to T. +// Failure to enforce invariance here can be weaponized, see #71550 for details. + +trait Foo { + type Bar; +} + +fn make() -> Box> { + panic!() +} + +fn take<'a>(_: &'a u32) { + let _: Box> = make(); + //~^ ERROR mismatched types [E0308] +} + +fn main() {} diff --git a/src/test/ui/variance/variance-associated-types2.stderr b/src/test/ui/variance/variance-associated-types2.stderr new file mode 100644 index 0000000000000..52cdd6493b06d --- /dev/null +++ b/src/test/ui/variance/variance-associated-types2.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/variance-associated-types2.rs:13:42 + | +LL | let _: Box> = make(); + | ^^^^^^ lifetime mismatch + | + = note: expected trait object `dyn Foo` + found trait object `dyn Foo` +note: the lifetime `'a` as defined on the function body at 12:9... + --> $DIR/variance-associated-types2.rs:12:9 + | +LL | fn take<'a>(_: &'a u32) { + | ^^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/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..731d31ac34f62 100644 --- a/src/test/ui/wf/wf-fn-where-clause.stderr +++ b/src/test/ui/wf/wf-fn-where-clause.stderr @@ -2,26 +2,34 @@ 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` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/wf-fn-where-clause.rs:16:12 + | +LL | struct Vec { + | ^ this could be changed to `T: ?Sized`... +LL | t: T, + | - ...if indirection was used here: `Box` 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-region.stderr b/src/test/ui/wf/wf-impl-associated-type-region.stderr index 9942c80effe4b..f3b32ad3f7e85 100644 --- a/src/test/ui/wf/wf-impl-associated-type-region.stderr +++ b/src/test/ui/wf/wf-impl-associated-type-region.stderr @@ -4,13 +4,7 @@ error[E0309]: the parameter type `T` may not live long enough LL | impl<'a, T> Foo<'a> for T { | - help: consider adding an explicit lifetime bound...: `T: 'a` LL | type Bar = &'a T; - | ^^^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'a T` does not outlive the data it points at - --> $DIR/wf-impl-associated-type-region.rs:10:5 - | -LL | type Bar = &'a T; - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at error: aborting due to previous error 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-type-static.stderr b/src/test/ui/wf/wf-in-fn-type-static.stderr index 7dc8f5a96611b..a79c446247794 100644 --- a/src/test/ui/wf/wf-in-fn-type-static.stderr +++ b/src/test/ui/wf/wf-in-fn-type-static.stderr @@ -5,13 +5,7 @@ LL | struct Foo { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | // needs T: 'static LL | x: fn() -> &'static T - | ^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'static T` does not outlive the data it points at - --> $DIR/wf-in-fn-type-static.rs:13:5 - | -LL | x: fn() -> &'static T - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at error[E0310]: the parameter type `T` may not live long enough --> $DIR/wf-in-fn-type-static.rs:18:5 @@ -20,13 +14,7 @@ LL | struct Bar { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | // needs T: Copy LL | x: fn(&'static T) - | ^^^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'static T` does not outlive the data it points at - --> $DIR/wf-in-fn-type-static.rs:18:5 - | -LL | x: fn(&'static T) - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at error: aborting due to 2 previous errors 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-static.stderr b/src/test/ui/wf/wf-in-obj-type-static.stderr index 32c3198d55be4..c0057f3c82977 100644 --- a/src/test/ui/wf/wf-in-obj-type-static.stderr +++ b/src/test/ui/wf/wf-in-obj-type-static.stderr @@ -5,13 +5,7 @@ LL | struct Foo { | - help: consider adding an explicit lifetime bound...: `T: 'static` LL | // needs T: 'static LL | x: dyn Object<&'static T> - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'static T` does not outlive the data it points at - --> $DIR/wf-in-obj-type-static.rs:14:5 - | -LL | x: dyn Object<&'static T> - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at 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-outlives-ty-in-fn-or-trait.stderr b/src/test/ui/wf/wf-outlives-ty-in-fn-or-trait.stderr index 52786fb3bca96..4c25ab9593958 100644 --- a/src/test/ui/wf/wf-outlives-ty-in-fn-or-trait.stderr +++ b/src/test/ui/wf/wf-outlives-ty-in-fn-or-trait.stderr @@ -4,13 +4,7 @@ error[E0309]: the parameter type `T` may not live long enough LL | impl<'a, T> Trait<'a, T> for usize { | - help: consider adding an explicit lifetime bound...: `T: 'a` LL | type Out = &'a fn(T); - | ^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'a fn(T)` does not outlive the data it points at - --> $DIR/wf-outlives-ty-in-fn-or-trait.rs:9:5 - | -LL | type Out = &'a fn(T); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ ...so that the reference type `&'a fn(T)` does not outlive the data it points at error[E0309]: the parameter type `T` may not live long enough --> $DIR/wf-outlives-ty-in-fn-or-trait.rs:19:5 @@ -18,13 +12,7 @@ error[E0309]: the parameter type `T` may not live long enough LL | impl<'a, T> Trait<'a, T> for u32 { | - help: consider adding an explicit lifetime bound...: `T: 'a` LL | type Out = &'a dyn Baz; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...so that the reference type `&'a (dyn Baz + 'a)` does not outlive the data it points at - --> $DIR/wf-outlives-ty-in-fn-or-trait.rs:19:5 - | -LL | type Out = &'a dyn Baz; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the reference type `&'a (dyn Baz + 'a)` does not outlive the data it points at error: aborting due to 2 previous errors 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-region.stderr b/src/test/ui/wf/wf-trait-associated-type-region.stderr index 9bbfad90cdb85..ae681ba6c9bb5 100644 --- a/src/test/ui/wf/wf-trait-associated-type-region.stderr +++ b/src/test/ui/wf/wf-trait-associated-type-region.stderr @@ -5,11 +5,7 @@ LL | type Type2 = &'a Self::Type1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `>::Type1: 'a`... -note: ...so that the reference type `&'a >::Type1` does not outlive the data it points at - --> $DIR/wf-trait-associated-type-region.rs:9:5 - | -LL | type Type2 = &'a Self::Type1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...so that the reference type `&'a >::Type1` does not outlive the data it points at 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.nll.stderr b/src/test/ui/where-clauses/where-for-self-2.nll.stderr new file mode 100644 index 0000000000000..d0c476dc6ec05 --- /dev/null +++ b/src/test/ui/where-clauses/where-for-self-2.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/where-for-self-2.rs:23:5 + | +LL | foo(&X); + | ^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/where-clauses/where-for-self-2.rs b/src/test/ui/where-clauses/where-for-self-2.rs index 31174fd4cf163..37c6954fd52ee 100644 --- a/src/test/ui/where-clauses/where-for-self-2.rs +++ b/src/test/ui/where-clauses/where-for-self-2.rs @@ -14,9 +14,11 @@ impl Bar for &'static u32 { } fn foo(x: &T) - where for<'a> &'a T: Bar -{} +where + for<'a> &'a T: Bar, +{ +} fn main() { - foo(&X); //~ ERROR trait bound + foo(&X); //~ ERROR implementation of `Bar` is not general enough } 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..30eb78b2da4f7 100644 --- a/src/test/ui/where-clauses/where-for-self-2.stderr +++ b/src/test/ui/where-clauses/where-for-self-2.stderr @@ -1,17 +1,16 @@ -error[E0277]: the trait bound `for<'a> &'a _: Bar` is not satisfied - --> $DIR/where-for-self-2.rs:21:5 +error: implementation of `Bar` is not general enough + --> $DIR/where-for-self-2.rs:23:5 | -LL | fn foo(x: &T) - | --- -LL | where for<'a> &'a T: Bar - | --- required by this bound in `foo` +LL | / trait Bar { +LL | | fn bar(&self); +LL | | } + | |_- trait `Bar` defined here ... -LL | foo(&X); - | ^^^ the trait `for<'a> Bar` is not implemented for `&'a _` +LL | foo(&X); + | ^^^ implementation of `Bar` is not general enough | - = help: the following implementations were found: - <&'static u32 as Bar> + = note: `Bar` would have to be implemented for the type `&'0 u32`, for any lifetime `'0`... + = note: ...but `Bar` is actually implemented for the type `&'1 u32`, for some specific lifetime `'1` error: aborting due to previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/where-clauses/where-lifetime-resolution.rs b/src/test/ui/where-clauses/where-lifetime-resolution.rs index 0d426386768c8..d8677ee959abd 100644 --- a/src/test/ui/where-clauses/where-lifetime-resolution.rs +++ b/src/test/ui/where-clauses/where-lifetime-resolution.rs @@ -7,7 +7,6 @@ fn f() where //~^ ERROR use of undeclared lifetime name `'a` for<'a> dyn for<'b> Trait2<'a, 'b>: Trait2<'a, 'b>, //~^ ERROR use of undeclared lifetime name `'b` - //~| ERROR nested quantification of lifetimes {} fn main() {} diff --git a/src/test/ui/where-clauses/where-lifetime-resolution.stderr b/src/test/ui/where-clauses/where-lifetime-resolution.stderr index 49799a93017eb..6c52664154bbf 100644 --- a/src/test/ui/where-clauses/where-lifetime-resolution.stderr +++ b/src/test/ui/where-clauses/where-lifetime-resolution.stderr @@ -7,12 +7,6 @@ LL | for<'a> dyn Trait1<'a>: Trait1<'a>, // OK LL | (dyn for<'a> Trait1<'a>): Trait1<'a>, | ^^ undeclared lifetime -error[E0316]: nested quantification of lifetimes - --> $DIR/where-lifetime-resolution.rs:8:17 - | -LL | for<'a> dyn for<'b> Trait2<'a, 'b>: Trait2<'a, 'b>, - | ^^^^^^^^^^^^^^^^^^^^^^ - error[E0261]: use of undeclared lifetime name `'b` --> $DIR/where-lifetime-resolution.rs:8:52 | @@ -22,6 +16,6 @@ LL | fn f() where LL | for<'a> dyn for<'b> Trait2<'a, 'b>: Trait2<'a, 'b>, | ^^ undeclared lifetime -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0261`. 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..50dfce3448c34 100644 --- a/src/test/ui/write-to-static-mut-in-static.stderr +++ b/src/test/ui/write-to-static-mut-in-static.stderr @@ -2,13 +2,13 @@ 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 + --> $DIR/write-to-static-mut-in-static.rs:5:1 | LL | pub static mut C: u32 = unsafe { C = 1; 0 }; - | ^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: ...which requires const-evaluating `C`... --> $DIR/write-to-static-mut-in-static.rs:5:1 diff --git a/src/test/ui/xc-private-method.stderr b/src/test/ui/xc-private-method.stderr index 6a68bef90efd7..8b7e43ccc04e9 100644 --- a/src/test/ui/xc-private-method.stderr +++ b/src/test/ui/xc-private-method.stderr @@ -1,14 +1,14 @@ error[E0624]: associated function `static_meth_struct` is private - --> $DIR/xc-private-method.rs:6:13 + --> $DIR/xc-private-method.rs:6:44 | LL | let _ = xc_private_method_lib::Struct::static_meth_struct(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ private associated function error[E0624]: associated function `static_meth_enum` is private - --> $DIR/xc-private-method.rs:9:13 + --> $DIR/xc-private-method.rs:9:42 | LL | let _ = xc_private_method_lib::Enum::static_meth_enum(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ private associated function error: aborting due to 2 previous errors diff --git a/src/test/ui/xc-private-method2.stderr b/src/test/ui/xc-private-method2.stderr index 84a8b9817c0d0..0ebdb0a06d82b 100644 --- a/src/test/ui/xc-private-method2.stderr +++ b/src/test/ui/xc-private-method2.stderr @@ -2,13 +2,13 @@ error[E0624]: associated function `meth_struct` is private --> $DIR/xc-private-method2.rs:6:52 | LL | let _ = xc_private_method_lib::Struct{ x: 10 }.meth_struct(); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ private associated function error[E0624]: associated function `meth_enum` is private --> $DIR/xc-private-method2.rs:9:55 | LL | let _ = xc_private_method_lib::Enum::Variant1(20).meth_enum(); - | ^^^^^^^^^ + | ^^^^^^^^^ private associated function error: aborting due to 2 previous errors diff --git a/src/test/ui/xcrate/xcrate-private-by-default.stderr b/src/test/ui/xcrate/xcrate-private-by-default.stderr index 842069d6135cb..a97f55de5f833 100644 --- a/src/test/ui/xcrate/xcrate-private-by-default.stderr +++ b/src/test/ui/xcrate/xcrate-private-by-default.stderr @@ -2,7 +2,7 @@ error[E0603]: static `j` is private --> $DIR/xcrate-private-by-default.rs:23:29 | LL | static_priv_by_default::j; - | ^ this static is private + | ^ private static | note: the static `j` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:47:1 @@ -14,7 +14,7 @@ error[E0603]: function `k` is private --> $DIR/xcrate-private-by-default.rs:25:29 | LL | static_priv_by_default::k; - | ^ this function is private + | ^ private function | note: the function `k` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:48:1 @@ -26,7 +26,7 @@ error[E0603]: unit struct `l` is private --> $DIR/xcrate-private-by-default.rs:27:29 | LL | static_priv_by_default::l; - | ^ this unit struct is private + | ^ private unit struct | note: the unit struct `l` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:49:1 @@ -38,7 +38,7 @@ error[E0603]: enum `m` is private --> $DIR/xcrate-private-by-default.rs:29:35 | LL | foo::(); - | ^ this enum is private + | ^ private enum | note: the enum `m` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:50:1 @@ -50,7 +50,7 @@ error[E0603]: type alias `n` is private --> $DIR/xcrate-private-by-default.rs:31:35 | LL | foo::(); - | ^ this type alias is private + | ^ private type alias | note: the type alias `n` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:51:1 @@ -62,7 +62,7 @@ error[E0603]: module `foo` is private --> $DIR/xcrate-private-by-default.rs:35:29 | LL | static_priv_by_default::foo::a; - | ^^^ this module is private + | ^^^ private module | note: the module `foo` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:12:1 @@ -74,7 +74,7 @@ error[E0603]: module `foo` is private --> $DIR/xcrate-private-by-default.rs:37:29 | LL | static_priv_by_default::foo::b; - | ^^^ this module is private + | ^^^ private module | note: the module `foo` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:12:1 @@ -86,7 +86,7 @@ error[E0603]: module `foo` is private --> $DIR/xcrate-private-by-default.rs:39:29 | LL | static_priv_by_default::foo::c; - | ^^^ this module is private + | ^^^ private module | note: the module `foo` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:12:1 @@ -98,7 +98,7 @@ error[E0603]: module `foo` is private --> $DIR/xcrate-private-by-default.rs:41:35 | LL | foo::(); - | ^^^ this module is private + | ^^^ private module | note: the module `foo` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:12:1 @@ -110,7 +110,7 @@ error[E0603]: module `foo` is private --> $DIR/xcrate-private-by-default.rs:43:35 | LL | foo::(); - | ^^^ this module is private + | ^^^ private module | note: the module `foo` is defined here --> $DIR/auxiliary/static_priv_by_default.rs:12:1 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..9eb43eb2df43f 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -7,7 +7,6 @@ #![deny(warnings)] use serde::Serialize; -use toml; use std::collections::BTreeMap; use std::collections::HashMap; @@ -56,6 +55,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 +138,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 +227,6 @@ struct Builder { clippy_release: String, rustfmt_release: String, llvm_tools_release: String, - lldb_release: String, miri_release: String, input: PathBuf, @@ -241,7 +242,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 +250,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 +280,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 +295,6 @@ fn main() { clippy_release, rustfmt_release, llvm_tools_release, - lldb_release, miri_release, input, @@ -313,7 +310,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 +318,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 +332,6 @@ enum PkgType { Clippy, Rustfmt, LlvmTools, - Lldb, Miri, Other, } @@ -352,7 +346,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 +360,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 +369,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 +443,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 +473,6 @@ impl Builder { "rls-preview", "rust-src", "llvm-tools-preview", - "lldb-preview", "rust-analysis", "miri-preview", ], @@ -559,7 +547,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 +676,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 +689,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 +702,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 bda50510d1daf..c26576f9adddd 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit bda50510d1daf6e9c53ad6ccf603da6e0fa8103f +Subproject commit c26576f9adddd254b3dd63aecba176434290a9f6 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 8485d40a3264b..0000000000000 --- a/src/tools/clippy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8485d40a3264b15b92d391e99cb3d1abf8f25025 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/blank_issue.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md new file mode 100644 index 0000000000000..9aef3ebe637a1 --- /dev/null +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.md @@ -0,0 +1,4 @@ +--- +name: Blank Issue +about: Create a blank issue. +--- diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..d8f0c44148cae --- /dev/null +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,47 @@ +--- +name: Bug Report +about: Create a bug report for Clippy +labels: L-bug +--- + + +I tried this code: + +```rust + +``` + +I expected to see this happen: *explanation* + +Instead, this happened: *explanation* + +### Meta + +- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) +- `rustc -Vv`: + ``` + rustc 1.46.0-nightly (f455e46ea 2020-06-20) + binary: rustc + commit-hash: f455e46eae1a227d735091091144601b467e1565 + commit-date: 2020-06-20 + host: x86_64-unknown-linux-gnu + release: 1.46.0-nightly + LLVM version: 10.0 + ``` + + +

Backtrace +

+ + ``` + + ``` + +

+
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/config.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000000..bd7dc0ac95c1f --- /dev/null +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Rust Programming Language Forum + url: https://users.rust-lang.org + about: Please ask and answer questions about Rust here. diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md new file mode 100644 index 0000000000000..3abe76bf2c497 --- /dev/null +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/ice.md @@ -0,0 +1,53 @@ +--- +name: Internal Compiler Error +about: Create a report for an internal compiler error in Clippy. +labels: L-bug, L-crash +--- + + +### Code + +```rust + +``` + +### Meta + +- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) +- `rustc -Vv`: + ``` + rustc 1.46.0-nightly (f455e46ea 2020-06-20) + binary: rustc + commit-hash: f455e46eae1a227d735091091144601b467e1565 + commit-date: 2020-06-20 + host: x86_64-unknown-linux-gnu + release: 1.46.0-nightly + LLVM version: 10.0 + ``` + +### Error output + +``` + +``` + + +
Backtrace +

+ + ``` + + ``` + +

+
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md new file mode 100644 index 0000000000000..70445d7ef2503 --- /dev/null +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.md @@ -0,0 +1,35 @@ +--- +name: New lint suggestion +about: Suggest a new Clippy lint. +labels: L-lint +--- + +### What it does + +*What does this lint do?* + +### Categories (optional) + +- Kind: *See for list of lint kinds* + +*What benefit of this lint over old code?* + +For example: +- Remove bounce checking inserted by ... +- Remove the need to duplicating/storing/typo ... + +### Drawbacks + +None. + +### Example + +```rust + +``` + +Could be written as: + +```rust + +``` 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..137a73630940a --- /dev/null +++ b/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,32 @@ +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. + +--- + +*Please keep the line below* +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..2c17c4203ae5c --- /dev/null +++ b/src/tools/clippy/.github/driver.sh @@ -0,0 +1,41 @@ +#!/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 + + +# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same +SYSROOT=`rustc --print sysroot` +diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose) + + +echo "fn main() {}" > target/driver_test.rs +# we can't run 2 rustcs on the same file at the same time +CLIPPY=`LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc` +RUSTC=`rustc ./target/driver_test.rs` +diff <($CLIPPY) <($RUSTC) + +# 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..5fa8009a8b42c --- /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@v2 + 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..0c80394f03e3c --- /dev/null +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -0,0 +1,330 @@ +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@v2 + 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@v2 + 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' + # FIXME: re-enable once fmt_macros is renamed in RLS + # - '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@v2 + 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: [changelog, base, integration_build, 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: [changelog, base, integration_build, 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..adc945a69441d --- /dev/null +++ b/src/tools/clippy/CHANGELOG.md @@ -0,0 +1,1735 @@ +# 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 + +[7ea7cd1...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master) + +## Rust 1.45 + +Current beta, release 2020-07-16 + +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1) + +### New lints + +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582) +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493) +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332) +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506) +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439) +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522) +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576) +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583) +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550) + +### Moves and Deprecations + +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408) +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622) +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599) +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529) +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568) +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`]. +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563) + +### Enhancements + +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505) +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631) +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592) +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616) + +### False Positive Fixes + +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525) +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609) +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558) +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596) +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535) +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491) +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602) +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564) +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611) +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636) +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647) +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`] +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651) +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429) + +### Suggestion Improvements + +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536) +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511) +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530) +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547) + +### ICE Fixes + +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590) +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499) + +### Documentation + +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639) +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541) + +## Rust 1.44 + +Current stable, released 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 + +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_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice +[`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 +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants +[`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_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by +[`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 +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns +[`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 +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero +[`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..9f7bdcb1be7e5 --- /dev/null +++ b/src/tools/clippy/CONTRIBUTING.md @@ -0,0 +1,293 @@ +# 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]. + +- [Contributing to Clippy](#contributing-to-clippy) + - [Getting started](#getting-started) + - [Finding something to fix/improve](#finding-something-to-fiximprove) + - [Writing code](#writing-code) + - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work) + - [How Clippy works](#how-clippy-works) + - [Fixing build failures caused by Rust](#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 + +## Getting code-completion for rustc internals to work + +Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals +using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not +available via a `rustup` component at the time of writing. +To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via +`git clone https://github.com/rust-lang/rust/`. +Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies +which rust-analyzer will be able to understand. +Run `cargo dev ra-setup --repo-path ` where `` is an absolute path to the rustc repo +you just cloned. +The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to +Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses. +Just make sure to remove the dependencies again before finally making a pull request! + +[ra_homepage]: https://rust-analyzer.github.io/ +[rustc_repo]: https://github.com/rust-lang/rust/ + +## 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 currently gets built with `rustc` of the `rust-lang/rust` `master` +branch. Most of the times we have to adapt to the changes and only very rarely +there's an actual bug in Rust. + +If you decide to make Clippy work again with a Rust commit that breaks it, you +have to sync the `rust-lang/rust-clippy` repository with the `subtree` copy of +Clippy in the `rust-lang/rust` repository. + +For general information about `subtree`s in the Rust repository see [Rust's +`CONTRIBUTING.md`][subtree]. + +Here is a TL;DR version of the sync process (all of the following commands have +to be run inside the `rust` directory): + +1. Clone the [`rust-lang/rust`] repository +2. Sync the changes to the rust-copy of Clippy to your Clippy fork: + ```bash + # Make sure to change `your-github-name` to your github name in the following command + git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust + ``` + _Note:_ This will directly push to the remote repository. You can also push + to your local copy by replacing the remote address with `/path/to/rust-clippy` + directory. + + _Note:_ Most of the time you have to create a merge commit in the + `rust-clippy` repo (this has to be done in the Clippy repo, not in the + rust-copy of Clippy): + ```bash + git fetch origin && git fetch upstream + git checkout sync-from-rust + git merge upstream/master + ``` +3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to + accelerate the process ping the `@rust-lang/clippy` team in your PR and/or + ~~annoy~~ ask them in the [Discord] channel.) +4. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: + ```bash + git checkout -b sync-from-clippy + git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master + ``` +5. Open a PR to [`rust-lang/rust`] + +Also, you may want to define remotes, so you don't have to type out the remote +addresses on every sync. You can do this with the following commands (these +commands still have to be run inside the `rust` directory): + +```bash +# Set clippy-upstream remote for pulls +$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy +# Make sure to not push to the upstream repo +$ git remote set-url --push clippy-upstream DISABLED +# Set clippy-origin remote to your fork for pushes +$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy +# Set a local remote +$ git remote add clippy-local /path/to/rust-clippy +``` + +You can then sync with the remote names from above, e.g.: + +```bash +$ git subtree push -P src/tools/clippy clippy-local sync-from-rust +``` + +_Note:_ The first time running `git subtree push` a cache has to be built. This +involves going through the complete Clippy history once. For this you have to +increase the stack limit though, which you can do with `ulimit -s 60000`. For +this to work, you will need the fix of `git subtree` available +[here][gitgitgadget-pr]. + +[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 +[subtree]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#external-dependencies-subtree +[`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..836897927b015 --- /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.1" +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..5baa31d5cde0c --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -0,0 +1,525 @@ +#![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 ra_setup; +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::new); + 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..281037ae37c97 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -0,0 +1,134 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] + +use clap::{App, Arg, SubCommand}; +use clippy_dev::{fmt, new_lint, ra_setup, 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."), + ) + .subcommand( + SubCommand::with_name("ra-setup") + .about("Alter dependencies so rust-analyzer can find rustc internals") + .arg( + Arg::with_name("rustc-repo-path") + .long("repo-path") + .short("r") + .help("The path to a rustc repo that will be used for setting the dependencies") + .takes_value(true) + .value_name("path") + .required(true), + ), + ) + .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(); + }, + ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), + _ => {}, + } +} 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..1e032a7bc20cd --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -0,0 +1,219 @@ +use crate::clippy_project_root; +use std::fs::{self, OpenOptions}; +use std::io::prelude::*; +use std::io::{self, ErrorKind}; +use std::path::{Path, PathBuf}; + +struct LintData<'a> { + pass: &'a str, + name: &'a str, + category: &'a str, + project_root: PathBuf, +} + +trait Context { + fn context>(self, text: C) -> Self; +} + +impl Context for io::Result { + fn context>(self, text: C) -> Self { + match self { + Ok(t) => Ok(t), + Err(e) => { + let message = format!("{}: {}", text.as_ref(), e); + Err(io::Error::new(ErrorKind::Other, message)) + }, + } + } +} + +/// Creates the files required to implement and test a new lint and runs `update_lints`. +/// +/// # Errors +/// +/// This function errors out if the files couldn't be created or written to. +pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> io::Result<()> { + let lint = LintData { + pass: pass.expect("`pass` argument is validated by clap"), + name: lint_name.expect("`name` argument is validated by clap"), + category: category.expect("`category` argument is validated by clap"), + project_root: clippy_project_root(), + }; + + create_lint(&lint).context("Unable to create lint implementation")?; + create_test(&lint).context("Unable to create a test for the new lint") +} + +fn create_lint(lint: &LintData) -> io::Result<()> { + let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass { + "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"), + "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"), + _ => { + unreachable!("`pass_type` should only ever be `early` or `late`!"); + }, + }; + + let camel_case_name = to_camel_case(lint.name); + let lint_contents = get_lint_file_contents( + pass_type, + pass_lifetimes, + lint.name, + &camel_case_name, + lint.category, + pass_import, + context_import, + ); + + let lint_path = format!("clippy_lints/src/{}.rs", lint.name); + write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes()) +} + +fn create_test(lint: &LintData) -> io::Result<()> { + fn create_project_layout>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> { + let mut path = location.into().join(case); + fs::create_dir(&path)?; + write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?; + + path.push("src"); + fs::create_dir(&path)?; + let header = format!("// compile-flags: --crate-name={}", lint_name); + write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?; + + Ok(()) + } + + if lint.category == "cargo" { + let relative_test_dir = format!("tests/ui-cargo/{}", lint.name); + let test_dir = lint.project_root.join(relative_test_dir); + fs::create_dir(&test_dir)?; + + create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?; + create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint") + } else { + let test_path = format!("tests/ui/{}.rs", lint.name); + let test_contents = get_test_file_contents(lint.name, None); + write_file(lint.project_root.join(test_path), test_contents) + } +} + +fn write_file, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + fn inner(path: &Path, contents: &[u8]) -> io::Result<()> { + OpenOptions::new() + .write(true) + .create_new(true) + .open(path)? + .write_all(contents) + } + + inner(path.as_ref(), contents.as_ref()).context(format!("writing to file: {}", path.as_ref().display())) +} + +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, header_commands: Option<&str>) -> String { + let mut contents = format!( + "#![warn(clippy::{})] + +fn main() {{ + // test code goes here +}} +", + lint_name + ); + + if let Some(header) = header_commands { + contents = format!("{}\n{}", header, contents); + } + + contents +} + +fn get_manifest_contents(lint_name: &str, hint: &str) -> String { + format!( + r#" +# {} + +[package] +name = "{}" +version = "0.1.0" +publish = false + +[workspace] +"#, + hint, 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/ra_setup.rs b/src/tools/clippy/clippy_dev/src/ra_setup.rs new file mode 100644 index 0000000000000..8617445c8a600 --- /dev/null +++ b/src/tools/clippy/clippy_dev/src/ra_setup.rs @@ -0,0 +1,90 @@ +#![allow(clippy::filter_map)] + +use std::fs; +use std::fs::File; +use std::io::prelude::*; +use std::path::PathBuf; + +// This module takes an absolute path to a rustc repo and alters the dependencies to point towards +// the respective rustc subcrates instead of using extern crate xyz. +// This allows rust analyzer to analyze rustc internals and show proper information inside clippy +// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details + +pub fn run(rustc_path: Option<&str>) { + // we can unwrap here because the arg is required here + let rustc_path = PathBuf::from(rustc_path.unwrap()); + assert!(rustc_path.is_dir(), "path is not a directory"); + let rustc_source_basedir = rustc_path.join("src"); + assert!( + rustc_source_basedir.is_dir(), + "are you sure the path leads to a rustc repo?" + ); + + let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml"); + let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs"); + inject_deps_into_manifest( + &rustc_source_basedir, + "Cargo.toml", + &clippy_root_manifest, + &clippy_root_lib_rs, + ) + .expect("Failed to inject deps into ./Cargo.toml"); + + let clippy_lints_manifest = + fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml"); + let clippy_lints_lib_rs = + fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs"); + inject_deps_into_manifest( + &rustc_source_basedir, + "clippy_lints/Cargo.toml", + &clippy_lints_manifest, + &clippy_lints_lib_rs, + ) + .expect("Failed to inject deps into ./clippy_lints/Cargo.toml"); +} + +fn inject_deps_into_manifest( + rustc_source_dir: &PathBuf, + manifest_path: &str, + cargo_toml: &str, + lib_rs: &str, +) -> std::io::Result<()> { + let extern_crates = lib_rs + .lines() + // get the deps + .filter(|line| line.starts_with("extern crate")) + // we have something like "extern crate foo;", we only care about the "foo" + // ↓ ↓ + // extern crate rustc_middle; + .map(|s| &s[13..(s.len() - 1)]); + + let new_deps = extern_crates.map(|dep| { + // format the dependencies that are going to be put inside the Cargo.toml + format!( + "{dep} = {{ path = \"{source_path}/lib{dep}\" }}\n", + dep = dep, + source_path = rustc_source_dir.display() + ) + }); + + // format a new [dependencies]-block with the new deps we need to inject + let mut all_deps = String::from("[dependencies]\n"); + new_deps.for_each(|dep_line| { + all_deps.push_str(&dep_line); + }); + + // replace "[dependencies]" with + // [dependencies] + // dep1 = { path = ... } + // dep2 = { path = ... } + // etc + let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1); + + // println!("{}", new_manifest); + let mut file = File::create(manifest_path)?; + file.write_all(new_manifest.as_bytes())?; + + println!("Dependency paths injected: {}", manifest_path); + + Ok(()) +} 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..e959c1a651122 --- /dev/null +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -0,0 +1,39 @@ +[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.1" +if_chain = "1.0.0" +itertools = "0.9" +lazy_static = "1.0.2" +pulldown-cmark = { version = "0.7.1", 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"] } +quote = "1" +syn = { version = "1", features = ["full"] } + +[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..13e61fe98bac1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/assign_ops.rs @@ -0,0 +1,265 @@ +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; + /// // ... + /// // Bad + /// a = a + b; + /// + /// // Good + /// 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..fca9aaaff9dce --- /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..41f125d48398f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -0,0 +1,668 @@ +//! 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, + 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 { + 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, + EMPTY_LINE_AFTER_OUTER_ATTR, +]); + +impl EarlyLintPass for EarlyAttributes { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { + check_empty_line_after_outer_attr(cx, item); + } + + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { + check_deprecated_cfg_attr(cx, attr); + check_mismatched_target_os(cx, attr); + } +} + +fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { + for attr in &item.attrs { + let attr_item = if let AttrKind::Normal(ref attr) = attr.kind { + attr + } else { + return; + }; + + 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(), item.span.lo(), item.span.ctxt()); + let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.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?", + ); + } + } + } + } +} + +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..a88f922d8e03d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/await_holding_lock.rs @@ -0,0 +1,92 @@ +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}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + 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..f92c564543b89 --- /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..531531a654d0e --- /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..c40a387d29797 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs @@ -0,0 +1,97 @@ +//! 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" + /// authors = ["Someone "] + /// 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 missing_warning(cx: &LateContext<'_, '_>, package: &cargo_metadata::Package, field: &str) { + let message = format!("package `{}` is missing `{}` metadata", package.name, field); + span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &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 = unwrap_cargo_metadata!(cx, CARGO_COMMON_METADATA, false); + + 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..88145015ba8bd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -0,0 +1,340 @@ +//! 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 let Some(cv) = result { + if let Some(to_type) = cv.to_type { + 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); + + upper.zip(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 { + match (self.to_type, other.to_type) { + (Some(l), Some(r)) => l == r, + _ => true, + } + } + + /// 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, INTS, "max_value", "MAX"); + + 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, SINTS, "min_value", "MIN") { + 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<'_>, + types: &'a [&str], + func: &'a str, + assoc_const: &'a str, +) -> Option<(&'a str, &'a str)> { + // `to_type::max_value() as from_type` + // or `to_type::MAX as from_type` + let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { + // to_type::max_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::max_value())` + let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { + if_chain! { + // `from_type::from, to_type::max_value()` + if let ExprKind::Call(ref from_func, ref args) = &expr.kind; + // `to_type::max_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 { + match limit.kind { + // `from_type::from(_)` + ExprKind::Call(path, _) => { + if let ExprKind::Path(ref path) = path.kind { + // `to_type` + if let Some(to_type) = get_implementing_type(path, types, func) { + return Some((from_type, to_type)); + } + } + }, + // `to_type::MAX` + ExprKind::Path(ref path) => { + if let Some(to_type) = get_implementing_type(path, types, assoc_const) { + return Some((from_type, to_type)); + } + }, + _ => {}, + } + }; + 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 + } + } +} + +/// 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 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..550752396c732 --- /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 as u128, + "" => i16::MAX as u128, + "" => i32::MAX as u128, + "" => i64::MAX as u128, + "" => i128::MAX 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_opt(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..b6d50bdfa1466 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -0,0 +1,413 @@ +use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; +use crate::utils::{SpanlessEq, SpanlessHash}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Ty, TyS}; +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>(lhs: &FxHashMap>, rhs: &FxHashMap>) -> bool { + lhs.len() == rhs.len() + && lhs + .iter() + .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(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(&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..1cd30ae2c6381 --- /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..1eb380a22cc6b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/double_parens.rs @@ -0,0 +1,89 @@ +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 + /// // Bad + /// fn simple_double_parens() -> i32 { + /// ((0)) + /// } + /// + /// // Good + /// fn simple_no_parens() -> i32 { + /// 0 + /// } + /// + /// // or + /// + /// # fn foo(bar: usize) {} + /// // Bad + /// foo((0)); + /// + /// // Good + /// foo(0); + /// ``` + 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..5a7f759486edd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/drop_bounds.rs @@ -0,0 +1,73 @@ +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() {} + /// ``` + /// Could be written as: + /// ```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..7171dcef968c1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/duration_subsec.rs @@ -0,0 +1,71 @@ +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); + /// + /// // Bad + /// let _micros = dur.subsec_nanos() / 1_000; + /// let _millis = dur.subsec_nanos() / 1_000_000; + /// + /// // Good + /// let _micros = dur.subsec_micros(); + /// let _millis = dur.subsec_millis(); + /// ``` + 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..f625058b6703c --- /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..12b62f5cf9789 --- /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) => {}, + _ => 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..cb0fd59a2d407 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/enum_variants.rs @@ -0,0 +1,327 @@ +//! 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, + /// } + /// ``` + /// Could be written as: + /// ```rust + /// enum Cake { + /// BlackForest, + /// Hummingbird, + /// Battenberg, + /// } + /// ``` + pub ENUM_VARIANT_NAMES, + style, + "enums where all variants share a prefix/postfix" +} + +declare_clippy_lint! { + /// **What it does:** Detects public enumeration variants that are + /// prefixed or suffixed by the same characters. + /// + /// **Why is this bad?** Public enumeration variant names should specify their variant, + /// not repeat the enumeration name. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// pub enum Cake { + /// BlackForestCake, + /// HummingbirdCake, + /// BattenbergCake, + /// } + /// ``` + /// Could be written as: + /// ```rust + /// pub enum Cake { + /// BlackForest, + /// Hummingbird, + /// Battenberg, + /// } + /// ``` + pub PUB_ENUM_VARIANT_NAMES, + pedantic, + "public 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; + /// } + /// ``` + /// Could be written as: + /// ```rust + /// mod cake { + /// struct BlackForest; + /// } + /// ``` + 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..d7819d737ea04 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/eq_op.rs @@ -0,0 +1,233 @@ +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 + /// // Bad + /// &x == y + /// + /// // Good + /// 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..77e90eeac4958 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -0,0 +1,171 @@ +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, PlaceBase, PlaceWithHirId}; + +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) {} + /// + /// // Bad + /// let x = Box::new(1); + /// foo(*x); + /// println!("{}", *x); + /// + /// // Good + /// let x = 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: &PlaceWithHirId<'tcx>, mode: ConsumeMode) { + if cmt.place.projections.is_empty() { + if let PlaceBase::Local(lid) = cmt.place.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: &PlaceWithHirId<'tcx>, _: ty::BorrowKind) { + if cmt.place.projections.is_empty() { + if let PlaceBase::Local(lid) = cmt.place.base { + self.set.remove(&lid); + } + } + } + + fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>) { + if cmt.place.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.place.ty) && !self.is_large_box(cmt.place.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..a889856de2742 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -0,0 +1,231 @@ +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 + /// // Bad + /// xs.map(|x| foo(x)) + /// + /// // Good + /// xs.map(foo) + /// ``` + /// 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..74144fb299de2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs @@ -0,0 +1,364 @@ +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; + /// + /// // Bad + /// let a = { + /// x = 1; + /// 1 + /// } + x; + /// // Unclear whether a is 1 or 2. + /// + /// // Good + /// let tmp = { + /// x = 1; + /// 1 + /// }; + /// let a = tmp + x; + /// ``` + 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..7269e2b52c2ce --- /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..92812816461c5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -0,0 +1,149 @@ +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); + /// + /// // Bad + /// impl From for Foo { + /// fn from(s: String) -> Self { + /// Foo(s.parse().unwrap()) + /// } + /// } + /// ``` + /// + /// ```rust + /// // Good + /// struct Foo(i32); + /// + /// use std::convert::TryFrom; + /// impl TryFrom for Foo { + /// type Error = (); + /// fn try_from(s: String) -> Result { + /// if let Ok(parsed) = s.parse() { + /// Ok(Foo(parsed)) + /// } else { + /// Err(()) + /// } + /// } + /// } + /// ``` + 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..4c604cd01075e --- /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"), + LitFloatType::Unsuffixed => 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..ad4f66c52c2c8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -0,0 +1,501 @@ +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..4cae5ca2c4326 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -0,0 +1,204 @@ +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 + /// + /// // Bad + /// # let foo = "foo"; + /// format!("{}", foo); + /// + /// // Good + /// 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..156246fb8bbb0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/formatting.rs @@ -0,0 +1,322 @@ +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) { + if let (StmtKind::Expr(first), StmtKind::Expr(second) | StmtKind::Semi(second)) = (&w[0].kind, &w[1].kind) { + 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..1f9bd7a691b52 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions.rs @@ -0,0 +1,667 @@ +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,ignore + /// // Bad + /// pub fn foo(x: *const u8) { + /// println!("{}", unsafe { *x }); + /// } + /// + /// // Good + /// pub unsafe 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.at(span), cx.param_env) + || 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..17dd3cd5493e7 --- /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, PredicateKind::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.kind() { + let trait_ref = trait_pred.to_poly_trait_ref(); + db.note(&*format!( + "`{}` doesn't implement `{}`", + trait_ref.skip_binder().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..3629ba623ce43 --- /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..04d17c91d63c1 --- /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..6a1fcdd1ce445 --- /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..fdaf37e5e08fa --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -0,0 +1,166 @@ +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; + /// } + /// + /// // 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..a860a9def2422 --- /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..e91fb0c2f27cd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs @@ -0,0 +1,171 @@ +//! 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..d537ef3f3238e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/integer_division.rs @@ -0,0 +1,59 @@ +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 + /// // Bad + /// let x = 3 / 2; + /// println!("{}", x); + /// + /// // Good + /// let x = 3f32 / 2f32; + /// 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..c8576bcfcb444 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs @@ -0,0 +1,87 @@ +//! 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 + /// // Bad + /// fn foo() { + /// println!("cake"); + /// } + /// + /// fn main() { + /// foo(); // prints "foo" + /// fn foo() { + /// println!("foo"); + /// } + /// foo(); // prints "foo" + /// } + /// ``` + /// + /// ```rust + /// // Good + /// fn foo() { + /// println!("cake"); + /// } + /// + /// fn main() { + /// fn foo() { + /// println!("foo"); + /// } + /// foo(); // prints "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..7838e8e8ab774 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -0,0 +1,320 @@ +use crate::utils::{get_item_name, higher, 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 { + /// Special case ranges until `range_is_empty` is stabilized. See issue 3807. + fn should_skip_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + higher::range(cx, expr).map_or(false, |_| { + !cx.tcx + .features() + .declared_lib_features + .iter() + .any(|(name, _)| name.as_str() == "range_is_empty") + }) + } + + /// Gets an `AssocItem` and return true if it matches `is_empty(self)`. + fn is_is_empty(cx: &LateContext<'_, '_>, item: &ty::AssocItem) -> bool { + if let ty::AssocKind::Fn = item.kind { + 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)) + }) + } + + if should_skip_range(cx, expr) { + return false; + } + + 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_and_return.rs b/src/tools/clippy/clippy_lints/src/let_and_return.rs new file mode 100644 index 0000000000000..6d3fb317bcfc5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/let_and_return.rs @@ -0,0 +1,141 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; + +declare_clippy_lint! { + /// **What it does:** Checks for `let`-bindings, which are subsequently + /// returned. + /// + /// **Why is this bad?** It is just extraneous code. Remove it to make your code + /// more rusty. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo() -> String { + /// let x = String::new(); + /// x + /// } + /// ``` + /// instead, use + /// ``` + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + pub LET_AND_RETURN, + style, + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" +} + +declare_lint_pass!(LetReturn => [LET_AND_RETURN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn { + fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr + if_chain! { + if let Some(retexpr) = block.expr; + if let Some(stmt) = block.stmts.iter().last(); + if let StmtKind::Local(local) = &stmt.kind; + if local.ty.is_none(); + if local.attrs.is_empty(); + if let Some(initexpr) = &local.init; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + if let ExprKind::Path(qpath) = &retexpr.kind; + if match_qpath(qpath, &[&*ident.name.as_str()]); + if !last_statement_borrows(cx, initexpr); + if !in_external_macro(cx.sess(), initexpr.span); + if !in_external_macro(cx.sess(), retexpr.span); + if !in_external_macro(cx.sess(), local.span); + if !in_macro(local.span); + then { + span_lint_and_then( + cx, + LET_AND_RETURN, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(snippet) = snippet_opt(cx, initexpr.span) { + err.multipart_suggestion( + "return the expression directly", + vec![ + (local.span, String::new()), + (retexpr.span, snippet), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); + } + } + } +} + +fn last_statement_borrows<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + borrows: bool, +} + +impl BorrowVisitor<'_, '_> { + fn fn_def_id(&self, expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::MethodCall(..) => self.cx.tables.type_dependent_def_id(expr.hir_id), + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + .. + }, + .., + ) => self.cx.tables.qpath_res(qpath, expr.hir_id).opt_def_id(), + _ => None, + } + } +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = self.fn_def_id(expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs new file mode 100644 index 0000000000000..e097f40f87e47 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs @@ -0,0 +1,204 @@ +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.at(span), + cx.param_env, + ); + 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..acd628bbaca59 --- /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 its 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..501220f28e5db --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -0,0 +1,1805 @@ +// error-pattern:cargo-clippy + +#![feature(bindings_after_at)] +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(concat_idents)] +#![feature(crate_visibility_modifier)] +#![feature(drain_filter)] +#![feature(or_patterns)] +#![feature(rustc_private)] +#![feature(stmt_expr_attributes)] +#![recursion_limit = "512"] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)] +#![warn(trivial_casts, trivial_numeric_casts)] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] +// warn on rustc internal lints +#![deny(rustc::internal)] + +// FIXME: switch to something more ergonomic here, once available. +// (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate rustc_ast; +extern crate rustc_ast_pretty; +extern crate rustc_attr; +extern crate rustc_data_structures; +extern crate rustc_errors; +extern crate rustc_hir; +extern crate rustc_hir_pretty; +extern crate rustc_index; +extern crate rustc_infer; +extern crate rustc_lexer; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_mir; +extern crate rustc_parse; +extern crate rustc_parse_format; +extern crate rustc_session; +extern crate rustc_span; +extern crate rustc_target; +extern crate rustc_trait_selection; +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)] +/// extern crate rustc_session; +/// use rustc_session::declare_tool_lint; +/// use clippy_lints::declare_clippy_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_and_return; +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 unnecessary_sort_by; +mod unnested_or_patterns; +mod unsafe_removed_from_name; +mod unused_io_amount; +mod unused_self; +mod unwrap; +mod use_self; +mod useless_conversion; +mod vec; +mod vec_resize_to_zero; +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) { + store.register_pre_expansion_pass(|| box write::Write::default()); + 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_and_return::LET_AND_RETURN, + &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_WILDCARD_FOR_SINGLE_VARIANTS, + &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_NEXT_SLICE, + &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::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, + &unnecessary_sort_by::UNNECESSARY_SORT_BY, + &unnested_or_patterns::UNNESTED_OR_PATTERNS, + &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, + &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, + &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 unnecessary_sort_by::UnnecessarySortBy); + 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_late_pass(|| box let_and_return::LetReturn); + 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_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_early_pass(|| box redundant_field_names::RedundantFieldNames); + store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); + let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; + store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { + single_char_binding_names_threshold, + }); + store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); + store.register_late_pass(|| box macro_use::MacroUseImports::default()); + + 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::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), + LintId::of(&matches::MATCH_WILD_ERR_ARM), + 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_PTR_ALIGNMENT), + 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(&unnested_or_patterns::UNNESTED_OR_PATTERNS), + 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_and_return::LET_AND_RETURN), + 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::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_NEXT_SLICE), + 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::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_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(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(&unwrap::PANICKING_UNWRAP), + LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&useless_conversion::USELESS_CONVERSION), + LintId::of(&vec::USELESS_VEC), + LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), + 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(&let_and_return::LET_AND_RETURN), + LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), + LintId::of(&loops::EMPTY_LOOP), + LintId::of(&loops::FOR_KV_MAP), + LintId::of(&loops::NEEDLESS_RANGE_LOOP), + LintId::of(&loops::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::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_NEXT_SLICE), + 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::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(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + 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_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), + LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), + ]); + + 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..318d0b69d57b7 --- /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::OpaqueDef(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..7ba43562d7d44 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -0,0 +1,442 @@ +//! 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 + /// // Bad + /// let x: u64 = 61864918973511; + /// + /// // Good + /// let x: u64 = 61_864_918_973_511; + /// ``` + 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 + /// // Probably mistyped + /// 2_32; + /// + /// // Good + /// 2_i32; + /// ``` + 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 + /// // Bad + /// let x: u64 = 618_64_9189_73_511; + /// + /// // Good + /// let x: u64 = 61_864_918_973_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..9c8e8d8fabf4e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops.rs @@ -0,0 +1,2449 @@ +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, 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, TyS}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::symbol::Symbol; +use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +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 TyS::same_type(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 TyS::same_type(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<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + hir_id_low: Option, + hir_id_high: Option, + span_low: Option, + span_high: Option, +} + +impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { + fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: ConsumeMode) {} + + fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { + if let ty::BorrowKind::MutBorrow = bk { + if let PlaceBase::Local(id) = cmt.place.base { + if Some(id) == self.hir_id_low { + self.span_low = Some(self.cx.tcx.hir().span(cmt.hir_id)) + } + if Some(id) == self.hir_id_high { + self.span_high = Some(self.cx.tcx.hir().span(cmt.hir_id)) + } + } + } + } + + fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>) { + if let PlaceBase::Local(id) = cmt.place.base { + if Some(id) == self.hir_id_low { + self.span_low = Some(self.cx.tcx.hir().span(cmt.hir_id)) + } + if Some(id) == self.hir_id_high { + self.span_high = Some(self.cx.tcx.hir().span(cmt.hir_id)) + } + } + } +} + +impl 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<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + body: &Expr<'_>, + bound_ids: &[Option], +) -> (Option, Option) { + let mut delegate = MutatePairDelegate { + cx, + 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 expr.hir_id == self.end_expr.hir_id { + 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<'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_span(expr, sym!(collect)); + 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_span(expr, sym!(iter)); + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + span, + NEEDLESS_COLLECT_MSG, + "replace with", + "get(0).is_none()".to_string(), + Applicability::MachineApplicable, + ); + } + if method.ident.name == sym!(contains) { + let contains_arg = snippet(cx, args[1].span, "??"); + let span = shorten_span(expr, sym!(collect)); + 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_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { + let mut current_expr = expr; + while let ExprKind::MethodCall(ref path, ref span, ref args, _) = current_expr.kind { + if path.ident.name == target_fn_name { + return expr.span.with_lo(span.lo()); + } + current_expr = &args[0]; + } + 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..b845b20d2c012 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -0,0 +1,232 @@ +use crate::utils::{in_macro, snippet, span_lint_and_sugg}; +use hir::def::{DefKind, Res}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{edition::Edition, Span}; + +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:** None. + /// + /// **Example:** + /// ```rust + /// #[macro_use] + /// use lazy_static; + /// ``` + pub MACRO_USE_IMPORTS, + pedantic, + "#[macro_use] is no longer needed" +} + +const BRACKETS: &[char] = &['<', '>']; + +#[derive(Clone, Debug, PartialEq, Eq)] +struct PathAndSpan { + path: String, + span: Span, +} + +/// `MacroRefData` includes the name of the macro +/// and the path from `SourceMap::span_to_filename`. +#[derive(Debug, Clone)] +pub struct MacroRefData { + name: String, + path: String, +} + +impl MacroRefData { + pub fn new(name: String, callee: Span, cx: &LateContext<'_, '_>) -> Self { + let mut path = cx.sess().source_map().span_to_filename(callee).to_string(); + + // std lib paths are <::std::module::file type> + // so remove brackets, space and type. + if path.contains('<') { + path = path.replace(BRACKETS, ""); + } + if path.contains(' ') { + path = path.split(' ').next().unwrap().to_string(); + } + Self { name, path } + } +} + +#[derive(Default)] +#[allow(clippy::module_name_repetitions)] +pub struct MacroUseImports { + /// the actual import path used and the span of the attribute above it. + imports: Vec<(String, Span)>, + /// the span of the macro reference, kept to ensure only one reference is used per macro call. + collected: FxHashSet, + mac_refs: Vec, +} + +impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]); + +impl MacroUseImports { + fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, span: Span) { + let call_site = span.source_callsite(); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = span.source_callee() { + if !self.collected.contains(&call_site) { + let name = if name.contains("::") { + name.split("::").last().unwrap().to_string() + } else { + name.to_string() + }; + + self.mac_refs.push(MacroRefData::new(name, callee.def_site, cx)); + self.collected.insert(call_site); + } + } + } + + fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, span: Span) { + let call_site = span.source_callsite(); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = span.source_callee() { + if !self.collected.contains(&call_site) { + self.mac_refs + .push(MacroRefData::new(name.to_string(), callee.def_site, cx)); + self.collected.insert(call_site); + } + } + } +} + +impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { + fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item<'_>) { + if_chain! { + if cx.sess().opts.edition == Edition::Edition2018; + if let hir::ItemKind::Use(path, _kind) = &item.kind; + if let Some(mac_attr) = item + .attrs + .iter() + .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); + if let Res::Def(DefKind::Mod, id) = path.res; + then { + for kid in cx.tcx.item_children(id).iter() { + if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { + let span = mac_attr.span; + let def_path = cx.tcx.def_path_str(mac_id); + self.imports.push((def_path, span)); + } + } + } else { + if in_macro(item.span) { + self.push_unique_macro_pat_ty(cx, item.span); + } + } + } + } + fn check_attribute(&mut self, cx: &LateContext<'_, '_>, attr: &ast::Attribute) { + if in_macro(attr.span) { + self.push_unique_macro(cx, attr.span); + } + } + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { + if in_macro(expr.span) { + self.push_unique_macro(cx, expr.span); + } + } + fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) { + if in_macro(stmt.span) { + self.push_unique_macro(cx, stmt.span); + } + } + fn check_pat(&mut self, cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) { + if in_macro(pat.span) { + self.push_unique_macro_pat_ty(cx, pat.span); + } + } + fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) { + if in_macro(ty.span) { + self.push_unique_macro_pat_ty(cx, ty.span); + } + } + #[allow(clippy::too_many_lines)] + fn check_crate_post(&mut self, cx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) { + let mut used = FxHashMap::default(); + let mut check_dup = vec![]; + for (import, span) in &self.imports { + let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name)); + + if let Some(idx) = found_idx { + let _ = self.mac_refs.remove(idx); + let seg = import.split("::").collect::>(); + + match seg.as_slice() { + // an empty path is impossible + // a path should always consist of 2 or more segments + [] | [_] => return, + [root, item] => { + if !check_dup.contains(&(*item).to_string()) { + used.entry(((*root).to_string(), span)) + .or_insert_with(Vec::new) + .push((*item).to_string()); + check_dup.push((*item).to_string()); + } + }, + [root, rest @ ..] => { + if rest.iter().all(|item| !check_dup.contains(&(*item).to_string())) { + let filtered = rest + .iter() + .filter_map(|item| { + if check_dup.contains(&(*item).to_string()) { + None + } else { + Some((*item).to_string()) + } + }) + .collect::>(); + used.entry(((*root).to_string(), span)) + .or_insert_with(Vec::new) + .push(filtered.join("::")); + check_dup.extend(filtered); + } else { + let rest = rest.to_vec(); + used.entry(((*root).to_string(), span)) + .or_insert_with(Vec::new) + .push(rest.join("::")); + check_dup.extend(rest.iter().map(ToString::to_string)); + } + }, + } + } + } + + let mut suggestions = vec![]; + for ((root, span), path) in used { + if path.len() == 1 { + suggestions.push((span, format!("{}::{}", root, path[0]))) + } else { + suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")))) + } + } + + // If mac_refs is not empty we have encountered an import we could not handle + // such as `std::prelude::v1::foo` or some other macro that expands to an import. + if self.mac_refs.is_empty() { + for (span, import) in suggestions { + let help = format!("use {};", import); + span_lint_and_sugg( + cx, + MACRO_USE_IMPORTS, + *span, + "`macro_use` attributes are no longer needed in the Rust 2018 edition", + "remove the attribute and import the macro directly, try", + help, + Applicability::MaybeIncorrect, + ) + } + } + } +} 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..03ab274d9ca9c --- /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::OpaqueDef(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..8f4fdc685ef38 --- /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..8f4b674c04f49 --- /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..6d7af45a47224 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/matches.rs @@ -0,0 +1,1335 @@ +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"); + /// + /// // Bad + /// match x { + /// Some(ref foo) => bar(foo), + /// _ => (), + /// } + /// + /// // Good + /// if let Some(ref foo) = x { + /// 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 + /// // Bad + /// match x { + /// &A(ref y) => foo(y), + /// &B => bar(), + /// _ => frob(&x), + /// } + /// + /// // Good + /// 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, similar to + /// 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, + pedantic, + "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; + /// + /// // Bad + /// let r: Option<&()> = match x { + /// None => None, + /// Some(ref v) => Some(v), + /// }; + /// + /// // Good + /// let r: Option<&()> = x.as_ref(); + /// ``` + 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); + /// + /// // Bad + /// match x { + /// Foo::A(_) => {}, + /// _ => {}, + /// } + /// + /// // Good + /// match x { + /// Foo::A(_) => {}, + /// Foo::B(_) => {}, + /// } + /// ``` + pub WILDCARD_ENUM_MATCH_ARM, + restriction, + "a wildcard enum match arm using `_`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for wildcard enum matches for a single variant. + /// + /// **Why is this bad?** New enum variants added by library updates can be missed. + /// + /// **Known problems:** Suggested replacements may not use correct path to enum + /// if it's not present in the current scope. + /// + /// **Example:** + /// + /// ```rust + /// # enum Foo { A, B, C } + /// # let x = Foo::B; + /// // Bad + /// match x { + /// Foo::A => {}, + /// Foo::B => {}, + /// _ => {}, + /// } + /// + /// // Good + /// match x { + /// Foo::A => {}, + /// Foo::B => {}, + /// Foo::C => {}, + /// } + /// ``` + pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + pedantic, + "a wildcard enum match for a single variant" +} + +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 + /// // Bad + /// match "foo" { + /// "a" => {}, + /// "bar" | _ => {}, + /// } + /// + /// // Good + /// match "foo" { + /// "a" => {}, + /// _ => {}, + /// } + /// ``` + 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, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + 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, or use `.except(msg)` if the error case is unreachable", + ); + } + } + } + } + } + } +} + +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, ref patterns, ..) = arm.pat.kind { + if let QPath::Resolved(_, p) = path { + // Some simple checks for exhaustive patterns. + // There is a room for improvements to detect more cases, + // but it can be more expensive to do so. + let is_pattern_exhaustive = |pat: &&Pat<'_>| { + if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind { + true + } else { + false + } + }; + if patterns.iter().all(is_pattern_exhaustive) { + 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("_")); + } + } + + if suggestion.len() == 1 { + // No need to check for non-exhaustive enum as in that case len would be greater than 1 + span_lint_and_sugg( + cx, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + wildcard_span, + message, + "try this", + suggestion[0].clone(), + Applicability::MaybeIncorrect, + ) + }; + + span_lint_and_sugg( + cx, + WILDCARD_ENUM_MATCH_ARM, + wildcard_span, + message, + "try this", + suggestion.join(" | "), + Applicability::MaybeIncorrect, + ) + } +} + +// 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..e2672e02b36da --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -0,0 +1,243 @@ +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<'_>, dest: &Expr<'_>, expr_span: Span) { + if_chain! { + // check if replacement is mem::MaybeUninit::uninit().assume_init() + if let Some(method_def_id) = cx.tables.type_dependent_def_id(src.hir_id); + if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::MaybeUninit::uninit().assume_init()`", + "consider using", + format!( + "std::ptr::read({})", + snippet_with_applicability(cx, dest.span, "", &mut applicability) + ), + applicability, + ); + return; + } + } + + if_chain! { + if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind; + 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) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_WITH_UNINIT, + expr_span, + "replacing with `mem::uninitialized()`", + "consider using", + format!( + "std::ptr::read({})", + snippet_with_applicability(cx, dest.span, "", &mut applicability) + ), + applicability, + ); + } 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, dest, 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..4f5c06e785c23 --- /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" | "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..f25a9782813bb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -0,0 +1,3662 @@ +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, Ty, TyS}; +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, higher, implements_trait, in_macro, is_copy, + is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, + match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, + remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, + walk_ptrs_ty_depth, SpanlessEq, +}; + +declare_clippy_lint! { + /// **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::<_, ()>(()); + /// + /// // Bad + /// x.ok().expect("why did I do this again?"); + /// + /// // Good + /// x.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); + /// + /// // Bad + /// opt.map_or(None, |a| Some(a + 1)); + /// + /// // Good + /// opt.and_then(|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 using `_.flat_map(_)` + /// + /// **Known problems:** + /// + /// **Example:** + /// ```rust + /// let vec = vec![vec![1]]; + /// + /// // Bad + /// vec.iter().map(|x| x.iter()).flatten(); + /// + /// // Good + /// vec.iter().flat_map(|x| x.iter()); + /// ``` + 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]; + /// + /// // Bad + /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2); + /// + /// // Good + /// vec.iter().filter_map(|x| if *x == 0 { + /// Some(*x * 2) + /// } else { + /// None + /// }); + /// ``` + 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); + /// + /// // Bad + /// x.clone(); + /// + /// // Good + /// Rc::clone(&x); + /// ``` + 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:** + /// ```rust,ignore + /// // Bad + /// _.split("x"); + /// + /// // Good + /// _.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 = "_"; + /// + /// // Bad + /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-'); + /// + /// // Good + /// name.ends_with('_') || name.ends_with('-'); + /// ``` + 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: + /// 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: + /// 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 + /// // Bad + /// let _ = (&vec![3, 4, 5]).into_iter(); + /// + /// // Good + /// let _ = (&vec![3, 4, 5]).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_clippy_lint! { + /// **What it does:** Checks for usage of `iter().next()` on a Slice or an Array + /// + /// **Why is this bad?** These can be shortened into `.get()` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let a = [1, 2, 3]; + /// # let b = vec![1, 2, 3]; + /// a[2..].iter().next(); + /// b.iter().next(); + /// ``` + /// should be written as: + /// ```rust + /// # let a = [1, 2, 3]; + /// # let b = vec![1, 2, 3]; + /// a.get(2); + /// b.get(0); + /// ``` + pub ITER_NEXT_SLICE, + style, + "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" +} + +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_NEXT_SLICE, + 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]), + ["next", "iter"] => lint_iter_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" | "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" | "checked_sub" | "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) => TyS::same_type(self_ty, inner_ty), + + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) + }; + + // walk the return type and check for Self (this does not check associated types) + if contains_self_ty(ret_ty) { + 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 { + if let ty::PredicateKind::Projection(poly_projection_predicate) = predicate.0.kind() { + 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" && !TyS::same_type(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, + ) { + if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { + if path.ident.as_str() == "len" { + let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0])); + + match ty.kind { + ty::Slice(_) | ty::Array(_, _) => return, + _ => (), + } + + if match_type(cx, ty, &paths::VEC) { + return; + } + } + } + + // (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 | 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_next<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { + let caller_expr = &iter_args[0]; + + // Skip lint if the `iter().next()` expression is a for loop argument, + // since it is already covered by `&loops::ITER_NEXT_LOOP` + let mut parent_expr_opt = get_parent_expr(cx, expr); + while let Some(parent_expr) = parent_expr_opt { + if higher::for_loop(parent_expr).is_some() { + return; + } + parent_expr_opt = get_parent_expr(cx, parent_expr); + } + + if derefs_to_slice(cx, caller_expr, cx.tables.expr_ty(caller_expr)).is_some() { + // caller is a Slice + if_chain! { + if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind; + if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) + = higher::range(cx, index_expr); + if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; + if let ast::LitKind::Int(start_idx, _) = start_lit.node; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "Using `.iter().next()` on a Slice without end index.", + "try calling", + format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), + applicability, + ); + } + } + } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(caller_expr), sym!(vec_type)) + || matches!(&walk_ptrs_ty(cx.tables.expr_ty(caller_expr)).kind, ty::Array(_, _)) + { + // caller is a Vec or an Array + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "Using `.iter().next()` on an array", + "try calling", + format!( + "{}.get(0)", + snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability) + ), + applicability, + ); + } +} + +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..a0947608e6077 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -0,0 +1,720 @@ +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,ignore + /// // Bad + /// fn foo(ref x: u8) -> bool { + /// true + /// } + /// + /// // Good + /// fn foo(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; + /// + /// // Bad + /// if x == f32::NAN { } + /// + /// // Good + /// if x.is_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; + /// + /// // Bad + /// if y == 1.23f64 { } + /// if y != x {} // where both are floats + /// + /// // Good + /// let error = 0.01f64; // Use an epsilon for comparison + /// if (y - 1.23f64).abs() < error { } + /// if (y - x).abs() > error { } + /// ``` + 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 + /// // Bad + /// let a = 0 as *const u32; + /// + /// // Good + /// let a = std::ptr::null::(); + /// ``` + 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; + /// + /// // Bad + /// if x == ONE { } // where both are floats + /// + /// // Good + /// let error = 0.1f64; // Use an epsilon for comparison + /// if (x - ONE).abs() < error { } + /// ``` + 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) { + if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind { + 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..ad39e59d0678a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/misc_early.rs @@ -0,0 +1,675 @@ +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 + /// // Bad + /// fn foo(a: i32, _a: i32) {} + /// + /// // Good + /// fn bar(a: i32, _b: 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 + /// // Bad + /// let a = (|| 42)() + /// + /// // Good + /// let a = 42 + /// ``` + pub REDUNDANT_CLOSURE_CALL, + complexity, + "throwaway closures called in the expression they are defined" +} + +declare_clippy_lint! { + /// **What it does:** Detects expressions of the form `--x`. + /// + /// **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 + /// // Bad + /// let y = 0x1a9BAcD; + /// + /// // Good + /// 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 + /// // Bad + /// let y = 123832i32; + /// + /// // Good + /// let y = 123832_i32; + /// ``` + 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"); + /// + /// // Bad + /// match v { + /// Some(x) => (), + /// y @ _ => (), + /// } + /// + /// // Good + /// match v { + /// Some(x) => (), + /// 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); + /// + /// // Bad + /// match t { + /// TupleStruct(0, .., _) => (), + /// _ => (), + /// } + /// + /// // Good + /// 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 ", + BindingMode::ByValue(..) => "", + }; + + 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..9cfc8d1913497 --- /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; + } + }, + FnKind::Closure(..) => 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..0fd1e87f9e415 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -0,0 +1,203 @@ +// 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", + }; + 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..1802470b1841e --- /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().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(_) => 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..6c42014b4c8a1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/multiple_crate_versions.rs @@ -0,0 +1,96 @@ +//! lint on multiple versions of a crate being used + +use crate::utils::{run_lints, span_lint}; +use rustc_hir::def_id::LOCAL_CRATE; +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 cargo_metadata::{DependencyKind, Node, Package, PackageId}; +use if_chain::if_chain; +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 = unwrap_cargo_metadata!(cx, MULTIPLE_CRATE_VERSIONS, true); + let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str(); + let mut packages = metadata.packages; + packages.sort_by(|a, b| a.name.cmp(&b.name)); + + if_chain! { + if let Some(resolve) = &metadata.resolve; + if let Some(local_id) = packages + .iter() + .find_map(|p| if p.name == *local_name { Some(&p.id) } else { None }); + then { + for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { + let group: Vec<&Package> = group.collect(); + + if group.len() <= 1 { + continue; + } + + if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) { + let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect(); + versions.sort(); + let versions = versions.iter().join(", "); + + span_lint( + cx, + MULTIPLE_CRATE_VERSIONS, + DUMMY_SP, + &format!("multiple versions for dependency `{}`: {}", name, versions), + ); + } + } + } + } + } +} + +fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool { + fn depends_on(node: &Node, dep_id: &PackageId) -> bool { + node.deps.iter().any(|dep| { + dep.pkg == *dep_id + && dep + .dep_kinds + .iter() + .any(|info| matches!(info.kind, DependencyKind::Normal)) + }) + } + + nodes + .iter() + .filter(|node| depends_on(node, dep_id)) + .any(|node| node.id == *local_id || is_normal_dep(nodes, local_id, &node.id)) +} 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..93569a04f7a3a --- /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.at(span), cx.param_env), + _ => 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..7fcf15f8acbe8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -0,0 +1,86 @@ +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 passing a mutable reference to a function that only + /// requires an immutable reference. + /// + /// **Why is this bad?** The immutable reference rules out all other references + /// to the value. Also the code misleads about the intent of the call site. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// // Bad + /// my_vec.push(&mut value) + /// + /// // Good + /// my_vec.push(&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..78b15afc5a7fa --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -0,0 +1,98 @@ +//! 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 + /// # let y = true; + /// + /// // Bad + /// # use std::sync::Mutex; + /// let x = Mutex::new(&y); + /// + /// // Good + /// # use std::sync::atomic::AtomicBool; + /// let x = AtomicBool::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); + /// + /// // Good + /// # use std::sync::atomic::AtomicUsize; + /// let x = AtomicUsize::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..15b129fa09802 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -0,0 +1,347 @@ +//! 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 suggests 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..5880d1d610206 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_borrow.rs @@ -0,0 +1,128 @@ +//! 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. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// let x: &i32 = &&&&&&5; + /// + /// // Good + /// let x: &i32 = &5; + /// ``` + 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..a971d041ca661 --- /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.get(0).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..ca87deac9891c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -0,0 +1,346 @@ +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(..) => (), + FnKind::Closure(..) => 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()) + .filter(|p| !p.is_global()) + .filter_map(|obligation| { + if let ty::PredicateKind::Trait(poly_trait_ref, _) = obligation.predicate.kind() { + if poly_trait_ref.def_id() == sized_trait || poly_trait_ref.skip_binder().has_escaping_bound_vars() + { + 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) + .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::PlaceWithHirId<'_>) { + if let euv::PlaceBase::Local(vid) = cmt.place.base { + self.moved_vars.insert(vid); + } + } +} + +impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { + fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, mode: euv::ConsumeMode) { + if let euv::ConsumeMode::Move = mode { + self.move_common(cmt); + } + } + + fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: ty::BorrowKind) {} + + fn mutate(&mut self, _: &euv::PlaceWithHirId<'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..d866bab2f642c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_update.rs @@ -0,0 +1,63 @@ +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 }; + /// + /// // Bad + /// Point { + /// x: 1, + /// y: 1, + /// z: 1, + /// ..zero_point + /// }; + /// + /// // Ok + /// 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..42200385932b0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -0,0 +1,162 @@ +use crate::utils::paths; +use crate::utils::sugg::DiagnosticBuilderExt; +use crate::utils::{get_trait_def_id, return_ty, span_lint_hir_and_then}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::HirIdSet; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{Ty, TyS}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +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). + /// + /// **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()) + /// } + /// } + /// ``` + /// + /// To fix the lint, add a `Default` implementation that delegates to `new`: + /// + /// ```ignore + /// struct Foo(Bar); + /// + /// impl Default for Foo { + /// fn default() -> Self { + /// Foo::new() + /// } + /// } + /// ``` + 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_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id)); + let self_ty = cx.tcx.type_of(self_def_id); + if_chain! { + if TyS::same_type(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_def_id).ty_adt_def(); + if let Some(self_local_did) = self_def.did.as_local(); + then { + let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); + if impling_types.contains(&self_id) { + return; + } + } + } + + 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) +} 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..2eacd3c80c486 --- /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 | DefKind::Variant | 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..230dfd2ebf566 --- /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.at(DUMMY_SP), cx.param_env) || 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..5f14fe97afefa --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -0,0 +1,423 @@ +use crate::utils::{span_lint, span_lint_and_then}; +use rustc_ast::ast::{ + Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind, +}; +use rustc_ast::attr; +use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +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, _) => { + if !pat.span.from_expansion() { + 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 in_external_macro(cx.sess, item.span) { + return; + } + + 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 in_external_macro(cx.sess, item.span) { + return; + } + + 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..2d4629b683f05 --- /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..fd653044a1bcb --- /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..88ad1e0914f25 --- /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..7dce23dd22306 --- /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..c77b44e0c99c7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -0,0 +1,316 @@ +//! Checks for usage of `&Vec[_]` and `&String`. + +use crate::utils::ptr::get_spans; +use crate::utils::{ + is_allowed, 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 + /// // Bad + /// fn foo(&Vec) { .. } + /// + /// // Good + /// fn foo(&[u32]) { .. } + /// ``` + 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 + /// // Bad + /// if x == ptr::null { + /// .. + /// } + /// + /// // Good + /// if x.is_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(); + let body = opt_body_id.map(|id| cx.tcx.hir().body(id)); + + for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() { + // Honor the allow attribute on parameters. See issue 5644. + if let Some(body) = &body { + if is_allowed(cx, PTR_ARG, body.params[idx].hir_id) { + continue; + } + } + + 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..d23d7e59b73fc --- /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..d8a73f8054bca --- /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.at(expression.span), cx.param_env) + } + + 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..fcd02a196e7bf --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -0,0 +1,355 @@ +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_for_loop_arg(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let mut cur_expr = expr; + while let Some(parent_expr) = get_parent_expr(cx, cur_expr) { + match higher::for_loop(parent_expr) { + Some((_, args, _)) if args.hir_id == expr.hir_id => return true, + _ => cur_expr = parent_expr, + } + } + + false + } + + 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) { + // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ... + if ordering != Ordering::Equal { + span_lint( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is reversed and using it to index a slice will panic at run-time", + ); + } + // ... except in for loop arguments for backwards compatibility with `reverse_range_loop` + } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) { + 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..2a81170e49e75 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs @@ -0,0 +1,67 @@ +use crate::utils::span_lint_and_sugg; +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +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 in_external_macro(cx.sess, expr.span) { + return; + } + 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..3c528a295b044 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/redundant_pattern_matching.rs @@ -0,0 +1,260 @@ +use crate::utils::{in_constant, 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, HirId, MatchSource, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_mir::const_eval::is_const_fn; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Symbol; + +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, +) { + fn find_suggestion(cx: &LateContext<'_, '_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { + if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { + return Some("is_ok()"); + } + if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") { + return Some("is_err()"); + } + if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") { + return Some("is_some()"); + } + if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") { + return Some("is_none()"); + } + None + } + + let hir_id = expr.hir_id; + 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 { + find_suggestion(cx, hir_id, path) + } else { + None + } + }, + PatKind::Path(ref path) => find_suggestion(cx, hir_id, path), + _ => None, + }; + let good_method = match good_method { + Some(method) => method, + 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 hir_id = expr.hir_id; + 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()", + || can_suggest(cx, hir_id, sym!(result_type), "is_ok"), + || can_suggest(cx, hir_id, sym!(result_type), "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()", + || can_suggest(cx, hir_id, sym!(option_type), "is_some"), + || can_suggest(cx, hir_id, sym!(option_type), "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 + ); + }, + ); + } + } +} + +#[allow(clippy::too_many_arguments)] +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, + can_suggest_left: impl Fn() -> bool, + can_suggest_right: impl Fn() -> bool, +) -> 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)) if can_suggest_left() => Some(should_be_left), + (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right), + _ => None, + }, + _ => None, + } +} + +fn can_suggest(cx: &LateContext<'_, '_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool { + if !in_constant(cx, hir_id) { + return true; + } + + // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697. + cx.tcx + .get_diagnostic_item(diag_item) + .and_then(|def_id| { + cx.tcx.inherent_impls(def_id).iter().find_map(|imp| { + cx.tcx + .associated_items(*imp) + .in_definition_order() + .find_map(|item| match item.kind { + ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id), + _ => None, + }) + }) + }) + .map_or(false, |def_id| is_const_fn(cx.tcx, def_id)) +} 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..fe457aad50e36 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -0,0 +1,103 @@ +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 + /// // Bad + /// let a = f(*&mut b); + /// let c = *&d; + /// + /// // Good + /// let a = f(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..a2c35c4267344 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -0,0 +1,265 @@ +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..3c93974417356 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -0,0 +1,283 @@ +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::{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 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, 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 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, + ); + }, + }, + } + } +} + +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) { + 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..68c36f9189184 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -0,0 +1,399 @@ +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; + /// + /// // Bad + /// let x = &x; + /// + /// // Good + /// let y = &x; // use different variable name + /// ``` + 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; + /// + /// // Bad + /// let x = z; // shadows the earlier binding + /// + /// // Good + /// let w = z; // use different variable name + /// ``` + 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..2e853e8301d69 --- /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..44c9cc19cfb4e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -0,0 +1,330 @@ +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; + /// + /// // Bad + /// let mut vec1 = Vec::with_capacity(len); + /// vec1.resize(len, 0); + /// + /// let mut vec2 = Vec::with_capacity(len); + /// vec2.extend(repeat(0).take(len)); + /// + /// // Good + /// let mut vec1 = vec![0; len]; + /// let mut vec2 = vec![0; 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..d8e4bff3d702a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -0,0 +1,208 @@ +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"; + /// + /// // More readable + /// x += ", World"; + /// x.push_str(", 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 + /// // Bad + /// let bs = "a byte string".as_bytes(); + /// + /// // Good + /// let bs = b"a byte string"; + /// ``` + 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..cf71c3144a2eb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -0,0 +1,203 @@ +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::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<'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::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..4f132c6db76fa --- /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..1869638f6ffb1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute.rs @@ -0,0 +1,650 @@ +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::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::Char, ty::Ref(..) | 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::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::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::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::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(from_adt, from_substs), ty::Adt(to_adt, 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..146ac4b09d5a4 --- /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<'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(..) => (), + FnKind::Closure(..) => 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..98de08f79f3d7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/types.rs @@ -0,0 +1,2622 @@ +#![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, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, + ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn, + TraitItem, TraitItemKind, TyKind, UnOp, +}; +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, TyS, 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, indent_of, int_bits, is_type_diagnostic_item, + last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, + qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, +}; + +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, _) => { + let args_to_recover = args + .iter() + .filter(|arg| { + if is_unit(cx.tables.expr_ty(arg)) && !is_unit_literal(arg) { + if let ExprKind::Match(.., MatchSource::TryDesugar) = &arg.kind { + false + } else { + true + } + } else { + false + } + }) + .collect::>(); + if !args_to_recover.is_empty() { + lint_unit_args(cx, expr, &args_to_recover); + } + }, + _ => (), + } + } +} + +fn lint_unit_args(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + let (singular, plural) = if args_to_recover.len() > 1 { + ("", "s") + } else { + ("a ", "") + }; + span_lint_and_then( + cx, + UNIT_ARG, + expr.span, + &format!("passing {}unit value{} to a function", singular, plural), + |db| { + let mut or = ""; + args_to_recover + .iter() + .filter_map(|arg| { + if_chain! { + if let ExprKind::Block(block, _) = arg.kind; + if block.expr.is_none(); + if let Some(last_stmt) = block.stmts.iter().last(); + if let StmtKind::Semi(last_expr) = last_stmt.kind; + if let Some(snip) = snippet_opt(cx, last_expr.span); + then { + Some(( + last_stmt.span, + snip, + )) + } + else { + None + } + } + }) + .for_each(|(span, sugg)| { + db.span_suggestion( + span, + "remove the semicolon from the last statement in the block", + sugg, + Applicability::MaybeIncorrect, + ); + or = "or "; + }); + let sugg = args_to_recover + .iter() + .filter(|arg| !is_empty_block(arg)) + .enumerate() + .map(|(i, arg)| { + let indent = if i == 0 { + 0 + } else { + indent_of(cx, expr.span).unwrap_or(0) + }; + format!( + "{}{};", + " ".repeat(indent), + snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) + ) + }) + .collect::>(); + let mut and = ""; + if !sugg.is_empty() { + let plural = if sugg.len() > 1 { "s" } else { "" }; + db.span_suggestion( + expr.span.with_hi(expr.span.lo()), + &format!("{}move the expression{} in front of the call...", or, plural), + format!("{}\n", sugg.join("\n")), + applicability, + ); + and = "...and " + } + db.multipart_suggestion( + &format!("{}use {}unit literal{} instead", and, singular, plural), + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + }, + ); +} + +fn is_empty_block(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Block( + Block { + stmts: &[], expr: None, .. + }, + _, + ) + ) +} + +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. Is over-zealous: Casts with manual alignment checks or casts like + /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis. + /// + /// **Example:** + /// ```rust + /// let _ = (&1u8 as *const u8) as *const u16; + /// let _ = (&mut 1u8 as *mut u8) as *mut u16; + /// ``` + pub CAST_PTR_ALIGNMENT, + pedantic, + "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 >> (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 >> (128 - int_bits(cx.tcx, ity)), ity) => { + Maximum + }, + (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, 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 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)), FullInt::S(i128::from(i8::MAX))), + IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))), + IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))), + IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))), + IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)), + IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)), + }), + ty::Uint(uint_ty) => Some(match uint_ty { + UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))), + UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))), + UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))), + UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))), + UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)), + UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX 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 !TyS::same_type(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/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs new file mode 100644 index 0000000000000..6ac6a12529c86 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs @@ -0,0 +1,270 @@ +use crate::utils; +use crate::utils::paths; +use crate::utils::sugg::Sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// **What it does:** + /// Detects when people use `Vec::sort_by` and pass in a function + /// which compares the two arguments, either directly or indirectly. + /// + /// **Why is this bad?** + /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if + /// possible) than to use `Vec::sort_by` and and a more complicated + /// closure. + /// + /// **Known problems:** + /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't + /// imported by a use statement in the current frame, then a `use` + /// statement that imports it will need to be added (which this lint + /// can't do). + /// + /// **Example:** + /// + /// ```rust + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec = Vec::new(); + /// vec.sort_by(|a, b| a.foo().cmp(&b.foo())); + /// ``` + /// Use instead: + /// ```rust + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec = Vec::new(); + /// vec.sort_by_key(|a| a.foo()); + /// ``` + pub UNNECESSARY_SORT_BY, + complexity, + "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer" +} + +declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]); + +enum LintTrigger { + Sort(SortDetection), + SortByKey(SortByKeyDetection), +} + +struct SortDetection { + vec_name: String, + unstable: bool, +} + +struct SortByKeyDetection { + vec_name: String, + closure_arg: String, + closure_body: String, + reverse: bool, + unstable: bool, +} + +/// Detect if the two expressions are mirrored (identical, except one +/// contains a and the other replaces it with b) +fn mirrored_exprs( + cx: &LateContext<'_, '_>, + a_expr: &Expr<'_>, + a_ident: &Ident, + b_expr: &Expr<'_>, + b_ident: &Ident, +) -> bool { + match (&a_expr.kind, &b_expr.kind) { + // Two boxes with mirrored contents + (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, + // Two arrays with mirrored contents + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // The two exprs are function calls. + // Check to see that the function itself and its arguments are mirrored + (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, + // The two exprs are method calls. + // Check to see that the function is the same and the arguments are mirrored + // This is enough because the receiver of the method is listed in the arguments + ( + ExprKind::MethodCall(left_segment, _, left_args, _), + ExprKind::MethodCall(right_segment, _, right_args, _), + ) => { + left_segment.ident == right_segment.ident + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, + // Two tuples with mirrored contents + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // Two binary ops, which are the same operation and which have mirrored arguments + (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { + left_op.node == right_op.node + && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) + && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident) + }, + // Two unary ops, which are the same operation and which have the same argument + (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => { + left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, + // The two exprs are literals of some kind + (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, + (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => { + mirrored_exprs(cx, left_block, a_ident, right_block, b_ident) + }, + (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => { + left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident) + }, + // Two paths: either one is a and the other is b, or they're identical to each other + ( + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: left_segments, + .. + }, + )), + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: right_segments, + .. + }, + )), + ) => { + (left_segments + .iter() + .zip(right_segments.iter()) + .all(|(left, right)| left.ident == right.ident) + && left_segments + .iter() + .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) + || (left_segments.len() == 1 + && &left_segments[0].ident == a_ident + && right_segments.len() == 1 + && &right_segments[0].ident == b_ident) + }, + // Matching expressions, but one or both is borrowed + ( + ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), + ExprKind::AddrOf(right_kind, Mutability::Not, right_expr), + ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => { + mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident) + }, + (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), + _ => false, + } +} + +fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; + if let name = name_ident.ident.name.to_ident_string(); + if name == "sort_by" || name == "sort_unstable_by"; + if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; + if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + if let closure_body = cx.tcx.hir().body(*closure_body_id); + if let &[ + Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, + Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } + ] = &closure_body.params; + if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr], _) = &closure_body.value.kind; + if method_path.ident.name.to_ident_string() == "cmp"; + then { + let (closure_body, closure_arg, reverse) = if mirrored_exprs( + &cx, + &left_expr, + &left_ident, + &right_expr, + &right_ident + ) { + (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string(), false) + } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { + (Sugg::hir(cx, &left_expr, "..").to_string(), right_ident.name.to_string(), true) + } else { + return None; + }; + let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); + let unstable = name == "sort_unstable_by"; + if_chain! { + if let ExprKind::Path(QPath::Resolved(_, Path { + segments: [PathSegment { ident: left_name, .. }], .. + })) = &left_expr.kind; + if left_name == left_ident; + then { + Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) + } + else { + Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })) + } + } + } else { + None + } + } +} + +impl LateLintPass<'_, '_> for UnnecessarySortBy { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + match detect_lint(cx, expr) { + Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + "use Vec::sort_by_key here instead", + "try", + format!( + "{}.sort{}_by_key(|&{}| {})", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + trigger.closure_arg, + if trigger.reverse { + format!("Reverse({})", trigger.closure_body) + } else { + trigger.closure_body.to_string() + }, + ), + if trigger.reverse { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }, + ), + Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + "use Vec::sort here instead", + "try", + format!( + "{}.sort{}()", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + ), + Applicability::MachineApplicable, + ), + None => {}, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs new file mode 100644 index 0000000000000..4d3682263f14f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -0,0 +1,407 @@ +#![allow(clippy::wildcard_imports, clippy::enum_glob_use)] + +use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; +use crate::utils::{over, span_lint_and_then}; +use rustc_ast::ast::{self, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; +use rustc_ast::mut_visit::*; +use rustc_ast::ptr::P; +use rustc_ast_pretty::pprust; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::DUMMY_SP; + +use std::cell::Cell; +use std::mem; + +declare_clippy_lint! { + /// **What it does:** + /// + /// Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and + /// suggests replacing the pattern with a nested one, `Some(0 | 2)`. + /// + /// Another way to think of this is that it rewrites patterns in + /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. + /// + /// **Why is this bad?** + /// + /// In the example above, `Some` is repeated, which unncessarily complicates the pattern. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// if let Some(0) | Some(2) = Some(0) {} + /// } + /// ``` + /// Use instead: + /// ```rust + /// #![feature(or_patterns)] + /// + /// fn main() { + /// if let Some(0 | 2) = Some(0) {} + /// } + /// ``` + pub UNNESTED_OR_PATTERNS, + pedantic, + "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" +} + +declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); + +impl EarlyLintPass for UnnestedOrPatterns { + fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { + lint_unnested_or_patterns(cx, &a.pat); + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + if let ast::ExprKind::Let(pat, _) = &e.kind { + lint_unnested_or_patterns(cx, pat); + } + } + + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { + lint_unnested_or_patterns(cx, &p.pat); + } + + fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { + lint_unnested_or_patterns(cx, &l.pat); + } +} + +fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { + if !cx.sess.opts.unstable_features.is_nightly_build() { + // User cannot do `#![feature(or_patterns)]`, so bail. + return; + } + + if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { + // This is a leaf pattern, so cloning is unprofitable. + return; + } + + let mut pat = P(pat.clone()); + + // Nix all the paren patterns everywhere so that they aren't in our way. + remove_all_parens(&mut pat); + + // Transform all unnested or-patterns into nested ones, and if there were none, quit. + if !unnest_or_patterns(&mut pat) { + return; + } + + span_lint_and_then(cx, UNNESTED_OR_PATTERNS, pat.span, "unnested or-patterns", |db| { + insert_necessary_parens(&mut pat); + db.span_suggestion_verbose( + pat.span, + "nest the patterns", + pprust::pat_to_string(&pat), + Applicability::MachineApplicable, + ); + }); +} + +/// Remove all `(p)` patterns in `pat`. +fn remove_all_parens(pat: &mut P) { + struct Visitor; + impl MutVisitor for Visitor { + fn visit_pat(&mut self, pat: &mut P) { + noop_visit_pat(pat, self); + let inner = match &mut pat.kind { + Paren(i) => mem::replace(&mut i.kind, Wild), + _ => return, + }; + pat.kind = inner; + } + } + Visitor.visit_pat(pat); +} + +/// Insert parens where necessary according to Rust's precedence rules for patterns. +fn insert_necessary_parens(pat: &mut P) { + struct Visitor; + impl MutVisitor for Visitor { + fn visit_pat(&mut self, pat: &mut P) { + use ast::{BindingMode::*, Mutability::*}; + noop_visit_pat(pat, self); + let target = match &mut pat.kind { + // `i @ a | b`, `box a | b`, and `& mut? a | b`. + Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p, + Ref(p, Not) if matches!(p.kind, Ident(ByValue(Mut), ..)) => p, // `&(mut x)` + _ => return, + }; + target.kind = Paren(P(take_pat(target))); + } + } + Visitor.visit_pat(pat); +} + +/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`. +/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`. +fn unnest_or_patterns(pat: &mut P) -> bool { + struct Visitor { + changed: bool, + } + impl MutVisitor for Visitor { + fn visit_pat(&mut self, p: &mut P) { + // This is a bottom up transformation, so recurse first. + noop_visit_pat(p, self); + + // Don't have an or-pattern? Just quit early on. + let alternatives = match &mut p.kind { + Or(ps) => ps, + _ => return, + }; + + // Collapse or-patterns directly nested in or-patterns. + let mut idx = 0; + let mut this_level_changed = false; + while idx < alternatives.len() { + let inner = if let Or(ps) = &mut alternatives[idx].kind { + mem::take(ps) + } else { + idx += 1; + continue; + }; + this_level_changed = true; + alternatives.splice(idx..=idx, inner); + } + + // Focus on `p_n` and then try to transform all `p_i` where `i > n`. + let mut focus_idx = 0; + while focus_idx < alternatives.len() { + this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx); + focus_idx += 1; + } + self.changed |= this_level_changed; + + // Deal with `Some(Some(0)) | Some(Some(1))`. + if this_level_changed { + noop_visit_pat(p, self); + } + } + } + + let mut visitor = Visitor { changed: false }; + visitor.visit_pat(pat); + visitor.changed +} + +/// Match `$scrutinee` against `$pat` and extract `$then` from it. +/// Panics if there is no match. +macro_rules! always_pat { + ($scrutinee:expr, $pat:pat => $then:expr) => { + match $scrutinee { + $pat => $then, + _ => unreachable!(), + } + }; +} + +/// Focus on `focus_idx` in `alternatives`, +/// attempting to extend it with elements of the same constructor `C` +/// in `alternatives[focus_idx + 1..]`. +fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) -> bool { + // Extract the kind; we'll need to make some changes in it. + let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild); + // We'll focus on `alternatives[focus_idx]`, + // so we're draining from `alternatives[focus_idx + 1..]`. + let start = focus_idx + 1; + + // We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`. + let changed = match &mut focus_kind { + // These pattern forms are "leafs" and do not have sub-patterns. + // Therefore they are not some form of constructor `C`, + // with which a pattern `C(p_0)` may be formed, + // which we would want to join with other `C(p_j)`s. + Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) + // Dealt with elsewhere. + | Or(_) | Paren(_) => false, + // Transform `box x | ... | box y` into `box (x | y)`. + // + // The cases below until `Slice(...)` deal with *singleton* products. + // These patterns have the shape `C(p)`, and not e.g., `C(p0, ..., pn)`. + Box(target) => extend_with_matching( + target, start, alternatives, + |k| matches!(k, Box(_)), + |k| always_pat!(k, Box(p) => p), + ), + // Transform `&m x | ... | &m y` into `&m (x | y)`. + Ref(target, m1) => extend_with_matching( + target, start, alternatives, + |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match. + |k| always_pat!(k, Ref(p, _) => p), + ), + // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. + Ident(b1, i1, Some(target)) => extend_with_matching( + target, start, alternatives, + // Binding names must match. + |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)), + |k| always_pat!(k, Ident(_, _, Some(p)) => p), + ), + // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`. + Slice(ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)), + |k| always_pat!(k, Slice(ps) => ps), + ), + // Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post)`. + Tuple(ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)), + |k| always_pat!(k, Tuple(ps) => ps), + ), + // Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post)`. + TupleStruct(path1, ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!( + k, + TupleStruct(path2, ps2) if eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) + ), + |k| always_pat!(k, TupleStruct(_, ps) => ps), + ), + // Transform a record pattern `S { fp_0, ..., fp_n }`. + Struct(path1, fps1, rest1) => extend_with_struct_pat(path1, fps1, *rest1, start, alternatives), + }; + + alternatives[focus_idx].kind = focus_kind; + changed +} + +/// Here we focusing on a record pattern `S { fp_0, ..., fp_n }`. +/// In particular, for a record pattern, the order in which the field patterns is irrelevant. +/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern +/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. +fn extend_with_struct_pat( + path1: &ast::Path, + fps1: &mut Vec, + rest1: bool, + start: usize, + alternatives: &mut Vec>, +) -> bool { + (0..fps1.len()).any(|idx| { + let pos_in_2 = Cell::new(None); // The element `k`. + let tail_or = drain_matching( + start, + alternatives, + |k| { + matches!(k, Struct(path2, fps2, rest2) + if rest1 == *rest2 // If one struct pattern has `..` so must the other. + && eq_path(path1, path2) + && fps1.len() == fps2.len() + && fps1.iter().enumerate().all(|(idx_1, fp1)| { + if idx_1 == idx { + // In the case of `k`, we merely require identical field names + // so that we will transform into `ident_k: p1_k | p2_k`. + let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident)); + pos_in_2.set(pos); + pos.is_some() + } else { + fps2.iter().any(|fp2| eq_field_pat(fp1, fp2)) + } + })) + }, + // Extract `p2_k`. + |k| always_pat!(k, Struct(_, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat), + ); + extend_with_tail_or(&mut fps1[idx].pat, tail_or) + }) +} + +/// Like `extend_with_matching` but for products with > 1 factor, e.g., `C(p_0, ..., p_n)`. +/// Here, the idea is that we fixate on some `p_k` in `C`, +/// allowing it to vary between two `targets` and `ps2` (returned by `extract`), +/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post), +/// where `~` denotes semantic equality. +fn extend_with_matching_product( + targets: &mut Vec>, + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind, &[P], usize) -> bool, + extract: impl Fn(PatKind) -> Vec>, +) -> bool { + (0..targets.len()).any(|idx| { + let tail_or = drain_matching( + start, + alternatives, + |k| predicate(k, targets, idx), + |k| extract(k).swap_remove(idx), + ); + extend_with_tail_or(&mut targets[idx], tail_or) + }) +} + +/// Extract the pattern from the given one and replace it with `Wild`. +/// This is meant for temporarily swapping out the pattern for manipulation. +fn take_pat(from: &mut Pat) -> Pat { + let dummy = Pat { + id: DUMMY_NODE_ID, + kind: Wild, + span: DUMMY_SP, + }; + mem::replace(from, dummy) +} + +/// Extend `target` as an or-pattern with the alternatives +/// in `tail_or` if there are any and return if there were. +fn extend_with_tail_or(target: &mut Pat, tail_or: Vec>) -> bool { + fn extend(target: &mut Pat, mut tail_or: Vec>) { + match target { + // On an existing or-pattern in the target, append to it. + Pat { kind: Or(ps), .. } => ps.append(&mut tail_or), + // Otherwise convert the target to an or-pattern. + target => { + let mut init_or = vec![P(take_pat(target))]; + init_or.append(&mut tail_or); + target.kind = Or(init_or); + }, + } + } + + let changed = !tail_or.is_empty(); + if changed { + // Extend the target. + extend(target, tail_or); + } + changed +} + +// Extract all inner patterns in `alternatives` matching our `predicate`. +// Only elements beginning with `start` are considered for extraction. +fn drain_matching( + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind) -> bool, + extract: impl Fn(PatKind) -> P, +) -> Vec> { + let mut tail_or = vec![]; + let mut idx = 0; + for pat in alternatives.drain_filter(|p| { + // Check if we should extract, but only if `idx >= start`. + idx += 1; + idx > start && predicate(&p.kind) + }) { + tail_or.push(extract(pat.into_inner().kind)); + } + tail_or +} + +fn extend_with_matching( + target: &mut Pat, + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind) -> bool, + extract: impl Fn(PatKind) -> P, +) -> bool { + extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract)) +} + +/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? +fn eq_pre_post(ps1: &[P], ps2: &[P], idx: usize) -> bool { + ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. + && ps1.len() == ps2.len() + && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) + && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r)) +} 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..5f4b5fd9dd91c --- /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..a6c7b5d405cda --- /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 | BinOpKind::BitAnd) | (true, BinOpKind::Or | 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..78d249482d53d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -0,0 +1,181 @@ +use crate::utils::{ + is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, + span_lint_and_help, span_lint_and_sugg, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, TyS}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`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`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" +} + +#[derive(Default)] +pub struct UselessConversion { + try_desugar_arm: Vec, +} + +impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); + +#[allow(clippy::too_many_lines)] +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 TyS::same_type(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 to the same type", + "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 TyS::same_type(a, b) { + let sugg = snippet(cx, args[0].span, "").into_owned(); + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion to the same type", + "consider removing `.into_iter()`", + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into" { + if_chain! { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if is_type_diagnostic_item(cx, a, sym!(result_type)); + if let ty::Adt(_, substs) = a.kind; + if let Some(a_type) = substs.types().next(); + if TyS::same_type(a_type, b); + + then { + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion to the same type", + None, + "consider removing `.try_into()`", + ); + } + } + } + }, + + ExprKind::Call(ref path, ref args) => { + if_chain! { + if args.len() == 1; + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id(); + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + + then { + if_chain! { + if match_def_path(cx, def_id, &paths::TRY_FROM); + if is_type_diagnostic_item(cx, a, sym!(result_type)); + if let ty::Adt(_, substs) = a.kind; + if let Some(a_type) = substs.types().next(); + if TyS::same_type(a_type, b); + + then { + let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion to the same type", + None, + &hint, + ); + } + } + + if_chain! { + if match_def_path(cx, def_id, &paths::FROM_FROM); + if TyS::same_type(a, b); + + then { + 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 to the same type", + &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/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs new file mode 100755 index 0000000000000..e60e2a81e070b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs @@ -0,0 +1,526 @@ +//! Utilities for manipulating and extracting information from `rustc_ast::ast`. +//! +//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s. + +#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] + +use crate::utils::{both, over}; +use rustc_ast::ast::{self, *}; +use rustc_ast::ptr::P; +use rustc_span::symbol::Ident; +use std::mem; + +/// Checks if each element in the first slice is contained within the latter as per `eq_fn`. +pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { + left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) +} + +pub fn eq_id(l: Ident, r: Ident) -> bool { + l.name == r.name +} + +pub fn eq_pat(l: &Pat, r: &Pat) -> bool { + use PatKind::*; + match (&l.kind, &r.kind) { + (Paren(l), _) => eq_pat(l, r), + (_, Paren(r)) => eq_pat(l, r), + (Wild, Wild) | (Rest, Rest) => true, + (Lit(l), Lit(r)) => eq_expr(l, r), + (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)), + (Range(lf, lt, le), Range(rf, rt, re)) => { + eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node) + }, + (Box(l), Box(r)) + | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) + | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), + (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (TupleStruct(lp, lfs), TupleStruct(rp, rfs)) => eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)), + (Struct(lp, lfs, lr), Struct(rp, rfs, rr)) => { + lr == rr && eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf)) + }, + (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), + (MacCall(l), MacCall(r)) => eq_mac_call(l, r), + _ => false, + } +} + +pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool { + match (l, r) { + (RangeEnd::Excluded, RangeEnd::Excluded) => true, + (RangeEnd::Included(l), RangeEnd::Included(r)) => { + matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq) + }, + _ => false, + } +} + +pub fn eq_field_pat(l: &FieldPat, r: &FieldPat) -> bool { + l.is_placeholder == r.is_placeholder + && eq_id(l.ident, r.ident) + && eq_pat(&l.pat, &r.pat) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { + l.position == r.position && eq_ty(&l.ty, &r.ty) +} + +pub fn eq_path(l: &Path, r: &Path) -> bool { + over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r)) +} + +pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { + eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r)) +} + +pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { + match (l, r) { + (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => { + over(&l.args, &r.args, |l, r| eq_angle_arg(l, r)) + }, + (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => { + over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output) + }, + _ => false, + } +} + +pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool { + match (l, r) { + (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r), + (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r), + _ => false, + } +} + +pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool { + match (l, r) { + (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident), + (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r), + (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value), + _ => false, + } +} + +pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { + both(l, r, |l, r| eq_expr(l, r)) +} + +pub fn eq_expr(l: &Expr, r: &Expr) -> bool { + use ExprKind::*; + if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) { + return false; + } + match (&l.kind, &r.kind) { + (Paren(l), _) => eq_expr(l, r), + (_, Paren(r)) => eq_expr(l, r), + (Err, Err) => true, + (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r), + (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), + (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), + (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), + (MethodCall(lc, la, _), MethodCall(rc, ra, _)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), + (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), + (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), + (Lit(l), Lit(r)) => l.kind == r.kind, + (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), + (Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re), + (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), + (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), + (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { + eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) + }, + (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt), + (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), + (TryBlock(l), TryBlock(r)) => eq_block(l, r), + (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), + (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re), + (Continue(ll), Continue(rl)) => eq_label(ll, rl), + (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2), + (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), + (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), + (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)), + (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => { + lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb) + }, + (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb), + (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), + (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (MacCall(l), MacCall(r)) => eq_mac_call(l, r), + (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => { + eq_path(lp, rp) && eq_expr_opt(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) + }, + _ => false, + } +} + +pub fn eq_field(l: &Field, r: &Field) -> bool { + l.is_placeholder == r.is_placeholder + && eq_id(l.ident, r.ident) + && eq_expr(&l.expr, &r.expr) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_arm(l: &Arm, r: &Arm) -> bool { + l.is_placeholder == r.is_placeholder + && eq_pat(&l.pat, &r.pat) + && eq_expr(&l.body, &r.body) + && eq_expr_opt(&l.guard, &r.guard) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_label(l: &Option: Sized, +{ + let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); +} + +fn return_str() -> str +where + str: Sized, +{ + *"Sized".to_string().into_boxed_str() +} + +fn use_op(s: String) -> String +where + String: ::std::ops::Neg, +{ + -s +} + +fn use_for() +where + i32: Iterator, +{ + for _ in 2i32 {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3969.stderr b/src/tools/clippy/tests/ui/crashes/ice-3969.stderr new file mode 100644 index 0000000000000..923db0664a714 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3969.stderr @@ -0,0 +1,22 @@ +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-3969.rs:25:17 + | +LL | for<'a> Dst: Sized, + | ^^^^^^ help: use `dyn`: `dyn A + 'a` + | + = note: `-D bare-trait-objects` implied by `-D warnings` + +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-3969.rs:27:16 + | +LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); + | ^ help: use `dyn`: `dyn A` + +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-3969.rs:27:57 + | +LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); + | ^ help: use `dyn`: `dyn A` + +error: aborting due to 3 previous errors + 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-5389.rs b/src/tools/clippy/tests/ui/crashes/ice-5389.rs new file mode 100644 index 0000000000000..de262199004b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5389.rs @@ -0,0 +1,13 @@ +#![allow(clippy::explicit_counter_loop)] + +fn main() { + let v = vec![1, 2, 3]; + let mut i = 0; + let max_storage_size = [0; 128 * 1024]; + for item in &v { + bar(i, *item); + i += 1; + } +} + +fn bar(_: usize, _: u32) {} 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..3d5063d1a3a7d --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/regressions.rs @@ -0,0 +1,13 @@ +// run-pass + +#![allow(clippy::blacklisted_name)] + +pub fn foo(bar: *const u8) { + println!("{:#p}", bar); +} + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/4917 +/// 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..3e92bca986ab5 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs @@ -0,0 +1,113 @@ +// aux-build:proc_macro_attr.rs +#![warn(clippy::empty_line_after_outer_attr)] +#![allow(clippy::assertions_on_constants)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#[macro_use] +extern crate proc_macro_attr; + +// 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; + +// This should not produce a warning +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +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..bf753a732f000 --- /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:11: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:23: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:28: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:35: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:43: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:51: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..b59dbb3e76c64 --- /dev/null +++ b/src/tools/clippy/tests/ui/future_not_send.stderr @@ -0,0 +1,145 @@ +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 { + | ^^^^ future returned by `private_future2` is not `Send` + | +note: captured value is not `Send` + --> $DIR/future_not_send.rs:20:26 + | +LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^ has type `std::rc::Rc<[u8]>` which is not `Send` + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` +note: captured value is not `Send` + --> $DIR/future_not_send.rs:20:40 + | +LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^^^ has type `&std::cell::Cell` which is not `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]>) {} + | ^ future returned by `public_future2` is not `Send` + | +note: captured value is not `Send` + --> $DIR/future_not_send.rs:24:29 + | +LL | pub async fn public_future2(rc: Rc<[u8]>) {} + | ^^ has type `std::rc::Rc<[u8]>` which is not `Send` + = 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) {} + | ^ future returned by `unclear_future` is not `Send` + | +note: captured value is not `Send` + --> $DIR/future_not_send.rs:66:28 + | +LL | async fn unclear_future(t: T) {} + | ^ has type `T` which is not `Send` + = 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..9c5fe02f7519b --- /dev/null +++ b/src/tools/clippy/tests/ui/if_same_then_else.rs @@ -0,0 +1,157 @@ +#![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]) {} + +mod issue_5698 { + fn mul_not_always_commutative(x: i32, y: i32) -> i32 { + if x == 42 { + x * y + } else if x == 21 { + y * x + } else { + 0 + } + } +} + +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..2f32a7b157821 --- /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 { + i_8 -= 1; + } + + // Lint + if i_8 != i8::MIN { + i_8 -= 1; + } + + // Lint + if i_8 != i8::MIN { + 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 { + i_16 -= 1; + } + + // Lint + if i_16 != i16::MIN { + i_16 -= 1; + } + + // Lint + if i_16 != i16::MIN { + 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 { + i_32 -= 1; + } + + // Lint + if i_32 != i32::MIN { + i_32 -= 1; + } + + // Lint + if i_32 != i32::MIN { + 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 < 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..2eb2023b3b9ef --- /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 { +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 { +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 { +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 { +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 { +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 { +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 < 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..7f92d0dbdc973 --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed @@ -0,0 +1,45 @@ +// 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() + + let _ = (&[1, 2, 3]).iter().next(); //~ WARN 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..416056d3fdb9c --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.rs @@ -0,0 +1,45 @@ +// 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() + + let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN 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..1cd6400b0195b --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr @@ -0,0 +1,166 @@ +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: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:44:26 + | +LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: aborting due to 27 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_next_slice.fixed b/src/tools/clippy/tests/ui/iter_next_slice.fixed new file mode 100644 index 0000000000000..79c1db87ac3c4 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_next_slice.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::iter_next_slice)] + +fn main() { + // test code goes here + let s = [1, 2, 3]; + let v = vec![1, 2, 3]; + + s.get(0); + // Should be replaced by s.get(0) + + s.get(2); + // Should be replaced by s.get(2) + + v.get(5); + // Should be replaced by v.get(5) + + v.get(0); + // Should be replaced by v.get(0) + + let o = Some(5); + o.iter().next(); + // Shouldn't be linted since this is not a Slice or an Array +} diff --git a/src/tools/clippy/tests/ui/iter_next_slice.rs b/src/tools/clippy/tests/ui/iter_next_slice.rs new file mode 100644 index 0000000000000..ef9a55f3d997c --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_next_slice.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::iter_next_slice)] + +fn main() { + // test code goes here + let s = [1, 2, 3]; + let v = vec![1, 2, 3]; + + s.iter().next(); + // Should be replaced by s.get(0) + + s[2..].iter().next(); + // Should be replaced by s.get(2) + + v[5..].iter().next(); + // Should be replaced by v.get(5) + + v.iter().next(); + // Should be replaced by v.get(0) + + let o = Some(5); + o.iter().next(); + // Shouldn't be linted since this is not a Slice or an Array +} diff --git a/src/tools/clippy/tests/ui/iter_next_slice.stderr b/src/tools/clippy/tests/ui/iter_next_slice.stderr new file mode 100644 index 0000000000000..bbf61df0cda68 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_next_slice.stderr @@ -0,0 +1,28 @@ +error: Using `.iter().next()` on an array + --> $DIR/iter_next_slice.rs:9:5 + | +LL | s.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)` + | + = note: `-D clippy::iter-next-slice` implied by `-D warnings` + +error: Using `.iter().next()` on a Slice without end index. + --> $DIR/iter_next_slice.rs:12:5 + | +LL | s[2..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` + +error: Using `.iter().next()` on a Slice without end index. + --> $DIR/iter_next_slice.rs:15:5 + | +LL | v[5..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` + +error: Using `.iter().next()` on an array + --> $DIR/iter_next_slice.rs:18:5 + | +LL | v.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)` + +error: aborting due to 4 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..a29b832eb6019 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -0,0 +1,151 @@ +// 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() {} +} + +mod issue_3807 { + // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. + // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 + fn no_suggestion() { + let _ = (0..42).len() == 0; + } +} diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs new file mode 100644 index 0000000000000..8fd0093f4fdbb --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -0,0 +1,151 @@ +// 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 {} +} + +mod issue_3807 { + // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. + // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 + fn no_suggestion() { + let _ = (0..42).len() == 0; + } +} diff --git a/src/tools/clippy/tests/ui/len_zero.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/len_zero_ranges.fixed b/src/tools/clippy/tests/ui/len_zero_ranges.fixed new file mode 100644 index 0000000000000..7da26f8ff4d47 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero_ranges.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(range_is_empty)] +#![warn(clippy::len_zero)] +#![allow(unused)] + +mod issue_3807 { + // With the feature enabled, `is_empty` should be suggested + fn suggestion_is_fine() { + let _ = (0..42).is_empty(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/len_zero_ranges.rs b/src/tools/clippy/tests/ui/len_zero_ranges.rs new file mode 100644 index 0000000000000..be7b4244bc06c --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero_ranges.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(range_is_empty)] +#![warn(clippy::len_zero)] +#![allow(unused)] + +mod issue_3807 { + // With the feature enabled, `is_empty` should be suggested + fn suggestion_is_fine() { + let _ = (0..42).len() == 0; + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/len_zero_ranges.stderr b/src/tools/clippy/tests/ui/len_zero_ranges.stderr new file mode 100644 index 0000000000000..6e5fa41fb08a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero_ranges.stderr @@ -0,0 +1,10 @@ +error: length comparison to zero + --> $DIR/len_zero_ranges.rs:10:17 + | +LL | let _ = (0..42).len() == 0; + | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()` + | + = note: `-D clippy::len-zero` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs new file mode 100644 index 0000000000000..09614b8c1ad78 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_and_return.rs @@ -0,0 +1,138 @@ +#![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); + +mod no_lint_if_stmt_borrows { + mod issue_3792 { + use std::io::{self, BufRead, Stdin}; + + fn read_line() -> String { + let stdin = io::stdin(); + let line = stdin.lock().lines().next().unwrap().unwrap(); + line + } + } + + mod issue_3324 { + use std::cell::RefCell; + use std::rc::{Rc, Weak}; + + fn test(value: Weak>) -> u32 { + let value = value.upgrade().unwrap(); + let ret = value.borrow().baz(); + ret + } + + struct Bar {} + + impl Bar { + fn new() -> Self { + Bar {} + } + fn baz(&self) -> u32 { + 0 + } + } + + fn main() { + let a = Rc::new(RefCell::new(Bar::new())); + let b = Rc::downgrade(&a); + test(b); + } + } + + mod free_function { + struct Inner; + + struct Foo<'a> { + inner: &'a Inner, + } + + impl Drop for Foo<'_> { + fn drop(&mut self) {} + } + + impl Foo<'_> { + fn value(&self) -> i32 { + 42 + } + } + + fn some_foo(inner: &Inner) -> Foo<'_> { + Foo { inner } + } + + fn test() -> i32 { + let x = Inner {}; + let value = some_foo(&x).value(); + value + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.stderr b/src/tools/clippy/tests/ui/let_and_return.stderr new file mode 100644 index 0000000000000..eacf948b3927a --- /dev/null +++ b/src/tools/clippy/tests/ui/let_and_return.stderr @@ -0,0 +1,31 @@ +error: returning the result of a `let` binding from a block + --> $DIR/let_and_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_and_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_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_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.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed new file mode 100644 index 0000000000000..91e34c62160a1 --- /dev/null +++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed @@ -0,0 +1,43 @@ +// compile-flags: --edition 2018 +// aux-build:macro_rules.rs +// aux-build:macro_use_helper.rs +// run-rustfix +// ignore-32bit + +#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] +#![allow(clippy::single_component_path_imports)] +#![warn(clippy::macro_use_imports)] + +#[macro_use] +extern crate macro_use_helper as mac; + +#[macro_use] +extern crate clippy_mini_macro_test as mini_mac; + +mod a { + use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro}; + use mac; + use mini_mac::ClippyMiniMacroTest; + use mini_mac; + use mac::{inner::foofoo, inner::try_err}; + use mac::inner; + use mac::inner::nested::string_add; + use mac::inner::nested; + + #[derive(ClippyMiniMacroTest)] + struct Test; + + fn test() { + pub_macro!(); + inner_mod_macro!(); + pub_in_private_macro!(_var); + function_macro!(); + let v: ty_macro!() = Vec::default(); + + inner::try_err!(); + inner::foofoo!(); + nested::string_add!(); + } +} + +fn main() {} 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..9c3c50c5d49f2 --- /dev/null +++ b/src/tools/clippy/tests/ui/macro_use_imports.rs @@ -0,0 +1,43 @@ +// compile-flags: --edition 2018 +// aux-build:macro_rules.rs +// aux-build:macro_use_helper.rs +// run-rustfix +// ignore-32bit + +#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] +#![allow(clippy::single_component_path_imports)] +#![warn(clippy::macro_use_imports)] + +#[macro_use] +extern crate macro_use_helper as mac; + +#[macro_use] +extern crate clippy_mini_macro_test as mini_mac; + +mod a { + #[macro_use] + use mac; + #[macro_use] + use mini_mac; + #[macro_use] + use mac::inner; + #[macro_use] + use mac::inner::nested; + + #[derive(ClippyMiniMacroTest)] + struct Test; + + fn test() { + pub_macro!(); + inner_mod_macro!(); + pub_in_private_macro!(_var); + function_macro!(); + let v: ty_macro!() = Vec::default(); + + inner::try_err!(); + inner::foofoo!(); + nested::string_add!(); + } +} + +fn main() {} 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..f8c86c8d9179f --- /dev/null +++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr @@ -0,0 +1,28 @@ +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:18:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};` + | + = note: `-D clippy::macro-use-imports` implied by `-D warnings` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:20:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:22:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` + +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:24:5 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` + +error: aborting due to 4 previous errors + 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..6a2a02987dea7 --- /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, or use `.except(msg)` if the error case is unreachable + +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, or use `.except(msg)` if the error case is unreachable + +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, or use `.except(msg)` if the error case is unreachable + +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, or use `.except(msg)` if the error case is unreachable + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed new file mode 100644 index 0000000000000..519200977a798 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed @@ -0,0 +1,59 @@ +// run-rustfix + +#![warn(clippy::match_wildcard_for_single_variants)] +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), +} + +fn main() { + let f = Foo::A; + match f { + Foo::A => {}, + Foo::B => {}, + Foo::C => {}, + } + + let color = Color::Red; + + // check exhaustive bindings + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_r, _g, _b) => {}, + Color::Blue => {}, + } + + // check exhaustive wild + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(..) => {}, + Color::Blue => {}, + } + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_, _, _) => {}, + Color::Blue => {}, + } + + // shouldn't lint as there is one missing variant + // and one that isn't exhaustively covered + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(255, _, _) => {}, + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs new file mode 100644 index 0000000000000..1df917e085c71 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.rs @@ -0,0 +1,59 @@ +// run-rustfix + +#![warn(clippy::match_wildcard_for_single_variants)] +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), +} + +fn main() { + let f = Foo::A; + match f { + Foo::A => {}, + Foo::B => {}, + _ => {}, + } + + let color = Color::Red; + + // check exhaustive bindings + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_r, _g, _b) => {}, + _ => {}, + } + + // check exhaustive wild + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(..) => {}, + _ => {}, + } + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_, _, _) => {}, + _ => {}, + } + + // shouldn't lint as there is one missing variant + // and one that isn't exhaustively covered + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(255, _, _) => {}, + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr new file mode 100644 index 0000000000000..82790aa9e80bb --- /dev/null +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr @@ -0,0 +1,28 @@ +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:24:9 + | +LL | _ => {}, + | ^ help: try this: `Foo::C` + | + = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` + +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:34:9 + | +LL | _ => {}, + | ^ help: try this: `Color::Blue` + +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:42:9 + | +LL | _ => {}, + | ^ help: try this: `Color::Blue` + +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:48:9 + | +LL | _ => {}, + | ^ help: try this: `Color::Blue` + +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..be37dc16b9a3e --- /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.get(0).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..9113aad90dd7c --- /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:29 + | +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:15 + | +LL | if sample.iter().collect::>().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:15:28 + | +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:35 + | +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..ca70e3b7148ef --- /dev/null +++ b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs @@ -0,0 +1,62 @@ +//! 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; + +#[allow(clippy::unnested_or_patterns)] +#[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..8c5d548222e0d --- /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:16: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:19:30 + | +LL | let _not_less_or_equal = !(a_value <= another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:22:24 + | +LL | let _not_greater = !(a_value > another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:25: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..3b6041823d878 --- /dev/null +++ b/src/tools/clippy/tests/ui/new_without_default.rs @@ -0,0 +1,162 @@ +#![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!() + } +} + +pub struct NewNotEqualToDerive { + foo: i32, +} + +impl NewNotEqualToDerive { + // This `new` implementation is not equal to a derived `Default`, so do not suggest deriving. + pub fn new() -> Self { + NewNotEqualToDerive { foo: 1 } + } +} + +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..e529e441eb735 --- /dev/null +++ b/src/tools/clippy/tests/ui/new_without_default.stderr @@ -0,0 +1,71 @@ +error: you should consider adding 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 | impl Default for Foo { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: you should consider adding a `Default` implementation for `Bar` + --> $DIR/new_without_default.rs:16:5 + | +LL | / pub fn new() -> Self { +LL | | Bar +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for Bar { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +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: you should consider adding a `Default` implementation for `NewNotEqualToDerive` + --> $DIR/new_without_default.rs:157:5 + | +LL | / pub fn new() -> Self { +LL | | NewNotEqualToDerive { foo: 1 } +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for NewNotEqualToDerive { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: aborting due to 4 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..a2617a13ecace --- /dev/null +++ b/src/tools/clippy/tests/ui/option_option.rs @@ -0,0 +1,87 @@ +#![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)); +} + +extern crate serde; +mod issue_4298 { + use serde::{Deserialize, Deserializer, Serialize}; + use std::borrow::Cow; + + #[derive(Serialize, Deserialize)] + struct Foo<'a> { + #[serde(deserialize_with = "func")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(borrow)] + // FIXME: should not lint here + #[allow(clippy::option_option)] + foo: Option>>, + } + + #[allow(clippy::option_option)] + fn func<'a, D>(_: D) -> Result>>, D::Error> + where + D: Deserializer<'a>, + { + Ok(Some(Some(Cow::Borrowed("hi")))) + } +} 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..0cd4c96eb4f9a --- /dev/null +++ b/src/tools/clippy/tests/ui/option_option.stderr @@ -0,0 +1,68 @@ +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: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:77:14 + | +LL | foo: Option>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 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..2045ffdb5f09d --- /dev/null +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -0,0 +1,119 @@ +// 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_else(|| 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_else(|| Some(Bar(b, Duration::from_secs(2)))); + + let vec = vec!["foo"]; + let _ = opt.ok_or(vec.len()); + + let array = ["foo"]; + let _ = opt.ok_or(array.len()); + + let slice = &["foo"][..]; + let _ = opt.ok_or(slice.len()); +} + +// 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..522f31b72d01f --- /dev/null +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -0,0 +1,119 @@ +// 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)))); + + let vec = vec!["foo"]; + let _ = opt.ok_or(vec.len()); + + let array = ["foo"]; + let _ = opt.ok_or(array.len()); + + let slice = &["foo"][..]; + let _ = opt.ok_or(slice.len()); +} + +// 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..bc5978b538f16 --- /dev/null +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -0,0 +1,94 @@ +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:32:19 + | +LL | with_const_fn.unwrap_or(Duration::from_secs(5)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Duration::from_secs(5))` + | + = note: `-D clippy::or-fun-call` implied by `-D warnings` + +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)` + +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: use of `or` followed by a function call + --> $DIR/or_fun_call.rs:97:10 + | +LL | .or(Some(Bar(b, Duration::from_secs(2)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` + +error: aborting due to 15 previous errors + 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..541225e635102 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -0,0 +1,116 @@ +#![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]>) {} + +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) {} +} + +// Check that the allow attribute on parameters is honored +mod issue_5644 { + use std::borrow::Cow; + + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + + struct S {} + impl S { + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + } + + trait T { + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + } +} 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..718e391e8bf69 --- /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 { + let _ = ptr.add(offset_usize); + let _ = ptr.offset(offset_isize as isize); + let _ = ptr.offset(offset_u8 as isize); + + let _ = ptr.wrapping_add(offset_usize); + let _ = ptr.wrapping_offset(offset_isize as isize); + let _ = 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..f613742c741ef --- /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 { + let _ = ptr.offset(offset_usize as isize); + let _ = ptr.offset(offset_isize as isize); + let _ = ptr.offset(offset_u8 as isize); + + let _ = ptr.wrapping_offset(offset_usize as isize); + let _ = ptr.wrapping_offset(offset_isize as isize); + let _ = 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..fd45224ca067f --- /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:17 + | +LL | let _ = 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:17 + | +LL | let _ = 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..6ba5cfb1d7177 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed @@ -0,0 +1,161 @@ +// run-rustfix + +#![feature(const_if_match)] +#![feature(const_loop)] +#![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(); + issue5697(); + + 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() {} +} + +// None of these should be linted because none of the suggested methods +// are `const fn` without toggling a feature. +const fn issue5697() { + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} 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..17de66f9ad0eb --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.rs @@ -0,0 +1,182 @@ +// run-rustfix + +#![feature(const_if_match)] +#![feature(const_loop)] +#![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(); + issue5697(); + + 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!() {} +} + +// None of these should be linted because none of the suggested methods +// are `const fn` without toggling a feature. +const fn issue5697() { + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} 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..1b9a4b40a2f02 --- /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:10: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:12: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:14: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: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:18: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:24: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:26: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:28: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:30: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:32: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:35: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:51: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:56: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:61: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:66: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:71: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:76: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:81: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:86: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:89: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:95: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:97: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:99: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:101: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:134: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:135: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:141: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:142: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_pattern_matching_const_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.fixed new file mode 100644 index 0000000000000..c8bc5458067d3 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.fixed @@ -0,0 +1,46 @@ +// run-rustfix + +#![feature(const_if_match)] +#![feature(const_loop)] +#![feature(const_result)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused)] + +// Test that results are linted with the feature enabled. + +const fn issue_5697() { + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + while Ok::(10).is_ok() {} + + while Ok::(10).is_err() {} + + Ok::(42).is_ok(); + + Err::(42).is_err(); + + // These should not be linted until `const_option` is implemented. + // See https://github.com/rust-lang/rust/issues/67441 + + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.rs new file mode 100644 index 0000000000000..75f37ec15c622 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.rs @@ -0,0 +1,52 @@ +// run-rustfix + +#![feature(const_if_match)] +#![feature(const_loop)] +#![feature(const_result)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused)] + +// Test that results are linted with the feature enabled. + +const fn issue_5697() { + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + + // These should not be linted until `const_option` is implemented. + // See https://github.com/rust-lang/rust/issues/67441 + + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.stderr new file mode 100644 index 0000000000000..c32292f0eee8b --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_const_result.stderr @@ -0,0 +1,46 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_const_result.rs:12: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_const_result.rs:14:12 + | +LL | if let Err(_) = Err::(42) {} + | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_const_result.rs:16: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_const_result.rs:18:15 + | +LL | while let Err(_) = Ok::(10) {} + | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_const_result.rs:20: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_const_result.rs:25:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Err::(42).is_err()` + +error: aborting due to 6 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..ad5b8e4857d17 --- /dev/null +++ b/src/tools/clippy/tests/ui/repl_uninit.rs @@ -0,0 +1,41 @@ +#![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::MaybeUninit::uninit().assume_init()); + 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..09468eeaea4bf --- /dev/null +++ b/src/tools/clippy/tests/ui/repl_uninit.stderr @@ -0,0 +1,30 @@ +error: replacing with `mem::uninitialized()` + --> $DIR/repl_uninit.rs:15:23 + | +LL | let taken_v = mem::replace(&mut v, mem::uninitialized()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)` + | + = note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings` + +error: replacing with `mem::MaybeUninit::uninit().assume_init()` + --> $DIR/repl_uninit.rs:21:23 + | +LL | let taken_v = mem::replace(&mut v, mem::MaybeUninit::uninit().assume_init()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(&mut v)` + +error: replacing with `mem::zeroed()` + --> $DIR/repl_uninit.rs:27: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:39:28 + | +LL | let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::ptr::read(uref)` + +error: aborting due to 4 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..79e482eec3037 --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed @@ -0,0 +1,29 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + // These should be linted: + + (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 {} + + // This range is empty but should be ignored, see issue #5689 + let _ = &arr[0..0]; +} 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..b2e8bf33771ac --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.rs @@ -0,0 +1,29 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + // These should be linted: + + (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 {} + + // This range is empty but should be ignored, see issue #5689 + let _ = &arr[0..0]; +} 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..de83c4f3d633c --- /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:9: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:10: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:12: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:13: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..264d3d1e95af4 --- /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 arr = [1, 2, 3, 4, 5]; + let _ = &arr[3usize..=1usize]; + let _ = &arr[SOME_NUM..1]; + + for _ in ANSWER..ANSWER {} + + // Should not be linted, see issue #5689 + let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); +} 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..f23d4eb0f9ca4 --- /dev/null +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -0,0 +1,22 @@ +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:8:18 + | +LL | let _ = &arr[3usize..=1usize]; + | ^^^^^^^^^^^^^^^ + | + = 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:9:18 + | +LL | let _ = &arr[SOME_NUM..1]; + | ^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_unfixable.rs:11:14 + | +LL | for _ in ANSWER..ANSWER {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 3 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..ccf8f61c4a92c --- /dev/null +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed @@ -0,0 +1,24 @@ +// 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 current_version = env!("CARGO_PKG_VERSION").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..178df08e249ef --- /dev/null +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs @@ -0,0 +1,24 @@ +// 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 current_version = env!("CARGO_PKG_VERSION").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..99c512354d589 --- /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:19: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:21: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.rs b/src/tools/clippy/tests/ui/unit_arg.rs new file mode 100644 index 0000000000000..2992abae775b8 --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg.rs @@ -0,0 +1,90 @@ +#![warn(clippy::unit_arg)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] + +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({ + 1; + }); + foo(foo(1)); + foo({ + foo(1); + foo(2); + }); + let b = Bar; + b.bar({ + 1; + }); + taking_multiple_units(foo(0), foo(1)); + taking_multiple_units(foo(0), { + foo(1); + foo(2); + }); + taking_multiple_units( + { + foo(0); + foo(1); + }, + { + foo(2); + foo(3); + }, + ); +} + +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()?) + } +} + +#[allow(dead_code)] +fn returning_expr() -> Option<()> { + Some(foo(1)) +} + +fn taking_multiple_units(a: (), b: ()) {} + +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..56f6a855dfa55 --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg.stderr @@ -0,0 +1,181 @@ +error: passing a unit value to a function + --> $DIR/unit_arg.rs:23:5 + | +LL | / foo({ +LL | | 1; +LL | | }); + | |______^ + | + = note: `-D clippy::unit-arg` implied by `-D warnings` +help: remove the semicolon from the last statement in the block + | +LL | 1 + | +help: or move the expression in front of the call... + | +LL | { +LL | 1; +LL | }; + | +help: ...and use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:26:5 + | +LL | foo(foo(1)); + | ^^^^^^^^^^^ + | +help: move the expression in front of the call... + | +LL | foo(1); + | +help: ...and use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:27:5 + | +LL | / foo({ +LL | | foo(1); +LL | | foo(2); +LL | | }); + | |______^ + | +help: remove the semicolon from the last statement in the block + | +LL | foo(2) + | +help: or move the expression in front of the call... + | +LL | { +LL | foo(1); +LL | foo(2); +LL | }; + | +help: ...and use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:32:5 + | +LL | / b.bar({ +LL | | 1; +LL | | }); + | |______^ + | +help: remove the semicolon from the last statement in the block + | +LL | 1 + | +help: or move the expression in front of the call... + | +LL | { +LL | 1; +LL | }; + | +help: ...and use a unit literal instead + | +LL | b.bar(()); + | ^^ + +error: passing unit values to a function + --> $DIR/unit_arg.rs:35:5 + | +LL | taking_multiple_units(foo(0), foo(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: move the expressions in front of the call... + | +LL | foo(0); +LL | foo(1); + | +help: ...and use unit literals instead + | +LL | taking_multiple_units((), ()); + | ^^ ^^ + +error: passing unit values to a function + --> $DIR/unit_arg.rs:36:5 + | +LL | / taking_multiple_units(foo(0), { +LL | | foo(1); +LL | | foo(2); +LL | | }); + | |______^ + | +help: remove the semicolon from the last statement in the block + | +LL | foo(2) + | +help: or move the expressions in front of the call... + | +LL | foo(0); +LL | { +LL | foo(1); +LL | foo(2); +LL | }; + | +help: ...and use unit literals instead + | +LL | taking_multiple_units((), ()); + | ^^ ^^ + +error: passing unit values to a function + --> $DIR/unit_arg.rs:40:5 + | +LL | / taking_multiple_units( +LL | | { +LL | | foo(0); +LL | | foo(1); +... | +LL | | }, +LL | | ); + | |_____^ + | +help: remove the semicolon from the last statement in the block + | +LL | foo(1) + | +help: remove the semicolon from the last statement in the block + | +LL | foo(3) + | +help: or move the expressions in front of the call... + | +LL | { +LL | foo(0); +LL | foo(1); +LL | }; +LL | { +LL | foo(2); + ... +help: ...and use unit literals instead + | +LL | (), +LL | (), + | + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:82:5 + | +LL | Some(foo(1)) + | ^^^^^^^^^^^^ + | +help: move the expression in front of the call... + | +LL | foo(1); + | +help: ...and use a unit literal instead + | +LL | Some(()) + | ^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs new file mode 100644 index 0000000000000..18a31eb3deee2 --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs @@ -0,0 +1,26 @@ +#![warn(clippy::unit_arg)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] + +use std::fmt::Debug; + +fn foo(t: T) { + println!("{:?}", t); +} + +fn foo3(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +fn bad() { + foo({}); + foo3({}, 2, 2); + taking_two_units({}, foo(0)); + taking_three_units({}, foo(0), foo(1)); +} + +fn taking_two_units(a: (), b: ()) {} +fn taking_three_units(a: (), b: (), c: ()) {} + +fn main() { + bad(); +} diff --git a/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr new file mode 100644 index 0000000000000..bb58483584b3e --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr @@ -0,0 +1,51 @@ +error: passing a unit value to a function + --> $DIR/unit_arg_empty_blocks.rs:15:5 + | +LL | foo({}); + | ^^^^--^ + | | + | help: use a unit literal instead: `()` + | + = note: `-D clippy::unit-arg` implied by `-D warnings` + +error: passing a unit value to a function + --> $DIR/unit_arg_empty_blocks.rs:16:5 + | +LL | foo3({}, 2, 2); + | ^^^^^--^^^^^^^ + | | + | help: use a unit literal instead: `()` + +error: passing unit values to a function + --> $DIR/unit_arg_empty_blocks.rs:17:5 + | +LL | taking_two_units({}, foo(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: move the expression in front of the call... + | +LL | foo(0); + | +help: ...and use unit literals instead + | +LL | taking_two_units((), ()); + | ^^ ^^ + +error: passing unit values to a function + --> $DIR/unit_arg_empty_blocks.rs:18:5 + | +LL | taking_three_units({}, foo(0), foo(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: move the expressions in front of the call... + | +LL | foo(0); +LL | foo(1); + | +help: ...and use unit literals instead + | +LL | taking_three_units((), (), ()); + | ^^ ^^ ^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unit_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/unnecessary_sort_by.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed new file mode 100644 index 0000000000000..779fd57707ad4 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed @@ -0,0 +1,26 @@ +// run-rustfix + +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + // Forward examples + vec.sort(); + vec.sort_unstable(); + vec.sort_by_key(|&a| (a + 5).abs()); + vec.sort_unstable_by_key(|&a| id(-a)); + // Reverse examples + vec.sort_by_key(|&b| Reverse(b)); + vec.sort_by_key(|&b| Reverse((b + 5).abs())); + vec.sort_unstable_by_key(|&b| Reverse(id(-b))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_unstable_by(|a, _| a.cmp(c)); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs new file mode 100644 index 0000000000000..0485a5630afef --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.rs @@ -0,0 +1,26 @@ +// run-rustfix + +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + // Forward examples + vec.sort_by(|a, b| a.cmp(b)); + vec.sort_unstable_by(|a, b| a.cmp(b)); + vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); + vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); + // Reverse examples + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); + vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_unstable_by(|a, _| a.cmp(c)); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr new file mode 100644 index 0000000000000..903b6e5099ce8 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr @@ -0,0 +1,46 @@ +error: use Vec::sort here instead + --> $DIR/unnecessary_sort_by.rs:12:5 + | +LL | vec.sort_by(|a, b| a.cmp(b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` + | + = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` + +error: use Vec::sort here instead + --> $DIR/unnecessary_sort_by.rs:13:5 + | +LL | vec.sort_unstable_by(|a, b| a.cmp(b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:14:5 + | +LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:15:5 + | +LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:17:5 + | +LL | vec.sort_by(|a, b| b.cmp(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:18:5 + | +LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:19:5 + | +LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` + +error: aborting due to 7 previous errors + 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/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed new file mode 100644 index 0000000000000..b39e891094fd9 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed @@ -0,0 +1,33 @@ +// run-rustfix + +#![feature(or_patterns)] +#![feature(box_patterns)] +#![warn(clippy::unnested_or_patterns)] +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] + +fn main() { + if let box (0 | 2) = Box::new(0) {} + if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} + const C0: &u8 = &1; + if let &(0 | 2) | C0 = &0 {} + if let &mut (0 | 2) = &mut 0 {} + if let x @ (0 | 2) = 0 {} + if let (0, 1 | 2 | 3) = (0, 0) {} + if let (1 | 2 | 3, 0) = (0, 0) {} + if let (x, ..) | (x, 1 | 2) = (0, 1) {} + if let [0 | 1] = [0] {} + if let [x, 0 | 1] = [0, 1] {} + if let [x, 0 | 1 | 2] = [0, 1] {} + if let [x, ..] | [x, 1 | 2] = [0, 1] {} + struct TS(u8, u8); + if let TS(0 | 1, x) = TS(0, 0) {} + if let TS(1 | 2 | 3, 0) = TS(0, 0) {} + if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} + struct S { + x: u8, + y: u8, + } + if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} + if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs new file mode 100644 index 0000000000000..096f5a71150b8 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs @@ -0,0 +1,33 @@ +// run-rustfix + +#![feature(or_patterns)] +#![feature(box_patterns)] +#![warn(clippy::unnested_or_patterns)] +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] + +fn main() { + if let box 0 | box 2 = Box::new(0) {} + if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} + const C0: &u8 = &1; + if let &0 | C0 | &2 = &0 {} + if let &mut 0 | &mut 2 = &mut 0 {} + if let x @ 0 | x @ 2 = 0 {} + if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} + if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} + if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} + if let [0] | [1] = [0] {} + if let [x, 0] | [x, 1] = [0, 1] {} + if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} + if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} + struct TS(u8, u8); + if let TS(0, x) | TS(1, x) = TS(0, 0) {} + if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} + if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} + struct S { + x: u8, + y: u8, + } + if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} + if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr new file mode 100644 index 0000000000000..1899dc657dfee --- /dev/null +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr @@ -0,0 +1,179 @@ +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:10:12 + | +LL | if let box 0 | box 2 = Box::new(0) {} + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::unnested-or-patterns` implied by `-D warnings` +help: nest the patterns + | +LL | if let box (0 | 2) = Box::new(0) {} + | ^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:11:12 + | +LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:13:12 + | +LL | if let &0 | C0 | &2 = &0 {} + | ^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let &(0 | 2) | C0 = &0 {} + | ^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:14:12 + | +LL | if let &mut 0 | &mut 2 = &mut 0 {} + | ^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let &mut (0 | 2) = &mut 0 {} + | ^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:15:12 + | +LL | if let x @ 0 | x @ 2 = 0 {} + | ^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let x @ (0 | 2) = 0 {} + | ^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:16:12 + | +LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let (0, 1 | 2 | 3) = (0, 0) {} + | ^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:17:12 + | +LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let (1 | 2 | 3, 0) = (0, 0) {} + | ^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:18:12 + | +LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:19:12 + | +LL | if let [0] | [1] = [0] {} + | ^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [0 | 1] = [0] {} + | ^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:20:12 + | +LL | if let [x, 0] | [x, 1] = [0, 1] {} + | ^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [x, 0 | 1] = [0, 1] {} + | ^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:21:12 + | +LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [x, 0 | 1 | 2] = [0, 1] {} + | ^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:22:12 + | +LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:24:12 + | +LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let TS(0 | 1, x) = TS(0, 0) {} + | ^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:25:12 + | +LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {} + | ^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:26:12 + | +LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:31:12 + | +LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed new file mode 100644 index 0000000000000..02a129c55a3f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![feature(or_patterns)] +#![feature(box_patterns)] +#![warn(clippy::unnested_or_patterns)] +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] + +fn main() { + if let Some(Some(0 | 1)) = None {} + if let Some(Some(0 | 1 | 2)) = None {} + if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {} + if let Some(Some(0 | 1 | 2)) = None {} + if let ((0 | 1 | 2,),) = ((0,),) {} + if let 0 | 1 | 2 = 0 {} + if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} + if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {} +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.rs b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs new file mode 100644 index 0000000000000..acf3158989dcc --- /dev/null +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![feature(or_patterns)] +#![feature(box_patterns)] +#![warn(clippy::unnested_or_patterns)] +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] + +fn main() { + if let Some(Some(0)) | Some(Some(1)) = None {} + if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {} + if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {} + if let Some(Some(0) | Some(1 | 2)) = None {} + if let ((0,),) | ((1,) | (2,),) = ((0,),) {} + if let 0 | (1 | 2) = 0 {} + if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {} + if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {} +} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr new file mode 100644 index 0000000000000..1847fd8e098c7 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnested_or_patterns2.stderr @@ -0,0 +1,91 @@ +error: unnested or-patterns + --> $DIR/unnested_or_patterns2.rs:10:12 + | +LL | if let Some(Some(0)) | Some(Some(1)) = None {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnested-or-patterns` implied by `-D warnings` +help: nest the patterns + | +LL | if let Some(Some(0 | 1)) = None {} + | ^^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns2.rs:11:12 + | +LL | if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let Some(Some(0 | 1 | 2)) = None {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns2.rs:12:12 + | +LL | if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns2.rs:13:12 + | +LL | if let Some(Some(0) | Some(1 | 2)) = None {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let Some(Some(0 | 1 | 2)) = None {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns2.rs:14:12 + | +LL | if let ((0,),) | ((1,) | (2,),) = ((0,),) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let ((0 | 1 | 2,),) = ((0,),) {} + | ^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns2.rs:15:12 + | +LL | if let 0 | (1 | 2) = 0 {} + | ^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let 0 | 1 | 2 = 0 {} + | ^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns2.rs:16:12 + | +LL | if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: unnested or-patterns + --> $DIR/unnested_or_patterns2.rs:17:12 + | +LL | if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 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..84ec53702788c --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -0,0 +1,68 @@ +error: useless conversion to the same type + --> $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 to the same type + --> $DIR/useless_conversion.rs:7:5 + | +LL | val.into() + | ^^^^^^^^^^ help: consider removing `.into()`: `val` + +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:19:22 + | +LL | let _: i32 = 0i32.into(); + | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` + +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:51:21 + | +LL | let _: String = "foo".to_string().into(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` + +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:52:21 + | +LL | let _: String = From::from("foo".to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` + +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:53:13 + | +LL | let _ = String::from("foo".to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` + +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:54:13 + | +LL | let _ = String::from(format!("A: {:04}", 123)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` + +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:55:13 + | +LL | let _ = "".lines().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` + +error: useless conversion to the same type + --> $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 to the same type + --> $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/useless_conversion_try.rs b/src/tools/clippy/tests/ui/useless_conversion_try.rs new file mode 100644 index 0000000000000..3787ea991445c --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_conversion_try.rs @@ -0,0 +1,42 @@ +#![deny(clippy::useless_conversion)] + +use std::convert::{TryFrom, TryInto}; + +fn test_generic(val: T) -> T { + let _ = T::try_from(val).unwrap(); + val.try_into().unwrap() +} + +fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.try_into().unwrap(); + let _: U = val.try_into().unwrap(); + let _ = U::try_from(val).unwrap(); +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + + let _: String = "foo".try_into().unwrap(); + let _: String = TryFrom::try_from("foo").unwrap(); + let _ = String::try_from("foo").unwrap(); + #[allow(clippy::useless_conversion)] + { + let _ = String::try_from("foo").unwrap(); + let _: String = "foo".try_into().unwrap(); + } + let _: String = "foo".to_string().try_into().unwrap(); + let _: String = TryFrom::try_from("foo".to_string()).unwrap(); + let _ = String::try_from("foo".to_string()).unwrap(); + let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); + let _: String = format!("Hello {}", "world").try_into().unwrap(); + let _: String = "".to_owned().try_into().unwrap(); + let _: String = match String::from("_").try_into() { + Ok(a) => a, + Err(_) => "".into(), + }; + // FIXME this is a false negative + #[allow(clippy::cmp_owned)] + if String::from("a") == TryInto::::try_into(String::from("a")).unwrap() {} +} diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.stderr b/src/tools/clippy/tests/ui/useless_conversion_try.stderr new file mode 100644 index 0000000000000..b765727c168f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_conversion_try.stderr @@ -0,0 +1,79 @@ +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:6:13 + | +LL | let _ = T::try_from(val).unwrap(); + | ^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/useless_conversion_try.rs:1:9 + | +LL | #![deny(clippy::useless_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider removing `T::try_from()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:7:5 + | +LL | val.try_into().unwrap() + | ^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:29:21 + | +LL | let _: String = "foo".to_string().try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:30:21 + | +LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `TryFrom::try_from()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:31:13 + | +LL | let _ = String::try_from("foo".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `String::try_from()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:32:13 + | +LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `String::try_from()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:33:21 + | +LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:34:21 + | +LL | let _: String = "".to_owned().try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:35:27 + | +LL | let _: String = match String::from("_").try_into() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: aborting due to 9 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/vec_resize_to_zero.rs b/src/tools/clippy/tests/ui/vec_resize_to_zero.rs new file mode 100644 index 0000000000000..0263e2f5f20c1 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_resize_to_zero.rs @@ -0,0 +1,15 @@ +#![warn(clippy::vec_resize_to_zero)] + +fn main() { + // applicable here + vec![1, 2, 3, 4, 5].resize(0, 5); + + // not applicable + vec![1, 2, 3, 4, 5].resize(2, 5); + + // applicable here, but only implemented for integer litterals for now + vec!["foo", "bar", "baz"].resize(0, "bar"); + + // not applicable + vec!["foo", "bar", "baz"].resize(2, "bar") +} diff --git a/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr b/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr new file mode 100644 index 0000000000000..feb846298c656 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr @@ -0,0 +1,13 @@ +error: emptying a vector with `resize` + --> $DIR/vec_resize_to_zero.rs:5:5 + | +LL | vec![1, 2, 3, 4, 5].resize(0, 5); + | ^^^^^^^^^^^^^^^^^^^^------------ + | | + | help: ...or you can empty the vector with: `clear()` + | + = note: `-D clippy::vec-resize-to-zero` implied by `-D warnings` + = help: the arguments may be inverted... + +error: aborting due to previous error + 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..4f8754a930120 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed @@ -0,0 +1,102 @@ +// run-rustfix + +#![deny(clippy::wildcard_enum_match_arm)] +#![allow( + unreachable_code, + unused_variables, + dead_code, + clippy::single_match, + clippy::wildcard_in_or_patterns, + clippy::unnested_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..5e66644ceca0f --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs @@ -0,0 +1,102 @@ +// run-rustfix + +#![deny(clippy::wildcard_enum_match_arm)] +#![allow( + unreachable_code, + unused_variables, + dead_code, + clippy::single_match, + clippy::wildcard_in_or_patterns, + clippy::unnested_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..e03b3be43ed23 --- /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:38: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:42: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:46: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:62: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:79: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/common.rs b/src/tools/compiletest/src/common.rs index 64c0298c1fa4e..703b87634cec3 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -123,6 +123,7 @@ pub enum FailMode { pub enum CompareMode { Nll, Polonius, + Chalk, } impl CompareMode { @@ -130,6 +131,7 @@ impl CompareMode { match *self { CompareMode::Nll => "nll", CompareMode::Polonius => "polonius", + CompareMode::Chalk => "chalk", } } @@ -137,6 +139,7 @@ impl CompareMode { match s.as_str() { "nll" => CompareMode::Nll, "polonius" => CompareMode::Polonius, + "chalk" => CompareMode::Chalk, x => panic!("unknown --compare-mode option: {}", x), } } diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 2a24a8c3c9485..7d2c83881d13b 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -43,6 +43,10 @@ impl EarlyProps { let mut props = EarlyProps::default(); let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some(); let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(); + let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target); + let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); + let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); + let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target); iter_header(testfile, None, rdr, &mut |ln| { // we should check if any only- exists and if it exists @@ -74,7 +78,25 @@ impl EarlyProps { props.ignore = true; } - if !rustc_has_sanitizer_support && config.parse_needs_sanitizer_support(ln) { + if !rustc_has_sanitizer_support + && config.parse_name_directive(ln, "needs-sanitizer-support") + { + props.ignore = true; + } + + if !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address") { + props.ignore = true; + } + + if !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak") { + props.ignore = true; + } + + if !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory") { + props.ignore = true; + } + + if !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread") { props.ignore = true; } @@ -191,6 +213,7 @@ impl EarlyProps { return true; } if let Some(ref actual_version) = config.llvm_version { + let actual_version = version_to_int(actual_version); if line.starts_with("min-llvm-version") { let min_version = line .trim_end() @@ -199,7 +222,7 @@ impl EarlyProps { .expect("Malformed llvm version directive"); // Ignore if actual version is smaller the minimum required // version - &actual_version[..] < min_version + actual_version < version_to_int(min_version) } else if line.starts_with("min-system-llvm-version") { let min_version = line .trim_end() @@ -208,7 +231,7 @@ impl EarlyProps { .expect("Malformed llvm version directive"); // Ignore if using system LLVM and actual version // is smaller the minimum required version - config.system_llvm && &actual_version[..] < min_version + config.system_llvm && actual_version < version_to_int(min_version) } else if line.starts_with("ignore-llvm-version") { // Syntax is: "ignore-llvm-version [- ]" let range_components = line @@ -219,15 +242,15 @@ impl EarlyProps { .take(3) // 3 or more = invalid, so take at most 3. .collect::>(); match range_components.len() { - 1 => &actual_version[..] == range_components[0], + 1 => actual_version == version_to_int(range_components[0]), 2 => { - let v_min = range_components[0]; - let v_max = range_components[1]; + let v_min = version_to_int(range_components[0]); + let v_max = version_to_int(range_components[1]); if v_max < v_min { panic!("Malformed LLVM version range: max < min") } // Ignore if version lies inside of range. - &actual_version[..] >= v_min && &actual_version[..] <= v_max + actual_version >= v_min && actual_version <= v_max } _ => panic!("Malformed LLVM version directive"), } @@ -238,6 +261,20 @@ impl EarlyProps { false } } + + fn version_to_int(version: &str) -> u32 { + let version_without_suffix = version.split('-').next().unwrap(); + let components: Vec = version_without_suffix + .split('.') + .map(|s| s.parse().expect("Malformed version component")) + .collect(); + match components.len() { + 1 => components[0] * 10000, + 2 => components[0] * 10000 + components[1] * 100, + 3 => components[0] * 10000 + components[1] * 100 + components[2], + _ => panic!("Malformed version"), + } + } } } @@ -814,10 +851,6 @@ impl Config { self.parse_name_directive(line, "needs-profiler-support") } - fn parse_needs_sanitizer_support(&self, line: &str) -> bool { - self.parse_name_directive(line, "needs-sanitizer-support") - } - /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86` /// or `normalize-stderr-32bit`. fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective { @@ -838,9 +871,11 @@ impl Config { name == util::get_pointer_width(&self.target) || // pointer width name == self.stage_id.split('-').next().unwrap() || // stage (self.target != self.host && name == "cross-compile") || + (self.remote_test_client.is_some() && name == "remote") || match self.compare_mode { Some(CompareMode::Nll) => name == "compare-mode-nll", Some(CompareMode::Polonius) => name == "compare-mode-polonius", + Some(CompareMode::Chalk) => name == "compare-mode-chalk", None => false, } || (cfg!(debug_assertions) && name == "debug") || diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 6c478f7e29da4..72af34d78260b 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -27,6 +27,12 @@ fn test_parse_normalization_string() { let first = parse_normalization_string(&mut s); assert_eq!(first, Some("something (32 bits)".to_owned())); assert_eq!(s, " -> \"something ($WORD bits)."); + + // Nothing to normalize (No quotes, 16-bit) + let mut s = "normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."; + let first = parse_normalization_string(&mut s); + assert_eq!(first, None); + assert_eq!(s, r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."#); } fn config() -> Config { @@ -122,9 +128,8 @@ fn llvm_version() { config.llvm_version = Some("9.3.1-rust-1.43.0-dev".to_owned()); assert!(!parse_rs(&config, "// min-llvm-version 9.2").ignore); - // FIXME. - // config.llvm_version = Some("10.0.0-rust".to_owned()); - // assert!(!parse_rs(&config, "// min-llvm-version 9.0").ignore); + config.llvm_version = Some("10.0.0-rust".to_owned()); + assert!(!parse_rs(&config, "// min-llvm-version 9.0").ignore); } #[test] @@ -196,3 +201,22 @@ fn debugger() { config.debugger = Some(Debugger::Lldb); assert!(parse_rs(&config, "// ignore-lldb").ignore); } + +#[test] +fn sanitizers() { + let mut config = config(); + + // Target that supports all sanitizers: + config.target = "x86_64-unknown-linux-gnu".to_owned(); + assert!(!parse_rs(&config, "// needs-sanitizer-address").ignore); + assert!(!parse_rs(&config, "// needs-sanitizer-leak").ignore); + assert!(!parse_rs(&config, "// needs-sanitizer-memory").ignore); + assert!(!parse_rs(&config, "// needs-sanitizer-thread").ignore); + + // Target that doesn't support sanitizers: + config.target = "wasm32-unknown-emscripten".to_owned(); + assert!(parse_rs(&config, "// needs-sanitizer-address").ignore); + assert!(parse_rs(&config, "// needs-sanitizer-leak").ignore); + assert!(parse_rs(&config, "// needs-sanitizer-memory").ignore); + assert!(parse_rs(&config, "// needs-sanitizer-thread").ignore); +} diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index 52d0cbd4bfd7a..6ac7c3b9b474a 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -4,7 +4,6 @@ use crate::errors::{Error, ErrorKind}; use crate::runtest::ProcRes; use serde::Deserialize; -use serde_json; use std::path::{Path, PathBuf}; use std::str::FromStr; diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index a61b940398704..134ac66b7d15b 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -1,5 +1,4 @@ #![crate_name = "compiletest"] -#![feature(vec_remove_item)] #![deny(warnings)] // The `test` crate is the only unstable feature // allowed here, just to share similar code. @@ -10,8 +9,6 @@ extern crate test; use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS}; use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, Pretty, TestPaths}; use crate::util::logv; -use env_logger; -use getopts; use getopts::Options; use log::*; use std::env; @@ -347,7 +344,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 +449,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. @@ -468,11 +461,13 @@ fn common_inputs_stamp(config: &Config) -> Stamp { // Relevant pretty printer files let pretty_printer_files = [ - "src/etc/debugger_pretty_printers_common.py", + "src/etc/rust_types.py", "src/etc/gdb_load_rust_pretty_printers.py", - "src/etc/gdb_rust_pretty_printing.py", + "src/etc/gdb_lookup.py", + "src/etc/gdb_providers.py", "src/etc/lldb_batchmode.py", - "src/etc/lldb_rust_formatters.py", + "src/etc/lldb_lookup.py", + "src/etc/lldb_providers.py", ]; for file in &pretty_printer_files { let path = rust_src_dir.join(file); @@ -494,7 +489,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 +532,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 +833,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 +894,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/read2.rs b/src/tools/compiletest/src/read2.rs index da1d3db49d70e..30a922057eb20 100644 --- a/src/tools/compiletest/src/read2.rs +++ b/src/tools/compiletest/src/read2.rs @@ -25,7 +25,6 @@ mod imp { #[cfg(unix)] mod imp { - use libc; use std::io; use std::io::prelude::*; use std::mem; diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index b04012af515dd..dd0c68ecd4965 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -11,8 +11,8 @@ 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}; use rustfix::{apply_suggestions, get_suggestions_from_json, Filter}; @@ -20,7 +20,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 +44,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 +177,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 +356,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), } } @@ -1055,15 +1078,38 @@ impl<'test> TestCx<'test> { // Switch LLDB into "Rust mode" let rust_src_root = self.config.find_rust_src_root().expect("Could not find Rust source root"); - let rust_pp_module_rel_path = Path::new("./src/etc/lldb_rust_formatters.py"); + let rust_pp_module_rel_path = Path::new("./src/etc/lldb_lookup.py"); let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path).to_str().unwrap().to_owned(); + let rust_type_regexes = vec![ + "^(alloc::([a-z_]+::)+)String$", + "^&str$", + "^&\\[.+\\]$", + "^(std::ffi::([a-z_]+::)+)OsString$", + "^(alloc::([a-z_]+::)+)Vec<.+>$", + "^(alloc::([a-z_]+::)+)VecDeque<.+>$", + "^(alloc::([a-z_]+::)+)BTreeSet<.+>$", + "^(alloc::([a-z_]+::)+)BTreeMap<.+>$", + "^(std::collections::([a-z_]+::)+)HashMap<.+>$", + "^(std::collections::([a-z_]+::)+)HashSet<.+>$", + "^(alloc::([a-z_]+::)+)Rc<.+>$", + "^(alloc::([a-z_]+::)+)Arc<.+>$", + "^(core::([a-z_]+::)+)Cell<.+>$", + "^(core::([a-z_]+::)+)Ref<.+>$", + "^(core::([a-z_]+::)+)RefMut<.+>$", + "^(core::([a-z_]+::)+)RefCell<.+>$", + ]; + script_str .push_str(&format!("command script import {}\n", &rust_pp_module_abs_path[..])[..]); - script_str.push_str("type summary add --no-value "); - script_str.push_str("--python-function lldb_rust_formatters.print_val "); - script_str.push_str("-x \".*\" --category Rust\n"); + script_str.push_str("type synthetic add -l lldb_lookup.synthetic_lookup -x '.*' "); + script_str.push_str("--category Rust\n"); + for type_regex in rust_type_regexes { + script_str.push_str("type summary add -F lldb_lookup.summary_lookup -e -x -h "); + script_str.push_str(&format!("'{}' ", type_regex)); + script_str.push_str("--category Rust\n"); + } script_str.push_str("type category enable Rust\n"); // Set breakpoints on every line that contains the string "#break" @@ -1560,29 +1606,34 @@ impl<'test> TestCx<'test> { // // into // - // remote-test-client run program:support-lib.so arg1 arg2 + // remote-test-client run program 2 support-lib.so support-lib2.so arg1 arg2 // // The test-client program will upload `program` to the emulator // along with all other support libraries listed (in this case - // `support-lib.so`. It will then execute the program on the - // emulator with the arguments specified (in the environment we give - // the process) and then report back the same result. + // `support-lib.so` and `support-lib2.so`. It will then execute + // the program on the emulator with the arguments specified + // (in the environment we give the process) and then report back + // the same result. _ if self.config.remote_test_client.is_some() => { let aux_dir = self.aux_output_dir_name(); - let ProcArgs { mut prog, args } = self.make_run_args(); + let ProcArgs { prog, args } = self.make_run_args(); + let mut support_libs = Vec::new(); if let Ok(entries) = aux_dir.read_dir() { for entry in entries { let entry = entry.unwrap(); if !entry.path().is_file() { continue; } - prog.push_str(":"); - prog.push_str(entry.path().to_str().unwrap()); + support_libs.push(entry.path()); } } let mut test_client = Command::new(self.config.remote_test_client.as_ref().unwrap()); - test_client.args(&["run", &prog]).args(args).envs(env.clone()); + test_client + .args(&["run", &support_libs.len().to_string(), &prog]) + .args(support_libs) + .args(args) + .envs(env.clone()); self.compose_and_run( test_client, self.config.run_lib_path.to_str().unwrap(), @@ -1726,6 +1777,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 +1904,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 { @@ -1933,9 +1984,18 @@ impl<'test> TestCx<'test> { Some(CompareMode::Polonius) => { rustc.args(&["-Zpolonius", "-Zborrowck=mir"]); } + Some(CompareMode::Chalk) => { + rustc.args(&["-Zchalk"]); + } 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 +2018,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 +2546,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 +2596,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 +2637,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 +2841,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 +3097,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 +3218,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 +3276,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 +3369,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 +3388,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 +3435,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 +3507,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..0437ff8c9440a 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"), @@ -46,6 +47,7 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[ ("armv7", "arm"), ("armv7s", "arm"), ("asmjs", "asmjs"), + ("avr", "avr"), ("hexagon", "hexagon"), ("i386", "x86"), ("i586", "x86"), @@ -80,6 +82,23 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[ ("xcore", "xcore"), ]; +pub const ASAN_SUPPORTED_TARGETS: &'static [&'static str] = &[ + "aarch64-fuchsia", + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-fuchsia", + "x86_64-unknown-linux-gnu", +]; + +pub const LSAN_SUPPORTED_TARGETS: &'static [&'static str] = + &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + +pub const MSAN_SUPPORTED_TARGETS: &'static [&'static str] = + &["aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"]; + +pub const TSAN_SUPPORTED_TARGETS: &'static [&'static str] = + &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + pub fn matches_os(triple: &str, name: &str) -> bool { // For the wasm32 bare target we ignore anything also ignored on emscripten // and then we also recognize `wasm32-bare` as the os for the target @@ -113,6 +132,8 @@ pub fn matches_env(triple: &str, name: &str) -> bool { pub fn get_pointer_width(triple: &str) -> &'static str { if (triple.contains("64") && !triple.ends_with("gnux32")) || triple.starts_with("s390x") { "64bit" + } else if triple.starts_with("avr") { + "16bit" } else { "32bit" } 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 a7891c05f7fdb..59619775ee44a 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit a7891c05f7fdb329f68c991abbb39dafc1f6b4a5 +Subproject commit 59619775ee44a5e0c875efffc4ca55ae96fca7dc diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py index b389cd0373cc4..72437e070044c 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,25 @@ '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'}, +} + +LABELS = { + 'miri': ['A-miri', 'C-bug'], + 'rls': ['A-rls', 'C-bug'], + 'rustfmt': ['C-bug'], + 'book': ['C-bug'], + 'nomicon': ['C-bug'], + 'reference': ['C-bug'], + 'rust-by-example': ['C-bug'], + 'embedded-book': ['C-bug'], + 'edition-guide': ['C-bug'], + 'rustc-dev-guide': ['C-bug'], } 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 +62,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 +86,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') @@ -130,6 +145,7 @@ def issue( assignees, relevant_pr_number, relevant_pr_user, + labels, ): # Open an issue about the toolstate failure. if status == 'test-fail': @@ -153,7 +169,7 @@ def issue( )), 'title': '`{}` no longer builds after {}'.format(tool, relevant_pr_number), 'assignees': list(assignees), - 'labels': ['T-compiler', 'I-nominated'], + 'labels': labels, }) print("Creating issue:\n{}".format(request)) response = urllib2.urlopen(urllib2.Request( @@ -177,7 +193,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 +219,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: @@ -234,7 +250,7 @@ def update_latest( try: issue( tool, create_issue_for_status, MAINTAINERS.get(tool, ''), - relevant_pr_number, relevant_pr_user, + relevant_pr_number, relevant_pr_user, LABELS.get(tool, ''), ) except urllib2.HTTPError as e: # network errors will simply end up not creating an issue, but that's better diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs index d0ae8300bd6af..1fafe109d34e1 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()), @@ -39,13 +44,23 @@ fn main() { args.next().map(|s| s.into()), ), "push" => push(Path::new(&args.next().unwrap())), - "run" => run(args.next().unwrap(), args.collect()), - cmd => panic!("unknown command: {}", cmd), + "run" => run( + args.next().and_then(|count| count.parse().ok()).unwrap(), + // the last required parameter must remain the executable + // path so that the client works as a cargo runner + args.next().unwrap(), + args.collect(), + ), + "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); @@ -92,13 +107,23 @@ fn start_android_emulator(server: &Path) { Command::new("adb").arg("shell").arg("/data/tmp/testd").spawn().unwrap(); } -fn start_qemu_emulator(target: &str, rootfs: &Path, server: &Path, tmpdir: &Path) { +fn prepare_rootfs(target: &str, rootfs: &Path, server: &Path, rootfs_img: &Path) { + t!(fs::copy(server, rootfs.join("testd"))); + + match target { + "arm-unknown-linux-gnueabihf" | "aarch64-unknown-linux-gnu" => { + prepare_rootfs_cpio(rootfs, rootfs_img) + } + "riscv64gc-unknown-linux-gnu" => prepare_rootfs_ext4(rootfs, rootfs_img), + _ => panic!("{} is not supported", target), + } +} + +fn prepare_rootfs_cpio(rootfs: &Path, rootfs_img: &Path) { // Generate a new rootfs image now that we've updated the test server // executable. This is the equivalent of: // // find $rootfs -print 0 | cpio --null -o --format=newc > rootfs.img - t!(fs::copy(server, rootfs.join("testd"))); - let rootfs_img = tmpdir.join("rootfs.img"); let mut cmd = Command::new("cpio"); cmd.arg("--null") .arg("-o") @@ -113,6 +138,38 @@ fn start_qemu_emulator(target: &str, rootfs: &Path, server: &Path, tmpdir: &Path t!(io::copy(&mut child.stdout.take().unwrap(), &mut t!(File::create(&rootfs_img)))); assert!(t!(child.wait()).success()); + fn add_files(w: &mut dyn Write, root: &Path, cur: &Path) { + for entry in t!(cur.read_dir()) { + let entry = t!(entry); + let path = entry.path(); + let to_print = path.strip_prefix(root).unwrap(); + t!(write!(w, "{}\u{0}", to_print.to_str().unwrap())); + if t!(entry.file_type()).is_dir() { + add_files(w, root, &path); + } + } + } +} + +fn prepare_rootfs_ext4(rootfs: &Path, rootfs_img: &Path) { + let mut dd = Command::new("dd"); + dd.arg("if=/dev/zero") + .arg(&format!("of={}", rootfs_img.to_string_lossy())) + .arg("bs=1M") + .arg("count=1024"); + let mut dd_child = t!(dd.spawn()); + assert!(t!(dd_child.wait()).success()); + + let mut mkfs = Command::new("mkfs.ext4"); + mkfs.arg("-d").arg(rootfs).arg(rootfs_img); + let mut mkfs_child = t!(mkfs.spawn()); + assert!(t!(mkfs_child.wait()).success()); +} + +fn start_qemu_emulator(target: &str, rootfs: &Path, server: &Path, tmpdir: &Path) { + let rootfs_img = &tmpdir.join("rootfs.img"); + prepare_rootfs(target, rootfs, server, rootfs_img); + // Start up the emulator, in the background match target { "arm-unknown-linux-gnueabihf" => { @@ -155,24 +212,35 @@ fn start_qemu_emulator(target: &str, rootfs: &Path, server: &Path, tmpdir: &Path .arg("virtio-net-device,netdev=net0,mac=00:00:00:00:00:00"); t!(cmd.spawn()); } - _ => panic!("cannot start emulator for: {}" < target), - } - - fn add_files(w: &mut dyn Write, root: &Path, cur: &Path) { - for entry in t!(cur.read_dir()) { - let entry = t!(entry); - let path = entry.path(); - let to_print = path.strip_prefix(root).unwrap(); - t!(write!(w, "{}\u{0}", to_print.to_str().unwrap())); - if t!(entry.file_type()).is_dir() { - add_files(w, root, &path); - } + "riscv64gc-unknown-linux-gnu" => { + let mut cmd = Command::new("qemu-system-riscv64"); + cmd.arg("-nographic") + .arg("-machine") + .arg("virt") + .arg("-m") + .arg("1024") + .arg("-bios") + .arg("none") + .arg("-kernel") + .arg("/tmp/bbl") + .arg("-append") + .arg("quiet console=ttyS0 root=/dev/vda rw") + .arg("-netdev") + .arg("user,id=net0,hostfwd=tcp::12345-:12345") + .arg("-device") + .arg("virtio-net-device,netdev=net0,mac=00:00:00:00:00:00") + .arg("-device") + .arg("virtio-blk-device,drive=hd0") + .arg("-drive") + .arg(&format!("file={},format=raw,id=hd0", &rootfs_img.to_string_lossy())); + t!(cmd.spawn()); } + _ => panic!("cannot start emulator for: {}", target), } } 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")); @@ -188,12 +256,14 @@ fn push(path: &Path) { println!("done pushing {:?}", path); } -fn run(files: String, args: Vec) { - let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string()); +fn run(support_lib_count: usize, exe: String, all_args: Vec) { + 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 ")); + let (support_libs, args) = all_args.split_at(support_lib_count); + // Send over the args for arg in args { t!(client.write_all(arg.as_bytes())); @@ -207,7 +277,7 @@ fn run(files: String, args: Vec) { // by the client. for (k, v) in env::vars() { match &k[..] { - "PATH" | "LD_LIBRARY_PATH" | "PWD" => continue, + "PATH" | "LD_LIBRARY_PATH" | "PWD" | "RUST_TEST_TMPDIR" => continue, _ => {} } t!(client.write_all(k.as_bytes())); @@ -218,9 +288,7 @@ fn run(files: String, args: Vec) { t!(client.write_all(&[0])); // Send over support libraries - let mut files = files.split(':'); - let exe = files.next().unwrap(); - for file in files.map(Path::new) { + for file in support_libs.iter().map(Path::new) { send(&file, &mut client); } t!(client.write_all(&[0])); @@ -284,3 +352,41 @@ 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 [support_libs...] [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 and any specified support libs are pushed to the target. Finally, the + 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/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index 826e3d05111ae..8c56910e2dfad 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -12,15 +12,19 @@ #![deny(warnings)] +#[cfg(not(windows))] +use std::fs::Permissions; +#[cfg(not(windows))] +use std::os::unix::prelude::*; + use std::cmp; use std::env; -use std::fs::{self, File, Permissions}; +use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufReader}; use std::net::{TcpListener, TcpStream}; -use std::os::unix::prelude::*; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::{Command, ExitStatus, Stdio}; use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; @@ -37,6 +41,7 @@ macro_rules! t { static TEST: AtomicUsize = AtomicUsize::new(0); +#[derive(Copy, Clone)] struct Config { pub remote: bool, pub verbose: bool, @@ -67,26 +72,37 @@ impl Config { } } +fn print_verbose(s: &str, conf: Config) { + if conf.verbose { + println!("{}", s); + } +} + fn main() { println!("starting test server"); let config = Config::parse_args(); - let bind_addr = if cfg!(target_os = "android") || config.remote { + let bind_addr = if cfg!(target_os = "android") || cfg!(windows) || config.remote { "0.0.0.0:12345" } else { "10.0.2.15:12345" }; - let (listener, work) = if cfg!(target_os = "android") { - (t!(TcpListener::bind(bind_addr)), "/data/tmp/work") + let listener = t!(TcpListener::bind(bind_addr)); + let (work, tmp): (PathBuf, PathBuf) = if cfg!(target_os = "android") { + ("/data/tmp/work".into(), "/data/tmp/work/tmp".into()) } else { - (t!(TcpListener::bind(bind_addr)), "/tmp/work") + let mut work_dir = env::temp_dir(); + work_dir.push("work"); + let mut tmp_dir = work_dir.clone(); + tmp_dir.push("tmp"); + (work_dir, tmp_dir) }; - println!("listening!"); + println!("listening on {}!", bind_addr); - let work = Path::new(work); - t!(fs::create_dir_all(work)); + t!(fs::create_dir_all(&work)); + t!(fs::create_dir_all(&tmp)); let lock = Arc::new(Mutex::new(())); @@ -97,21 +113,25 @@ fn main() { continue; } if &buf[..] == b"ping" { + print_verbose("Received ping", config); t!(socket.write_all(b"pong")); } else if &buf[..] == b"push" { - handle_push(socket, work); + handle_push(socket, &work, config); } else if &buf[..] == b"run " { let lock = lock.clone(); - thread::spawn(move || handle_run(socket, work, &lock)); + let work = work.clone(); + let tmp = tmp.clone(); + thread::spawn(move || handle_run(socket, &work, &tmp, &lock, config)); } else { panic!("unknown command {:?}", buf); } } } -fn handle_push(socket: TcpStream, work: &Path) { +fn handle_push(socket: TcpStream, work: &Path, config: Config) { let mut reader = BufReader::new(socket); - recv(&work, &mut reader); + let dst = recv(&work, &mut reader); + print_verbose(&format!("push {:#?}", dst), config); let mut socket = reader.into_inner(); t!(socket.write_all(b"ack ")); @@ -127,7 +147,7 @@ impl Drop for RemoveOnDrop<'_> { } } -fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { +fn handle_run(socket: TcpStream, work: &Path, tmp: &Path, lock: &Mutex<()>, config: Config) { let mut arg = Vec::new(); let mut reader = BufReader::new(socket); @@ -194,19 +214,34 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { // binary is and then we'll download it all to the exe path we calculated // earlier. let exe = recv(&path, &mut reader); + print_verbose(&format!("run {:#?}", exe), config); let mut cmd = Command::new(&exe); - for arg in args { - cmd.arg(arg); - } - for (k, v) in env { - cmd.env(k, v); - } + cmd.args(args); + cmd.envs(env); // Support libraries were uploaded to `work` earlier, so make sure that's // in `LD_LIBRARY_PATH`. Also include our own current dir which may have // had some libs uploaded. - cmd.env("LD_LIBRARY_PATH", format!("{}:{}", work.display(), path.display())); + if cfg!(windows) { + // On windows, libraries are just searched in the executable directory, + // system directories, PWD, and PATH, in that order. PATH is the only one + // we can change for this. + cmd.env( + "PATH", + env::join_paths( + std::iter::once(work.to_owned()) + .chain(std::iter::once(path.clone())) + .chain(env::split_paths(&env::var_os("PATH").unwrap())), + ) + .unwrap(), + ); + } else { + cmd.env("LD_LIBRARY_PATH", format!("{}:{}", work.display(), path.display())); + } + + // Some tests assume RUST_TEST_TMPDIR exists + cmd.env("RUST_TEST_TMPDIR", tmp.to_owned()); // Spawn the child and ferry over stdout/stderr to the socket in a framed // fashion (poor man's style) @@ -223,10 +258,9 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { // Finally send over the exit status. let status = t!(child.wait()); - let (which, code) = match status.code() { - Some(n) => (0, n), - None => (1, status.signal().unwrap()), - }; + + let (which, code) = get_status_code(&status); + t!(socket.lock().unwrap().write_all(&[ which, (code >> 24) as u8, @@ -236,6 +270,19 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) { ])); } +#[cfg(not(windows))] +fn get_status_code(status: &ExitStatus) -> (u8, i32) { + match status.code() { + Some(n) => (0, n), + None => (1, status.signal().unwrap()), + } +} + +#[cfg(windows)] +fn get_status_code(status: &ExitStatus) -> (u8, i32) { + (0, status.code().unwrap()) +} + fn recv(dir: &Path, io: &mut B) -> PathBuf { let mut filename = Vec::new(); t!(io.read_until(0, &mut filename)); @@ -253,10 +300,17 @@ fn recv(dir: &Path, io: &mut B) -> PathBuf { let dst = dir.join(t!(str::from_utf8(&filename[..len]))); let amt = read_u32(io) as u64; t!(io::copy(&mut io.take(amt), &mut t!(File::create(&dst)))); - t!(fs::set_permissions(&dst, Permissions::from_mode(0o755))); + set_permissions(&dst); dst } +#[cfg(not(windows))] +fn set_permissions(path: &Path) { + t!(fs::set_permissions(&path, Permissions::from_mode(0o755))); +} +#[cfg(windows)] +fn set_permissions(_path: &Path) {} + fn my_copy(src: &mut dyn Read, which: u8, dst: &Mutex) { let mut b = [0; 1024]; loop { diff --git a/src/tools/rls b/src/tools/rls index 5fde462d8c53b..fb46b914c11b0 160000 --- a/src/tools/rls +++ b/src/tools/rls @@ -1 +1 @@ -Subproject commit 5fde462d8c53b86840100a927a17c8353bba3e3f +Subproject commit fb46b914c11b06828680cb526e2abe9e1d69b868 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..351e2d4481c0f 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -17,9 +17,12 @@ path = "lib.rs" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" features = [ + "aclapi", + "accctrl", "basetsd", "consoleapi", "errhandlingapi", + "fibersapi", "ioapiset", "jobapi", "jobapi2", @@ -59,17 +62,18 @@ 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'] } +syn = { version = "1", features = ['fold', 'full', 'extra-traits', 'visit'] } url = { version = "2.0", features = ['serde'] } -syn = { version = "0.15", features = ['full', 'extra-traits'] } [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 19cf0483b7624..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", - "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; -} - -process.exit(main(process.argv)); diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 7174474be1c2b..139e6f73f4216 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,19 +218,17 @@ 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", + var variablesToLoad = ["MAX_LEV_DISTANCE", "MAX_RESULTS", "NO_TYPE_FILTER", "GENERICS_DATA", "NAME", "INPUTS_DATA", "OUTPUT_DATA", "TY_PRIMITIVE", "TY_KEYWORD", "levenshtein_row2"]; @@ -239,96 +236,215 @@ 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(["hasOwnProperty", "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); -function main(argv) { - if (argv.length < 4) { - console.error("USAGE: node tester.js OUT_FOLDER [TESTS]"); - return 1; - } - if (argv[2].substr(-1) !== "/") { - argv[2] += "/"; - } - const out_folder = argv[2]; + return [loaded, index]; +} - var errors = 0; +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; - for (var j = 3; j < argv.length; ++j) { - const test_file = argv[j]; - const test_name = path.basename(test_file, ".js"); + var results = loaded.execSearch(loaded.getQuery(query), index, filter_crate); + var error_text = []; - process.stdout.write('Checking "' + test_name + '" ... '); - if (!fs.existsSync(test_file)) { - errors += 1; - console.error("FAILED"); - console.error("==> Missing '" + test_name + ".js' file..."); + 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]; - 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; + if (exact_check == true && entry.length !== results[key].length) { + error_text.push(queryName + "==> Expected exactly " + entry.length + + " results but found " + results[key].length + " in '" + 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; } - 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; - } + } + } + 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; + } + return 1; +} + +function runChecks(testFile, loaded, index) { + var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;'; + if (testFileContent.indexOf("FILTER_CRATE") !== -1) { + testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;"; + } + var loadedFile = loadContent(testFileContent); + + const expected = loadedFile.EXPECTED; + const query = loadedFile.QUERY; + + 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; + } + 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; } } - return errors; + 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; } process.exit(main(process.argv)); diff --git a/src/tools/rustfmt b/src/tools/rustfmt index 9f53665f91be1..c1e9b7b87493c 160000 --- a/src/tools/rustfmt +++ b/src/tools/rustfmt @@ -1 +1 @@ -Subproject commit 9f53665f91be16c9aa7afd83f7c79357fec9152b +Subproject commit c1e9b7b87493c5197c4330693bdf4ccb30a90971 diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index 43cae31f33f1f..f984e5b61a5fd 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -5,8 +5,7 @@ authors = ["Alex Crichton "] edition = "2018" [dependencies] +cargo_metadata = "0.9.1" regex = "1" -serde = { version = "1.0.8", features = ["derive"] } -serde_json = "1.0.2" lazy_static = "1" walkdir = "2" diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 7a20a96130ccb..093db2a49d029 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -1,13 +1,11 @@ -//! Checks the licenses of third-party dependencies by inspecting vendors. +//! Checks the licenses of third-party dependencies. -use std::collections::{BTreeSet, HashMap, HashSet}; -use std::fs; +use cargo_metadata::{Metadata, Package, PackageId, Resolve}; +use std::collections::{BTreeSet, HashSet}; use std::path::Path; -use std::process::Command; - -use serde::Deserialize; -use serde_json; +/// These are licenses that are allowed for all crates, including the runtime, +/// rustc, tools, etc. const LICENSES: &[&str] = &[ "MIT/Apache-2.0", "MIT / Apache-2.0", @@ -25,261 +23,277 @@ const LICENSES: &[&str] = &[ /// should be considered bugs. Exceptions are only allowed in Rust /// tooling. It is _crucial_ that no exception crates be dependencies /// of the Rust runtime (std/test). -const EXCEPTIONS: &[&str] = &[ - "mdbook", // MPL2, mdbook - "openssl", // BSD+advertising clause, cargo, mdbook - "pest", // MPL2, mdbook via handlebars - "arrayref", // BSD-2-Clause, mdbook via handlebars via pest - "thread-id", // Apache-2.0, mdbook - "toml-query", // MPL-2.0, mdbook - "is-match", // MPL-2.0, mdbook - "cssparser", // MPL-2.0, rustdoc - "smallvec", // MPL-2.0, rustdoc - "rdrand", // ISC, mdbook, rustfmt - "fuchsia-cprng", // BSD-3-Clause, mdbook, rustfmt - "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo - "fuchsia-zircon", // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir) - "cssparser-macros", // MPL-2.0, rustdoc - "selectors", // MPL-2.0, rustdoc - "clippy_lints", // MPL-2.0, rls - "colored", // MPL-2.0, rustfmt - "ordslice", // Apache-2.0, rls - "cloudabi", // BSD-2-Clause, (rls -> crossbeam-channel 0.2 -> rand 0.5) - "ryu", // Apache-2.0, rls/cargo/... (because of serde) - "bytesize", // Apache-2.0, cargo - "im-rc", // MPL-2.0+, cargo - "adler32", // BSD-3-Clause AND Zlib, cargo dep that isn't used - "constant_time_eq", // CC0-1.0, rustfmt - "utf8parse", // Apache-2.0 OR MIT, cargo via strip-ansi-escapes - "vte", // Apache-2.0 OR MIT, cargo via strip-ansi-escapes - "sized-chunks", // MPL-2.0+, cargo via im-rc - "bitmaps", // MPL-2.0+, cargo via im-rc +const EXCEPTIONS: &[(&str, &str)] = &[ + ("mdbook", "MPL-2.0"), // mdbook + ("openssl", "Apache-2.0"), // cargo, mdbook + ("toml-query", "MPL-2.0"), // mdbook + ("toml-query_derive", "MPL-2.0"), // mdbook + ("is-match", "MPL-2.0"), // mdbook + ("rdrand", "ISC"), // mdbook, rustfmt + ("fuchsia-cprng", "BSD-3-Clause"), // mdbook, rustfmt + ("fuchsia-zircon-sys", "BSD-3-Clause"), // rustdoc, rustc, cargo + ("fuchsia-zircon", "BSD-3-Clause"), // rustdoc, rustc, cargo (jobserver & tempdir) + ("colored", "MPL-2.0"), // rustfmt + ("ordslice", "Apache-2.0"), // rls + ("cloudabi", "BSD-2-Clause"), // (rls -> crossbeam-channel 0.2 -> rand 0.5) + ("ryu", "Apache-2.0 OR BSL-1.0"), // rls/cargo/... (because of serde) + ("bytesize", "Apache-2.0"), // cargo + ("im-rc", "MPL-2.0+"), // cargo + ("adler32", "BSD-3-Clause AND Zlib"), // cargo dep that isn't used + ("constant_time_eq", "CC0-1.0"), // rustfmt + ("sized-chunks", "MPL-2.0+"), // cargo via im-rc + ("bitmaps", "MPL-2.0+"), // cargo via im-rc // FIXME: this dependency violates the documentation comment above: - "fortanix-sgx-abi", // MPL-2.0+, libstd but only for `sgx` target - "dunce", // CC0-1.0 mdbook-linkcheck - "codespan-reporting", // Apache-2.0 mdbook-linkcheck - "codespan", // Apache-2.0 mdbook-linkcheck - "crossbeam-channel", // MIT/Apache-2.0 AND BSD-2-Clause, cargo + ("fortanix-sgx-abi", "MPL-2.0"), // libstd but only for `sgx` target + ("dunce", "CC0-1.0"), // mdbook-linkcheck + ("codespan-reporting", "Apache-2.0"), // mdbook-linkcheck + ("codespan", "Apache-2.0"), // mdbook-linkcheck + ("crossbeam-channel", "MIT/Apache-2.0 AND BSD-2-Clause"), // cargo ]; +/// These are the root crates that are part of the runtime. The licenses for +/// these and all their dependencies *must not* be in the exception list. +const RUNTIME_CRATES: &[&str] = &["std", "core", "alloc", "test", "panic_abort", "panic_unwind"]; + /// Which crates to check against the whitelist? -const WHITELIST_CRATES: &[CrateVersion<'_>] = - &[CrateVersion("rustc", "0.0.0"), CrateVersion("rustc_codegen_llvm", "0.0.0")]; +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. -const WHITELIST: &[Crate<'_>] = &[ - Crate("adler32"), - Crate("aho-corasick"), - Crate("annotate-snippets"), - Crate("ansi_term"), - Crate("arrayvec"), - Crate("atty"), - Crate("autocfg"), - Crate("backtrace"), - Crate("backtrace-sys"), - Crate("bitflags"), - Crate("build_const"), - Crate("byteorder"), - Crate("c2-chacha"), - Crate("cc"), - Crate("cfg-if"), - Crate("chalk-engine"), - Crate("chalk-macros"), - Crate("cloudabi"), - Crate("cmake"), - Crate("compiler_builtins"), - Crate("crc"), - Crate("crc32fast"), - Crate("crossbeam-deque"), - Crate("crossbeam-epoch"), - Crate("crossbeam-queue"), - Crate("crossbeam-utils"), - Crate("datafrog"), - Crate("dlmalloc"), - Crate("either"), - Crate("ena"), - Crate("env_logger"), - Crate("filetime"), - Crate("flate2"), - Crate("fortanix-sgx-abi"), - Crate("fuchsia-zircon"), - Crate("fuchsia-zircon-sys"), - Crate("getopts"), - Crate("getrandom"), - Crate("hashbrown"), - Crate("humantime"), - Crate("indexmap"), - Crate("itertools"), - Crate("jobserver"), - Crate("kernel32-sys"), - Crate("lazy_static"), - Crate("libc"), - Crate("libz-sys"), - Crate("lock_api"), - Crate("log"), - Crate("log_settings"), - Crate("measureme"), - Crate("memchr"), - Crate("memmap"), - Crate("memoffset"), - Crate("miniz-sys"), - Crate("miniz_oxide"), - Crate("miniz_oxide_c_api"), - Crate("nodrop"), - Crate("num_cpus"), - Crate("owning_ref"), - Crate("parking_lot"), - Crate("parking_lot_core"), - Crate("pkg-config"), - Crate("polonius-engine"), - Crate("ppv-lite86"), - Crate("proc-macro2"), - Crate("punycode"), - Crate("quick-error"), - Crate("quote"), - Crate("rand"), - Crate("rand_chacha"), - Crate("rand_core"), - Crate("rand_hc"), - Crate("rand_isaac"), - Crate("rand_pcg"), - Crate("rand_xorshift"), - Crate("redox_syscall"), - Crate("redox_termios"), - Crate("regex"), - Crate("regex-syntax"), - Crate("remove_dir_all"), - Crate("rustc-demangle"), - Crate("rustc-hash"), - Crate("rustc-rayon"), - Crate("rustc-rayon-core"), - Crate("rustc_version"), - Crate("scoped-tls"), - Crate("scopeguard"), - Crate("semver"), - Crate("semver-parser"), - Crate("serde"), - Crate("serde_derive"), - Crate("smallvec"), - Crate("stable_deref_trait"), - Crate("syn"), - Crate("synstructure"), - Crate("tempfile"), - Crate("termcolor"), - Crate("terminon"), - Crate("termion"), - Crate("termize"), - Crate("thread_local"), - Crate("ucd-util"), - Crate("unicode-normalization"), - Crate("unicode-script"), - Crate("unicode-security"), - Crate("unicode-width"), - Crate("unicode-xid"), - Crate("unreachable"), - Crate("utf8-ranges"), - Crate("vcpkg"), - Crate("version_check"), - Crate("void"), - Crate("wasi"), - Crate("winapi"), - Crate("winapi-build"), - Crate("winapi-i686-pc-windows-gnu"), - Crate("winapi-util"), - Crate("winapi-x86_64-pc-windows-gnu"), - Crate("wincolor"), - Crate("hermit-abi"), +/// +/// This list is here to provide a speed-bump to adding a new dependency to +/// rustc. Please check with the compiler team before adding an entry. +const WHITELIST: &[&str] = &[ + "adler32", + "aho-corasick", + "annotate-snippets", + "ansi_term", + "arrayvec", + "atty", + "autocfg", + "backtrace", + "backtrace-sys", + "bitflags", + "block-buffer", + "block-padding", + "byte-tools", + "byteorder", + "cc", + "cfg-if", + "chalk-derive", + "chalk-ir", + "cloudabi", + "cmake", + "compiler_builtins", + "crc32fast", + "crossbeam-deque", + "crossbeam-epoch", + "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", + "hermit-abi", + "humantime", + "indexmap", + "itertools", + "jobserver", + "kernel32-sys", + "lazy_static", + "libc", + "libz-sys", + "lock_api", + "log", + "log_settings", + "md-5", + "measureme", + "memchr", + "memmap", + "memoffset", + "miniz_oxide", + "nodrop", + "num_cpus", + "once_cell", + "opaque-debug", + "parking_lot", + "parking_lot_core", + "pkg-config", + "polonius-engine", + "ppv-lite86", + "proc-macro2", + "psm", + "punycode", + "quick-error", + "quote", + "rand", + "rand_chacha", + "rand_core", + "rand_hc", + "rand_isaac", + "rand_pcg", + "rand_xorshift", + "redox_syscall", + "regex", + "regex-syntax", + "remove_dir_all", + "rustc-demangle", + "rustc-hash", + "rustc-rayon", + "rustc-rayon-core", + "rustc_version", + "scoped-tls", + "scopeguard", + "semver", + "semver-parser", + "serde", + "serde_derive", + "sha-1", + "smallvec", + "stable_deref_trait", + "stacker", + "syn", + "synstructure", + "tempfile", + "termcolor", + "termize", + "thread_local", + "typenum", + "unicode-normalization", + "unicode-script", + "unicode-security", + "unicode-width", + "unicode-xid", + "vcpkg", + "version_check", + "wasi", + "winapi", + "winapi-build", + "winapi-i686-pc-windows-gnu", + "winapi-util", + "winapi-x86_64-pc-windows-gnu", + "wincolor", ]; -// Some types for Serde to deserialize the output of `cargo metadata` to. - -#[derive(Deserialize)] -struct Output { - resolve: Resolve, -} - -#[derive(Deserialize)] -struct Resolve { - nodes: Vec, -} - -#[derive(Deserialize)] -struct ResolveNode { - id: String, - dependencies: Vec, -} - -/// A unique identifier for a crate. -#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)] -struct Crate<'a>(&'a str); // (name) - -#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)] -struct CrateVersion<'a>(&'a str, &'a str); // (name, version) - -impl Crate<'_> { - pub fn id_str(&self) -> String { - format!("{} ", self.0) - } -} - -impl<'a> CrateVersion<'a> { - /// Returns the struct and whether or not the dependency is in-tree. - pub fn from_str(s: &'a str) -> (Self, bool) { - let mut parts = s.split(' '); - let name = parts.next().unwrap(); - let version = parts.next().unwrap(); - let path = parts.next().unwrap(); - - let is_path_dep = path.starts_with("(path+"); - - (CrateVersion(name, version), is_path_dep) - } - - pub fn id_str(&self) -> String { - format!("{} {}", self.0, self.1) - } +/// Dependency checks. +/// +/// `path` is path to the `src` directory, `cargo` is path to the cargo executable. +pub fn check(path: &Path, cargo: &Path, bad: &mut bool) { + let mut cmd = cargo_metadata::MetadataCommand::new(); + cmd.cargo_path(cargo) + .manifest_path(path.parent().unwrap().join("Cargo.toml")) + .features(cargo_metadata::CargoOpt::AllFeatures); + let metadata = t!(cmd.exec()); + check_exceptions(&metadata, bad); + check_whitelist(&metadata, bad); + check_crate_duplicate(&metadata, bad); } -impl<'a> From> for Crate<'a> { - fn from(cv: CrateVersion<'a>) -> Crate<'a> { - Crate(cv.0) +/// Check that all licenses are in the valid list in `LICENSES`. +/// +/// Packages listed in `EXCEPTIONS` are allowed for tools. +fn check_exceptions(metadata: &Metadata, bad: &mut bool) { + // Validate the EXCEPTIONS list hasn't changed. + for (name, license) in EXCEPTIONS { + // Check that the package actually exists. + if !metadata.packages.iter().any(|p| p.name == *name) { + println!( + "could not find exception package `{}`\n\ + Remove from EXCEPTIONS list if it is no longer used.", + name + ); + *bad = true; + } + // Check that the license hasn't changed. + for pkg in metadata.packages.iter().filter(|p| p.name == *name) { + if pkg.name == "fuchsia-cprng" { + // This package doesn't declare a license expression. Manual + // inspection of the license file is necessary, which appears + // to be BSD-3-Clause. + assert!(pkg.license.is_none()); + continue; + } + match &pkg.license { + None => { + println!( + "dependency exception `{}` does not declare a license expression", + pkg.id + ); + *bad = true; + } + Some(pkg_license) => { + if pkg_license.as_str() != *license { + println!("dependency exception `{}` license has changed", name); + println!(" previously `{}` now `{}`", license, pkg_license); + println!(" update EXCEPTIONS for the new license"); + *bad = true; + } + } + } + } } -} -/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed. -/// -/// Specifically, this checks that the license is correct. -pub fn check(path: &Path, bad: &mut bool) { - // Check licences. - let path = path.join("../vendor"); - assert!(path.exists(), "vendor directory missing"); - let mut saw_dir = false; - for dir in t!(path.read_dir()) { - saw_dir = true; - let dir = t!(dir); + let exception_names: Vec<_> = EXCEPTIONS.iter().map(|(name, _license)| *name).collect(); + let runtime_ids = compute_runtime_crates(metadata); - // Skip our exceptions. - let is_exception = EXCEPTIONS.iter().any(|exception| { - dir.path().to_str().unwrap().contains(&format!("vendor/{}", exception)) - }); - if is_exception { + // Check if any package does not have a valid license. + for pkg in &metadata.packages { + if pkg.source.is_none() { + // No need to check local packages. continue; } - - let toml = dir.path().join("Cargo.toml"); - *bad = !check_license(&toml) || *bad; + if !runtime_ids.contains(&pkg.id) && exception_names.contains(&pkg.name.as_str()) { + continue; + } + let license = match &pkg.license { + Some(license) => license, + None => { + println!("dependency `{}` does not define a license expression", pkg.id,); + *bad = true; + continue; + } + }; + if !LICENSES.contains(&license.as_str()) { + if pkg.name == "fortanix-sgx-abi" { + // This is a specific exception because SGX is considered + // "third party". See + // https://github.com/rust-lang/rust/issues/62620 for more. In + // general, these should never be added. + continue; + } + println!("invalid license `{}` in `{}`", license, pkg.id); + *bad = true; + } } - assert!(saw_dir, "no vendored source"); } /// Checks the dependency of `WHITELIST_CRATES` at the given path. Changes `bad` to `true` if a /// check failed. /// /// Specifically, this checks that the dependencies are on the `WHITELIST`. -pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) { - // Get dependencies from Cargo metadata. - let resolve = get_deps(path, cargo); - +fn check_whitelist(metadata: &Metadata, bad: &mut bool) { + // Check that the WHITELIST does not have unused entries. + for name in WHITELIST { + if !metadata.packages.iter().any(|p| p.name == *name) { + println!( + "could not find whitelisted package `{}`\n\ + Remove from WHITELIST list if it is no longer used.", + name + ); + *bad = true; + } + } // Get the whitelist in a convenient form. let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect(); @@ -287,142 +301,157 @@ pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) { let mut visited = BTreeSet::new(); let mut unapproved = BTreeSet::new(); for &krate in WHITELIST_CRATES.iter() { - let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false); + let pkg = pkg_from_name(metadata, krate); + let mut bad = check_crate_whitelist(&whitelist, metadata, &mut visited, pkg); unapproved.append(&mut bad); } if !unapproved.is_empty() { println!("Dependencies not on the whitelist:"); for dep in unapproved { - println!("* {}", dep.id_str()); + println!("* {}", dep); } *bad = true; } - - check_crate_duplicate(&resolve, bad); -} - -fn check_license(path: &Path) -> bool { - if !path.exists() { - panic!("{} does not exist", path.display()); - } - let contents = t!(fs::read_to_string(&path)); - - let mut found_license = false; - for line in contents.lines() { - if !line.starts_with("license") { - continue; - } - let license = extract_license(line); - if !LICENSES.contains(&&*license) { - println!("invalid license {} in {}", license, path.display()); - return false; - } - found_license = true; - break; - } - if !found_license { - println!("no license in {}", path.display()); - return false; - } - - true -} - -fn extract_license(line: &str) -> String { - let first_quote = line.find('"'); - let last_quote = line.rfind('"'); - if let (Some(f), Some(l)) = (first_quote, last_quote) { - let license = &line[f + 1..l]; - license.into() - } else { - "bad-license-parse".into() - } -} - -/// Gets the dependencies of the crate at the given path using `cargo metadata`. -fn get_deps(path: &Path, cargo: &Path) -> Resolve { - // Run `cargo metadata` to get the set of dependencies. - let output = Command::new(cargo) - .arg("metadata") - .arg("--format-version") - .arg("1") - .arg("--manifest-path") - .arg(path.join("../Cargo.toml")) - .output() - .expect("Unable to run `cargo metadata`") - .stdout; - let output = String::from_utf8_lossy(&output); - let output: Output = serde_json::from_str(&output).unwrap(); - - output.resolve } /// Checks the dependencies of the given crate from the given cargo metadata to see if they are on /// the whitelist. Returns a list of illegal dependencies. fn check_crate_whitelist<'a>( - whitelist: &'a HashSet>, - resolve: &'a Resolve, - visited: &mut BTreeSet>, - krate: CrateVersion<'a>, - must_be_on_whitelist: bool, -) -> BTreeSet> { + whitelist: &'a HashSet<&'static str>, + metadata: &'a Metadata, + visited: &mut BTreeSet<&'a PackageId>, + krate: &'a Package, +) -> BTreeSet<&'a PackageId> { // This will contain bad deps. let mut unapproved = BTreeSet::new(); // Check if we have already visited this crate. - if visited.contains(&krate) { + if visited.contains(&krate.id) { return unapproved; } - visited.insert(krate); + visited.insert(&krate.id); // If this path is in-tree, we don't require it to be on the whitelist. - if must_be_on_whitelist { + if krate.source.is_some() { // If this dependency is not on `WHITELIST`, add to bad set. - if !whitelist.contains(&krate.into()) { - unapproved.insert(krate.into()); + if !whitelist.contains(krate.name.as_str()) { + unapproved.insert(&krate.id); } } - // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!). - let to_check = resolve - .nodes - .iter() - .find(|n| n.id.starts_with(&krate.id_str())) - .expect("crate does not exist"); + // Do a DFS in the crate graph. + let to_check = deps_of(metadata, &krate.id); - for dep in to_check.dependencies.iter() { - let (krate, is_path_dep) = CrateVersion::from_str(dep); - - let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep); + for dep in to_check { + let mut bad = check_crate_whitelist(whitelist, metadata, visited, dep); unapproved.append(&mut bad); } unapproved } -fn check_crate_duplicate(resolve: &Resolve, bad: &mut bool) { +/// Prevents multiple versions of some expensive crates. +fn check_crate_duplicate(metadata: &Metadata, bad: &mut bool) { const FORBIDDEN_TO_HAVE_DUPLICATES: &[&str] = &[ // These two crates take quite a long time to build, so don't allow two versions of them // 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", ]; - let mut name_to_id: HashMap<_, Vec<_>> = HashMap::new(); - for node in resolve.nodes.iter() { - name_to_id.entry(node.id.split_whitespace().next().unwrap()).or_default().push(&node.id); - } - for name in FORBIDDEN_TO_HAVE_DUPLICATES { - if name_to_id[name].len() <= 1 { - continue; - } - println!("crate `{}` is duplicated in `Cargo.lock`", name); - for id in name_to_id[name].iter() { - println!(" * {}", id); + for &name in FORBIDDEN_TO_HAVE_DUPLICATES { + let matches: Vec<_> = metadata.packages.iter().filter(|pkg| pkg.name == name).collect(); + match matches.len() { + 0 => { + println!( + "crate `{}` is missing, update `check_crate_duplicate` \ + if it is no longer used", + name + ); + *bad = true; + } + 1 => {} + _ => { + println!( + "crate `{}` is duplicated in `Cargo.lock`, \ + it is too expensive to build multiple times, \ + so make sure only one version appears across all dependencies", + name + ); + for pkg in matches { + println!(" * {}", pkg.id); + } + *bad = true; + } } - *bad = true; + } +} + +/// Returns a list of dependencies for the given package. +fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> { + let resolve = metadata.resolve.as_ref().unwrap(); + let node = resolve + .nodes + .iter() + .find(|n| &n.id == pkg_id) + .unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id)); + node.deps + .iter() + .map(|dep| { + metadata.packages.iter().find(|pkg| pkg.id == dep.pkg).unwrap_or_else(|| { + panic!("could not find dep `{}` for pkg `{}` in resolve", dep.pkg, pkg_id) + }) + }) + .collect() +} + +/// Finds a package with the given name. +fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package { + let mut i = metadata.packages.iter().filter(|p| p.name == name); + let result = + i.next().unwrap_or_else(|| panic!("could not find package `{}` in package list", name)); + assert!(i.next().is_none(), "more than one package found for `{}`", name); + result +} + +/// Finds all the packages that are in the rust runtime. +fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> { + let resolve = metadata.resolve.as_ref().unwrap(); + let mut result = HashSet::new(); + for name in RUNTIME_CRATES { + let id = &pkg_from_name(metadata, name).id; + normal_deps_of_r(resolve, id, &mut result); + } + result +} + +/// Recursively find all normal dependencies. +fn normal_deps_of_r<'a>( + resolve: &'a Resolve, + pkg_id: &'a PackageId, + result: &mut HashSet<&'a PackageId>, +) { + if !result.insert(pkg_id) { + return; + } + let node = resolve + .nodes + .iter() + .find(|n| &n.id == pkg_id) + .unwrap_or_else(|| panic!("could not find `{}` in resolve", pkg_id)); + // Don't care about dev-dependencies. + // Build dependencies *shouldn't* matter unless they do some kind of + // codegen. For now we'll assume they don't. + let deps = node.deps.iter().filter(|node_dep| { + node_dep + .dep_kinds + .iter() + .any(|kind_info| kind_info.kind == cargo_metadata::DependencyKind::Normal) + }); + for dep in deps { + normal_deps_of_r(resolve, &dep.pkg, result); } } 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/tidy/src/features.rs b/src/tools/tidy/src/features.rs index d9320e9147cff..3fa637b5a696f 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -444,10 +444,7 @@ fn map_lib_features( level: Status::Unstable, since: None, has_gate_test: false, - // FIXME(#57563): #57563 is now used as a common tracking issue, - // although we would like to have specific tracking issues for each - // `rustc_const_unstable` in the future. - tracking_issue: NonZeroU32::new(57563), + tracking_issue: find_attr_val(line, "issue").and_then(handle_issue_none), }; mf(Ok((feature_name, feature)), file, i + 1); continue; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 909529d730784..e2856c690550a 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -30,10 +30,7 @@ fn main() { pal::check(&path, &mut bad); unstable_book::check(&path, collected, &mut bad); unit_tests::check(&path, &mut bad); - if !args.iter().any(|s| *s == "--no-vendor") { - deps::check(&path, &mut bad); - } - deps::check_whitelist(&path, &cargo, &mut bad); + deps::check(&path, &cargo, &mut bad); extdeps::check(&path, &mut bad); ui_tests::check(&path, &mut bad); error_codes_check::check(&path, &mut bad); diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 4247fcb3b7f53..396d6c0cfcdef 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -174,6 +174,11 @@ pub fn check(path: &Path, bad: &mut bool) { let can_contain = contents.contains("// ignore-tidy-") || contents.contains("# ignore-tidy-"); + // Enable testing ICE's that require specific (untidy) + // file formats easily eg. `issue-1234-ignore-tidy.rs` + if filename.contains("ignore-tidy") { + return; + } let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr"); let mut skip_undocumented_unsafe = contains_ignore_directive(can_contain, &contents, "undocumented-unsafe"); 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..63cc29b670f6b 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 { + if unique_words.len() > u8::MAX 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 ec32771583334..73ca7abfed363 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -10,6 +10,8 @@ allow-unauthenticated = [ [assign] +[glacier] + [ping.icebreakers-llvm] alias = ["llvm", "llvms"] message = """\ @@ -18,18 +20,162 @@ Hey LLVM ICE-breakers! This bug has been identified as a good [instructions] for tackling these sorts of bugs. Maybe take a look? Thanks! <3 -[instructions]: https://rustc-dev-guide.rust-lang.org/ice-breaker/llvm.html +[instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/llvm.html """ label = "ICEBreaker-LLVM" [ping.icebreakers-cleanup-crew] -alias = ["cleanup", "cleanups", "shrink", "reduce", "bisect"] +alias = ["cleanup", "cleanups", "cleanup-crew", "shrink", "reduce", "bisect"] message = """\ Hey Cleanup Crew ICE-breakers! This bug has been identified as a good "Cleanup ICE-breaking candidate". In case it's useful, here are some [instructions] for tackling these sorts of bugs. Maybe take a look? Thanks! <3 -[instructions]: https://rustc-dev-guide.rust-lang.org/ice-breaker/cleanup-crew.html +[instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/cleanup-crew.html """ label = "ICEBreaker-Cleanup-Crew" + +[ping.windows] +message = """\ +Hey Windows Group! This bug has been identified as a good "Windows candidate". +In case it's useful, here are some [instructions] for tackling these sorts of +bugs. Maybe take a look? +Thanks! <3 + +[instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/windows.html +""" +label = "O-windows" + +[ping.arm] +message = """\ +Hey ARM Group! This bug has been identified as a good "ARM candidate". +In case it's useful, here are some [instructions] for tackling these sorts of +bugs. Maybe take a look? +Thanks! <3 + +[instructions]: https://rustc-dev-guide.rust-lang.org/notification-groups/arm.html +""" +label = "O-ARM" + +[prioritize] +label = "I-prioritize" + +[autolabel."I-prioritize"] +trigger_labels = [ + "regression-from-stable-to-stable", + "regression-from-stable-to-beta", + "regression-from-stable-to-nightly", + "I-unsound 💥", +] +exclude_labels = [ + "P-*", + "T-infra", + "T-release", + "requires-nightly", +] + +[notify-zulip."I-prioritize"] +zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts +topic = "I-prioritize #{number} {title}" +message_on_add = """\ +@*WG-prioritization/alerts* issue #{number} has been requested for prioritization. + +# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#Unprioritized-I-prioritize) +- Priority? +- Regression? +- Notify people/groups? +- Needs `I-nominated`? +""" +message_on_remove = "Issue #{number}'s prioritization request has been removed." + +[notify-zulip."I-nominated"] +required_labels = ["T-compiler"] +zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts +topic = "I-prioritize #{number} {title}" +message_on_add = """\ +@*WG-prioritization/alerts* #{number} has been nominated for discussion in `T-compiler` meeting. + +# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#I-nominated) +- Already discussed? +- Worth the meeting time? +- Add agenda entry: + - Why nominated? + - Assignee? + - Issue? PR? What's the status? + - Summary and important details? +""" +message_on_remove = "#{number}'s nomination has been removed." + +[notify-zulip."beta-nominated"] +zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts +topic = "Backport #{number} {title}" +message_on_add = """\ +@*WG-prioritization/alerts* PR #{number} has been requested for beta backport. + +# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#StableBeta-nominations) +Prepare agenda entry: +- Why nominated? +- Author, assignee? +- Important details? +""" +message_on_remove = "PR #{number}'s beta backport request has been removed." + +[notify-zulip."stable-nominated"] +zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts +topic = "Backport #{number} {title}" +message_on_add = """\ +@*WG-prioritization/alerts* PR #{number} has been requested for stable backport. + +# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#StableBeta-nominations) +Prepare agenda entry: +- Why nominated? +- Author, assignee? +- Important details? +""" +message_on_remove = "PR #{number}'s stable backport request has been removed." + +[notify-zulip."S-waiting-on-team"] +required_labels = ["T-compiler"] +zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts +topic = "S-waiting-on-team #{number} {title}" +message_on_add = """\ +@*WG-prioritization/alerts* PR #{number} is waiting on `T-compiler`. + +# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#PR%E2%80%99s-waiting-on-team) +- Prepare agenda entry: + - What is it waiting for? + - Important details? +- Could be resolved quickly? Tag `I-nominated`. +""" +message_on_remove = "PR #{number}'s is no longer waiting on `T-compiler`." + +[notify-zulip."P-critical"] +zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts +topic = "P-critical #{number} {title}" +message_on_add = """\ +@*WG-prioritization/alerts* issue #{number} has been assigned `P-critical`. + +# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#P-critical-and-Unassigned-P-high-regressions) +- Notify people/groups? +- Assign if possible? +- Add to agenda: + - Assignee? + - Summary and important details? +- Other actions to move forward? +""" + +[notify-zulip."P-high"] +required_labels = ["regression-from-stable-to-[bn]*"] # only nightly and beta regressions +zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts +topic = "P-high regression #{number} {title}" +message_on_add = """\ +@*WG-prioritization/alerts* issue #{number} has been assigned `P-high` and is a regression. + +# [Procedure](https://hackmd.io/WJ0G17DHTHGgv0OW9I2PxA?view#P-critical-and-Unassigned-P-high-regressions) +Is issue assigned? If not: +- Try to find an assignee? +- Otherwise add to agenda: + - Mark as unassigned. + - Summary and important details? +"""

for PathBuf { fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |p| self.push(p.as_ref())); } + + #[inline] + fn extend_one(&mut self, p: P) { + self.push(p.as_ref()); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/prelude/mod.rs b/src/libstd/prelude/mod.rs index 3085c3d829653..48f7cf169885f 100644 --- a/src/libstd/prelude/mod.rs +++ b/src/libstd/prelude/mod.rs @@ -10,22 +10,6 @@ //! things, particularly traits, which are used in almost every single Rust //! program. //! -//! On a technical level, Rust inserts -//! -//! ``` -//! # #[allow(unused_extern_crates)] -//! extern crate std; -//! ``` -//! -//! into the crate root of every crate, and -//! -//! ``` -//! # #[allow(unused_imports)] -//! use std::prelude::v1::*; -//! ``` -//! -//! into every module. -//! //! # Other preludes //! //! Preludes can be seen as a pattern to make using multiple types more diff --git a/src/libstd/prelude/v1.rs b/src/libstd/prelude/v1.rs index 7c0efe828c27a..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,6 +54,14 @@ pub use core::prelude::v1::{ PartialEq, PartialOrd, RustcDecodable, RustcEncodable, }; +#[unstable( + feature = "cfg_accessible", + issue = "64797", + reason = "`cfg_accessible` is not fully implemented" +)] +#[doc(hidden)] +pub use core::prelude::v1::cfg_accessible; + // The file so far is equivalent to src/libcore/prelude/v1.rs, // and below to src/liballoc/prelude.rs. // Those files are duplicated rather than using glob imports 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..4ba1940fd0ece 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. @@ -2091,8 +2105,8 @@ mod tests { } #[test] - fn test_command_implements_send() { - fn take_send_type(_: T) {} - take_send_type(Command::new("")) + fn test_command_implements_send_sync() { + fn take_send_sync_type(_: T) {} + take_send_sync_type(Command::new("")) } } diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 77e521eae9afe..2250c0d4203ef 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -609,7 +609,6 @@ mod tests { use crate::sync::{Arc, Condvar, Mutex}; use crate::thread; use crate::time::Duration; - use crate::u64; #[test] fn smoke() { diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index e70204d6839fc..d6cc811154f11 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -2176,8 +2176,7 @@ mod tests { #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn very_long_recv_timeout_wont_panic() { let (tx, rx) = channel::<()>(); - let join_handle = - thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::max_value()))); + let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX))); thread::sleep(Duration::from_secs(1)); assert!(tx.send(()).is_ok()); assert_eq!(join_handle.join().unwrap(), Ok(())); 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..898654f21f2ea 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,9 +354,7 @@ impl Packet { // See comments on Arc::clone() on why we do this (for `mem::forget`). if old_count > MAX_REFCOUNT { - unsafe { - abort(); - } + 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..733761671a041 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,9 +358,7 @@ impl Packet { // See comments on Arc::clone() on why we do this (for `mem::forget`). if old_count > MAX_REFCOUNT { - unsafe { - abort(); - } + abort(); } } @@ -411,7 +412,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..8478457eabfc2 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -107,7 +107,62 @@ use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; /// /// *guard += 1; /// ``` +/// +/// It is sometimes necessary to manually drop the mutex guard to unlock it +/// sooner than the end of the enclosing scope. +/// +/// ``` +/// use std::sync::{Arc, Mutex}; +/// use std::thread; +/// +/// const N: usize = 3; +/// +/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4])); +/// let res_mutex = Arc::new(Mutex::new(0)); +/// +/// let mut threads = Vec::with_capacity(N); +/// (0..N).for_each(|_| { +/// let data_mutex_clone = Arc::clone(&data_mutex); +/// let res_mutex_clone = Arc::clone(&res_mutex); +/// +/// threads.push(thread::spawn(move || { +/// let mut data = data_mutex_clone.lock().unwrap(); +/// // This is the result of some important and long-ish work. +/// let result = data.iter().fold(0, |acc, x| acc + x * 2); +/// data.push(result); +/// drop(data); +/// *res_mutex_clone.lock().unwrap() += result; +/// })); +/// }); +/// +/// let mut data = data_mutex.lock().unwrap(); +/// // This is the result of some important and long-ish work. +/// let result = data.iter().fold(0, |acc, x| acc + x * 2); +/// data.push(result); +/// // We drop the `data` explicitly because it's not necessary anymore and the +/// // thread still has work to do. This allow other threads to start working on +/// // the data immediately, without waiting for the rest of the unrelated work +/// // to be done here. +/// // +/// // It's even more important here than in the threads because we `.join` the +/// // threads after that. If we had not dropped the mutex guard, a thread could +/// // be waiting forever for it, causing a deadlock. +/// drop(data); +/// // Here the mutex guard is not assigned to a variable and so, even if the +/// // scope does not end after this line, the mutex is still released: there is +/// // no deadlock. +/// *res_mutex.lock().unwrap() += result; +/// +/// threads.into_iter().for_each(|thread| { +/// thread +/// .join() +/// .expect("The thread creating or execution failed !") +/// }); +/// +/// assert_eq!(*res_mutex.lock().unwrap(), 800); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "mutex_type")] pub struct Mutex { // Note that this mutex is in a *box*, not inlined into the struct itself. // Once a native mutex has been used once, its address can never change (it diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 1e6b6c430be90..7dc822db3d027 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -272,7 +272,7 @@ impl Once { /// result in an immediate panic. If `f` panics, the `Once` will remain /// in a poison state. If `f` does _not_ panic, the `Once` will no /// longer be in a poison state and all future calls to `call_once` or - /// `call_one_force` will be no-ops. + /// `call_once_force` will be no-ops. /// /// The closure `f` is yielded a [`OnceState`] structure which can be used /// to query the poison status of the `Once`. @@ -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/condvar.rs b/src/libstd/sys/cloudabi/condvar.rs index 3ba51d77494d4..dabdc0c9b510a 100644 --- a/src/libstd/sys/cloudabi/condvar.rs +++ b/src/libstd/sys/cloudabi/condvar.rs @@ -42,7 +42,7 @@ impl Condvar { let ret = abi::condvar_signal( condvar as *mut abi::condvar, abi::scope::PRIVATE, - abi::nthreads::max_value(), + abi::nthreads::MAX, ); assert_eq!(ret, abi::errno::SUCCESS, "Failed to broadcast on condition variable"); } diff --git a/src/libstd/sys/cloudabi/mod.rs b/src/libstd/sys/cloudabi/mod.rs index e5f1dd9843587..8dbc31472d637 100644 --- a/src/libstd/sys/cloudabi/mod.rs +++ b/src/libstd/sys/cloudabi/mod.rs @@ -51,7 +51,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { } } -pub unsafe fn abort_internal() -> ! { +pub fn abort_internal() -> ! { core::intrinsics::abort(); } diff --git a/src/libstd/sys/cloudabi/mutex.rs b/src/libstd/sys/cloudabi/mutex.rs index 4aa25e2505271..580ab0e8ad863 100644 --- a/src/libstd/sys/cloudabi/mutex.rs +++ b/src/libstd/sys/cloudabi/mutex.rs @@ -53,16 +53,16 @@ pub struct ReentrantMutex { } impl ReentrantMutex { - pub unsafe fn uninitialized() -> ReentrantMutex { + pub const unsafe fn uninitialized() -> ReentrantMutex { ReentrantMutex { lock: UnsafeCell::new(MaybeUninit::uninit()), recursion: UnsafeCell::new(MaybeUninit::uninit()), } } - pub unsafe fn init(&mut self) { - self.lock = UnsafeCell::new(MaybeUninit::new(AtomicU32::new(abi::LOCK_UNLOCKED.0))); - self.recursion = UnsafeCell::new(MaybeUninit::new(0)); + pub unsafe fn init(&self) { + *self.lock.get() = MaybeUninit::new(AtomicU32::new(abi::LOCK_UNLOCKED.0)); + *self.recursion.get() = MaybeUninit::new(0); } pub unsafe fn try_lock(&self) -> bool { 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..132e579b3a5cb 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) { @@ -36,7 +35,7 @@ impl Condvar { pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { let nanos = dur.as_nanos(); - let nanos = cmp::min(i64::max_value() as u128, nanos); + let nanos = cmp::min(i64::MAX as u128, nanos); // add current task to the wait queue let _ = abi::add_queue(self.id(), nanos as i64); @@ -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/mutex.rs b/src/libstd/sys/hermit/mutex.rs index b5c75f738d228..3d4813209cbc4 100644 --- a/src/libstd/sys/hermit/mutex.rs +++ b/src/libstd/sys/hermit/mutex.rs @@ -46,13 +46,13 @@ pub struct ReentrantMutex { } impl ReentrantMutex { - pub unsafe fn uninitialized() -> ReentrantMutex { + pub const unsafe fn uninitialized() -> ReentrantMutex { ReentrantMutex { inner: ptr::null() } } #[inline] - pub unsafe fn init(&mut self) { - let _ = abi::recmutex_init(&mut self.inner as *mut *const c_void); + pub unsafe fn init(&self) { + let _ = abi::recmutex_init(&self.inner as *const *const c_void as *mut _); } #[inline] diff --git a/src/libstd/sys/hermit/net.rs b/src/libstd/sys/hermit/net.rs index 82917e71be1f8..8a788a9265f63 100644 --- a/src/libstd/sys/hermit/net.rs +++ b/src/libstd/sys/hermit/net.rs @@ -1,291 +1,405 @@ use crate::convert::TryFrom; use crate::fmt; -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::str; +use crate::sync::Arc; +use crate::sys::hermit::abi; +use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6}; use crate::sys::{unsupported, Void}; +use crate::sys_common::AsInner; 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); +#[derive(Debug, Clone)] +pub struct Socket(abi::Handle); + +impl AsInner for Socket { + fn as_inner(&self) -> &abi::Handle { + &self.0 + } +} + +impl Drop for Socket { + fn drop(&mut self) { + let _ = abi::tcpstream::close(self.0); + } +} + +// Arc is used to count the number of used sockets. +// Only if all sockets are released, the drop +// method will close the socket. +#[derive(Clone)] +pub struct TcpStream(Arc); 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(Arc::new(Socket(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(Arc::new(Socket(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.as_inner(), 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.as_inner(), + 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.as_inner()) + .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.as_inner()) + .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.as_inner(), 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 ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]) + .map_err(|_| io::Error::new(ErrorKind::Other, "Unable to read on socket"))?; + + if ret != 0 { + size += 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.as_inner(), 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 {} + let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner()) + .map_err(|_| io::Error::new(ErrorKind::Other, "peer_addr failed"))?; + + let saddr = match ipaddr { + Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port), + Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port), + _ => { + return Err(io::Error::new(ErrorKind::Other, "peer_addr failed")); + } + }; + + Ok(saddr) } 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.as_inner(), how as i32) + .map_err(|_| io::Error::new(ErrorKind::Other, "unable to shutdown socket")) } pub fn duplicate(&self) -> io::Result { - match self.0 {} + Ok(self.clone()) } - 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.as_inner(), 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.as_inner()) + .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.as_inner(), 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.as_inner()) + .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.as_inner(), mode) + .map_err(|_| io::Error::new(ErrorKind::Other, "unable to set blocking mode")) } } impl fmt::Debug for TcpStream { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} + Ok(()) } } -pub struct TcpListener(Void); +#[derive(Clone)] +pub struct TcpListener(SocketAddr); impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + Ok(TcpListener(*addr)) } pub fn socket_addr(&self) -> io::Result { - match self.0 {} + Ok(self.0) } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - match self.0 {} + let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port()) + .map_err(|_| io::Error::new(ErrorKind::Other, "accept failed"))?; + let saddr = match ipaddr { + Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port), + Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port), + _ => { + return Err(io::Error::new(ErrorKind::Other, "accept failed")); + } + }; + + Ok((TcpStream(Arc::new(Socket(handle))), saddr)) } pub fn duplicate(&self) -> io::Result { - match self.0 {} + Ok(self.clone()) } 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..f3654ee38716c 100644 --- a/src/libstd/sys/hermit/stdio.rs +++ b/src/libstd/sys/hermit/stdio.rs @@ -10,24 +10,31 @@ impl Stdin { pub fn new() -> io::Result { Ok(Stdin) } +} - pub fn read(&self, data: &mut [u8]) -> io::Result { +impl io::Read for Stdin { + fn read(&mut self, data: &mut [u8]) -> io::Result { self.read_vectored(&mut [IoSliceMut::new(data)]) } - pub fn read_vectored(&self, _data: &mut [IoSliceMut<'_>]) -> io::Result { - //ManuallyDrop::new(unsafe { WasiFd::from_raw(libc::STDIN_FILENO as u32) }) - // .read(data) + fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result { Ok(0) } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } } impl Stdout { pub fn new() -> io::Result { Ok(Stdout) } +} - pub fn write(&self, data: &[u8]) -> io::Result { +impl io::Write for Stdout { + fn write(&mut self, data: &[u8]) -> io::Result { let len; unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } @@ -39,7 +46,7 @@ impl Stdout { } } - pub fn write_vectored(&self, data: &[IoSlice<'_>]) -> io::Result { + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { let len; unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) } @@ -51,7 +58,12 @@ impl Stdout { } } - pub fn flush(&self) -> io::Result<()> { + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { Ok(()) } } @@ -60,8 +72,10 @@ impl Stderr { pub fn new() -> io::Result { Ok(Stderr) } +} - pub fn write(&self, data: &[u8]) -> io::Result { +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result { let len; unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } @@ -73,7 +87,7 @@ impl Stderr { } } - pub fn write_vectored(&self, data: &[IoSlice<'_>]) -> io::Result { + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { let len; unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) } @@ -85,17 +99,13 @@ impl Stderr { } } - pub fn flush(&self) -> io::Result<()> { - Ok(()) + #[inline] + fn is_write_vectored(&self) -> bool { + true } -} -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - (&*self).write(data) - } fn flush(&mut self) -> io::Result<()> { - (&*self).flush() + 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/entry.S b/src/libstd/sys/sgx/abi/entry.S index 1f06c9da3a993..fc6ce5770338e 100644 --- a/src/libstd/sys/sgx/abi/entry.S +++ b/src/libstd/sys/sgx/abi/entry.S @@ -26,18 +26,10 @@ IMAGE_BASE: .Lxsave_clear: .org .+24 .Lxsave_mxcsr: - .int 0 + .short 0x1f80 /* We can store a bunch of data in the gap between MXCSR and the XSAVE header */ -/* MXCSR initialization value for ABI */ -.Lmxcsr_init: - .int 0x1f80 - -/* x87 FPU control word initialization value for ABI */ -.Lfpucw_init: - .int 0x037f - /* The following symbols point at read-only data that will be filled in by the */ /* post-linker. */ @@ -177,13 +169,17 @@ sgx_entry: jz .Lskip_debug_init mov %r10,%gs:tcsls_debug_panic_buf_ptr .Lskip_debug_init: +/* reset cpu state */ + mov %rdx, %r10 + mov $-1, %rax + mov $-1, %rdx + xrstor .Lxsave_clear(%rip) + mov %r10, %rdx + /* check if returning from usercall */ mov %gs:tcsls_last_rsp,%r11 test %r11,%r11 jnz .Lusercall_ret -/* reset user state */ - ldmxcsr .Lmxcsr_init(%rip) - fldcw .Lfpucw_init(%rip) /* setup stack */ mov %gs:tcsls_tos,%rsp /* initially, RSP is not set to the correct value */ /* here. This is fixed below under "adjust stack". */ @@ -324,7 +320,9 @@ usercall: /* return */ mov %rsi,%rax /* RAX = return value */ /* NOP: mov %rdx,%rdx */ /* RDX = return value */ - ret + pop %r11 + lfence + jmp *%r11 /* The following functions need to be defined externally: @@ -343,20 +341,28 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64 .global get_tcs_addr get_tcs_addr: mov %gs:tcsls_tcs_addr,%rax - ret + pop %r11 + lfence + jmp *%r11 .global get_tls_ptr get_tls_ptr: mov %gs:tcsls_tls_ptr,%rax - ret + pop %r11 + lfence + jmp *%r11 .global set_tls_ptr set_tls_ptr: mov %rdi,%gs:tcsls_tls_ptr - ret + pop %r11 + lfence + jmp *%r11 .global take_debug_panic_buf_ptr take_debug_panic_buf_ptr: xor %rax,%rax xchg %gs:tcsls_debug_panic_buf_ptr,%rax - ret + pop %r11 + lfence + jmp *%r11 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/abi/mod.rs b/src/libstd/sys/sgx/abi/mod.rs index 87e7a5da2b7a9..5ef26d4cc4dc6 100644 --- a/src/libstd/sys/sgx/abi/mod.rs +++ b/src/libstd/sys/sgx/abi/mod.rs @@ -56,6 +56,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) { // able to specify this #[cfg(not(test))] #[no_mangle] +#[allow(improper_ctypes_definitions)] extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) { // FIXME: how to support TLS in library mode? let tls = Box::new(tls::Tls::new()); 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/mutex.rs b/src/libstd/sys/sgx/mutex.rs index eebbea1b285ba..4911c2f538769 100644 --- a/src/libstd/sys/sgx/mutex.rs +++ b/src/libstd/sys/sgx/mutex.rs @@ -75,7 +75,7 @@ impl ReentrantMutex { } #[inline] - pub unsafe fn init(&mut self) {} + pub unsafe fn init(&self) {} #[inline] pub unsafe fn lock(&self) { 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/android.rs b/src/libstd/sys/unix/android.rs index 8fc2599f0d762..ea05ee3d7cedf 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -95,7 +95,7 @@ pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { match ftruncate64.get() { Some(f) => cvt_r(|| f(fd, size as i64)).map(drop), None => { - if size > i32::max_value() as u64 { + if size > i32::MAX as u64 { Err(io::Error::new(io::ErrorKind::InvalidInput, "cannot truncate >2GB")) } else { cvt_r(|| ftruncate(fd, size as i32)).map(drop) diff --git a/src/libstd/sys/unix/args.rs b/src/libstd/sys/unix/args.rs index 09acc3f6e3ea8..1d1cdda1257aa 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", @@ -204,6 +205,7 @@ mod imp { #[cfg(target_arch = "aarch64")] extern "C" { fn objc_msgSend(obj: NsId, sel: Sel) -> NsId; + #[cfg_attr(not(bootstrap), allow(clashing_extern_decl))] #[link_name = "objc_msgSend"] fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId; } @@ -211,6 +213,7 @@ mod imp { #[cfg(not(target_arch = "aarch64"))] extern "C" { fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; + #[cfg_attr(not(bootstrap), allow(clashing_extern_decl))] #[link_name = "objc_msgSend"] fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId; } diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs index b4896b7ad7476..9f1847943f326 100644 --- a/src/libstd/sys/unix/condvar.rs +++ b/src/libstd/sys/unix/condvar.rs @@ -10,14 +10,10 @@ unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} const TIMESPEC_MAX: libc::timespec = - libc::timespec { tv_sec: ::max_value(), tv_nsec: 1_000_000_000 - 1 }; + libc::timespec { tv_sec: ::MAX, tv_nsec: 1_000_000_000 - 1 }; fn saturating_cast_to_time_t(value: u64) -> libc::time_t { - if value > ::max_value() as u64 { - ::max_value() - } else { - value as libc::time_t - } + if value > ::MAX as u64 { ::MAX } else { value as libc::time_t } } impl Condvar { 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/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 732cd677a1859..e4d714936047e 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -242,7 +242,8 @@ pub trait PermissionsExt { /// let permissions = metadata.permissions(); /// /// println!("permissions: {:o}", permissions.mode()); - /// Ok(()) } + /// Ok(()) + /// } /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] fn mode(&self) -> u32; @@ -262,7 +263,8 @@ pub trait PermissionsExt { /// /// permissions.set_mode(0o644); // Read/write for owner and read for others. /// assert_eq!(permissions.mode(), 0o644); - /// Ok(()) } + /// Ok(()) + /// } /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] fn set_mode(&mut self, mode: u32); diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 4c3cb67c9ee0f..ada8eaa1c9745 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -2,11 +2,9 @@ //! Unix-specific networking functionality -#[cfg(unix)] -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 +611,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 +632,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 +653,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 +673,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(()) } @@ -1069,7 +1087,7 @@ impl<'a> Iterator for Incoming<'a> { } fn size_hint(&self) -> (usize, Option) { - (usize::max_value(), None) + (usize::MAX, None) } } diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index fa8670b4aecac..048ce24d6ba88 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -111,7 +111,7 @@ pub trait CommandExt { /// /// Set the first process argument, `argv[0]`, to something other than the /// default executable path. - #[unstable(feature = "process_set_argv0", issue = "66510")] + #[stable(feature = "process_set_argv0", since = "1.45.0")] fn arg0(&mut self, arg: S) -> &mut process::Command where S: AsRef; 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..c481ca8961f86 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -23,11 +23,7 @@ fn max_len() -> usize { // intentionally showing odd behavior by rejecting any read with a size // larger than or equal to INT_MAX. To handle both of these the read // size is capped on both platforms. - if cfg!(target_os = "macos") { - ::max_value() as usize - 1 - } else { - ::max_value() as usize - } + if cfg!(target_os = "macos") { ::MAX as usize - 1 } else { ::MAX as usize } } impl FileDesc { @@ -58,12 +54,17 @@ impl FileDesc { libc::readv( self.fd, bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::max_value() as usize) as c_int, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, ) })?; Ok(ret as usize) } + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { let mut me = self; (&mut me).read_to_end(buf) @@ -110,12 +111,17 @@ impl FileDesc { libc::writev( self.fd, bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::max_value() as usize) as c_int, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, ) })?; Ok(ret as usize) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { #[cfg(target_os = "android")] use super::android::cvt_pwrite64; @@ -153,6 +159,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 +176,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..29cdbf05354fb 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) } @@ -1160,7 +1196,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { let mut written = 0u64; while written < len { let copy_result = if has_copy_file_range { - let bytes_to_copy = cmp::min(len - written, usize::max_value() as u64) as usize; + let bytes_to_copy = cmp::min(len - written, usize::MAX as u64) as usize; let copy_result = unsafe { // We actually don't have to adjust the offsets, // because copy_file_range adjusts the file offset automatically 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 b38375a2e03c5..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 @@ -92,11 +98,11 @@ unsafe impl Send for ReentrantMutex {} unsafe impl Sync for ReentrantMutex {} impl ReentrantMutex { - pub unsafe fn uninitialized() -> ReentrantMutex { + pub const unsafe fn uninitialized() -> ReentrantMutex { ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } } - pub unsafe fn init(&mut self) { + pub unsafe fn init(&self) { let mut attr = MaybeUninit::::uninit(); let result = libc::pthread_mutexattr_init(attr.as_mut_ptr()); debug_assert_eq!(result, 0); diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index b37675e0a0a00..3717c660b575d 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -148,7 +148,7 @@ impl Socket { timeout = 1; } - let timeout = cmp::min(timeout, c_int::max_value() as u64) as c_int; + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; match unsafe { libc::poll(&mut pollfd, 1, timeout) } { -1 => { @@ -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) => { @@ -273,8 +283,8 @@ impl Socket { )); } - let secs = if dur.as_secs() > libc::time_t::max_value() as u64 { - libc::time_t::max_value() + let secs = if dur.as_secs() > libc::time_t::MAX as u64 { + libc::time_t::MAX } else { dur.as_secs() as libc::time_t }; @@ -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_common.rs b/src/libstd/sys/unix/process/process_common.rs index 859da691ad278..6e33cdd3c4826 100644 --- a/src/libstd/sys/unix/process/process_common.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -86,11 +86,13 @@ pub struct Command { stderr: Option, } -// Create a new type for argv, so that we can make it `Send` +// Create a new type for argv, so that we can make it `Send` and `Sync` struct Argv(Vec<*const c_char>); -// It is safe to make Argv Send, because it contains pointers to memory owned by `Command.args` +// It is safe to make `Argv` `Send` and `Sync`, because it contains +// pointers to memory owned by `Command.args` unsafe impl Send for Argv {} +unsafe impl Sync for Argv {} // passed back to std::process with the pipes connected to the child, if any // were requested @@ -426,6 +428,7 @@ mod tests { // ignored there. #[cfg_attr(target_arch = "arm", ignore)] #[cfg_attr(target_arch = "aarch64", ignore)] + #[cfg_attr(target_arch = "riscv64", ignore)] fn test_process_mask() { unsafe { // Test to make sure that a signal mask does not get inherited. 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/rand.rs b/src/libstd/sys/unix/rand.rs index 9ce5f3d014cc1..eed6fbf13b7d2 100644 --- a/src/libstd/sys/unix/rand.rs +++ b/src/libstd/sys/unix/rand.rs @@ -12,6 +12,7 @@ pub fn hashmap_random_keys() -> (u64, u64) { #[cfg(all( unix, + not(target_os = "macos"), not(target_os = "ios"), not(target_os = "openbsd"), not(target_os = "freebsd"), @@ -92,6 +93,42 @@ mod imp { } } +#[cfg(target_os = "macos")] +mod imp { + use crate::fs::File; + use crate::io::Read; + use crate::sys::os::errno; + use libc::{c_int, c_void, size_t}; + + fn getentropy_fill_bytes(v: &mut [u8]) -> bool { + weak!(fn getentropy(*mut c_void, size_t) -> c_int); + + getentropy + .get() + .map(|f| { + // getentropy(2) permits a maximum buffer size of 256 bytes + for s in v.chunks_mut(256) { + let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) }; + if ret == -1 { + panic!("unexpected getentropy error: {}", errno()); + } + } + true + }) + .unwrap_or(false) + } + + pub fn fill_bytes(v: &mut [u8]) { + if getentropy_fill_bytes(v) { + return; + } + + // for older macos which doesn't support getentropy + let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom"); + file.read_exact(v).expect("failed to read /dev/urandom") + } +} + #[cfg(target_os = "openbsd")] mod imp { use crate::sys::os::errno; 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 9e8be55075578..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" ))] @@ -45,8 +46,9 @@ mod imp { use libc::{mmap, munmap}; use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE}; - use libc::{MAP_ANON, MAP_PRIVATE, PROT_READ, PROT_WRITE, SIGSEGV}; + use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV}; + use crate::sys::unix::os::page_size; use crate::sys_common::thread_info; #[cfg(any(target_os = "linux", target_os = "android"))] @@ -137,12 +139,22 @@ mod imp { } unsafe fn get_stackp() -> *mut libc::c_void { - let stackp = - mmap(ptr::null_mut(), SIGSTKSZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + let stackp = mmap( + ptr::null_mut(), + SIGSTKSZ + page_size(), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0, + ); if stackp == MAP_FAILED { panic!("failed to allocate an alternative stack"); } - stackp + let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE); + if guard_result != 0 { + panic!("failed to set up alternative stack guard page"); + } + stackp.add(page_size()) } #[cfg(any( @@ -151,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 } @@ -190,7 +203,9 @@ mod imp { ss_size: SIGSTKSZ, }; sigaltstack(&stack, ptr::null_mut()); - munmap(handler._data, SIGSTKSZ); + // We know from `get_stackp` that the alternate stack we installed is part of a mapping + // that started one page earlier, so walk back a page and unmap from there. + munmap(handler._data.sub(page_size()), SIGSTKSZ + page_size()); } } } @@ -201,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..7b3d69dcaa015 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) { @@ -164,7 +171,7 @@ impl Thread { unsafe { while secs > 0 || nsecs > 0 { let mut ts = libc::timespec { - tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t, + tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, tv_nsec: nsecs, }; secs -= ts.tv_sec as u64; diff --git a/src/libstd/sys/vxworks/args.rs b/src/libstd/sys/vxworks/args.rs index efd615f404db6..adff6c489bbc9 100644 --- a/src/libstd/sys/vxworks/args.rs +++ b/src/libstd/sys/vxworks/args.rs @@ -56,7 +56,6 @@ mod imp { use crate::ffi::{CStr, OsString}; use crate::marker::PhantomData; use crate::ptr; - use libc; use crate::sys_common::mutex::Mutex; diff --git a/src/libstd/sys/vxworks/condvar.rs b/src/libstd/sys/vxworks/condvar.rs index f2a1d6815290d..5a77966d97468 100644 --- a/src/libstd/sys/vxworks/condvar.rs +++ b/src/libstd/sys/vxworks/condvar.rs @@ -10,14 +10,10 @@ unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} const TIMESPEC_MAX: libc::timespec = - libc::timespec { tv_sec: ::max_value(), tv_nsec: 1_000_000_000 - 1 }; + libc::timespec { tv_sec: ::MAX, tv_nsec: 1_000_000_000 - 1 }; fn saturating_cast_to_time_t(value: u64) -> libc::time_t { - if value > ::max_value() as u64 { - ::max_value() - } else { - value as libc::time_t - } + if value > ::MAX as u64 { ::MAX } else { value as libc::time_t } } impl Condvar { diff --git a/src/libstd/sys/vxworks/ext/fs.rs b/src/libstd/sys/vxworks/ext/fs.rs index 9864a855df738..7cc64658ee1a9 100644 --- a/src/libstd/sys/vxworks/ext/fs.rs +++ b/src/libstd/sys/vxworks/ext/fs.rs @@ -6,7 +6,6 @@ use crate::path::Path; use crate::sys; use crate::sys::platform::fs::MetadataExt as UnixMetadataExt; use crate::sys_common::{AsInner, AsInnerMut, FromInner}; -use libc; /// Unix-specific extensions to [`File`]. /// diff --git a/src/libstd/sys/vxworks/ext/process.rs b/src/libstd/sys/vxworks/ext/process.rs index 31e691dd1360b..c3710f4b9124d 100644 --- a/src/libstd/sys/vxworks/ext/process.rs +++ b/src/libstd/sys/vxworks/ext/process.rs @@ -111,7 +111,7 @@ pub trait CommandExt { /// /// Set the first process argument, `argv[0]`, to something other than the /// default executable path. - #[unstable(feature = "process_set_argv0", issue = "66510")] + #[stable(feature = "process_set_argv0", since = "1.45.0")] fn arg0(&mut self, arg: S) -> &mut process::Command where S: AsRef; diff --git a/src/libstd/sys/vxworks/fd.rs b/src/libstd/sys/vxworks/fd.rs index 65c67dabc1ad0..7fa86f0db043f 100644 --- a/src/libstd/sys/vxworks/fd.rs +++ b/src/libstd/sys/vxworks/fd.rs @@ -17,7 +17,7 @@ fn max_len() -> usize { // The maximum read limit on most posix-like systems is `SSIZE_MAX`, // with the man page quoting that if the count of bytes to read is // greater than `SSIZE_MAX` the result is "unspecified". - ::max_value() as usize + ::MAX as usize } impl FileDesc { @@ -48,12 +48,17 @@ impl FileDesc { libc::readv( self.fd, bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::max_value() as usize) as c_int, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, ) })?; Ok(ret as usize) } + #[inline] + fn is_read_vectored(&self) -> bool { + true + } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { let mut me = self; (&mut me).read_to_end(buf) @@ -93,12 +98,17 @@ impl FileDesc { libc::writev( self.fd, bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::max_value() as usize) as c_int, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, ) })?; Ok(ret as usize) } + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { unsafe fn cvt_pwrite( fd: c_int, 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/mutex.rs b/src/libstd/sys/vxworks/mutex.rs index b38375a2e03c5..103d87e3d2f91 100644 --- a/src/libstd/sys/vxworks/mutex.rs +++ b/src/libstd/sys/vxworks/mutex.rs @@ -92,11 +92,11 @@ unsafe impl Send for ReentrantMutex {} unsafe impl Sync for ReentrantMutex {} impl ReentrantMutex { - pub unsafe fn uninitialized() -> ReentrantMutex { + pub const unsafe fn uninitialized() -> ReentrantMutex { ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } } - pub unsafe fn init(&mut self) { + pub unsafe fn init(&self) { let mut attr = MaybeUninit::::uninit(); let result = libc::pthread_mutexattr_init(attr.as_mut_ptr()); debug_assert_eq!(result, 0); diff --git a/src/libstd/sys/vxworks/net.rs b/src/libstd/sys/vxworks/net.rs index 7d4e5624f7e39..32c27ab6e9e8d 100644 --- a/src/libstd/sys/vxworks/net.rs +++ b/src/libstd/sys/vxworks/net.rs @@ -107,7 +107,7 @@ impl Socket { timeout = 1; } - let timeout = cmp::min(timeout, c_int::max_value() as u64) as c_int; + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; match unsafe { libc::poll(&mut pollfd, 1, timeout) } { -1 => { @@ -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) => { @@ -210,8 +220,8 @@ impl Socket { )); } - let secs = if dur.as_secs() > libc::time_t::max_value() as u64 { - libc::time_t::max_value() + let secs = if dur.as_secs() > libc::time_t::MAX as u64 { + libc::time_t::MAX } else { dur.as_secs() as libc::time_t }; 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/process/process_common.rs b/src/libstd/sys/vxworks/process/process_common.rs index 6d5506bec5f7d..78b6e9a4db785 100644 --- a/src/libstd/sys/vxworks/process/process_common.rs +++ b/src/libstd/sys/vxworks/process/process_common.rs @@ -49,11 +49,13 @@ pub struct Command { stderr: Option, } -// Create a new type for argv, so that we can make it `Send` +// Create a new type for `Argv`, so that we can make it `Send` and `Sync` struct Argv(Vec<*const c_char>); -// It is safe to make Argv Send, because it contains pointers to memory owned by `Command.args` +// It is safe to make `Argv` `Send` and `Sync`, because it contains +// pointers to memory owned by `Command.args` unsafe impl Send for Argv {} +unsafe impl Sync for Argv {} // passed back to std::process with the pipes connected to the child, if any // were requested diff --git a/src/libstd/sys/vxworks/rand.rs b/src/libstd/sys/vxworks/rand.rs index 87ebd2c9593fc..3a1ff5fd3b9c6 100644 --- a/src/libstd/sys/vxworks/rand.rs +++ b/src/libstd/sys/vxworks/rand.rs @@ -13,7 +13,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { mod imp { use crate::io; use core::sync::atomic::{AtomicBool, Ordering::Relaxed}; - use libc; pub fn fill_bytes(v: &mut [u8]) { static RNG_INIT: AtomicBool = AtomicBool::new(false); diff --git a/src/libstd/sys/vxworks/rwlock.rs b/src/libstd/sys/vxworks/rwlock.rs index fd2e1a6e7bcfb..c90304c2b4a6a 100644 --- a/src/libstd/sys/vxworks/rwlock.rs +++ b/src/libstd/sys/vxworks/rwlock.rs @@ -1,6 +1,5 @@ use crate::cell::UnsafeCell; use crate::sync::atomic::{AtomicUsize, Ordering}; -use libc; pub struct RWLock { inner: UnsafeCell, diff --git a/src/libstd/sys/vxworks/thread.rs b/src/libstd/sys/vxworks/thread.rs index e0d104b5f3ec9..24a2e0f965d28 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() } @@ -89,7 +96,7 @@ impl Thread { unsafe { while secs > 0 || nsecs > 0 { let mut ts = libc::timespec { - tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t, + tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, tv_nsec: nsecs, }; secs -= ts.tv_sec as u64; diff --git a/src/libstd/sys/vxworks/time.rs b/src/libstd/sys/vxworks/time.rs index 8ebbf89213f32..8365c9ee9c995 100644 --- a/src/libstd/sys/vxworks/time.rs +++ b/src/libstd/sys/vxworks/time.rs @@ -1,7 +1,6 @@ use crate::cmp::Ordering; use crate::time::Duration; use ::core::hash::{Hash, Hasher}; -use libc; pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; use crate::convert::TryInto; @@ -104,7 +103,6 @@ mod inner { use crate::fmt; use crate::sys::cvt; use crate::time::Duration; - use libc; use super::Timespec; diff --git a/src/libstd/sys/wasi/alloc.rs b/src/libstd/sys/wasi/alloc.rs index e9760d050e105..57187851a14e3 100644 --- a/src/libstd/sys/wasi/alloc.rs +++ b/src/libstd/sys/wasi/alloc.rs @@ -1,7 +1,6 @@ use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN}; -use libc; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { @@ -10,7 +9,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..4fe9661421b03 100644 --- a/src/libstd/sys/wasi/mod.rs +++ b/src/libstd/sys/wasi/mod.rs @@ -64,7 +64,7 @@ pub fn unsupported_err() -> std_io::Error { pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { use std_io::ErrorKind::*; - if errno > u16::max_value() as i32 || errno < 0 { + if errno > u16::MAX as i32 || errno < 0 { return Other; } match errno as u16 { @@ -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..78e3911dc4efe 100644 --- a/src/libstd/sys/wasi/stdio.rs +++ b/src/libstd/sys/wasi/stdio.rs @@ -11,16 +11,24 @@ impl Stdin { Ok(Stdin) } - pub fn read(&self, data: &mut [u8]) -> io::Result { + #[inline] + pub fn as_raw_fd(&self) -> u32 { + 0 + } +} + +impl io::Read for Stdin { + fn read(&mut self, data: &mut [u8]) -> io::Result { self.read_vectored(&mut [IoSliceMut::new(data)]) } - pub fn read_vectored(&self, data: &mut [IoSliceMut<'_>]) -> io::Result { + fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result { ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).read(data) } - pub fn as_raw_fd(&self) -> u32 { - 0 + #[inline] + fn is_read_vectored(&self) -> bool { + true } } @@ -29,20 +37,27 @@ impl Stdout { Ok(Stdout) } - pub fn write(&self, data: &[u8]) -> io::Result { + #[inline] + pub fn as_raw_fd(&self) -> u32 { + 1 + } +} + +impl io::Write for Stdout { + fn write(&mut self, data: &[u8]) -> io::Result { self.write_vectored(&[IoSlice::new(data)]) } - pub fn write_vectored(&self, data: &[IoSlice<'_>]) -> io::Result { + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) } - pub fn flush(&self) -> io::Result<()> { - Ok(()) + #[inline] + fn is_write_vectored(&self) -> bool { + true } - - pub fn as_raw_fd(&self) -> u32 { - 1 + fn flush(&mut self) -> io::Result<()> { + Ok(()) } } @@ -51,18 +66,7 @@ impl Stderr { Ok(Stderr) } - pub fn write(&self, data: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(data)]) - } - - pub fn write_vectored(&self, data: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - + #[inline] pub fn as_raw_fd(&self) -> u32 { 2 } @@ -70,11 +74,20 @@ impl Stderr { impl io::Write for Stderr { fn write(&mut self, data: &[u8]) -> io::Result { - (&*self).write(data) + self.write_vectored(&[IoSlice::new(data)]) + } + + fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { + ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true } fn flush(&mut self) -> io::Result<()> { - (&*self).flush() + Ok(()) } } diff --git a/src/libstd/sys/wasi/thread.rs b/src/libstd/sys/wasi/thread.rs index 0986759b89b7c..0d39b1cec328c 100644 --- a/src/libstd/sys/wasi/thread.rs +++ b/src/libstd/sys/wasi/thread.rs @@ -25,7 +25,7 @@ impl Thread { pub fn sleep(dur: Duration) { let nanos = dur.as_nanos(); - assert!(nanos <= u64::max_value() as u128); + assert!(nanos <= u64::MAX as u128); const USERDATA: wasi::Userdata = 0x0123_45678; diff --git a/src/libstd/sys/wasm/condvar_atomics.rs b/src/libstd/sys/wasm/condvar_atomics.rs index a4021c0ee8380..1859cdd5a0ed8 100644 --- a/src/libstd/sys/wasm/condvar_atomics.rs +++ b/src/libstd/sys/wasm/condvar_atomics.rs @@ -48,7 +48,7 @@ impl Condvar { #[inline] pub unsafe fn notify_all(&self) { self.cnt.fetch_add(1, SeqCst); - wasm32::atomic_notify(self.ptr(), u32::max_value()); // -1 == "wake everyone" + wasm32::atomic_notify(self.ptr(), u32::MAX); // -1 == "wake everyone" } pub unsafe fn wait(&self, mutex: &Mutex) { @@ -72,7 +72,7 @@ impl Condvar { let ticket = self.cnt.load(SeqCst) as i32; mutex.unlock(); let nanos = dur.as_nanos(); - let nanos = cmp::min(i64::max_value() as u128, nanos); + let nanos = cmp::min(i64::MAX as u128, nanos); // If the return value is 2 then a timeout happened, so we return // `false` as we weren't actually notified. 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/mutex.rs b/src/libstd/sys/wasm/mutex.rs index 07238d087308f..7aaf1b3a343b6 100644 --- a/src/libstd/sys/wasm/mutex.rs +++ b/src/libstd/sys/wasm/mutex.rs @@ -47,11 +47,11 @@ impl Mutex { pub struct ReentrantMutex {} impl ReentrantMutex { - pub unsafe fn uninitialized() -> ReentrantMutex { + pub const unsafe fn uninitialized() -> ReentrantMutex { ReentrantMutex {} } - pub unsafe fn init(&mut self) {} + pub unsafe fn init(&self) {} pub unsafe fn lock(&self) {} diff --git a/src/libstd/sys/wasm/mutex_atomics.rs b/src/libstd/sys/wasm/mutex_atomics.rs index 90c628a19c22e..268a53bb5641c 100644 --- a/src/libstd/sys/wasm/mutex_atomics.rs +++ b/src/libstd/sys/wasm/mutex_atomics.rs @@ -80,11 +80,11 @@ unsafe impl Sync for ReentrantMutex {} // released when this recursion counter reaches 0. impl ReentrantMutex { - pub unsafe fn uninitialized() -> ReentrantMutex { + pub const unsafe fn uninitialized() -> ReentrantMutex { ReentrantMutex { owner: AtomicU32::new(0), recursions: UnsafeCell::new(0) } } - pub unsafe fn init(&mut self) { + pub unsafe fn init(&self) { // nothing to do... } 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/wasm/thread.rs b/src/libstd/sys/wasm/thread.rs index 0e0e78a827670..0a11896a0048f 100644 --- a/src/libstd/sys/wasm/thread.rs +++ b/src/libstd/sys/wasm/thread.rs @@ -38,7 +38,7 @@ impl Thread { // 2). let mut nanos = dur.as_nanos(); while nanos > 0 { - let amt = cmp::min(i64::max_value() as u128, nanos); + let amt = cmp::min(i64::MAX as u128, nanos); let mut x = 0; let val = unsafe { wasm32::i32_atomic_wait(&mut x, 0, amt as i64) }; debug_assert_eq!(val, 2); diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 7f93ef8795308..f440442ca3062 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -161,6 +161,8 @@ pub const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD; pub const PROGRESS_CONTINUE: DWORD = 0; +// List of Windows system error codes with descriptions: +// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes#system-error-codes pub const ERROR_FILE_NOT_FOUND: DWORD = 2; pub const ERROR_PATH_NOT_FOUND: DWORD = 3; pub const ERROR_ACCESS_DENIED: DWORD = 5; @@ -171,13 +173,26 @@ pub const ERROR_FILE_EXISTS: DWORD = 80; pub const ERROR_INVALID_PARAMETER: DWORD = 87; pub const ERROR_BROKEN_PIPE: DWORD = 109; pub const ERROR_CALL_NOT_IMPLEMENTED: DWORD = 120; +pub const ERROR_SEM_TIMEOUT: DWORD = 121; pub const ERROR_INSUFFICIENT_BUFFER: DWORD = 122; pub const ERROR_ALREADY_EXISTS: DWORD = 183; -pub const ERROR_NO_DATA: DWORD = 232; pub const ERROR_ENVVAR_NOT_FOUND: DWORD = 203; +pub const ERROR_NO_DATA: DWORD = 232; +pub const ERROR_DRIVER_CANCEL_TIMEOUT: DWORD = 594; pub const ERROR_OPERATION_ABORTED: DWORD = 995; pub const ERROR_IO_PENDING: DWORD = 997; -pub const ERROR_TIMEOUT: DWORD = 0x5B4; +pub const ERROR_SERVICE_REQUEST_TIMEOUT: DWORD = 1053; +pub const ERROR_COUNTER_TIMEOUT: DWORD = 1121; +pub const ERROR_TIMEOUT: DWORD = 1460; +pub const ERROR_RESOURCE_CALL_TIMED_OUT: DWORD = 5910; +pub const ERROR_CTX_MODEM_RESPONSE_TIMEOUT: DWORD = 7012; +pub const ERROR_CTX_CLIENT_QUERY_TIMEOUT: DWORD = 7040; +pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: DWORD = 8014; +pub const ERROR_DS_TIMELIMIT_EXCEEDED: DWORD = 8226; +pub const DNS_ERROR_RECORD_TIMED_OUT: DWORD = 9705; +pub const ERROR_IPSEC_IKE_TIMED_OUT: DWORD = 13805; +pub const ERROR_RUNLEVEL_SWITCH_TIMEOUT: DWORD = 15402; +pub const ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT: DWORD = 15403; pub const E_NOTIMPL: HRESULT = 0x80004001u32 as HRESULT; @@ -216,7 +231,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 +792,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..0d4baa3b340df 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -70,7 +70,7 @@ impl RawHandle { pub fn read(&self, buf: &mut [u8]) -> io::Result { let mut read = 0; - let len = cmp::min(buf.len(), ::max_value() as usize) as c::DWORD; + let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; let res = cvt(unsafe { c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, len, &mut read, ptr::null_mut()) }); @@ -92,9 +92,14 @@ 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; + let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; let res = unsafe { let mut overlapped: c::OVERLAPPED = mem::zeroed(); overlapped.Offset = offset as u32; @@ -113,10 +118,9 @@ impl RawHandle { buf: &mut [u8], overlapped: *mut c::OVERLAPPED, ) -> io::Result> { - let len = cmp::min(buf.len(), ::max_value() as usize) as c::DWORD; + let len = cmp::min(buf.len(), ::MAX 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) => { @@ -161,7 +165,7 @@ impl RawHandle { pub fn write(&self, buf: &[u8]) -> io::Result { let mut amt = 0; - let len = cmp::min(buf.len(), ::max_value() as usize) as c::DWORD; + let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; cvt(unsafe { c::WriteFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, ptr::null_mut()) })?; @@ -172,9 +176,14 @@ 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; + let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; unsafe { let mut overlapped: c::OVERLAPPED = mem::zeroed(); overlapped.Offset = offset as u32; diff --git a/src/libstd/sys/windows/io.rs b/src/libstd/sys/windows/io.rs index 5525d2832526f..fb06df1f80cda 100644 --- a/src/libstd/sys/windows/io.rs +++ b/src/libstd/sys/windows/io.rs @@ -12,7 +12,7 @@ pub struct IoSlice<'a> { impl<'a> IoSlice<'a> { #[inline] pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - assert!(buf.len() <= c::ULONG::max_value() as usize); + assert!(buf.len() <= c::ULONG::MAX as usize); IoSlice { vec: c::WSABUF { len: buf.len() as c::ULONG, @@ -49,7 +49,7 @@ pub struct IoSliceMut<'a> { impl<'a> IoSliceMut<'a> { #[inline] pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - assert!(buf.len() <= c::ULONG::max_value() as usize); + assert!(buf.len() <= c::ULONG::MAX as usize); IoSliceMut { vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_mut_ptr() as *mut c::CHAR }, _p: PhantomData, diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index b004cd19020f8..640c9f3636d4b 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -61,7 +61,22 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound, c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound, c::ERROR_NO_DATA => return ErrorKind::BrokenPipe, - c::ERROR_OPERATION_ABORTED => return ErrorKind::TimedOut, + c::ERROR_SEM_TIMEOUT + | c::WAIT_TIMEOUT + | c::ERROR_DRIVER_CANCEL_TIMEOUT + | c::ERROR_OPERATION_ABORTED + | c::ERROR_SERVICE_REQUEST_TIMEOUT + | c::ERROR_COUNTER_TIMEOUT + | c::ERROR_TIMEOUT + | c::ERROR_RESOURCE_CALL_TIMED_OUT + | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT + | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT + | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT + | c::ERROR_DS_TIMELIMIT_EXCEEDED + | c::DNS_ERROR_RECORD_TIMED_OUT + | c::ERROR_IPSEC_IKE_TIMED_OUT + | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT + | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut, _ => {} } @@ -81,10 +96,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 +273,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, @@ -251,7 +310,7 @@ pub fn dur2timeout(dur: Duration) -> c::DWORD { .checked_mul(1000) .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000)) .and_then(|ms| ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 { 1 } else { 0 })) - .map(|ms| if ms > ::max_value() as u64 { c::INFINITE } else { ms as c::DWORD }) + .map(|ms| if ms > ::MAX as u64 { c::INFINITE } else { ms as c::DWORD }) .unwrap_or(c::INFINITE) } @@ -264,10 +323,10 @@ 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(); diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs index 281eb294c65d8..63dfc640908e9 100644 --- a/src/libstd/sys/windows/mutex.rs +++ b/src/libstd/sys/windows/mutex.rs @@ -109,7 +109,7 @@ impl Mutex { 0 => {} n => return n as *mut _, } - let mut re = box ReentrantMutex::uninitialized(); + let re = box ReentrantMutex::uninitialized(); re.init(); let re = Box::into_raw(re); match self.lock.compare_and_swap(0, re as usize, Ordering::SeqCst) { @@ -157,11 +157,11 @@ unsafe impl Send for ReentrantMutex {} unsafe impl Sync for ReentrantMutex {} impl ReentrantMutex { - pub fn uninitialized() -> ReentrantMutex { + pub const fn uninitialized() -> ReentrantMutex { ReentrantMutex { inner: UnsafeCell::new(MaybeUninit::uninit()) } } - pub unsafe fn init(&mut self) { + pub unsafe fn init(&self) { c::InitializeCriticalSection((&mut *self.inner.get()).as_mut_ptr()); } diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs index d8d4fdfce2fe4..9e74454bc2335 100644 --- a/src/libstd/sys/windows/net.rs +++ b/src/libstd/sys/windows/net.rs @@ -228,7 +228,7 @@ impl Socket { fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result { // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. - let len = cmp::min(buf.len(), i32::max_value() as usize) as i32; + let len = cmp::min(buf.len(), i32::MAX as usize) as i32; unsafe { match c::recv(self.0, buf.as_mut_ptr() as *mut c_void, len, flags) { -1 if c::WSAGetLastError() == c::WSAESHUTDOWN => Ok(0), @@ -245,7 +245,7 @@ impl Socket { pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. - let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD; + let len = cmp::min(bufs.len(), c::DWORD::MAX as usize) as c::DWORD; let mut nread = 0; let mut flags = 0; unsafe { @@ -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) } @@ -277,7 +282,7 @@ impl Socket { ) -> io::Result<(usize, SocketAddr)> { let mut storage: c::SOCKADDR_STORAGE_LH = unsafe { mem::zeroed() }; let mut addrlen = mem::size_of_val(&storage) as c::socklen_t; - let len = cmp::min(buf.len(), ::max_value() as usize) as wrlen_t; + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. @@ -308,7 +313,7 @@ impl Socket { } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let len = cmp::min(bufs.len(), c::DWORD::max_value() as usize) as c::DWORD; + let len = cmp::min(bufs.len(), c::DWORD::MAX as usize) as c::DWORD; let mut nwritten = 0; unsafe { cvt(c::WSASend( @@ -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/backtrace.rs b/src/libstd/sys_common/backtrace.rs index 2c7ba8f8ea1fd..e9b1e86d7ae49 100644 --- a/src/libstd/sys_common/backtrace.rs +++ b/src/libstd/sys_common/backtrace.rs @@ -28,7 +28,7 @@ pub fn lock() -> impl Drop { unsafe { LOCK.lock(); - return Guard; + Guard } } diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs index 135e8308afaea..81a5ef95e82dc 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,8 +265,13 @@ 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 len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; let ret = cvt(unsafe { c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) })?; @@ -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(); @@ -489,7 +502,7 @@ impl UdpSocket { } pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { - let len = cmp::min(buf.len(), ::max_value() as usize) as wrlen_t; + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; let (dstp, dstlen) = dst.into_inner(); let ret = cvt(unsafe { c::sendto( @@ -628,7 +641,7 @@ impl UdpSocket { } pub fn send(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::max_value() as usize) as wrlen_t; + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; let ret = cvt(unsafe { c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) })?; 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/remutex.rs b/src/libstd/sys_common/remutex.rs index a1ad44a3666ed..4f19bbc467f33 100644 --- a/src/libstd/sys_common/remutex.rs +++ b/src/libstd/sys_common/remutex.rs @@ -3,7 +3,6 @@ use crate::marker; use crate::ops::Deref; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sys::mutex as sys; -use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; /// A re-entrant mutual exclusion /// @@ -11,8 +10,7 @@ use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; /// available. The thread which has already locked the mutex can lock it /// multiple times without blocking, preventing a common source of deadlocks. pub struct ReentrantMutex { - inner: Box, - poison: poison::Flag, + inner: sys::ReentrantMutex, data: T, } @@ -39,23 +37,30 @@ pub struct ReentrantMutexGuard<'a, T: 'a> { // funny underscores due to how Deref currently works (it disregards field // privacy). __lock: &'a ReentrantMutex, - __poison: poison::Guard, } impl !marker::Send for ReentrantMutexGuard<'_, T> {} impl ReentrantMutex { /// Creates a new reentrant mutex in an unlocked state. - pub fn new(t: T) -> ReentrantMutex { - unsafe { - let mut mutex = ReentrantMutex { - inner: box sys::ReentrantMutex::uninitialized(), - poison: poison::Flag::new(), - data: t, - }; - mutex.inner.init(); - mutex - } + /// + /// # Unsafety + /// + /// This function is unsafe because it is required that `init` is called + /// once this mutex is in its final resting place, and only then are the + /// lock/unlock methods safe. + pub const unsafe fn new(t: T) -> ReentrantMutex { + ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t } + } + + /// Initializes this mutex so it's ready for use. + /// + /// # Unsafety + /// + /// Unsafe to call more than once, and must be called after this will no + /// longer move in memory. + pub unsafe fn init(&self) { + self.inner.init(); } /// Acquires a mutex, blocking the current thread until it is able to do so. @@ -70,7 +75,7 @@ impl ReentrantMutex { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn lock(&self) -> LockResult> { + pub fn lock(&self) -> ReentrantMutexGuard<'_, T> { unsafe { self.inner.lock() } ReentrantMutexGuard::new(&self) } @@ -87,12 +92,8 @@ impl ReentrantMutex { /// If another user of this mutex panicked while holding the mutex, then /// this call will return failure if the mutex would otherwise be /// acquired. - pub fn try_lock(&self) -> TryLockResult> { - if unsafe { self.inner.try_lock() } { - Ok(ReentrantMutexGuard::new(&self)?) - } else { - Err(TryLockError::WouldBlock) - } + pub fn try_lock(&self) -> Option> { + if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None } } } @@ -108,11 +109,8 @@ impl Drop for ReentrantMutex { impl fmt::Debug for ReentrantMutex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.try_lock() { - Ok(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(), - Err(TryLockError::Poisoned(err)) => { - f.debug_struct("ReentrantMutex").field("data", &**err.get_ref()).finish() - } - Err(TryLockError::WouldBlock) => { + Some(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(), + None => { struct LockedPlaceholder; impl fmt::Debug for LockedPlaceholder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -127,11 +125,8 @@ impl fmt::Debug for ReentrantMutex { } impl<'mutex, T> ReentrantMutexGuard<'mutex, T> { - fn new(lock: &'mutex ReentrantMutex) -> LockResult> { - poison::map_result(lock.poison.borrow(), |guard| ReentrantMutexGuard { - __lock: lock, - __poison: guard, - }) + fn new(lock: &'mutex ReentrantMutex) -> ReentrantMutexGuard<'mutex, T> { + ReentrantMutexGuard { __lock: lock } } } @@ -147,7 +142,6 @@ impl Drop for ReentrantMutexGuard<'_, T> { #[inline] fn drop(&mut self) { unsafe { - self.__lock.poison.done(&self.__poison); self.__lock.inner.unlock(); } } @@ -162,13 +156,17 @@ mod tests { #[test] fn smoke() { - let m = ReentrantMutex::new(()); + let m = unsafe { + let m = ReentrantMutex::new(()); + m.init(); + m + }; { - let a = m.lock().unwrap(); + let a = m.lock(); { - let b = m.lock().unwrap(); + let b = m.lock(); { - let c = m.lock().unwrap(); + let c = m.lock(); assert_eq!(*c, ()); } assert_eq!(*b, ()); @@ -179,15 +177,19 @@ mod tests { #[test] fn is_mutex() { - let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); + let m = unsafe { + let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); + m.init(); + m + }; let m2 = m.clone(); - let lock = m.lock().unwrap(); + let lock = m.lock(); let child = thread::spawn(move || { - let lock = m2.lock().unwrap(); + let lock = m2.lock(); assert_eq!(*lock.borrow(), 4950); }); for i in 0..100 { - let lock = m.lock().unwrap(); + let lock = m.lock(); *lock.borrow_mut() += i; } drop(lock); @@ -196,17 +198,21 @@ mod tests { #[test] fn trylock_works() { - let m = Arc::new(ReentrantMutex::new(())); + let m = unsafe { + let m = Arc::new(ReentrantMutex::new(())); + m.init(); + m + }; let m2 = m.clone(); - let _lock = m.try_lock().unwrap(); - let _lock2 = m.try_lock().unwrap(); + let _lock = m.try_lock(); + let _lock2 = m.try_lock(); thread::spawn(move || { let lock = m2.try_lock(); - assert!(lock.is_err()); + assert!(lock.is_none()); }) .join() .unwrap(); - let _lock3 = m.try_lock().unwrap(); + let _lock3 = m.try_lock(); } pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell>); @@ -215,22 +221,4 @@ mod tests { *self.0.borrow_mut() = 42; } } - - #[test] - fn poison_works() { - let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); - let mc = m.clone(); - let result = thread::spawn(move || { - let lock = mc.lock().unwrap(); - *lock.borrow_mut() = 1; - let lock2 = mc.lock().unwrap(); - *lock.borrow_mut() = 2; - let _answer = Answer(lock2); - panic!("What the answer to my lifetimes dilemma is?"); - }) - .join(); - assert!(result.is_err()); - let r = m.lock().err().unwrap().into_inner(); - assert_eq!(*r.borrow(), 42); - } } 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 7509e1ee35dee..bdb6a05464ed4 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -201,9 +201,8 @@ impl Wtf8Buf { /// Copied from String::push /// This does **not** include the WTF-8 concatenation check. fn push_code_point_unchecked(&mut self, code_point: CodePoint) { - let c = unsafe { char::from_u32_unchecked(code_point.value) }; let mut bytes = [0; 4]; - let bytes = c.encode_utf8(&mut bytes).as_bytes(); + let bytes = char::encode_utf8_raw(code_point.value, &mut bytes); self.bytes.extend_from_slice(bytes) } @@ -386,6 +385,17 @@ impl Extend for Wtf8Buf { self.bytes.reserve(low); iterator.for_each(move |code_point| self.push(code_point)); } + + #[inline] + fn extend_one(&mut self, code_point: CodePoint) { + self.push(code_point); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + // Lower bound of one byte per code point (ASCII only) + self.bytes.reserve(additional); + } } /// A borrowed slice of well-formed WTF-8 data. @@ -599,28 +609,24 @@ impl Wtf8 { #[inline] fn final_lead_surrogate(&self) -> Option { - let len = self.len(); - if len < 3 { - return None; - } - match self.bytes[(len - 3)..] { - [0xED, b2 @ 0xA0..=0xAF, b3] => Some(decode_surrogate(b2, b3)), + match self.bytes { + [.., 0xED, b2 @ 0xA0..=0xAF, b3] => Some(decode_surrogate(b2, b3)), _ => None, } } #[inline] fn initial_trail_surrogate(&self) -> Option { - let len = self.len(); - if len < 3 { - return None; - } - match self.bytes[..3] { - [0xED, b2 @ 0xB0..=0xBF, b3] => Some(decode_surrogate(b2, b3)), + match self.bytes { + [0xED, b2 @ 0xB0..=0xBF, b3, ..] => Some(decode_surrogate(b2, b3)), _ => None, } } + pub fn clone_into(&self, buf: &mut Wtf8Buf) { + self.bytes.clone_into(&mut buf.bytes) + } + /// Boxes this `Wtf8`. #[inline] pub fn into_box(&self) -> Box { @@ -645,6 +651,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`). @@ -803,8 +839,7 @@ impl<'a> Iterator for EncodeWide<'a> { let mut buf = [0; 2]; self.code_points.next().map(|code_point| { - let c = unsafe { char::from_u32_unchecked(code_point.value) }; - let n = c.encode_utf16(&mut buf).len(); + let n = char::encode_utf16_raw(code_point.value, &mut buf).len(); if n == 2 { self.extra = buf[1]; } @@ -845,12 +880,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/tests/run-time-detect.rs b/src/libstd/tests/run-time-detect.rs index 2e6d1bc8efd3f..8dd1a8ac0d2df 100644 --- a/src/libstd/tests/run-time-detect.rs +++ b/src/libstd/tests/run-time-detect.rs @@ -32,6 +32,7 @@ fn aarch64_linux() { println!("rdm: {}", is_aarch64_feature_detected!("rdm")); println!("rcpc: {}", is_aarch64_feature_detected!("rcpc")); println!("dotprod: {}", is_aarch64_feature_detected!("dotprod")); + println!("tme: {}", is_aarch64_feature_detected!("tme")); } #[test] 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 0dc43c7e6510a..d435ca6842518 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"); } @@ -1082,8 +1086,8 @@ impl ThreadId { /// it is not guaranteed which values new threads will return, and this may /// change across Rust versions. #[unstable(feature = "thread_id_value", issue = "67939")] - pub fn as_u64(&self) -> u64 { - self.0.get() + pub fn as_u64(&self) -> NonZeroU64 { + self.0 } } @@ -1272,7 +1276,7 @@ impl Thread { } fn cname(&self) -> Option<&CStr> { - self.inner.name.as_ref().map(|s| &**s) + self.inner.name.as_deref() } } @@ -1526,7 +1530,6 @@ mod tests { use crate::sync::mpsc::{channel, Sender}; use crate::thread::{self, ThreadId}; use crate::time::Duration; - use crate::u32; // !!! These tests are dangerous. If something is buggy, they will hang, !!! // !!! instead of exiting cleanly. This might wedge the buildbots. !!! diff --git a/src/libstd/time.rs b/src/libstd/time.rs index c36e78b1d004e..84fa35e01bb09 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -60,6 +60,21 @@ pub use core::time::Duration; /// } /// ``` /// +/// # OS-specific behaviors +/// +/// An `Instant` is a wrapper around system-specific types and it may behave +/// differently depending on the underlying operating system. For example, +/// the following snippet is fine on Linux but panics on macOS: +/// +/// ```no_run +/// use std::time::{Instant, Duration}; +/// +/// let now = Instant::now(); +/// let max_nanoseconds = u64::MAX / 1_000_000_000; +/// let duration = Duration::new(max_nanoseconds, 0); +/// println!("{:?}", now + duration); +/// ``` +/// /// # Underlying System calls /// Currently, the following system calls are being used to get the current time using `now()`: /// @@ -686,7 +701,7 @@ mod tests { // checked_add_duration will not panic on overflow let mut maybe_t = Some(Instant::now()); - let max_duration = Duration::from_secs(u64::max_value()); + let max_duration = Duration::from_secs(u64::MAX); // in case `Instant` can store `>= now + max_duration`. for _ in 0..2 { maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); @@ -766,7 +781,7 @@ mod tests { // checked_add_duration will not panic on overflow let mut maybe_t = Some(SystemTime::UNIX_EPOCH); - let max_duration = Duration::from_secs(u64::max_value()); + let max_duration = Duration::from_secs(u64::MAX); // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`. for _ in 0..2 { maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); @@ -796,11 +811,11 @@ mod tests { // Right now for CI this test is run in an emulator, and apparently the // aarch64 emulator's sense of time is that we're still living in the - // 70s. + // 70s. This is also true for riscv (also qemu) // // Otherwise let's assume that we're all running computers later than // 2000. - if !cfg!(target_arch = "aarch64") { + if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "riscv64") { assert!(a > thirty_years); } 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..97a659f22d757 100644 --- a/src/libtest/cli.rs +++ b/src/libtest/cli.rs @@ -115,7 +115,7 @@ fn optgroups() -> getopts::Options { .optflagopt( "", "report-time", - "Show execution time of each test. Awailable values: + "Show execution time of each test. Available values: plain = do not colorize the execution time (default); colored = colorize output according to the `color` parameter value; @@ -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..f6f16f686e557 100644 --- a/src/libunwind/build.rs +++ b/src/libunwind/build.rs @@ -30,16 +30,12 @@ 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") { // This is handled in the target spec with late_link_args_[static|dynamic] - - // cfg!(bootstrap) doesn't work in build scripts - if env::var("RUSTC_STAGE").ok() == Some("0".to_string()) { - println!("cargo:rustc-link-lib=static-nobundle=gcc_eh"); - println!("cargo:rustc-link-lib=static-nobundle=pthread"); - } } else if target.contains("uwp-windows-gnu") { println!("cargo:rustc-link-lib=unwind"); } else if target.contains("fuchsia") { @@ -87,6 +83,9 @@ mod llvm_libunwind { cfg.flag("-fno-rtti"); cfg.flag("-fstrict-aliasing"); cfg.flag("-funwind-tables"); + cfg.flag("-fvisibility=hidden"); + cfg.flag_if_supported("-fvisibility-global-new-delete-hidden"); + cfg.define("_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS", None); } let mut unwind_sources = vec![ diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs index 18d41be77398b..cc025da1af555 100644 --- a/src/libunwind/lib.rs +++ b/src/libunwind/lib.rs @@ -27,3 +27,7 @@ extern "C" {} #[link(name = "gcc_eh", kind = "static-nobundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] extern "C" {} + +#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] +#[link(name = "unwind", kind = "static-nobundle")] +extern "C" {} diff --git a/src/llvm-project b/src/llvm-project index 9f65ad057357b..0ddefeca92b2e 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 9f65ad057357b307180955831968f79e74090a90 +Subproject commit 0ddefeca92b2e1835c80e9b01d9ecc7efc906b1c diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 90d24d20737db..9bc111c26ba6b 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 @@ -67,7 +67,11 @@ extern "C" void LLVMInitializePasses() { } extern "C" void LLVMTimeTraceProfilerInitialize() { -#if LLVM_VERSION_GE(9, 0) +#if LLVM_VERSION_GE(10, 0) + timeTraceProfilerInitialize( + /* TimeTraceGranularity */ 0, + /* ProcName */ "rustc"); +#elif LLVM_VERSION_GE(9, 0) timeTraceProfilerInitialize(); #endif } @@ -134,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) { @@ -205,6 +203,12 @@ void LLVMRustAddLastExtensionPasses( #define SUBTARGET_AARCH64 #endif +#ifdef LLVM_COMPONENT_AVR +#define SUBTARGET_AVR SUBTARGET(AVR) +#else +#define SUBTARGET_AVR +#endif + #ifdef LLVM_COMPONENT_MIPS #define SUBTARGET_MIPS SUBTARGET(Mips) #else @@ -251,6 +255,7 @@ void LLVMRustAddLastExtensionPasses( SUBTARGET_X86 \ SUBTARGET_ARM \ SUBTARGET_AARCH64 \ + SUBTARGET_AVR \ SUBTARGET_MIPS \ SUBTARGET_PPC \ SUBTARGET_SYSTEMZ \ @@ -276,7 +281,7 @@ extern "C" bool LLVMRustHasFeature(LLVMTargetMachineRef TM, } enum class LLVMRustCodeModel { - Other, + Tiny, Small, Kernel, Medium, @@ -284,8 +289,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: @@ -294,6 +301,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."); } @@ -350,8 +359,7 @@ static PassBuilder::OptimizationLevel fromRust(LLVMRustPassBuilderOptLevel Level } } -enum class LLVMRustRelocMode { - Default, +enum class LLVMRustRelocModel { Static, PIC, DynamicNoPic, @@ -360,21 +368,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."); @@ -418,6 +424,12 @@ extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { printf("Available features for this target:\n"); for (auto &Feature : FeatTable) printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); + printf("\nRust-specific features:\n"); + printf(" %-*s - %s.\n", + MaxFeatLen, + "crt-static", + "Enables libraries with C Run-time Libraries(CRT) to be statically linked" + ); printf("\n"); printf("Use +feature to enable a feature, or -feature to disable it.\n" @@ -444,18 +456,20 @@ 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 FunctionSections, bool DataSections, bool TrapUnreachable, 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)); @@ -478,6 +492,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. @@ -493,9 +508,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); @@ -705,11 +717,12 @@ enum class LLVMRustOptStage { }; struct LLVMRustSanitizerOptions { + bool SanitizeAddress; + bool SanitizeAddressRecover; bool SanitizeMemory; + bool SanitizeMemoryRecover; + int SanitizeMemoryTrackOrigins; bool SanitizeThread; - bool SanitizeAddress; - bool SanitizeRecover; - int SanitizeMemoryTrackOrigins; }; extern "C" void @@ -720,7 +733,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, @@ -796,7 +809,7 @@ LLVMRustOptimizeWithNewPassManager( if (SanitizerOptions->SanitizeMemory) { MemorySanitizerOptions Options( SanitizerOptions->SanitizeMemoryTrackOrigins, - SanitizerOptions->SanitizeRecover, + SanitizerOptions->SanitizeMemoryRecover, /*CompileKernel=*/false); #if LLVM_VERSION_GE(10, 0) PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) { @@ -830,14 +843,14 @@ LLVMRustOptimizeWithNewPassManager( OptimizerLastEPCallbacks.push_back( [SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { FPM.addPass(AddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeRecover, + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover, /*UseAfterScope=*/true)); } ); PipelineStartEPCallbacks.push_back( [SanitizerOptions](ModulePassManager &MPM) { MPM.addPass(ModuleAddressSanitizerPass( - /*CompileKernel=*/false, SanitizerOptions->SanitizeRecover)); + /*CompileKernel=*/false, SanitizerOptions->SanitizeAddressRecover)); } ); } @@ -856,7 +869,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) { @@ -1232,15 +1245,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, @@ -1273,10 +1282,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` @@ -1342,11 +1349,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; } @@ -1492,6 +1495,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 25cfee3373dc4..cdb3a157eab97 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -5,6 +5,7 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Bitcode/BitcodeWriterPass.h" @@ -203,6 +204,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 +591,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 +623,6 @@ static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) { return Result; } -#endif enum class LLVMRustDebugEmissionKind { NoDebug, @@ -640,6 +643,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,14 +708,19 @@ 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 LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder, - LLVMMetadataRef File, LLVMMetadataRef ParameterTypes) { return wrap(Builder->createSubroutineType( DITypeRefArray(unwrap(ParameterTypes)))); @@ -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,29 +749,13 @@ 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); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateBasicType( LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, - uint64_t SizeInBits, uint32_t AlignInBits, unsigned Encoding) { + uint64_t SizeInBits, unsigned Encoding) { return wrap(Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding)); } @@ -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); @@ -956,9 +964,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, - LLVMMetadataRef Ty, LLVMMetadataRef File, unsigned LineNo, - unsigned ColumnNo) { + const char *Name, size_t NameLen, LLVMMetadataRef Ty) { return wrap(Builder->createTemplateTypeParameter( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(Ty))); } @@ -1082,19 +1088,24 @@ 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); MessageOS << Opt->getMsg(); } +enum class LLVMRustDiagnosticLevel { + Error, + Warning, + Note, + Remark, +}; + extern "C" void -LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI, unsigned *CookieOut, +LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI, + LLVMRustDiagnosticLevel *LevelOut, + unsigned *CookieOut, LLVMTwineRef *MessageOut, LLVMValueRef *InstructionOut) { // Undefined to call this not on an inline assembly diagnostic! @@ -1104,6 +1115,23 @@ LLVMRustUnpackInlineAsmDiagnostic(LLVMDiagnosticInfoRef DI, unsigned *CookieOut, *CookieOut = IA->getLocCookie(); *MessageOut = wrap(&IA->getMsgStr()); *InstructionOut = wrap(IA->getInstruction()); + + switch (IA->getSeverity()) { + case DS_Error: + *LevelOut = LLVMRustDiagnosticLevel::Error; + break; + case DS_Warning: + *LevelOut = LLVMRustDiagnosticLevel::Warning; + break; + case DS_Note: + *LevelOut = LLVMRustDiagnosticLevel::Note; + break; + case DS_Remark: + *LevelOut = LLVMRustDiagnosticLevel::Remark; + break; + default: + report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); + } } extern "C" void LLVMRustWriteDiagnosticInfoToString(LLVMDiagnosticInfoRef DI, @@ -1165,6 +1193,7 @@ extern "C" LLVMRustDiagnosticKind LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) { return toRust((DiagnosticKind)unwrap(DI)->getKind()); } + // This is kept distinct from LLVMGetTypeKind, because when // a new type kind is added, the Rust-side enum must be // updated or UB will result. @@ -1215,10 +1244,51 @@ extern "C" void LLVMRustSetInlineAsmDiagnosticHandler( unwrap(C)->setInlineAsmDiagnosticHandler(H, CX); } -extern "C" void LLVMRustWriteSMDiagnosticToString(LLVMSMDiagnosticRef D, - RustStringRef Str) { - RawRustStringOstream OS(Str); - unwrap(D)->print("", OS); +extern "C" bool LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, + RustStringRef MessageOut, + RustStringRef BufferOut, + LLVMRustDiagnosticLevel* LevelOut, + unsigned* LocOut, + unsigned* RangesOut, + size_t* NumRanges) { + SMDiagnostic& D = *unwrap(DRef); + RawRustStringOstream MessageOS(MessageOut); + MessageOS << D.getMessage(); + + switch (D.getKind()) { + case SourceMgr::DK_Error: + *LevelOut = LLVMRustDiagnosticLevel::Error; + break; + case SourceMgr::DK_Warning: + *LevelOut = LLVMRustDiagnosticLevel::Warning; + break; + case SourceMgr::DK_Note: + *LevelOut = LLVMRustDiagnosticLevel::Note; + break; + case SourceMgr::DK_Remark: + *LevelOut = LLVMRustDiagnosticLevel::Remark; + break; + default: + report_fatal_error("Invalid LLVMRustDiagnosticLevel value!"); + } + + if (D.getLoc() == SMLoc()) + return false; + + const SourceMgr &LSM = *D.getSourceMgr(); + const MemoryBuffer *LBuf = LSM.getMemoryBuffer(LSM.FindBufferContainingLoc(D.getLoc())); + LLVMRustStringWriteImpl(BufferOut, LBuf->getBufferStart(), LBuf->getBufferSize()); + + *LocOut = D.getLoc().getPointer() - LBuf->getBufferStart(); + + *NumRanges = std::min(*NumRanges, D.getRanges().size()); + size_t LineStart = *LocOut - (size_t)D.getColumnNo(); + for (size_t i = 0; i < *NumRanges; i++) { + RangesOut[i * 2] = LineStart + D.getRanges()[i].first; + RangesOut[i * 2 + 1] = LineStart + D.getRanges()[i].second; + } + + return true; } extern "C" LLVMValueRef LLVMRustBuildCleanupPad(LLVMBuilderRef B, @@ -1295,6 +1365,11 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Bundles)); } +extern "C" LLVMValueRef LLVMRustGetInstrprofIncrementIntrinsic(LLVMModuleRef M) { + return wrap(llvm::Intrinsic::getDeclaration(unwrap(M), + (llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment)); +} + extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, @@ -1333,8 +1408,13 @@ extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Val, LLVMValueRef Size, bool IsVolatile) { +#if LLVM_VERSION_GE(10, 0) + return wrap(unwrap(B)->CreateMemSet( + unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile)); +#else return wrap(unwrap(B)->CreateMemSet( unwrap(Dst), unwrap(Val), unwrap(Size), DstAlign, IsVolatile)); +#endif } extern "C" LLVMValueRef 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 55416b6729adb..769ec669bdc8d 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-02-29 +date: 2020-06-16 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 dea57529b3695..45340c0e2fdad 160000 --- a/src/stdarch +++ b/src/stdarch @@ -1 +1 @@ -Subproject commit dea57529b3695605909e7d327bb6551d7a10c788 +Subproject commit 45340c0e2fdadf2f131ef43cb683b5cafab0ff15 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/hexagon-types.rs b/src/test/assembly/asm/hexagon-types.rs new file mode 100644 index 0000000000000..ba2d8a363cd4e --- /dev/null +++ b/src/test/assembly/asm/hexagon-types.rs @@ -0,0 +1,130 @@ +// no-system-llvm +// assembly-output: emit-asm +// compile-flags: --target hexagon-unknown-linux-musl + +#![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! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +type ptr = *const i32; + +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for f32 {} +impl Copy for ptr {} +extern "C" { + fn extern_func(); + static extern_static: u8; +} + +macro_rules! check { + ($func:ident $ty:ident $class:ident) => { + #[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!("{} = {}", out($class) y, in($class) x); + y + } + }; +} + +// CHECK-LABEL: sym_static: +// CHECK: InlineAsm Start +// CHECK: r0 = #extern_static +// CHECK: InlineAsm End +#[no_mangle] +pub unsafe fn sym_static() { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + asm!("r0 = #{}", sym extern_static); +} + +// CHECK-LABEL: sym_fn: +// CHECK: InlineAsm Start +// CHECK: r0 = #extern_func +// CHECK: InlineAsm End +#[no_mangle] +pub unsafe fn sym_fn() { + // Hack to avoid function merging + extern "Rust" { + fn dont_merge(s: &str); + } + dont_merge(stringify!($func)); + + asm!("r0 = #{}", sym extern_func); +} + +// This is a test for multi-instruction packets, +// which require the escaped braces. +// +// CHECK-LABEL: packet: +// CHECK: InlineAsm Start +// CHECK: { +// CHECK: r{{[0-9]+}} = r0 +// CHECK: memw(r1) = r{{[0-9]+}} +// CHECK: } +// CHECK: InlineAsm End +#[no_mangle] +pub unsafe fn packet() { + let val = 1024; + asm!("{{ + {} = r0 + memw(r1) = {} + }}", out(reg) _, in(reg) &val); +} + +// CHECK-LABEL: ptr: +// CHECK: InlineAsm Start +// CHECK: r{{[0-9]+}} = r{{[0-9]+}} +// CHECK: InlineAsm End +check!(reg_ptr ptr reg); + +// CHECK-LABEL: reg_f32: +// CHECK: InlineAsm Start +// CHECK: r{{[0-9]+}} = r{{[0-9]+}} +// CHECK: InlineAsm End +check!(reg_f32 f32 reg); + +// CHECK-LABEL: reg_i32: +// CHECK: InlineAsm Start +// CHECK: r{{[0-9]+}} = r{{[0-9]+}} +// CHECK: InlineAsm End +check!(reg_i32 i32 reg); + +// CHECK-LABEL: reg_i8: +// CHECK: InlineAsm Start +// CHECK: r{{[0-9]+}} = r{{[0-9]+}} +// CHECK: InlineAsm End +check!(reg_i8 i8 reg); + +// CHECK-LABEL: reg_i16: +// CHECK: InlineAsm Start +// CHECK: r{{[0-9]+}} = r{{[0-9]+}} +// CHECK: InlineAsm End +check!(reg_i16 i16 reg); diff --git a/src/test/assembly/asm/nvptx-types.rs b/src/test/assembly/asm/nvptx-types.rs new file mode 100644 index 0000000000000..4ee79d1bcc839 --- /dev/null +++ b/src/test/assembly/asm/nvptx-types.rs @@ -0,0 +1,133 @@ +// no-system-llvm +// assembly-output: emit-asm +// compile-flags: --target nvptx64-nvidia-cuda +// compile-flags: --crate-type cdylib + +#![feature(no_core, lang_items, rustc_attrs)] +#![no_core] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} + +#[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 {} + +// NVPTX does not support static variables +#[no_mangle] +fn extern_func() {} + +// CHECK-LABEL: .visible .func sym_fn() +// CHECK: // begin inline asm +// CHECK: call extern_func; +// CHECK: // end inline asm +#[no_mangle] +pub unsafe fn sym_fn() { + asm!("call {};", sym extern_func); +} + +macro_rules! check { + ($func:ident $ty:ident $class:ident $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + let y; + asm!(concat!($mov, " {}, {};"), out($class) y, in($class) x); + y + } + }; +} + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg16_i8 +// CHECK: // begin inline asm +// CHECK: mov.i16 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg16_i8 i8 reg16 "mov.i16"); + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg16_i16 +// CHECK: // begin inline asm +// CHECK: mov.i16 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg16_i16 i16 reg16 "mov.i16"); + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg32_i8 +// CHECK: // begin inline asm +// CHECK: mov.i32 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg32_i8 i8 reg32 "mov.i32"); + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg32_i16 +// CHECK: // begin inline asm +// CHECK: mov.i32 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg32_i16 i16 reg32 "mov.i32"); + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg32_i32 +// CHECK: // begin inline asm +// CHECK: mov.i32 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg32_i32 i32 reg32 "mov.i32"); + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg32_f32 +// CHECK: // begin inline asm +// CHECK: mov.i32 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg32_f32 f32 reg32 "mov.i32"); + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg64_i8 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg64_i8 i8 reg64 "mov.i64"); + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg64_i16 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg64_i16 i16 reg64 "mov.i64"); + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg64_i32 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg64_i32 i32 reg64 "mov.i64"); + +// CHECK-LABEL: .visible .func (.param .b32 func_retval0) reg64_f32 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg64_f32 f32 reg64 "mov.i64"); + +// CHECK-LABEL: .visible .func (.param .b64 func_retval0) reg64_i64 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg64_i64 i64 reg64 "mov.i64"); + +// CHECK-LABEL: .visible .func (.param .b64 func_retval0) reg64_f64 +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg64_f64 f64 reg64 "mov.i64"); + +// CHECK-LABEL: .visible .func (.param .b64 func_retval0) reg64_ptr +// CHECK: // begin inline asm +// CHECK: mov.i64 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}; +// CHECK: // end inline asm +check!(reg64_ptr ptr reg64 "mov.i64"); 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/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/abi-main-signature-16bit-c-int.rs b/src/test/codegen/abi-main-signature-16bit-c-int.rs index d7b8c48c33e98..4ed491dfb2b43 100644 --- a/src/test/codegen/abi-main-signature-16bit-c-int.rs +++ b/src/test/codegen/abi-main-signature-16bit-c-int.rs @@ -10,6 +10,7 @@ // ignore-mips64 // ignore-powerpc // ignore-powerpc64 +// ignore-riscv64 // ignore-s390x // ignore-sparc // ignore-sparc64 diff --git a/src/test/codegen/abi-sysv64.rs b/src/test/codegen/abi-sysv64.rs index 6456ad47615e8..89c9bcee052fb 100644 --- a/src/test/codegen/abi-sysv64.rs +++ b/src/test/codegen/abi-sysv64.rs @@ -4,6 +4,7 @@ // ignore-arm // ignore-aarch64 +// ignore-riscv64 sysv64 not supported // compile-flags: -C no-prepopulate-passes diff --git a/src/test/codegen/abi-x86-interrupt.rs b/src/test/codegen/abi-x86-interrupt.rs index db215860f206b..25c155c949dcd 100644 --- a/src/test/codegen/abi-x86-interrupt.rs +++ b/src/test/codegen/abi-x86-interrupt.rs @@ -4,6 +4,7 @@ // ignore-arm // ignore-aarch64 +// ignore-riscv64 x86-interrupt is not supported // compile-flags: -C no-prepopulate-passes 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-multiple-options.rs b/src/test/codegen/asm-multiple-options.rs new file mode 100644 index 0000000000000..c702742bf1a63 --- /dev/null +++ b/src/test/codegen/asm-multiple-options.rs @@ -0,0 +1,53 @@ +// 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), options(nomem)); +} + +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), options(readonly)); + 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), 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), options(readonly)); + VAR = 2; + VAR +} 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..24e3d3cd64b58 --- /dev/null +++ b/src/test/codegen/call-llvm-intrinsics.rs @@ -0,0 +1,29 @@ +// compile-flags: -C no-prepopulate-passes + +// ignore-riscv64 + +#![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/catch-unwind.rs b/src/test/codegen/catch-unwind.rs index 3c9bc35d1c8bd..7ff9c0d15e003 100644 --- a/src/test/codegen/catch-unwind.rs +++ b/src/test/codegen/catch-unwind.rs @@ -1,5 +1,14 @@ // compile-flags: -O +// On x86 the closure is inlined in foo() producting something like +// define i32 @foo() [...] { +// tail call void @bar() [...] +// ret i32 0 +// } +// On riscv the closure is another function, placed before fn foo so CHECK can't +// find it +// ignore-riscv64 FIXME + #![crate_type = "lib"] extern "C" { diff --git a/src/test/codegen/cdylib-external-inline-fns.rs b/src/test/codegen/cdylib-external-inline-fns.rs new file mode 100644 index 0000000000000..519be6b6a99a4 --- /dev/null +++ b/src/test/codegen/cdylib-external-inline-fns.rs @@ -0,0 +1,43 @@ +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "cdylib"] + +// CHECK: define void @a() +#[no_mangle] +#[inline] +pub extern "C" fn a() {} + +// CHECK: define void @b() +#[export_name = "b"] +#[inline] +pub extern "C" fn b() {} + +// CHECK: define void @c() +#[no_mangle] +#[inline] +extern "C" fn c() {} + +// CHECK: define void @d() +#[export_name = "d"] +#[inline] +extern "C" fn d() {} + +// CHECK: define void @e() +#[no_mangle] +#[inline(always)] +pub extern "C" fn e() {} + +// CHECK: define void @f() +#[export_name = "f"] +#[inline(always)] +pub extern "C" fn f() {} + +// CHECK: define void @g() +#[no_mangle] +#[inline(always)] +extern "C" fn g() {} + +// CHECK: define void @h() +#[export_name = "h"] +#[inline(always)] +extern "C" fn h() {} diff --git a/src/test/codegen/cfguard_checks.rs b/src/test/codegen/cfguard_checks.rs index 40a7353eac045..96f9158f9d394 100644 --- a/src/test/codegen/cfguard_checks.rs +++ b/src/test/codegen/cfguard_checks.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z control_flow_guard=checks +// compile-flags: -Z control-flow-guard=checks #![crate_type = "lib"] diff --git a/src/test/codegen/cfguard_disabled.rs b/src/test/codegen/cfguard_disabled.rs index d1747931e15c8..1325ffc0f2595 100644 --- a/src/test/codegen/cfguard_disabled.rs +++ b/src/test/codegen/cfguard_disabled.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z control_flow_guard=disabled +// compile-flags: -Z control-flow-guard=no #![crate_type = "lib"] diff --git a/src/test/codegen/cfguard_nochecks.rs b/src/test/codegen/cfguard_nochecks.rs index c5d7afbae257b..ae1de4c4d26d5 100644 --- a/src/test/codegen/cfguard_nochecks.rs +++ b/src/test/codegen/cfguard_nochecks.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z control_flow_guard=nochecks +// compile-flags: -Z control-flow-guard=nochecks #![crate_type = "lib"] 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/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/export-no-mangle.rs b/src/test/codegen/export-no-mangle.rs index 78d41e4be0ae9..59e97601c838d 100644 --- a/src/test/codegen/export-no-mangle.rs +++ b/src/test/codegen/export-no-mangle.rs @@ -11,11 +11,21 @@ mod private { #[export_name = "BAR"] static BAR: u32 = 3; - // CHECK: void @foo() + // CHECK: void @a() #[no_mangle] - pub extern fn foo() {} + pub extern fn a() {} - // CHECK: void @bar() - #[export_name = "bar"] - extern fn bar() {} + // CHECK: void @b() + #[export_name = "b"] + extern fn b() {} + + // CHECK: void @c() + #[export_name = "c"] + #[inline] + extern fn c() {} + + // CHECK: void @d() + #[export_name = "d"] + #[inline(always)] + extern fn d() {} } diff --git a/src/test/codegen/external-no-mangle-fns.rs b/src/test/codegen/external-no-mangle-fns.rs index 902882144996f..41820b057f1ef 100644 --- a/src/test/codegen/external-no-mangle-fns.rs +++ b/src/test/codegen/external-no-mangle-fns.rs @@ -53,3 +53,23 @@ fn x() { core::ptr::read_volatile(&42); } } + +// CHECK: define void @i() +#[no_mangle] +#[inline] +fn i() {} + +// CHECK: define void @j() +#[no_mangle] +#[inline] +pub fn j() {} + +// CHECK: define void @k() +#[no_mangle] +#[inline(always)] +fn k() {} + +// CHECK: define void @l() +#[no_mangle] +#[inline(always)] +pub fn l() {} diff --git a/src/test/codegen/fastcall-inreg.rs b/src/test/codegen/fastcall-inreg.rs index f67487c83ba23..adbeae454494a 100644 --- a/src/test/codegen/fastcall-inreg.rs +++ b/src/test/codegen/fastcall-inreg.rs @@ -17,6 +17,7 @@ // ignore-powerpc64le // ignore-powerpc // ignore-r600 +// ignore-riscv64 // ignore-amdgcn // ignore-sparc // ignore-sparc64 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/integer-overflow.rs b/src/test/codegen/integer-overflow.rs new file mode 100644 index 0000000000000..183de56db9685 --- /dev/null +++ b/src/test/codegen/integer-overflow.rs @@ -0,0 +1,26 @@ +// no-system-llvm +// compile-flags: -O -C overflow-checks=on + +#![crate_type = "lib"] + + +pub struct S1<'a> { + data: &'a [u8], + position: usize, +} + +// CHECK-LABEL: @slice_no_index_order +#[no_mangle] +pub fn slice_no_index_order<'a>(s: &'a mut S1, n: usize) -> &'a [u8] { + // CHECK-NOT: slice_index_order_fail + let d = &s.data[s.position..s.position+n]; + s.position += n; + return d; +} + +// CHECK-LABEL: @test_check +#[no_mangle] +pub fn test_check<'a>(s: &'a mut S1, x: usize, y: usize) -> &'a [u8] { + // CHECK: slice_index_order_fail + &s.data[x..y] +} diff --git a/src/test/codegen/issue-69101-bounds-check.rs b/src/test/codegen/issue-69101-bounds-check.rs new file mode 100644 index 0000000000000..8ade583b57127 --- /dev/null +++ b/src/test/codegen/issue-69101-bounds-check.rs @@ -0,0 +1,44 @@ +// no-system-llvm +// compile-flags: -O +// ignore-debug: the debug assertions get in the way +#![crate_type = "lib"] + +// Make sure no bounds checks are emitted in the loop when upfront slicing +// ensures that the slices are big enough. +// In particular, bounds checks were not always optimized out if the upfront +// check was for a greater len than the loop requires. +// (i.e. `already_sliced_no_bounds_check` was not always optimized even when +// `already_sliced_no_bounds_check_exact` was) +// CHECK-LABEL: @already_sliced_no_bounds_check +#[no_mangle] +pub fn already_sliced_no_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { + // CHECK: slice_index_len_fail + // CHECK-NOT: panic_bounds_check + let _ = (&a[..2048], &b[..2048], &mut c[..2048]); + for i in 0..1024 { + c[i] = a[i] ^ b[i]; + } +} + +// CHECK-LABEL: @already_sliced_no_bounds_check_exact +#[no_mangle] +pub fn already_sliced_no_bounds_check_exact(a: &[u8], b: &[u8], c: &mut [u8]) { + // CHECK: slice_index_len_fail + // CHECK-NOT: panic_bounds_check + let _ = (&a[..1024], &b[..1024], &mut c[..1024]); + for i in 0..1024 { + c[i] = a[i] ^ b[i]; + } +} + +// Make sure we're checking for the right thing: there can be a panic if the slice is too small. +// CHECK-LABEL: @already_sliced_bounds_check +#[no_mangle] +pub fn already_sliced_bounds_check(a: &[u8], b: &[u8], c: &mut [u8]) { + // CHECK: slice_index_len_fail + // CHECK: panic_bounds_check + let _ = (&a[..1023], &b[..2048], &mut c[..2048]); + for i in 0..1024 { + c[i] = a[i] ^ b[i]; + } +} 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/repeat-trusted-len.rs b/src/test/codegen/repeat-trusted-len.rs index 8fbe712065bde..8e08b78ad1eee 100644 --- a/src/test/codegen/repeat-trusted-len.rs +++ b/src/test/codegen/repeat-trusted-len.rs @@ -5,14 +5,9 @@ use std::iter; -// CHECK: @helper([[USIZE:i[0-9]+]] %_1) -#[no_mangle] -pub fn helper(_: usize) { -} - // CHECK-LABEL: @repeat_take_collect #[no_mangle] pub fn repeat_take_collect() -> Vec { -// CHECK: call void @llvm.memset.p0i8.[[USIZE]](i8* {{(nonnull )?}}align 1{{.*}} %{{[0-9]+}}, i8 42, [[USIZE]] 100000, i1 false) +// CHECK: call void @llvm.memset.p0i8.i{{[0-9]+}}(i8* {{(nonnull )?}}align 1{{.*}} %{{[0-9]+}}, i8 42, i{{[0-9]+}} 100000, i1 false) iter::repeat(42).take(100000).collect() } diff --git a/src/test/codegen/repr-transparent-aggregates-1.rs b/src/test/codegen/repr-transparent-aggregates-1.rs index 018a7ba4756a9..c23c57c8c5900 100644 --- a/src/test/codegen/repr-transparent-aggregates-1.rs +++ b/src/test/codegen/repr-transparent-aggregates-1.rs @@ -7,6 +7,7 @@ // ignore-mips64 // ignore-powerpc // ignore-powerpc64 +// ignore-riscv64 see codegen/riscv-abi // ignore-windows // See repr-transparent.rs diff --git a/src/test/codegen/repr-transparent-aggregates-2.rs b/src/test/codegen/repr-transparent-aggregates-2.rs index 5669858672074..07e5af11f3577 100644 --- a/src/test/codegen/repr-transparent-aggregates-2.rs +++ b/src/test/codegen/repr-transparent-aggregates-2.rs @@ -6,6 +6,7 @@ // ignore-powerpc // ignore-powerpc64 // ignore-powerpc64le +// ignore-riscv64 see codegen/riscv-abi // ignore-s390x // ignore-sparc // ignore-sparc64 diff --git a/src/test/codegen/repr-transparent.rs b/src/test/codegen/repr-transparent.rs index 49fd015624ace..7647e0198769c 100644 --- a/src/test/codegen/repr-transparent.rs +++ b/src/test/codegen/repr-transparent.rs @@ -1,5 +1,8 @@ // compile-flags: -C no-prepopulate-passes +// ignore-riscv64 riscv64 has an i128 type used with test_Vector +// see codegen/riscv-abi for riscv functiona call tests + #![crate_type="lib"] #![feature(repr_simd, transparent_unions)] diff --git a/src/test/codegen/riscv-abi/call-llvm-intrinsics.rs b/src/test/codegen/riscv-abi/call-llvm-intrinsics.rs new file mode 100644 index 0000000000000..f100a23a31897 --- /dev/null +++ b/src/test/codegen/riscv-abi/call-llvm-intrinsics.rs @@ -0,0 +1,30 @@ +// compile-flags: -C no-prepopulate-passes + +// only-riscv64 + +#![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: store float 4.000000e+00, float* %{{.}}, align 4 + // CHECK: call float @llvm.sqrt.f32(float %{{.}} + sqrt(4.0); + } +} diff --git a/src/test/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs b/src/test/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs index f0f052fe5c557..180ba07764b61 100644 --- a/src/test/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs +++ b/src/test/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs @@ -39,12 +39,12 @@ pub extern "C" fn f_scalar_4(x: i64) -> i64 { x } -// CHECK: define float @f_fp_scalar_1(float) +// CHECK: define float @f_fp_scalar_1(float %0) #[no_mangle] pub extern "C" fn f_fp_scalar_1(x: f32) -> f32 { x } -// CHECK: define double @f_fp_scalar_2(double) +// CHECK: define double @f_fp_scalar_2(double %0) #[no_mangle] pub extern "C" fn f_fp_scalar_2(x: f64) -> f64 { x @@ -67,7 +67,7 @@ pub struct Tiny { d: u16, } -// CHECK: define void @f_agg_tiny(i64) +// CHECK: define void @f_agg_tiny(i64 %0) #[no_mangle] pub extern "C" fn f_agg_tiny(mut e: Tiny) { e.a += e.b; @@ -86,7 +86,7 @@ pub struct Small { b: *mut i64, } -// CHECK: define void @f_agg_small([2 x i64]) +// CHECK: define void @f_agg_small([2 x i64] %0) #[no_mangle] pub extern "C" fn f_agg_small(mut x: Small) { x.a += unsafe { *x.b }; @@ -104,7 +104,7 @@ pub struct SmallAligned { a: i128, } -// CHECK: define void @f_agg_small_aligned(i128) +// CHECK: define void @f_agg_small_aligned(i128 %0) #[no_mangle] pub extern "C" fn f_agg_small_aligned(mut x: SmallAligned) { x.a += x.a; @@ -130,7 +130,7 @@ pub extern "C" fn f_agg_large_ret(i: i32, j: i8) -> Large { Large { a: 1, b: 2, c: 3, d: 4 } } -// CHECK: define void @f_scalar_stack_1(i64, [2 x i64], i128, %Large* {{.*}}%d, i8 zeroext %e, i8 signext %f, i8 %g, i8 %h) +// CHECK: define void @f_scalar_stack_1(i64 %0, [2 x i64] %1, i128 %2, %Large* {{.*}}%d, i8 zeroext %e, i8 signext %f, i8 %g, i8 %h) #[no_mangle] pub extern "C" fn f_scalar_stack_1( a: Tiny, @@ -144,7 +144,7 @@ pub extern "C" fn f_scalar_stack_1( ) { } -// CHECK: define void @f_scalar_stack_2(%Large* {{.*}}sret{{.*}}, i64 %a, i128, i128, i64 %d, i8 zeroext %e, i8 %f, i8 %g) +// CHECK: define void @f_scalar_stack_2(%Large* {{.*}}sret{{.*}} %0, i64 %a, i128 %1, i128 %2, i64 %d, i8 zeroext %e, i8 %f, i8 %g) #[no_mangle] pub extern "C" fn f_scalar_stack_2( a: u64, diff --git a/src/test/codegen/riscv-abi/riscv64-lp64d-abi.rs b/src/test/codegen/riscv-abi/riscv64-lp64d-abi.rs index 66a3b9e4952a9..0b6e1878d4d3e 100644 --- a/src/test/codegen/riscv-abi/riscv64-lp64d-abi.rs +++ b/src/test/codegen/riscv-abi/riscv64-lp64d-abi.rs @@ -4,7 +4,7 @@ // only-linux #![crate_type = "lib"] -// CHECK: define void @f_fpr_tracking(double, double, double, double, double, double, double, double, i8 zeroext %i) +// CHECK: define void @f_fpr_tracking(double %0, double %1, double %2, double %3, double %4, double %5, double %6, double %7, i8 zeroext %i) #[no_mangle] pub extern "C" fn f_fpr_tracking( a: f64, @@ -36,7 +36,7 @@ pub struct DoubleFloat { g: f32, } -// CHECK: define void @f_double_s_arg(double) +// CHECK: define void @f_double_s_arg(double %0) #[no_mangle] pub extern "C" fn f_double_s_arg(a: Double) {} @@ -46,7 +46,7 @@ pub extern "C" fn f_ret_double_s() -> Double { Double { f: 1. } } -// CHECK: define void @f_double_double_s_arg({ double, double }) +// CHECK: define void @f_double_double_s_arg({ double, double } %0) #[no_mangle] pub extern "C" fn f_double_double_s_arg(a: DoubleDouble) {} @@ -56,7 +56,7 @@ pub extern "C" fn f_ret_double_double_s() -> DoubleDouble { DoubleDouble { f: 1., g: 2. } } -// CHECK: define void @f_double_float_s_arg({ double, float }) +// CHECK: define void @f_double_float_s_arg({ double, float } %0) #[no_mangle] pub extern "C" fn f_double_float_s_arg(a: DoubleFloat) {} @@ -66,7 +66,7 @@ pub extern "C" fn f_ret_double_float_s() -> DoubleFloat { DoubleFloat { f: 1., g: 2. } } -// CHECK: define void @f_double_double_s_arg_insufficient_fprs(double, double, double, double, double, double, double, [2 x i64]) +// CHECK: define void @f_double_double_s_arg_insufficient_fprs(double %0, double %1, double %2, double %3, double %4, double %5, double %6, [2 x i64] %7) #[no_mangle] pub extern "C" fn f_double_double_s_arg_insufficient_fprs( a: f64, @@ -104,7 +104,7 @@ pub struct DoubleInt64 { i: i64, } -// CHECK: define void @f_double_int8_s_arg({ double, i8 }) +// CHECK: define void @f_double_int8_s_arg({ double, i8 } %0) #[no_mangle] pub extern "C" fn f_double_int8_s_arg(a: DoubleInt8) {} @@ -114,7 +114,7 @@ pub extern "C" fn f_ret_double_int8_s() -> DoubleInt8 { DoubleInt8 { f: 1., i: 2 } } -// CHECK: define void @f_double_int32_s_arg({ double, i32 }) +// CHECK: define void @f_double_int32_s_arg({ double, i32 } %0) #[no_mangle] pub extern "C" fn f_double_int32_s_arg(a: DoubleInt32) {} @@ -124,7 +124,7 @@ pub extern "C" fn f_ret_double_int32_s() -> DoubleInt32 { DoubleInt32 { f: 1., i: 2 } } -// CHECK: define void @f_double_uint8_s_arg({ double, i8 }) +// CHECK: define void @f_double_uint8_s_arg({ double, i8 } %0) #[no_mangle] pub extern "C" fn f_double_uint8_s_arg(a: DoubleUInt8) {} @@ -134,7 +134,7 @@ pub extern "C" fn f_ret_double_uint8_s() -> DoubleUInt8 { DoubleUInt8 { f: 1., i: 2 } } -// CHECK: define void @f_double_int64_s_arg({ double, i64 }) +// CHECK: define void @f_double_int64_s_arg({ double, i64 } %0) #[no_mangle] pub extern "C" fn f_double_int64_s_arg(a: DoubleInt64) {} @@ -144,7 +144,7 @@ pub extern "C" fn f_ret_double_int64_s() -> DoubleInt64 { DoubleInt64 { f: 1., i: 2 } } -// CHECK: define void @f_double_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, [2 x i64]) +// CHECK: define void @f_double_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, [2 x i64] %0) #[no_mangle] pub extern "C" fn f_double_int8_s_arg_insufficient_gprs( a: i32, @@ -159,7 +159,7 @@ pub extern "C" fn f_double_int8_s_arg_insufficient_gprs( ) { } -// CHECK: define void @f_struct_double_int8_insufficient_fprs(float, double, double, double, double, double, double, double, [2 x i64]) +// CHECK: define void @f_struct_double_int8_insufficient_fprs(float %0, double %1, double %2, double %3, double %4, double %5, double %6, double %7, [2 x i64] %8) #[no_mangle] pub extern "C" fn f_struct_double_int8_insufficient_fprs( a: f32, @@ -179,7 +179,7 @@ pub struct DoubleArr1 { a: [f64; 1], } -// CHECK: define void @f_doublearr1_s_arg(double) +// CHECK: define void @f_doublearr1_s_arg(double %0) #[no_mangle] pub extern "C" fn f_doublearr1_s_arg(a: DoubleArr1) {} @@ -194,7 +194,7 @@ pub struct DoubleArr2 { a: [f64; 2], } -// CHECK: define void @f_doublearr2_s_arg({ double, double }) +// CHECK: define void @f_doublearr2_s_arg({ double, double } %0) #[no_mangle] pub extern "C" fn f_doublearr2_s_arg(a: DoubleArr2) {} @@ -214,7 +214,7 @@ pub struct DoubleArr2Tricky1 { g: [Tricky1; 2], } -// CHECK: define void @f_doublearr2_tricky1_s_arg({ double, double }) +// CHECK: define void @f_doublearr2_tricky1_s_arg({ double, double } %0) #[no_mangle] pub extern "C" fn f_doublearr2_tricky1_s_arg(a: DoubleArr2Tricky1) {} @@ -233,7 +233,7 @@ pub struct DoubleArr2Tricky2 { g: [Tricky1; 2], } -// CHECK: define void @f_doublearr2_tricky2_s_arg({ double, double }) +// CHECK: define void @f_doublearr2_tricky2_s_arg({ double, double } %0) #[no_mangle] pub extern "C" fn f_doublearr2_tricky2_s_arg(a: DoubleArr2Tricky2) {} @@ -267,7 +267,7 @@ pub struct CharCharDouble { c: f64, } -// CHECK: define void @f_char_char_double_s_arg([2 x i64]) +// CHECK: define void @f_char_char_double_s_arg([2 x i64] %0) #[no_mangle] pub extern "C" fn f_char_char_double_s_arg(a: CharCharDouble) {} @@ -282,7 +282,7 @@ pub union DoubleU { a: f64, } -// CHECK: define void @f_double_u_arg(i64) +// CHECK: define void @f_double_u_arg(i64 %0) #[no_mangle] pub extern "C" fn f_double_u_arg(a: DoubleU) {} diff --git a/src/test/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs b/src/test/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs index d843331f425de..1cea6e3db2a84 100644 --- a/src/test/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs +++ b/src/test/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs @@ -4,7 +4,7 @@ // only-linux #![crate_type = "lib"] -// CHECK: define void @f_fpr_tracking(float, float, float, float, float, float, float, float, i8 zeroext %i) +// CHECK: define void @f_fpr_tracking(float %0, float %1, float %2, float %3, float %4, float %5, float %6, float %7, i8 zeroext %i) #[no_mangle] pub extern "C" fn f_fpr_tracking( a: f32, @@ -30,7 +30,7 @@ pub struct FloatFloat { g: f32, } -// CHECK: define void @f_float_s_arg(float) +// CHECK: define void @f_float_s_arg(float %0) #[no_mangle] pub extern "C" fn f_float_s_arg(a: Float) {} @@ -40,7 +40,7 @@ pub extern "C" fn f_ret_float_s() -> Float { Float { f: 1. } } -// CHECK: define void @f_float_float_s_arg({ float, float }) +// CHECK: define void @f_float_float_s_arg({ float, float } %0) #[no_mangle] pub extern "C" fn f_float_float_s_arg(a: FloatFloat) {} @@ -50,7 +50,7 @@ pub extern "C" fn f_ret_float_float_s() -> FloatFloat { FloatFloat { f: 1., g: 2. } } -// CHECK: define void @f_float_float_s_arg_insufficient_fprs(float, float, float, float, float, float, float, i64) +// CHECK: define void @f_float_float_s_arg_insufficient_fprs(float %0, float %1, float %2, float %3, float %4, float %5, float %6, i64 %7) #[no_mangle] pub extern "C" fn f_float_float_s_arg_insufficient_fprs( a: f32, @@ -88,7 +88,7 @@ pub struct FloatInt64 { i: i64, } -// CHECK: define void @f_float_int8_s_arg({ float, i8 }) +// CHECK: define void @f_float_int8_s_arg({ float, i8 } %0) #[no_mangle] pub extern "C" fn f_float_int8_s_arg(a: FloatInt8) {} @@ -98,7 +98,7 @@ pub extern "C" fn f_ret_float_int8_s() -> FloatInt8 { FloatInt8 { f: 1., i: 2 } } -// CHECK: define void @f_float_int32_s_arg({ float, i32 }) +// CHECK: define void @f_float_int32_s_arg({ float, i32 } %0) #[no_mangle] pub extern "C" fn f_float_int32_s_arg(a: FloatInt32) {} @@ -108,7 +108,7 @@ pub extern "C" fn f_ret_float_int32_s() -> FloatInt32 { FloatInt32 { f: 1., i: 2 } } -// CHECK: define void @f_float_uint8_s_arg({ float, i8 }) +// CHECK: define void @f_float_uint8_s_arg({ float, i8 } %0) #[no_mangle] pub extern "C" fn f_float_uint8_s_arg(a: FloatUInt8) {} @@ -118,7 +118,7 @@ pub extern "C" fn f_ret_float_uint8_s() -> FloatUInt8 { FloatUInt8 { f: 1., i: 2 } } -// CHECK: define void @f_float_int64_s_arg({ float, i64 }) +// CHECK: define void @f_float_int64_s_arg({ float, i64 } %0) #[no_mangle] pub extern "C" fn f_float_int64_s_arg(a: FloatInt64) {} @@ -128,7 +128,7 @@ pub extern "C" fn f_ret_float_int64_s() -> FloatInt64 { FloatInt64 { f: 1., i: 2 } } -// CHECK: define void @f_float_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, i64) +// CHECK: define void @f_float_int8_s_arg_insufficient_gprs(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d, i32 signext %e, i32 signext %f, i32 signext %g, i32 signext %h, i64 %0) #[no_mangle] pub extern "C" fn f_float_int8_s_arg_insufficient_gprs( a: i32, @@ -143,7 +143,7 @@ pub extern "C" fn f_float_int8_s_arg_insufficient_gprs( ) { } -// CHECK: define void @f_struct_float_int8_insufficient_fprs(float, float, float, float, float, float, float, float, i64) +// CHECK: define void @f_struct_float_int8_insufficient_fprs(float %0, float %1, float %2, float %3, float %4, float %5, float %6, float %7, i64 %8) #[no_mangle] pub extern "C" fn f_struct_float_int8_insufficient_fprs( a: f32, @@ -163,7 +163,7 @@ pub struct FloatArr1 { a: [f32; 1], } -// CHECK: define void @f_floatarr1_s_arg(float) +// CHECK: define void @f_floatarr1_s_arg(float %0) #[no_mangle] pub extern "C" fn f_floatarr1_s_arg(a: FloatArr1) {} @@ -178,7 +178,7 @@ pub struct FloatArr2 { a: [f32; 2], } -// CHECK: define void @f_floatarr2_s_arg({ float, float }) +// CHECK: define void @f_floatarr2_s_arg({ float, float } %0) #[no_mangle] pub extern "C" fn f_floatarr2_s_arg(a: FloatArr2) {} @@ -198,7 +198,7 @@ pub struct FloatArr2Tricky1 { g: [Tricky1; 2], } -// CHECK: define void @f_floatarr2_tricky1_s_arg({ float, float }) +// CHECK: define void @f_floatarr2_tricky1_s_arg({ float, float } %0) #[no_mangle] pub extern "C" fn f_floatarr2_tricky1_s_arg(a: FloatArr2Tricky1) {} @@ -217,7 +217,7 @@ pub struct FloatArr2Tricky2 { g: [Tricky1; 2], } -// CHECK: define void @f_floatarr2_tricky2_s_arg({ float, float }) +// CHECK: define void @f_floatarr2_tricky2_s_arg({ float, float } %0) #[no_mangle] pub extern "C" fn f_floatarr2_tricky2_s_arg(a: FloatArr2Tricky2) {} @@ -234,7 +234,7 @@ pub struct IntFloatInt { c: i32, } -// CHECK: define void @f_int_float_int_s_arg([2 x i64]) +// CHECK: define void @f_int_float_int_s_arg([2 x i64] %0) #[no_mangle] pub extern "C" fn f_int_float_int_s_arg(a: IntFloatInt) {} @@ -251,7 +251,7 @@ pub struct CharCharFloat { c: f32, } -// CHECK: define void @f_char_char_float_s_arg(i64) +// CHECK: define void @f_char_char_float_s_arg(i64 %0) #[no_mangle] pub extern "C" fn f_char_char_float_s_arg(a: CharCharFloat) {} @@ -266,7 +266,7 @@ pub union FloatU { a: f32, } -// CHECK: define void @f_float_u_arg(i64) +// CHECK: define void @f_float_u_arg(i64 %0) #[no_mangle] pub extern "C" fn f_float_u_arg(a: FloatU) {} diff --git a/src/test/codegen/sanitizer-memory-track-orgins.rs b/src/test/codegen/sanitizer-memory-track-orgins.rs index 8ea41c5d44bb1..4bd50508d1520 100644 --- a/src/test/codegen/sanitizer-memory-track-orgins.rs +++ b/src/test/codegen/sanitizer-memory-track-orgins.rs @@ -1,9 +1,7 @@ // Verifies that MemorySanitizer track-origins level can be controlled // with -Zsanitizer-memory-track-origins option. // -// needs-sanitizer-support -// only-linux -// only-x86_64 +// needs-sanitizer-memory // revisions:MSAN-0 MSAN-1 MSAN-2 MSAN-1-LTO MSAN-2-LTO // //[MSAN-0] compile-flags: -Zsanitizer=memory diff --git a/src/test/codegen/sanitizer-no-sanitize-inlining.rs b/src/test/codegen/sanitizer-no-sanitize-inlining.rs index d96e76618d325..be0547afa4cd5 100644 --- a/src/test/codegen/sanitizer-no-sanitize-inlining.rs +++ b/src/test/codegen/sanitizer-no-sanitize-inlining.rs @@ -1,11 +1,9 @@ // Verifies that no_sanitize attribute prevents inlining when // given sanitizer is enabled, but has no effect on inlining otherwise. // -// needs-sanitizer-support -// only-x86_64 -// +// needs-sanitizer-address +// needs-sanitizer-leak // revisions: ASAN LSAN -// //[ASAN] compile-flags: -Zsanitizer=address -C opt-level=3 -Z mir-opt-level=3 //[LSAN] compile-flags: -Zsanitizer=leak -C opt-level=3 -Z mir-opt-level=3 @@ -13,7 +11,7 @@ #![feature(no_sanitize)] // ASAN-LABEL: define void @test -// ASAN: tail call fastcc void @random_inline +// ASAN: call {{.*}} @random_inline // ASAN: } // // LSAN-LABEL: define void @test diff --git a/src/test/codegen/sanitizer-no-sanitize.rs b/src/test/codegen/sanitizer-no-sanitize.rs index dfceb28c8dd10..1b2b18822e63e 100644 --- a/src/test/codegen/sanitizer-no-sanitize.rs +++ b/src/test/codegen/sanitizer-no-sanitize.rs @@ -1,7 +1,7 @@ // Verifies that no_sanitze attribute can be used to // selectively disable sanitizer instrumentation. // -// needs-sanitizer-support +// needs-sanitizer-address // compile-flags: -Zsanitizer=address #![crate_type="lib"] diff --git a/src/test/codegen/sanitizer-recover.rs b/src/test/codegen/sanitizer-recover.rs index 05b4ab5653cc8..719f219ce4ef1 100644 --- a/src/test/codegen/sanitizer-recover.rs +++ b/src/test/codegen/sanitizer-recover.rs @@ -1,9 +1,8 @@ // Verifies that AddressSanitizer and MemorySanitizer // recovery mode can be enabled with -Zsanitizer-recover. // -// needs-sanitizer-support -// only-linux -// only-x86_64 +// needs-sanitizer-address +// needs-sanitizer-memory // revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER MSAN-RECOVER-LTO // no-prefer-dynamic // 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/stack-probes.rs b/src/test/codegen/stack-probes.rs index b8ebf338cbf97..3e3222d4735ad 100644 --- a/src/test/codegen/stack-probes.rs +++ b/src/test/codegen/stack-probes.rs @@ -5,6 +5,7 @@ // ignore-powerpc // ignore-powerpc64 // ignore-powerpc64le +// ignore-riscv64 // ignore-s390x // ignore-sparc // ignore-sparc64 diff --git a/src/test/codegen/staticlib-external-inline-fns.rs b/src/test/codegen/staticlib-external-inline-fns.rs new file mode 100644 index 0000000000000..8876ab7376afe --- /dev/null +++ b/src/test/codegen/staticlib-external-inline-fns.rs @@ -0,0 +1,43 @@ +// compile-flags: -C no-prepopulate-passes + +#![crate_type = "staticlib"] + +// CHECK: define void @a() +#[no_mangle] +#[inline] +pub extern "C" fn a() {} + +// CHECK: define void @b() +#[export_name = "b"] +#[inline] +pub extern "C" fn b() {} + +// CHECK: define void @c() +#[no_mangle] +#[inline] +extern "C" fn c() {} + +// CHECK: define void @d() +#[export_name = "d"] +#[inline] +extern "C" fn d() {} + +// CHECK: define void @e() +#[no_mangle] +#[inline(always)] +pub extern "C" fn e() {} + +// CHECK: define void @f() +#[export_name = "f"] +#[inline(always)] +pub extern "C" fn f() {} + +// CHECK: define void @g() +#[no_mangle] +#[inline(always)] +extern "C" fn g() {} + +// CHECK: define void @h() +#[export_name = "h"] +#[inline(always)] +extern "C" fn h() {} 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/codegen/vec-clear.rs b/src/test/codegen/vec-clear.rs index b9ffce8b0cb3d..15bfe421e9d35 100644 --- a/src/test/codegen/vec-clear.rs +++ b/src/test/codegen/vec-clear.rs @@ -1,4 +1,3 @@ -// ignore-debug: the debug assertions get in the way // compile-flags: -O #![crate_type = "lib"] diff --git a/src/test/codegen/vec-optimizes-away.rs b/src/test/codegen/vec-optimizes-away.rs index ebede0908c6c4..9143fad234087 100644 --- a/src/test/codegen/vec-optimizes-away.rs +++ b/src/test/codegen/vec-optimizes-away.rs @@ -1,4 +1,3 @@ -// // ignore-debug: the debug assertions get in the way // no-system-llvm // compile-flags: -O diff --git a/src/test/codegen/x86_mmx.rs b/src/test/codegen/x86_mmx.rs index a08ba3617403f..9a58ef1c37a80 100644 --- a/src/test/codegen/x86_mmx.rs +++ b/src/test/codegen/x86_mmx.rs @@ -6,6 +6,7 @@ // ignore-powerpc // ignore-powerpc64 // ignore-powerpc64le +// ignore-riscv64 // ignore-sparc // ignore-sparc64 // ignore-s390x 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 597fbbf00d53c..00aca1d14baa7 100644 --- a/src/test/compile-fail/issue-52443.rs +++ b/src/test/compile-fail/issue-52443.rs @@ -7,10 +7,9 @@ fn main() { //~^ ERROR `while` is not allowed in a `const` //~| WARN denote infinite loops with [(); { for _ in 0usize.. {}; 0}]; - //~^ ERROR calls in constants are limited to constant functions + //~^ ERROR `for` is not allowed in a `const` + //~| ERROR calls in constants are limited to constant functions + //~| ERROR mutable references are not allowed in constants //~| ERROR calls in constants are limited to constant functions - //~| ERROR `for` is not allowed in a `const` - //~| ERROR references in constants may only refer to immutable values //~| ERROR evaluation of constant value failed - //~| ERROR constant contains unimplemented expression type } diff --git a/src/test/compile-fail/specialization/issue-50452.rs b/src/test/compile-fail/specialization/issue-50452.rs index d9e5280c7e11d..958f0eb266801 100644 --- a/src/test/compile-fail/specialization/issue-50452.rs +++ b/src/test/compile-fail/specialization/issue-50452.rs @@ -1,6 +1,6 @@ // compile-fail - #![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete pub trait Foo { fn foo(); 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/associated-types.rs b/src/test/debuginfo/associated-types.rs index 8d1170fd97402..0a0ce3c671f07 100644 --- a/src/test/debuginfo/associated-types.rs +++ b/src/test/debuginfo/associated-types.rs @@ -43,8 +43,8 @@ // lldb-command:run // lldb-command:print arg -// lldbg-check:[...]$0 = Struct { b: -1, b1: 0 } -// lldbr-check:(associated_types::Struct) arg = Struct { b: -1, b1: 0 } +// lldbg-check:[...]$0 = { b = -1, b1 = 0 } +// lldbr-check:(associated_types::Struct) arg = { b = -1, b1 = 0 } // lldb-command:continue // lldb-command:print inferred 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/borrowed-struct.rs b/src/test/debuginfo/borrowed-struct.rs index 3a164a41101b5..7f97d96b8db93 100644 --- a/src/test/debuginfo/borrowed-struct.rs +++ b/src/test/debuginfo/borrowed-struct.rs @@ -35,8 +35,8 @@ // lldb-command:run // lldb-command:print *stack_val_ref -// lldbg-check:[...]$0 = SomeStruct { x: 10, y: 23.5 } -// lldbr-check:(borrowed_struct::SomeStruct) *stack_val_ref = SomeStruct { x: 10, y: 23.5 } +// lldbg-check:[...]$0 = { x = 10 y = 23.5 } +// lldbr-check:(borrowed_struct::SomeStruct) *stack_val_ref = (x = 10, y = 23.5) // lldb-command:print *stack_val_interior_ref_1 // lldbg-check:[...]$1 = 10 @@ -47,12 +47,12 @@ // lldbr-check:(f64) *stack_val_interior_ref_2 = 23.5 // lldb-command:print *ref_to_unnamed -// lldbg-check:[...]$3 = SomeStruct { x: 11, y: 24.5 } -// lldbr-check:(borrowed_struct::SomeStruct) *ref_to_unnamed = SomeStruct { x: 11, y: 24.5 } +// lldbg-check:[...]$3 = { x = 11 y = 24.5 } +// lldbr-check:(borrowed_struct::SomeStruct) *ref_to_unnamed = (x = 11, y = 24.5) // lldb-command:print *unique_val_ref -// lldbg-check:[...]$4 = SomeStruct { x: 13, y: 26.5 } -// lldbr-check:(borrowed_struct::SomeStruct) *unique_val_ref = SomeStruct { x: 13, y: 26.5 } +// lldbg-check:[...]$4 = { x = 13 y = 26.5 } +// lldbr-check:(borrowed_struct::SomeStruct) *unique_val_ref = (x = 13, y = 26.5) // lldb-command:print *unique_val_interior_ref_1 // lldbg-check:[...]$5 = 13 diff --git a/src/test/debuginfo/borrowed-tuple.rs b/src/test/debuginfo/borrowed-tuple.rs index fc92b629ef369..be4895ef5363e 100644 --- a/src/test/debuginfo/borrowed-tuple.rs +++ b/src/test/debuginfo/borrowed-tuple.rs @@ -24,16 +24,16 @@ // lldb-command:run // lldb-command:print *stack_val_ref -// lldbg-check:[...]$0 = (-14, -19) -// lldbr-check:((i16, f32)) *stack_val_ref = { = -14 = -19 } +// lldbg-check:[...]$0 = { 0 = -14 1 = -19 } +// lldbr-check:((i16, f32)) *stack_val_ref = { 0 = -14 1 = -19 } // lldb-command:print *ref_to_unnamed -// lldbg-check:[...]$1 = (-15, -20) -// lldbr-check:((i16, f32)) *ref_to_unnamed = { = -15 = -20 } +// lldbg-check:[...]$1 = { 0 = -15 1 = -20 } +// lldbr-check:((i16, f32)) *ref_to_unnamed = { 0 = -15 1 = -20 } // lldb-command:print *unique_val_ref -// lldbg-check:[...]$2 = (-17, -22) -// lldbr-check:((i16, f32)) *unique_val_ref = { = -17 = -22 } +// lldbg-check:[...]$2 = { 0 = -17 1 = -22 } +// lldbr-check:((i16, f32)) *unique_val_ref = { 0 = -17 1 = -22 } #![allow(unused_variables)] diff --git a/src/test/debuginfo/box.rs b/src/test/debuginfo/box.rs index a539d78d3c997..e443b67ebfb31 100644 --- a/src/test/debuginfo/box.rs +++ b/src/test/debuginfo/box.rs @@ -20,8 +20,8 @@ // lldbg-check:[...]$0 = 1 // lldbr-check:(i32) *a = 1 // lldb-command:print *b -// lldbg-check:[...]$1 = (2, 3.5) -// lldbr-check:((i32, f64)) *b = { = 2 = 3.5 } +// lldbg-check:[...]$1 = { 0 = 2 1 = 3.5 } +// lldbr-check:((i32, f64)) *b = { 0 = 2 1 = 3.5 } #![allow(unused_variables)] #![feature(box_syntax)] diff --git a/src/test/debuginfo/boxed-struct.rs b/src/test/debuginfo/boxed-struct.rs index 8709fb681704b..04bdf72890135 100644 --- a/src/test/debuginfo/boxed-struct.rs +++ b/src/test/debuginfo/boxed-struct.rs @@ -22,12 +22,12 @@ // lldb-command:run // lldb-command:print *boxed_with_padding -// lldbg-check:[...]$0 = StructWithSomePadding { x: 99, y: 999, z: 9999, w: 99999 } -// lldbr-check:(boxed_struct::StructWithSomePadding) *boxed_with_padding = StructWithSomePadding { x: 99, y: 999, z: 9999, w: 99999 } +// lldbg-check:[...]$0 = { x = 99 y = 999 z = 9999 w = 99999 } +// lldbr-check:(boxed_struct::StructWithSomePadding) *boxed_with_padding = { x = 99 y = 999 z = 9999 w = 99999 } // lldb-command:print *boxed_with_dtor -// lldbg-check:[...]$1 = StructWithDestructor { x: 77, y: 777, z: 7777, w: 77777 } -// lldbr-check:(boxed_struct::StructWithDestructor) *boxed_with_dtor = StructWithDestructor { x: 77, y: 777, z: 7777, w: 77777 } +// lldbg-check:[...]$1 = { x = 77 y = 777 z = 7777 w = 77777 } +// lldbr-check:(boxed_struct::StructWithDestructor) *boxed_with_dtor = { x = 77 y = 777 z = 7777 w = 77777 } #![allow(unused_variables)] #![feature(box_syntax)] diff --git a/src/test/debuginfo/by-value-self-argument-in-trait-impl.rs b/src/test/debuginfo/by-value-self-argument-in-trait-impl.rs index deba18cc44ab4..e60cfc9242aa1 100644 --- a/src/test/debuginfo/by-value-self-argument-in-trait-impl.rs +++ b/src/test/debuginfo/by-value-self-argument-in-trait-impl.rs @@ -31,13 +31,13 @@ // lldb-command:continue // lldb-command:print self -// lldbg-check:[...]$1 = Struct { x: 2222, y: 3333 } -// lldbr-check:(by_value_self_argument_in_trait_impl::Struct) self = Struct { x: 2222, y: 3333 } +// lldbg-check:[...]$1 = { x = 2222 y = 3333 } +// lldbr-check:(by_value_self_argument_in_trait_impl::Struct) self = { x = 2222 y = 3333 } // lldb-command:continue // lldb-command:print self -// lldbg-check:[...]$2 = (4444.5, 5555, 6666, 7777.5) -// lldbr-check:((f64, isize, isize, f64)) self = { = 4444.5 = 5555 = 6666 = 7777.5 } +// lldbg-check:[...] $2 = { 0 = 4444.5 1 = 5555 2 = 6666 3 = 7777.5 } +// lldbr-check:((f64, isize, isize, f64)) self = { 0 = 4444.5 1 = 5555 2 = 6666 3 = 7777.5 } // lldb-command:continue #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/c-style-enum-in-composite.rs b/src/test/debuginfo/c-style-enum-in-composite.rs index 8a356f62d3c2b..f859fe8d1ce0e 100644 --- a/src/test/debuginfo/c-style-enum-in-composite.rs +++ b/src/test/debuginfo/c-style-enum-in-composite.rs @@ -40,31 +40,32 @@ // lldb-command:run // lldb-command:print tuple_interior_padding -// lldbg-check:[...]$0 = (0, OneHundred) -// lldbr-check:((i16, c_style_enum_in_composite::AnEnum)) tuple_interior_padding = { = 0 = c_style_enum_in_composite::AnEnum::OneHundred } +// lldbg-check:[...]$0 = { 0 = 0 1 = OneHundred } +// lldbr-check:((i16, c_style_enum_in_composite::AnEnum)) tuple_interior_padding = { 0 = 0 1 = OneHundred } // lldb-command:print tuple_padding_at_end -// lldbg-check:[...]$1 = ((1, OneThousand), 2) -// lldbr-check:(((u64, c_style_enum_in_composite::AnEnum), u64)) tuple_padding_at_end = { = { = 1 = c_style_enum_in_composite::AnEnum::OneThousand } = 2 } +// lldbg-check:[...]$1 = { 0 = { 0 = 1 1 = OneThousand } 1 = 2 } +// lldbr-check:(((u64, c_style_enum_in_composite::AnEnum), u64)) tuple_padding_at_end = { 0 = { 0 = 1 1 = OneThousand } 1 = 2 } + // lldb-command:print tuple_different_enums -// lldbg-check:[...]$2 = (OneThousand, MountainView, OneMillion, Vienna) -// lldbr-check:((c_style_enum_in_composite::AnEnum, c_style_enum_in_composite::AnotherEnum, c_style_enum_in_composite::AnEnum, c_style_enum_in_composite::AnotherEnum)) tuple_different_enums = { = c_style_enum_in_composite::AnEnum::OneThousand = c_style_enum_in_composite::AnotherEnum::MountainView = c_style_enum_in_composite::AnEnum::OneMillion = c_style_enum_in_composite::AnotherEnum::Vienna } +// lldbg-check:[...]$2 = { 0 = OneThousand 1 = MountainView 2 = OneMillion 3 = Vienna } +// lldbr-check:((c_style_enum_in_composite::AnEnum, c_style_enum_in_composite::AnotherEnum, c_style_enum_in_composite::AnEnum, c_style_enum_in_composite::AnotherEnum)) tuple_different_enums = { 0 = c_style_enum_in_composite::AnEnum::OneThousand 1 = c_style_enum_in_composite::AnotherEnum::MountainView 2 = c_style_enum_in_composite::AnEnum::OneMillion 3 = c_style_enum_in_composite::AnotherEnum::Vienna } // lldb-command:print padded_struct -// lldbg-check:[...]$3 = PaddedStruct { a: 3, b: OneMillion, c: 4, d: Toronto, e: 5 } -// lldbr-check:(c_style_enum_in_composite::PaddedStruct) padded_struct = PaddedStruct { a: 3, b: c_style_enum_in_composite::AnEnum::OneMillion, c: 4, d: c_style_enum_in_composite::AnotherEnum::Toronto, e: 5 } +// lldbg-check:[...]$3 = { a = 3 b = OneMillion c = 4 d = Toronto e = 5 } +// lldbr-check:(c_style_enum_in_composite::PaddedStruct) padded_struct = { a = 3 b = c_style_enum_in_composite::AnEnum::OneMillion c = 4 d = Toronto e = 5 } // lldb-command:print packed_struct -// lldbg-check:[...]$4 = PackedStruct { a: 6, b: OneHundred, c: 7, d: Vienna, e: 8 } -// lldbr-check:(c_style_enum_in_composite::PackedStruct) packed_struct = PackedStruct { a: 6, b: c_style_enum_in_composite::AnEnum::OneHundred, c: 7, d: c_style_enum_in_composite::AnotherEnum::Vienna, e: 8 } +// lldbg-check:[...]$4 = { a = 6 b = OneHundred c = 7 d = Vienna e = 8 } +// lldbr-check:(c_style_enum_in_composite::PackedStruct) packed_struct = { a = 6 b = c_style_enum_in_composite::AnEnum::OneHundred c = 7 d = Vienna e = 8 } // lldb-command:print non_padded_struct -// lldbg-check:[...]$5 = NonPaddedStruct { a: OneMillion, b: MountainView, c: OneThousand, d: Toronto } -// lldbr-check:(c_style_enum_in_composite::NonPaddedStruct) non_padded_struct = NonPaddedStruct { a: c_style_enum_in_composite::AnEnum::OneMillion, b: c_style_enum_in_composite::AnotherEnum::MountainView, c: c_style_enum_in_composite::AnEnum::OneThousand, d: c_style_enum_in_composite::AnotherEnum::Toronto } +// lldbg-check:[...]$5 = { a = OneMillion b = MountainView c = OneThousand d = Toronto } +// lldbr-check:(c_style_enum_in_composite::NonPaddedStruct) non_padded_struct = { a = c_style_enum_in_composite::AnEnum::OneMillion, b = c_style_enum_in_composite::AnotherEnum::MountainView, c = c_style_enum_in_composite::AnEnum::OneThousand, d = c_style_enum_in_composite::AnotherEnum::Toronto } // lldb-command:print struct_with_drop -// lldbg-check:[...]$6 = (StructWithDrop { a: OneHundred, b: Vienna }, 9) -// lldbr-check:((c_style_enum_in_composite::StructWithDrop, i64)) struct_with_drop = { = StructWithDrop { a: c_style_enum_in_composite::AnEnum::OneHundred, b: c_style_enum_in_composite::AnotherEnum::Vienna } = 9 } +// lldbg-check:[...]$6 = { 0 = { a = OneHundred b = Vienna } 1 = 9 } +// lldbr-check:((c_style_enum_in_composite::StructWithDrop, i64)) struct_with_drop = { 0 = { a = c_style_enum_in_composite::AnEnum::OneHundred b = c_style_enum_in_composite::AnotherEnum::Vienna } 1 = 9 } #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/cross-crate-spans.rs b/src/test/debuginfo/cross-crate-spans.rs index 96ddfd2a39ed0..7c58e1db23f35 100644 --- a/src/test/debuginfo/cross-crate-spans.rs +++ b/src/test/debuginfo/cross-crate-spans.rs @@ -44,8 +44,8 @@ extern crate cross_crate_spans; // lldb-command:run // lldb-command:print result -// lldbg-check:[...]$0 = (17, 17) -// lldbr-check:((u32, u32)) result = { = 17 = 17 } +// lldbg-check:[...]$0 = { 0 = 17 1 = 17 } +// lldbr-check:((u32, u32)) result = { 0 = 17 1 = 17 } // lldb-command:print a_variable // lldbg-check:[...]$1 = 123456789 // lldbr-check:(u32) a_variable = 123456789 @@ -55,8 +55,8 @@ extern crate cross_crate_spans; // lldb-command:continue // lldb-command:print result -// lldbg-check:[...]$3 = (1212, 1212) -// lldbr-check:((i16, i16)) result = { = 1212 = 1212 } +// lldbg-check:[...]$3 = { 0 = 1212 1 = 1212 } +// lldbr-check:((i16, i16)) result = { 0 = 1212 1 = 1212 } // lldb-command:print a_variable // lldbg-check:[...]$4 = 123456789 // lldbr-check:(u32) a_variable = 123456789 diff --git a/src/test/debuginfo/destructured-fn-argument.rs b/src/test/debuginfo/destructured-fn-argument.rs index 64133dc6860ab..a776f51907158 100644 --- a/src/test/debuginfo/destructured-fn-argument.rs +++ b/src/test/debuginfo/destructured-fn-argument.rs @@ -186,16 +186,16 @@ // lldbg-check:[...]$5 = 5 // lldbr-check:(isize) a = 5 // lldb-command:print b -// lldbg-check:[...]$6 = (6, 7) -// lldbr-check:((u32, u32)) b = { = 6 = 7 } +// lldbg-check:[...]$6 = { 0 = 6 1 = 7 } +// lldbr-check:((u32, u32)) b = { 0 = 6 1 = 7 } // lldb-command:continue // lldb-command:print h // lldbg-check:[...]$7 = 8 // lldbr-check:(i16) h = 8 // lldb-command:print i -// lldbg-check:[...]$8 = Struct { a: 9, b: 10 } -// lldbr-check:(destructured_fn_argument::Struct) i = Struct { a: 9, b: 10 } +// lldbg-check:[...]$8 = { a = 9 b = 10 } +// lldbr-check:(destructured_fn_argument::Struct) i = { a = 9 b = 10 } // lldb-command:print j // lldbg-check:[...]$9 = 11 // lldbr-check:(i16) j = 11 @@ -229,8 +229,8 @@ // lldbg-check:[...]$16 = 20 // lldbr-check:(i32) q = 20 // lldb-command:print r -// lldbg-check:[...]$17 = Struct { a: 21, b: 22 } -// lldbr-check:(destructured_fn_argument::Struct) r = Struct { a: 21, b: 22 } +// lldbg-check:[...]$17 = { a = 21 b = 22 } +// lldbr-check:(destructured_fn_argument::Struct) r = { a = 21, b = 22 } // lldb-command:continue // lldb-command:print s @@ -271,13 +271,13 @@ // lldb-command:continue // lldb-command:print aa -// lldbg-check:[...]$29 = (34, 35) -// lldbr-check:((isize, isize)) aa = { = 34 = 35 } +// lldbg-check:[...]$29 = { 0 = 34 1 = 35 } +// lldbr-check:((isize, isize)) aa = { 0 = 34 1 = 35 } // lldb-command:continue // lldb-command:print bb -// lldbg-check:[...]$30 = (36, 37) -// lldbr-check:((isize, isize)) bb = { = 36 = 37 } +// lldbg-check:[...]$30 = { 0 = 36 1 = 37 } +// lldbr-check:((isize, isize)) bb = { 0 = 36 1 = 37 } // lldb-command:continue // lldb-command:print cc @@ -286,21 +286,21 @@ // lldb-command:continue // lldb-command:print dd -// lldbg-check:[...]$32 = (40, 41, 42) -// lldbr-check:((isize, isize, isize)) dd = { = 40 = 41 = 42 } +// lldbg-check:[...]$32 = { 0 = 40 1 = 41 2 = 42 } +// lldbr-check:((isize, isize, isize)) dd = { 0 = 40 1 = 41 2 = 42 } // lldb-command:continue // lldb-command:print *ee -// lldbg-check:[...]$33 = (43, 44, 45) -// lldbr-check:((isize, isize, isize)) *ee = { = 43 = 44 = 45 } +// lldbg-check:[...]$33 = { 0 = 43 1 = 44 2 = 45 } +// lldbr-check:((isize, isize, isize)) *ee = { 0 = 43 1 = 44 2 = 45 } // lldb-command:continue // lldb-command:print *ff // lldbg-check:[...]$34 = 46 // lldbr-check:(isize) *ff = 46 // lldb-command:print gg -// lldbg-check:[...]$35 = (47, 48) -// lldbr-check:((isize, isize)) gg = { = 47 = 48 } +// lldbg-check:[...]$35 = { 0 = 47 1 = 48 } +// lldbr-check:((isize, isize)) gg = { 0 = 47 1 = 48 } // lldb-command:continue // lldb-command:print *hh diff --git a/src/test/debuginfo/destructured-for-loop-variable.rs b/src/test/debuginfo/destructured-for-loop-variable.rs index dbb98322317e9..868f2285f3574 100644 --- a/src/test/debuginfo/destructured-for-loop-variable.rs +++ b/src/test/debuginfo/destructured-for-loop-variable.rs @@ -164,13 +164,13 @@ // lldb-command:continue // lldb-command:print simple_struct_ident -// lldbg-check:[...]$22 = Struct { x: 3537, y: 35437.5, z: true } -// lldbr-check:(destructured_for_loop_variable::Struct) simple_struct_ident = Struct { x: 3537, y: 35437.5, z: true } +// lldbg-check:[...]$22 = { x = 3537 y = 35437.5 z = true } +// lldbr-check:(destructured_for_loop_variable::Struct) simple_struct_ident = { x = 3537 y = 35437.5 z = true } // lldb-command:continue // lldb-command:print simple_tuple_ident -// lldbg-check:[...]$23 = (34903493, 232323) -// lldbr-check:((u32, i64)) simple_tuple_ident = { = 34903493 = 232323 } +// lldbg-check:[...]$23 = { 0 = 34903493 1 = 232323 } +// lldbr-check:((u32, i64)) simple_tuple_ident = { 0 = 34903493 1 = 232323 } // lldb-command:continue #![allow(unused_variables)] diff --git a/src/test/debuginfo/destructured-local.rs b/src/test/debuginfo/destructured-local.rs index 78b6b2764e050..712168b5baa87 100644 --- a/src/test/debuginfo/destructured-local.rs +++ b/src/test/debuginfo/destructured-local.rs @@ -150,15 +150,15 @@ // lldbg-check:[...]$5 = 5 // lldbr-check:(isize) f = 5 // lldb-command:print g -// lldbg-check:[...]$6 = (6, 7) -// lldbr-check:((u32, u32)) g = { = 6 = 7 } +// lldbg-check:[...]$6 = { 0 = 6 1 = 7 } +// lldbr-check:((u32, u32)) g = { 0 = 6 1 = 7 } // lldb-command:print h // lldbg-check:[...]$7 = 8 // lldbr-check:(i16) h = 8 // lldb-command:print i -// lldbg-check:[...]$8 = Struct { a: 9, b: 10 } -// lldbr-check:(destructured_local::Struct) i = Struct { a: 9, b: 10 } +// lldbg-check:[...]$8 = { a = 9 b = 10 } +// lldbr-check:(destructured_local::Struct) i = { a = 9 b = 10 } // lldb-command:print j // lldbg-check:[...]$9 = 11 // lldbr-check:(i16) j = 11 @@ -188,8 +188,8 @@ // lldbg-check:[...]$16 = 20 // lldbr-check:(i32) q = 20 // lldb-command:print r -// lldbg-check:[...]$17 = Struct { a: 21, b: 22 } -// lldbr-check:(destructured_local::Struct) r = Struct { a: 21, b: 22 } +// lldbg-check:[...]$17 = { a = 21 b = 22 } +// lldbr-check:(destructured_local::Struct) r = { a = 21 b = 22 } // lldb-command:print s // lldbg-check:[...]$18 = 24 @@ -227,32 +227,32 @@ // lldbr-check:(i32) ue = 33 // lldb-command:print aa -// lldbg-check:[...]$29 = (34, 35) -// lldbr-check:((i32, i32)) aa = { = 34 = 35 } +// lldbg-check:[...]$29 = { 0 = 34 1 = 35 } +// lldbr-check:((i32, i32)) aa = { 0 = 34 1 = 35 } // lldb-command:print bb -// lldbg-check:[...]$30 = (36, 37) -// lldbr-check:((i32, i32)) bb = { = 36 = 37 } +// lldbg-check:[...]$30 = { 0 = 36 1 = 37 } +// lldbr-check:((i32, i32)) bb = { 0 = 36 1 = 37 } // lldb-command:print cc // lldbg-check:[...]$31 = 38 // lldbr-check:(i32) cc = 38 // lldb-command:print dd -// lldbg-check:[...]$32 = (40, 41, 42) -// lldbr-check:((i32, i32, i32)) dd = { = 40 = 41 = 42 } +// lldbg-check:[...]$32 = { 0 = 40 1 = 41 2 = 42 } +// lldbr-check:((i32, i32, i32)) dd = { 0 = 40 1 = 41 2 = 42} // lldb-command:print *ee -// lldbg-check:[...]$33 = (43, 44, 45) -// lldbr-check:((i32, i32, i32)) *ee = { = 43 = 44 = 45 } +// lldbg-check:[...]$33 = { 0 = 43 1 = 44 2 = 45 } +// lldbr-check:((i32, i32, i32)) *ee = { 0 = 43 1 = 44 2 = 45} // lldb-command:print *ff // lldbg-check:[...]$34 = 46 // lldbr-check:(i32) *ff = 46 // lldb-command:print gg -// lldbg-check:[...]$35 = (47, 48) -// lldbr-check:((i32, i32)) gg = { = 47 = 48 } +// lldbg-check:[...]$35 = { 0 = 47 1 = 48 } +// lldbr-check:((i32, i32)) gg = { 0 = 47 1 = 48 } // lldb-command:print *hh // lldbg-check:[...]$36 = 50 diff --git a/src/test/debuginfo/empty-string.rs b/src/test/debuginfo/empty-string.rs index bc4fac3183cea..66eb8bae26b9b 100644 --- a/src/test/debuginfo/empty-string.rs +++ b/src/test/debuginfo/empty-string.rs @@ -1,7 +1,7 @@ // ignore-windows failing on win32 bot // ignore-android: FIXME(#10381) // compile-flags:-g -// min-gdb-version: 7.7 +// min-gdb-version: 8.1 // ignore-gdb-version: 7.11.90 - 8.0.9 // min-lldb-version: 310 @@ -20,10 +20,10 @@ // lldb-command: run // lldb-command: fr v empty_string -// lldb-check:[...]empty_string = "" +// lldb-check:[...]empty_string = "" { vec = size=0 } // lldb-command: fr v empty_str -// lldb-check:[...]empty_str = "" +// lldb-check:[...]empty_str = "" { data_ptr = [...] length = 0 } fn main() { let empty_string = String::new(); diff --git a/src/test/debuginfo/enum-thinlto.rs b/src/test/debuginfo/enum-thinlto.rs index 13577b0587ff0..b10e04a4a9eb2 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 @@ -16,7 +15,8 @@ // lldb-command:run // lldb-command:print *abc -// lldbg-check:(enum_thinlto::ABC) $0 = ABC { } +// lldbg-check:(enum_thinlto::ABC) $0 = +// lldbr-check:(enum_thinlto::ABC) *abc = (x = 0, y = 8970181431921507452) #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/evec-in-struct.rs b/src/test/debuginfo/evec-in-struct.rs index cf86374ab311b..2033966adad4c 100644 --- a/src/test/debuginfo/evec-in-struct.rs +++ b/src/test/debuginfo/evec-in-struct.rs @@ -33,23 +33,23 @@ // lldb-command:run // lldb-command:print no_padding1 -// lldbg-check:[...]$0 = NoPadding1 { x: [0, 1, 2], y: -3, z: [4.5, 5.5] } -// lldbr-check:(evec_in_struct::NoPadding1) no_padding1 = NoPadding1 { x: [0, 1, 2], y: -3, z: [4.5, 5.5] } +// lldbg-check:[...]$0 = { x = { [0] = 0 [1] = 1 [2] = 2 } y = -3 z = { [0] = 4.5 [1] = 5.5 } } +// lldbr-check:(evec_in_struct::NoPadding1) no_padding1 = { x = { [0] = 0 [1] = 1 [2] = 2 } y = -3 z = { [0] = 4.5 [1] = 5.5 } } // lldb-command:print no_padding2 -// lldbg-check:[...]$1 = NoPadding2 { x: [6, 7, 8], y: [[9, 10], [11, 12]] } -// lldbr-check:(evec_in_struct::NoPadding2) no_padding2 = NoPadding2 { x: [6, 7, 8], y: [[9, 10], [11, 12]] } +// lldbg-check:[...]$1 = { x = { [0] = 6 [1] = 7 [2] = 8 } y = { [0] = { [0] = 9 [1] = 10 } [1] = { [0] = 11 [1] = 12 } } } +// lldbr-check:(evec_in_struct::NoPadding2) no_padding2 = { x = { [0] = 6 [1] = 7 [2] = 8 } y = { [0] = { [0] = 9 [1] = 10 } [1] = { [0] = 11 [1] = 12 } } } // lldb-command:print struct_internal_padding -// lldbg-check:[...]$2 = StructInternalPadding { x: [13, 14], y: [15, 16] } -// lldbr-check:(evec_in_struct::StructInternalPadding) struct_internal_padding = StructInternalPadding { x: [13, 14], y: [15, 16] } +// lldbg-check:[...]$2 = { x = { [0] = 13 [1] = 14 } y = { [0] = 15 [1] = 16 } } +// lldbr-check:(evec_in_struct::StructInternalPadding) struct_internal_padding = { x = { [0] = 13 [1] = 14 } y = { [0] = 15 [1] = 16 } } // lldb-command:print single_vec -// lldbg-check:[...]$3 = SingleVec { x: [17, 18, 19, 20, 21] } -// lldbr-check:(evec_in_struct::SingleVec) single_vec = SingleVec { x: [17, 18, 19, 20, 21] } +// lldbg-check:[...]$3 = { x = { [0] = 17 [1] = 18 [2] = 19 [3] = 20 [4] = 21 } } +// lldbr-check:(evec_in_struct::SingleVec) single_vec = { x = { [0] = 17 [1] = 18 [2] = 19 [3] = 20 [4] = 21 } } // lldb-command:print struct_padded_at_end -// lldbg-check:[...]$4 = StructPaddedAtEnd { x: [22, 23], y: [24, 25] } -// lldbr-check:(evec_in_struct::StructPaddedAtEnd) struct_padded_at_end = StructPaddedAtEnd { x: [22, 23], y: [24, 25] } +// lldbg-check:[...]$4 = { x = { [0] = 22 [1] = 23 } y = { [0] = 24 [1] = 25 } } +// lldbr-check:(evec_in_struct::StructPaddedAtEnd) struct_padded_at_end = { x = { [0] = 22 [1] = 23 } y = { [0] = 24 [1] = 25 } } #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/function-prologue-stepping-regular.rs b/src/test/debuginfo/function-prologue-stepping-regular.rs index 46901030f6525..699ff84ee0a6c 100644 --- a/src/test/debuginfo/function-prologue-stepping-regular.rs +++ b/src/test/debuginfo/function-prologue-stepping-regular.rs @@ -30,9 +30,9 @@ // NON IMMEDIATE ARGS // lldb-command:print a -// lldb-check:[...]$3 = BigStruct { a: 3, b: 4, c: 5, d: 6, e: 7, f: 8, g: 9, h: 10 } +// lldb-check:[...]$3 = { a = 3, b = 4, c = 5, d = 6, e = 7, f = 8, g = 9, h = 10 } // lldb-command:print b -// lldb-check:[...]$4 = BigStruct { a: 11, b: 12, c: 13, d: 14, e: 15, f: 16, g: 17, h: 18 } +// lldb-check:[...]$4 = { a = 11, b = 12, c = 13, d = 14, e = 15, f = 16, g = 17, h = 18 } // lldb-command:continue // BINDING diff --git a/src/test/debuginfo/gdb-pretty-struct-and-enums.rs b/src/test/debuginfo/gdb-pretty-struct-and-enums.rs index 7d2b4c95a05d8..2a8359de522f4 100644 --- a/src/test/debuginfo/gdb-pretty-struct-and-enums.rs +++ b/src/test/debuginfo/gdb-pretty-struct-and-enums.rs @@ -1,14 +1,14 @@ // ignore-tidy-linelength // ignore-lldb // ignore-android: FIXME(#10381) -// min-gdb-version: 7.11 +// min-gdb-version: 8.1 // compile-flags:-g // gdb-command: run // gdb-command: print regular_struct -// gdbg-check:$1 = RegularStruct = {the_first_field = 101, the_second_field = 102.5, the_third_field = false} +// gdbg-check:$1 = {the_first_field = 101, the_second_field = 102.5, the_third_field = false} // gdbr-check:$1 = gdb_pretty_struct_and_enums::RegularStruct {the_first_field: 101, the_second_field: 102.5, the_third_field: false} // gdb-command: print empty_struct diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs index f19a3c71dd8d2..0023f69d27fbd 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 @@ -25,16 +24,16 @@ // lldb-command:run // lldb-command:print b -// lldbg-check:(generator_objects::main::generator-0) $0 = generator-0(&0x[...]) +// lldbg-check:(generator_objects::main::generator-0) $0 = { 0 = 0x[...] } // lldb-command:continue // lldb-command:print b -// lldbg-check:(generator_objects::main::generator-0) $1 = generator-0(&0x[...]) +// lldbg-check:(generator_objects::main::generator-0) $1 = { 0 = 0x[...] } // lldb-command:continue // lldb-command:print b -// lldbg-check:(generator_objects::main::generator-0) $2 = generator-0(&0x[...]) +// lldbg-check:(generator_objects::main::generator-0) $2 = { 0 = 0x[...] } // lldb-command:continue // lldb-command:print b -// lldbg-check:(generator_objects::main::generator-0) $3 = generator-0(&0x[...]) +// lldbg-check:(generator_objects::main::generator-0) $3 = { 0 = 0x[...] } #![feature(omit_gdb_pretty_printer_section, generators, generator_trait)] #![omit_gdb_pretty_printer_section] 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..e8f3940c8360a 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 } -// 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 } } +// lldbg-check:[...]$5 = { a = 6 b = 7.5 } +// lldbr-check:(generic_function::Struct) *t1 = { a = 6 b = 7.5 } // lldb-command:continue #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/generic-method-on-generic-struct.rs b/src/test/debuginfo/generic-method-on-generic-struct.rs index 7d151cfaa4a7b..f7767292222b0 100644 --- a/src/test/debuginfo/generic-method-on-generic-struct.rs +++ b/src/test/debuginfo/generic-method-on-generic-struct.rs @@ -67,8 +67,8 @@ // STACK BY REF // lldb-command:print *self -// lldbg-check:[...]$0 = Struct<(u32, i32)> { x: (8888, -8888) } -// lldbr-check:(generic_method_on_generic_struct::Struct<(u32, i32)>) *self = { x = { = 8888 = -8888 } } +// lldbg-check:[...]$0 = { x = { 0 = 8888, 1 = -8888 } } +// lldbr-check:(generic_method_on_generic_struct::Struct<(u32, i32)>) *self = { x = { 0 = 8888 1 = -8888 } } // lldb-command:print arg1 // lldbg-check:[...]$1 = -1 // lldbr-check:(isize) arg1 = -1 @@ -79,8 +79,8 @@ // STACK BY VAL // lldb-command:print self -// lldbg-check:[...]$3 = Struct<(u32, i32)> { x: (8888, -8888) } -// lldbr-check:(generic_method_on_generic_struct::Struct<(u32, i32)>) self = { x = { = 8888 = -8888 } } +// lldbg-check:[...]$3 = { x = { 0 = 8888, 1 = -8888 } } +// lldbr-check:(generic_method_on_generic_struct::Struct<(u32, i32)>) self = { x = { 0 = 8888, 1 = -8888 } } // lldb-command:print arg1 // lldbg-check:[...]$4 = -3 // lldbr-check:(isize) arg1 = -3 @@ -91,8 +91,8 @@ // OWNED BY REF // lldb-command:print *self -// lldbg-check:[...]$6 = Struct { x: 1234.5 } -// lldbr-check:(generic_method_on_generic_struct::Struct) *self = Struct { x: 1234.5 } +// lldbg-check:[...]$6 = { x = 1234.5 } +// lldbr-check:(generic_method_on_generic_struct::Struct) *self = { x = 1234.5 } // lldb-command:print arg1 // lldbg-check:[...]$7 = -5 // lldbr-check:(isize) arg1 = -5 @@ -103,8 +103,8 @@ // OWNED BY VAL // lldb-command:print self -// lldbg-check:[...]$9 = Struct { x: 1234.5 } -// lldbr-check:(generic_method_on_generic_struct::Struct) self = Struct { x: 1234.5 } +// lldbg-check:[...]$9 = { x = 1234.5 } +// lldbr-check:(generic_method_on_generic_struct::Struct) self = { x = 1234.5 } // lldb-command:print arg1 // lldbg-check:[...]$10 = -7 // lldbr-check:(isize) arg1 = -7 @@ -115,8 +115,8 @@ // OWNED MOVED // lldb-command:print *self -// lldbg-check:[...]$12 = Struct { x: 1234.5 } -// lldbr-check:(generic_method_on_generic_struct::Struct) *self = Struct { x: 1234.5 } +// lldbg-check:[...]$12 = { x = 1234.5 } +// lldbr-check:(generic_method_on_generic_struct::Struct) *self = { x = 1234.5 } // lldb-command:print arg1 // lldbg-check:[...]$13 = -9 // lldbr-check:(isize) arg1 = -9 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-22656.rs b/src/test/debuginfo/issue-22656.rs index e4634d96a6f31..f286566277e9e 100644 --- a/src/test/debuginfo/issue-22656.rs +++ b/src/test/debuginfo/issue-22656.rs @@ -4,7 +4,6 @@ // min-lldb-version: 310 // ignore-gdb -// ignore-tidy-linelength // compile-flags:-g @@ -12,11 +11,11 @@ // lldb-command:run // lldb-command:print v -// lldbg-check:[...]$0 = vec![1, 2, 3] -// lldbr-check:(alloc::vec::Vec) v = vec![1, 2, 3] +// lldbg-check:[...]$0 = size=3 { [0] = 1 [1] = 2 [2] = 3 } +// lldbr-check:(alloc::vec::Vec) v = size=3 { [0] = 1 [1] = 2 [2] = 3 } // lldb-command:print zs -// lldbg-check:[...]$1 = StructWithZeroSizedField { x: ZeroSizedStruct[...], y: 123, z: ZeroSizedStruct[...], w: 456 } -// lldbr-check:(issue_22656::StructWithZeroSizedField) zs = StructWithZeroSizedField { x: ZeroSizedStruct { }, y: 123, z: ZeroSizedStruct { }, w: 456 } +// lldbg-check:[...]$1 = { x = y = 123 z = w = 456 } +// lldbr-check:(issue_22656::StructWithZeroSizedField) zs = { x = y = 123 z = w = 456 } // lldbr-command:continue #![allow(unused_variables)] diff --git a/src/test/debuginfo/issue-57822.rs b/src/test/debuginfo/issue-57822.rs index 4de88e9dae62b..c2cc6f9d24ca1 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 @@ -23,10 +22,10 @@ // lldb-command:run // lldb-command:print g -// lldbg-check:(issue_57822::main::closure-1) $0 = closure-1(closure-0(1)) +// lldbg-check:(issue_57822::main::closure-1) $0 = { 0 = { 0 = 1 } } // lldb-command:print b -// lldbg-check:(issue_57822::main::generator-3) $1 = generator-3(generator-2(2)) +// lldbg-check:(issue_57822::main::generator-3) $1 = { 0 = { 0 = 2 } } #![feature(omit_gdb_pretty_printer_section, generators, generator_trait)] #![omit_gdb_pretty_printer_section] diff --git a/src/test/debuginfo/method-on-struct.rs b/src/test/debuginfo/method-on-struct.rs index cf8ed13e814e9..c764cf6832378 100644 --- a/src/test/debuginfo/method-on-struct.rs +++ b/src/test/debuginfo/method-on-struct.rs @@ -63,7 +63,7 @@ // STACK BY REF // lldb-command:print *self -// lldbg-check:[...]$0 = Struct { x: 100 } +// lldbg-check:[...]$0 = { x = 100 } // lldbr-check:(method_on_struct::Struct) *self = Struct { x: 100 } // lldb-command:print arg1 // lldbg-check:[...]$1 = -1 @@ -75,7 +75,7 @@ // STACK BY VAL // lldb-command:print self -// lldbg-check:[...]$3 = Struct { x: 100 } +// lldbg-check:[...]$3 = { x = 100 } // lldbr-check:(method_on_struct::Struct) self = Struct { x: 100 } // lldb-command:print arg1 // lldbg-check:[...]$4 = -3 @@ -87,7 +87,7 @@ // OWNED BY REF // lldb-command:print *self -// lldbg-check:[...]$6 = Struct { x: 200 } +// lldbg-check:[...]$6 = { x = 200 } // lldbr-check:(method_on_struct::Struct) *self = Struct { x: 200 } // lldb-command:print arg1 // lldbg-check:[...]$7 = -5 @@ -99,7 +99,7 @@ // OWNED BY VAL // lldb-command:print self -// lldbg-check:[...]$9 = Struct { x: 200 } +// lldbg-check:[...]$9 = { x = 200 } // lldbr-check:(method_on_struct::Struct) self = Struct { x: 200 } // lldb-command:print arg1 // lldbg-check:[...]$10 = -7 @@ -111,7 +111,7 @@ // OWNED MOVED // lldb-command:print *self -// lldbg-check:[...]$12 = Struct { x: 200 } +// lldbg-check:[...]$12 = { x = 200 } // lldbr-check:(method_on_struct::Struct) *self = Struct { x: 200 } // lldb-command:print arg1 // lldbg-check:[...]$13 = -9 diff --git a/src/test/debuginfo/method-on-trait.rs b/src/test/debuginfo/method-on-trait.rs index 9b321a8fad22e..6dcf28967776f 100644 --- a/src/test/debuginfo/method-on-trait.rs +++ b/src/test/debuginfo/method-on-trait.rs @@ -63,8 +63,8 @@ // STACK BY REF // lldb-command:print *self -// lldbg-check:[...]$0 = Struct { x: 100 } -// lldbr-check:(method_on_trait::Struct) *self = Struct { x: 100 } +// lldbg-check:[...]$0 = { x = 100 } +// lldbr-check:(method_on_trait::Struct) *self = { x = 100 } // lldb-command:print arg1 // lldbg-check:[...]$1 = -1 // lldbr-check:(isize) arg1 = -1 @@ -75,8 +75,8 @@ // STACK BY VAL // lldb-command:print self -// lldbg-check:[...]$3 = Struct { x: 100 } -// lldbr-check:(method_on_trait::Struct) self = Struct { x: 100 } +// lldbg-check:[...]$3 = { x = 100 } +// lldbr-check:(method_on_trait::Struct) self = { x = 100 } // lldb-command:print arg1 // lldbg-check:[...]$4 = -3 // lldbr-check:(isize) arg1 = -3 @@ -87,8 +87,8 @@ // OWNED BY REF // lldb-command:print *self -// lldbg-check:[...]$6 = Struct { x: 200 } -// lldbr-check:(method_on_trait::Struct) *self = Struct { x: 200 } +// lldbg-check:[...]$6 = { x = 200 } +// lldbr-check:(method_on_trait::Struct) *self = { x = 200 } // lldb-command:print arg1 // lldbg-check:[...]$7 = -5 // lldbr-check:(isize) arg1 = -5 @@ -99,8 +99,8 @@ // OWNED BY VAL // lldb-command:print self -// lldbg-check:[...]$9 = Struct { x: 200 } -// lldbr-check:(method_on_trait::Struct) self = Struct { x: 200 } +// lldbg-check:[...]$9 = { x = 200 } +// lldbr-check:(method_on_trait::Struct) self = { x = 200 } // lldb-command:print arg1 // lldbg-check:[...]$10 = -7 // lldbr-check:(isize) arg1 = -7 @@ -111,8 +111,8 @@ // OWNED MOVED // lldb-command:print *self -// lldbg-check:[...]$12 = Struct { x: 200 } -// lldbr-check:(method_on_trait::Struct) *self = Struct { x: 200 } +// lldbg-check:[...]$12 = { x = 200 } +// lldbr-check:(method_on_trait::Struct) *self = { x = 200 } // lldb-command:print arg1 // lldbg-check:[...]$13 = -9 // lldbr-check:(isize) arg1 = -9 diff --git a/src/test/debuginfo/method-on-tuple-struct.rs b/src/test/debuginfo/method-on-tuple-struct.rs index dc32edd07a4ab..d06b606e973e8 100644 --- a/src/test/debuginfo/method-on-tuple-struct.rs +++ b/src/test/debuginfo/method-on-tuple-struct.rs @@ -63,8 +63,8 @@ // STACK BY REF // lldb-command:print *self -// lldbg-check:[...]$0 = TupleStruct(100, -100.5) -// lldbr-check:(method_on_tuple_struct::TupleStruct) *self = TupleStruct(100, -100.5) +// lldbg-check:[...]$0 = { 0 = 100 1 = -100.5 } +// lldbr-check:(method_on_tuple_struct::TupleStruct) *self = { 0 = 100 1 = -100.5 } // lldb-command:print arg1 // lldbg-check:[...]$1 = -1 // lldbr-check:(isize) arg1 = -1 @@ -75,8 +75,8 @@ // STACK BY VAL // lldb-command:print self -// lldbg-check:[...]$3 = TupleStruct(100, -100.5) -// lldbr-check:(method_on_tuple_struct::TupleStruct) self = TupleStruct(100, -100.5) +// lldbg-check:[...]$3 = { 0 = 100 1 = -100.5 } +// lldbr-check:(method_on_tuple_struct::TupleStruct) self = { 0 = 100 1 = -100.5 } // lldb-command:print arg1 // lldbg-check:[...]$4 = -3 // lldbr-check:(isize) arg1 = -3 @@ -87,8 +87,8 @@ // OWNED BY REF // lldb-command:print *self -// lldbg-check:[...]$6 = TupleStruct(200, -200.5) -// lldbr-check:(method_on_tuple_struct::TupleStruct) *self = TupleStruct(200, -200.5) +// lldbg-check:[...]$6 = { 0 = 200 1 = -200.5 } +// lldbr-check:(method_on_tuple_struct::TupleStruct) *self = { 0 = 200 1 = -200.5 } // lldb-command:print arg1 // lldbg-check:[...]$7 = -5 // lldbr-check:(isize) arg1 = -5 @@ -99,8 +99,8 @@ // OWNED BY VAL // lldb-command:print self -// lldbg-check:[...]$9 = TupleStruct(200, -200.5) -// lldbr-check:(method_on_tuple_struct::TupleStruct) self = TupleStruct(200, -200.5) +// lldbg-check:[...]$9 = { 0 = 200 1 = -200.5 } +// lldbr-check:(method_on_tuple_struct::TupleStruct) self = { 0 = 200 1 = -200.5 } // lldb-command:print arg1 // lldbg-check:[...]$10 = -7 // lldbr-check:(isize) arg1 = -7 @@ -111,8 +111,8 @@ // OWNED MOVED // lldb-command:print *self -// lldbg-check:[...]$12 = TupleStruct(200, -200.5) -// lldbr-check:(method_on_tuple_struct::TupleStruct) *self = TupleStruct(200, -200.5) +// lldbg-check:[...]$12 = { 0 = 200 1 = -200.5 } +// lldbr-check:(method_on_tuple_struct::TupleStruct) *self = { 0 = 200 1 = -200.5 } // lldb-command:print arg1 // lldbg-check:[...]$13 = -9 // lldbr-check:(isize) arg1 = -9 diff --git a/src/test/debuginfo/packed-struct-with-destructor.rs b/src/test/debuginfo/packed-struct-with-destructor.rs index d17e6e8193eec..380e882a0fba0 100644 --- a/src/test/debuginfo/packed-struct-with-destructor.rs +++ b/src/test/debuginfo/packed-struct-with-destructor.rs @@ -46,36 +46,36 @@ // lldb-command:run // lldb-command:print packed -// lldbg-check:[...]$0 = Packed { x: 123, y: 234, z: 345 } -// lldbr-check:(packed_struct_with_destructor::Packed) packed = Packed { x: 123, y: 234, z: 345 } +// lldbg-check:[...]$0 = { x = 123 y = 234 z = 345 } +// lldbr-check:(packed_struct_with_destructor::Packed) packed = { x = 123 y = 234 z = 345 } // lldb-command:print packedInPacked -// lldbg-check:[...]$1 = PackedInPacked { a: 1111, b: Packed { x: 2222, y: 3333, z: 4444 }, c: 5555, d: Packed { x: 6666, y: 7777, z: 8888 } } -// lldbr-check:(packed_struct_with_destructor::PackedInPacked) packedInPacked = PackedInPacked { a: 1111, b: Packed { x: 2222, y: 3333, z: 4444 }, c: 5555, d: Packed { x: 6666, y: 7777, z: 8888 } } +// lldbg-check:[...]$1 = { a = 1111 b = { x = 2222 y = 3333 z = 4444 } c = 5555 d = { x = 6666 y = 7777 z = 8888 } } +// lldbr-check:(packed_struct_with_destructor::PackedInPacked) packedInPacked = { a = 1111 b = { x = 2222 y = 3333 z = 4444 } c = 5555 d = { x = 6666 y = 7777 z = 8888 } } // lldb-command:print packedInUnpacked -// lldbg-check:[...]$2 = PackedInUnpacked { a: -1111, b: Packed { x: -2222, y: -3333, z: -4444 }, c: -5555, d: Packed { x: -6666, y: -7777, z: -8888 } } -// lldbr-check:(packed_struct_with_destructor::PackedInUnpacked) packedInUnpacked = PackedInUnpacked { a: -1111, b: Packed { x: -2222, y: -3333, z: -4444 }, c: -5555, d: Packed { x: -6666, y: -7777, z: -8888 } } +// lldbg-check:[...]$2 = { a = -1111 b = { x = -2222 y = -3333 z = -4444 } c = -5555 d = { x = -6666 y = -7777 z = -8888 } } +// lldbr-check:(packed_struct_with_destructor::PackedInUnpacked) packedInUnpacked = { a = -1111 b = { x = -2222 y = -3333 z = -4444 } c = -5555 d = { x = -6666 y = -7777 z = -8888 } } // lldb-command:print unpackedInPacked -// lldbg-check:[...]$3 = UnpackedInPacked { a: 987, b: Unpacked { x: 876, y: 765, z: 654 }, c: Unpacked { x: 543, y: 432, z: 321 }, d: 210 } -// lldbr-check:(packed_struct_with_destructor::UnpackedInPacked) unpackedInPacked = UnpackedInPacked { a: 987, b: Unpacked { x: 876, y: 765, z: 654 }, c: Unpacked { x: 543, y: 432, z: 321 }, d: 210 } +// lldbg-check:[...]$3 = { a = 987 b = { x = 876 y = 765 z = 654 } c = { x = 543 y = 432 z = 321 } d = 210 } +// lldbr-check:(packed_struct_with_destructor::UnpackedInPacked) unpackedInPacked = { a = 987 b = { x = 876 y = 765 z = 654 } c = { x = 543 y = 432 z = 321 } d = 210 } // lldb-command:print packedInPackedWithDrop -// lldbg-check:[...]$4 = PackedInPackedWithDrop { a: 11, b: Packed { x: 22, y: 33, z: 44 }, c: 55, d: Packed { x: 66, y: 77, z: 88 } } -// lldbr-check:(packed_struct_with_destructor::PackedInPackedWithDrop) packedInPackedWithDrop = PackedInPackedWithDrop { a: 11, b: Packed { x: 22, y: 33, z: 44 }, c: 55, d: Packed { x: 66, y: 77, z: 88 } } +// lldbg-check:[...]$4 = { a = 11 b = { x = 22 y = 33 z = 44 } c = 55 d = { x = 66 y = 77 z = 88 } } +// lldbr-check:(packed_struct_with_destructor::PackedInPackedWithDrop) packedInPackedWithDrop = { a = 11 b = { x = 22 y = 33 z = 44 } c = 55 d = { x = 66 y = 77 z = 88 } } // lldb-command:print packedInUnpackedWithDrop -// lldbg-check:[...]$5 = PackedInUnpackedWithDrop { a: -11, b: Packed { x: -22, y: -33, z: -44 }, c: -55, d: Packed { x: -66, y: -77, z: -88 } } -// lldbr-check:(packed_struct_with_destructor::PackedInUnpackedWithDrop) packedInUnpackedWithDrop = PackedInUnpackedWithDrop { a: -11, b: Packed { x: -22, y: -33, z: -44 }, c: -55, d: Packed { x: -66, y: -77, z: -88 } } +// lldbg-check:[...]$5 = { a = -11 b = { x = -22 y = -33 z = -44 } c = -55 d = { x = -66 y = -77 z = -88 } } +// lldbr-check:(packed_struct_with_destructor::PackedInUnpackedWithDrop) packedInUnpackedWithDrop = { a = -11 b = { x = -22 y = -33 z = -44 } c = -55 d = { x = -66 y = -77 z = -88 } } // lldb-command:print unpackedInPackedWithDrop -// lldbg-check:[...]$6 = UnpackedInPackedWithDrop { a: 98, b: Unpacked { x: 87, y: 76, z: 65 }, c: Unpacked { x: 54, y: 43, z: 32 }, d: 21 } -// lldbr-check:(packed_struct_with_destructor::UnpackedInPackedWithDrop) unpackedInPackedWithDrop = UnpackedInPackedWithDrop { a: 98, b: Unpacked { x: 87, y: 76, z: 65 }, c: Unpacked { x: 54, y: 43, z: 32 }, d: 21 } +// lldbg-check:[...]$6 = { a = 98 b = { x = 87 y = 76 z = 65 } c = { x = 54 y = 43 z = 32 } d = 21 } +// lldbr-check:(packed_struct_with_destructor::UnpackedInPackedWithDrop) unpackedInPackedWithDrop = { a = 98 b = { x = 87 y = 76 z = 65 } c = { x = 54 y = 43 z = 32 } d = 21 } // lldb-command:print deeplyNested -// lldbg-check:[...]$7 = DeeplyNested { a: PackedInPacked { a: 1, b: Packed { x: 2, y: 3, z: 4 }, c: 5, d: Packed { x: 6, y: 7, z: 8 } }, b: UnpackedInPackedWithDrop { a: 9, b: Unpacked { x: 10, y: 11, z: 12 }, c: Unpacked { x: 13, y: 14, z: 15 }, d: 16 }, c: PackedInUnpacked { a: 17, b: Packed { x: 18, y: 19, z: 20 }, c: 21, d: Packed { x: 22, y: 23, z: 24 } }, d: PackedInUnpackedWithDrop { a: 25, b: Packed { x: 26, y: 27, z: 28 }, c: 29, d: Packed { x: 30, y: 31, z: 32 } }, e: UnpackedInPacked { a: 33, b: Unpacked { x: 34, y: 35, z: 36 }, c: Unpacked { x: 37, y: 38, z: 39 }, d: 40 }, f: PackedInPackedWithDrop { a: 41, b: Packed { x: 42, y: 43, z: 44 }, c: 45, d: Packed { x: 46, y: 47, z: 48 } } } -// lldbr-check:(packed_struct_with_destructor::DeeplyNested) deeplyNested = DeeplyNested { a: PackedInPacked { a: 1, b: Packed { x: 2, y: 3, z: 4 }, c: 5, d: Packed { x: 6, y: 7, z: 8 } }, b: UnpackedInPackedWithDrop { a: 9, b: Unpacked { x: 10, y: 11, z: 12 }, c: Unpacked { x: 13, y: 14, z: 15 }, d: 16 }, c: PackedInUnpacked { a: 17, b: Packed { x: 18, y: 19, z: 20 }, c: 21, d: Packed { x: 22, y: 23, z: 24 } }, d: PackedInUnpackedWithDrop { a: 25, b: Packed { x: 26, y: 27, z: 28 }, c: 29, d: Packed { x: 30, y: 31, z: 32 } }, e: UnpackedInPacked { a: 33, b: Unpacked { x: 34, y: 35, z: 36 }, c: Unpacked { x: 37, y: 38, z: 39 }, d: 40 }, f: PackedInPackedWithDrop { a: 41, b: Packed { x: 42, y: 43, z: 44 }, c: 45, d: Packed { x: 46, y: 47, z: 48 } } } +// lldbg-check:[...]$7 = { a = { a = 1 b = { x = 2 y = 3 z = 4 } c = 5 d = { x = 6 y = 7 z = 8 } } b = { a = 9 b = { x = 10 y = 11 z = 12 } c = { x = 13 y = 14 z = 15 } d = 16 } c = { a = 17 b = { x = 18 y = 19 z = 20 } c = 21 d = { x = 22 y = 23 z = 24 } } d = { a = 25 b = { x = 26 y = 27 z = 28 } c = 29 d = { x = 30 y = 31 z = 32 } } e = { a = 33 b = { x = 34 y = 35 z = 36 } c = { x = 37 y = 38 z = 39 } d = 40 } f = { a = 41 b = { x = 42 y = 43 z = 44 } c = 45 d = { x = 46 y = 47 z = 48 } } } +// lldbr-check:(packed_struct_with_destructor::DeeplyNested) deeplyNested = { a = { a = 1 b = { x = 2 y = 3 z = 4 } c = 5 d = { x = 6 y = 7 z = 8 } } b = { a = 9 b = { x = 10 y = 11 z = 12 } c = { x = 13 y = 14 z = 15 } d = 16 } c = { a = 17 b = { x = 18 y = 19 z = 20 } c = 21 d = { x = 22 y = 23 z = 24 } } d = { a = 25 b = { x = 26 y = 27 z = 28 } c = 29 d = { x = 30 y = 31 z = 32 } } e = { a = 33 b = { x = 34 y = 35 z = 36 } c = { x = 37 y = 38 z = 39 } d = 40 } f = { a = 41 b = { x = 42 y = 43 z = 44 } c = 45 d = { x = 46 y = 47 z = 48 } } } #![allow(unused_variables)] diff --git a/src/test/debuginfo/packed-struct.rs b/src/test/debuginfo/packed-struct.rs index 494b61f9a2488..9654847ce5de4 100644 --- a/src/test/debuginfo/packed-struct.rs +++ b/src/test/debuginfo/packed-struct.rs @@ -36,20 +36,20 @@ // lldb-command:run // lldb-command:print packed -// lldbg-check:[...]$0 = Packed { x: 123, y: 234, z: 345 } -// lldbr-check:(packed_struct::Packed) packed = Packed { x: 123, y: 234, z: 345 } +// lldbg-check:[...]$0 = { x = 123 y = 234 z = 345 } +// lldbr-check:(packed_struct::Packed) packed = { x = 123 y = 234 z = 345 } // lldb-command:print packedInPacked -// lldbg-check:[...]$1 = PackedInPacked { a: 1111, b: Packed { x: 2222, y: 3333, z: 4444 }, c: 5555, d: Packed { x: 6666, y: 7777, z: 8888 } } -// lldbr-check:(packed_struct::PackedInPacked) packedInPacked = PackedInPacked { a: 1111, b: Packed { x: 2222, y: 3333, z: 4444 }, c: 5555, d: Packed { x: 6666, y: 7777, z: 8888 } } +// lldbg-check:[...]$1 = { a = 1111 b = { x = 2222 y = 3333 z = 4444 } c = 5555 d = { x = 6666 y = 7777 z = 8888 } } +// lldbr-check:(packed_struct::PackedInPacked) packedInPacked = { a = 1111 b = { x = 2222 y = 3333 z = 4444 } c = 5555 d = { x = 6666 y = 7777 z = 8888 } } // lldb-command:print packedInUnpacked -// lldbg-check:[...]$2 = PackedInUnpacked { a: -1111, b: Packed { x: -2222, y: -3333, z: -4444 }, c: -5555, d: Packed { x: -6666, y: -7777, z: -8888 } } -// lldbr-check:(packed_struct::PackedInUnpacked) packedInUnpacked = PackedInUnpacked { a: -1111, b: Packed { x: -2222, y: -3333, z: -4444 }, c: -5555, d: Packed { x: -6666, y: -7777, z: -8888 } } +// lldbg-check:[...]$2 = { a = -1111 b = { x = -2222 y = -3333 z = -4444 } c = -5555 d = { x = -6666 y = -7777 z = -8888 } } +// lldbr-check:(packed_struct::PackedInUnpacked) packedInUnpacked = { a = -1111 b = { x = -2222 y = -3333 z = -4444 } c = -5555 d = { x = -6666 y = -7777 z = -8888 } } // lldb-command:print unpackedInPacked -// lldbg-check:[...]$3 = UnpackedInPacked { a: 987, b: Unpacked { x: 876, y: 765, z: 654, w: 543 }, c: Unpacked { x: 432, y: 321, z: 210, w: 109 }, d: -98 } -// lldbr-check:(packed_struct::UnpackedInPacked) unpackedInPacked = UnpackedInPacked { a: 987, b: Unpacked { x: 876, y: 765, z: 654, w: 543 }, c: Unpacked { x: 432, y: 321, z: 210, w: 109 }, d: -98 } +// lldbg-check:[...]$3 = { a = 987 b = { x = 876 y = 765 z = 654 w = 543 } c = { x = 432 y = 321 z = 210 w = 109 } d = -98 } +// lldbr-check:(packed_struct::UnpackedInPacked) unpackedInPacked = { a = 987 b = { x = 876 y = 765 z = 654 w = 543 } c = { x = 432 y = 321 z = 210 w = 109 } d = -98 } // lldb-command:print sizeof(packed) // lldbg-check:[...]$4 = 14 diff --git a/src/test/debuginfo/pretty-huge-vec.rs b/src/test/debuginfo/pretty-huge-vec.rs index 2e3386b7a360e..2616c9465246e 100644 --- a/src/test/debuginfo/pretty-huge-vec.rs +++ b/src/test/debuginfo/pretty-huge-vec.rs @@ -2,7 +2,7 @@ // ignore-freebsd: gdb package too new // ignore-android: FIXME(#10381) // compile-flags:-g -// min-gdb-version 7.7 +// min-gdb-version 8.1 // min-lldb-version: 310 // === GDB TESTS =================================================================================== @@ -10,11 +10,10 @@ // gdb-command: run // gdb-command: print vec -// gdb-check:$1 = Vec(len: 1000000000, cap: 1000000000) = {[...]...} +// gdb-check:$1 = Vec(size=1000000000) = {[...]...} // gdb-command: print slice -// gdb-check:$2 = &[u8](len: 1000000000) = {[...]...} - +// gdb-check:$2 = &[u8] {data_ptr: [...]"\000", length: 1000000000} #![allow(unused_variables)] diff --git a/src/test/debuginfo/pretty-std-collections.rs b/src/test/debuginfo/pretty-std-collections.rs index f8997fad9a53f..4e95a028e0749 100644 --- a/src/test/debuginfo/pretty-std-collections.rs +++ b/src/test/debuginfo/pretty-std-collections.rs @@ -15,37 +15,102 @@ // gdb-command: run // gdb-command: print btree_set -// gdb-check:$1 = BTreeSet(len: 15) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} +// gdb-check:$1 = BTreeSet(size=15) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} + +// gdb-command: print empty_btree_set +// gdb-check:$2 = BTreeSet(size=0) // gdb-command: print btree_map -// gdb-check:$2 = BTreeMap(len: 15) = {[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 5, [6] = 6, [7] = 7, [8] = 8, [9] = 9, [10] = 10, [11] = 11, [12] = 12, [13] = 13, [14] = 14} +// gdb-check:$3 = BTreeMap(size=15) = {[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 5, [6] = 6, [7] = 7, [8] = 8, [9] = 9, [10] = 10, [11] = 11, [12] = 12, [13] = 13, [14] = 14} + +// gdb-command: print empty_btree_map +// gdb-check:$4 = BTreeMap(size=0) + +// gdb-command: print option_btree_map +// gdb-check:$5 = BTreeMap(size=2) = {[false] = [...], [true] = [...]} +// (abbreviated because both values vary wildly over gdb versions and/or linux distributions) + +// gdb-command: print nasty_btree_map +// gdb-check:$6 = BTreeMap(size=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:$3 = VecDeque(len: 3, cap: 8) = {5, 3, 7} +// gdb-check:$7 = VecDeque(size=3) = {5, 3, 7} // gdb-command: print vec_deque2 -// gdb-check:$4 = VecDeque(len: 7, cap: 8) = {2, 3, 4, 5, 6, 7, 8} +// gdb-check:$8 = VecDeque(size=7) = {2, 3, 4, 5, 6, 7, 8} + +// gdb-command: print hash_map +// gdb-check:$9 = HashMap(size=4) = {[1] = 10, [2] = 20, [3] = 30, [4] = 40} + +// gdb-command: print hash_set +// gdb-check:$10 = HashSet(size=4) = {1, 2, 3, 4} + +// === LLDB TESTS ================================================================================== + +// lldb-command:run + +// lldb-command:print vec_deque +// lldbg-check:[...]$0 = size=3 { [0] = 5 [1] = 3 [2] = 7 } +// lldbr-check:(alloc::collections::vec_deque::VecDeque) vec_deque = size=3 = { [0] = 5 [1] = 3 [2] = 7 } + +// lldb-command:print vec_deque2 +// lldbg-check:[...]$1 = size=7 { [0] = 2 [1] = 3 [2] = 4 [3] = 5 [4] = 6 [5] = 7 [6] = 8 } +// lldbr-check:(alloc::collections::vec_deque::VecDeque) vec_deque2 = size=7 = { [0] = 2 [1] = 3 [2] = 4 [3] = 5 [4] = 6 [5] = 7 [6] = 8 } + +// lldb-command:print hash_map +// lldbg-check:[...]$2 = size=4 { [0] = { 0 = 1 1 = 10 } [1] = { 0 = 2 1 = 20 } [2] = { 0 = 3 1 = 30 } [3] = { 0 = 4 1 = 40 } } +// lldbr-check:(std::collections::hash::map::HashMap) hash_map = size=4 size=4 { [0] = { 0 = 1 1 = 10 } [1] = { 0 = 2 1 = 20 } [2] = { 0 = 3 1 = 30 } [3] = { 0 = 4 1 = 40 } } + +// lldb-command:print hash_set +// lldbg-check:[...]$3 = size=4 { [0] = 1 [1] = 2 [2] = 3 [3] = 4 } +// lldbr-check:(std::collections::hash::set::HashSet) hash_set = size=4 { [0] = 1 [1] = 2 [2] = 3 [3] = 4 } #![allow(unused_variables)] -use std::collections::BTreeSet; use std::collections::BTreeMap; +use std::collections::BTreeSet; use std::collections::VecDeque; +use std::collections::HashMap; +use std::collections::HashSet; +use std::hash::{BuildHasherDefault, Hasher}; +struct MyLeafNode(i32); // helps to ensure we don't blindly replace substring "LeafNode" -fn main() { +#[derive(Default)] +struct SimpleHasher { hash: u64 } +impl Hasher for SimpleHasher { + fn finish(&self) -> u64 { self.hash } + fn write(&mut self, bytes: &[u8]) {} + fn write_u64(&mut self, i: u64) { self.hash = i } +} + +fn main() { // BTreeSet let mut btree_set = BTreeSet::new(); for i in 0..15 { btree_set.insert(i); } + let mut empty_btree_set: BTreeSet = BTreeSet::new(); + // BTreeMap let mut btree_map = BTreeMap::new(); for i in 0..15 { btree_map.insert(i, i); } + 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(); + for i in 0..15 { + nasty_btree_map.insert(i, MyLeafNode(i)); + } + // VecDeque let mut vec_deque = VecDeque::new(); vec_deque.push_back(5); @@ -60,7 +125,21 @@ fn main() { vec_deque2.pop_front(); vec_deque2.push_back(8); + // HashMap + let mut hash_map = HashMap::>::default(); + for i in 1..5 { + hash_map.insert(i, i * 10); + } + + // HashSet + let mut hash_set = HashSet::>::default(); + for i in 1..5 { + hash_set.insert(i); + } + zzz(); // #break } -fn zzz() { () } +fn zzz() { + () +} diff --git a/src/test/debuginfo/pretty-std.rs b/src/test/debuginfo/pretty-std.rs index a684d3b88fd07..57721ce103c39 100644 --- a/src/test/debuginfo/pretty-std.rs +++ b/src/test/debuginfo/pretty-std.rs @@ -61,6 +61,9 @@ // lldb-command: print none // lldb-check:[...]$5 = None +// lldb-command: print os_string +// lldb-check:[...]$6 = "IAMA OS string 😃"[...] + // === CDB TESTS ================================================================================== diff --git a/src/test/debuginfo/pretty-uninitialized-vec.rs b/src/test/debuginfo/pretty-uninitialized-vec.rs index 37aae65d42b69..7ce004681e100 100644 --- a/src/test/debuginfo/pretty-uninitialized-vec.rs +++ b/src/test/debuginfo/pretty-uninitialized-vec.rs @@ -2,7 +2,7 @@ // ignore-freebsd: gdb package too new // ignore-android: FIXME(#10381) // compile-flags:-g -// min-gdb-version 7.7 +// min-gdb-version 8.1 // min-lldb-version: 310 // === GDB TESTS =================================================================================== @@ -10,7 +10,7 @@ // gdb-command: run // gdb-command: print vec -// gdb-check:$1 = Vec(len: [...], cap: [...])[...] +// gdb-check:$1 = Vec(size=[...])[...] #![allow(unused_variables)] diff --git a/src/test/debuginfo/rc_arc.rs b/src/test/debuginfo/rc_arc.rs new file mode 100644 index 0000000000000..8ab8a2f9c1cbd --- /dev/null +++ b/src/test/debuginfo/rc_arc.rs @@ -0,0 +1,37 @@ +// compile-flags:-g + +// min-gdb-version: 8.1 + +// === GDB TESTS ================================================================================== + +// gdb-command:run + +// gdb-command:print r +// gdb-check:[...]$1 = Rc(strong=2, weak=1) = {value = 42, strong = 2, weak = 1} +// gdb-command:print a +// gdb-check:[...]$2 = Arc(strong=2, weak=1) = {value = 42, strong = 2, weak = 1} + + +// === LLDB TESTS ================================================================================== + +// lldb-command:run + +// lldb-command:print r +// lldb-check:[...]$0 = strong=2, weak=1 { value = 42 } +// lldb-command:print a +// lldb-check:[...]$1 = strong=2, weak=1 { data = 42 } + +use std::rc::Rc; +use std::sync::Arc; + +fn main() { + let r = Rc::new(42); + let r1 = Rc::clone(&r); + let w1 = Rc::downgrade(&r); + + let a = Arc::new(42); + let a1 = Arc::clone(&a); + let w2 = Arc::downgrade(&a); + + print!(""); // #break +} 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/self-in-default-method.rs b/src/test/debuginfo/self-in-default-method.rs index bc150e3378945..e15c08577e15a 100644 --- a/src/test/debuginfo/self-in-default-method.rs +++ b/src/test/debuginfo/self-in-default-method.rs @@ -63,7 +63,7 @@ // STACK BY REF // lldb-command:print *self -// lldbg-check:[...]$0 = Struct { x: 100 } +// lldbg-check:[...]$0 = { x = 100 } // lldbr-check:(self_in_default_method::Struct) *self = Struct { x: 100 } // lldb-command:print arg1 // lldbg-check:[...]$1 = -1 @@ -75,7 +75,7 @@ // STACK BY VAL // lldb-command:print self -// lldbg-check:[...]$3 = Struct { x: 100 } +// lldbg-check:[...]$3 = { x = 100 } // lldbr-check:(self_in_default_method::Struct) self = Struct { x: 100 } // lldb-command:print arg1 // lldbg-check:[...]$4 = -3 @@ -87,7 +87,7 @@ // OWNED BY REF // lldb-command:print *self -// lldbg-check:[...]$6 = Struct { x: 200 } +// lldbg-check:[...]$6 = { x = 200 } // lldbr-check:(self_in_default_method::Struct) *self = Struct { x: 200 } // lldb-command:print arg1 // lldbg-check:[...]$7 = -5 @@ -99,7 +99,7 @@ // OWNED BY VAL // lldb-command:print self -// lldbg-check:[...]$9 = Struct { x: 200 } +// lldbg-check:[...]$9 = { x = 200 } // lldbr-check:(self_in_default_method::Struct) self = Struct { x: 200 } // lldb-command:print arg1 // lldbg-check:[...]$10 = -7 @@ -111,7 +111,7 @@ // OWNED MOVED // lldb-command:print *self -// lldbg-check:[...]$12 = Struct { x: 200 } +// lldbg-check:[...]$12 = { x = 200 } // lldbr-check:(self_in_default_method::Struct) *self = Struct { x: 200 } // lldb-command:print arg1 // lldbg-check:[...]$13 = -9 diff --git a/src/test/debuginfo/self-in-generic-default-method.rs b/src/test/debuginfo/self-in-generic-default-method.rs index 6c156230e5343..7634e3247d591 100644 --- a/src/test/debuginfo/self-in-generic-default-method.rs +++ b/src/test/debuginfo/self-in-generic-default-method.rs @@ -63,7 +63,7 @@ // STACK BY REF // lldb-command:print *self -// lldbg-check:[...]$0 = Struct { x: 987 } +// lldbg-check:[...]$0 = { x = 987 } // lldbr-check:(self_in_generic_default_method::Struct) *self = Struct { x: 987 } // lldb-command:print arg1 // lldbg-check:[...]$1 = -1 @@ -75,7 +75,7 @@ // STACK BY VAL // lldb-command:print self -// lldbg-check:[...]$3 = Struct { x: 987 } +// lldbg-check:[...]$3 = { x = 987 } // lldbr-check:(self_in_generic_default_method::Struct) self = Struct { x: 987 } // lldb-command:print arg1 // lldbg-check:[...]$4 = -3 @@ -87,7 +87,7 @@ // OWNED BY REF // lldb-command:print *self -// lldbg-check:[...]$6 = Struct { x: 879 } +// lldbg-check:[...]$6 = { x = 879 } // lldbr-check:(self_in_generic_default_method::Struct) *self = Struct { x: 879 } // lldb-command:print arg1 // lldbg-check:[...]$7 = -5 @@ -99,7 +99,7 @@ // OWNED BY VAL // lldb-command:print self -// lldbg-check:[...]$9 = Struct { x: 879 } +// lldbg-check:[...]$9 = { x = 879 } // lldbr-check:(self_in_generic_default_method::Struct) self = Struct { x: 879 } // lldb-command:print arg1 // lldbg-check:[...]$10 = -7 @@ -111,7 +111,7 @@ // OWNED MOVED // lldb-command:print *self -// lldbg-check:[...]$12 = Struct { x: 879 } +// lldbg-check:[...]$12 = { x = 879 } // lldbr-check:(self_in_generic_default_method::Struct) *self = Struct { x: 879 } // lldb-command:print arg1 // lldbg-check:[...]$13 = -9 diff --git a/src/test/debuginfo/simple-struct.rs b/src/test/debuginfo/simple-struct.rs index b9475d5429ac5..49aa3bcbcaaa8 100644 --- a/src/test/debuginfo/simple-struct.rs +++ b/src/test/debuginfo/simple-struct.rs @@ -100,28 +100,28 @@ // lldb-command:run // lldb-command:print no_padding16 -// lldbg-check:[...]$0 = NoPadding16 { x: 10000, y: -10001 } -// lldbr-check:(simple_struct::NoPadding16) no_padding16 = NoPadding16 { x: 10000, y: -10001 } +// lldbg-check:[...]$0 = { x = 10000 y = -10001 } +// lldbr-check:(simple_struct::NoPadding16) no_padding16 = { x = 10000 y = -10001 } // lldb-command:print no_padding32 -// lldbg-check:[...]$1 = NoPadding32 { x: -10002, y: -10003.5, z: 10004 } -// lldbr-check:(simple_struct::NoPadding32) no_padding32 = NoPadding32 { x: -10002, y: -10003.5, z: 10004 } +// lldbg-check:[...]$1 = { x = -10002 y = -10003.5 z = 10004 } +// lldbr-check:(simple_struct::NoPadding32) no_padding32 = { x = -10002 y = -10003.5 z = 10004 } // lldb-command:print no_padding64 -// lldbg-check:[...]$2 = NoPadding64 { x: -10005.5, y: 10006, z: 10007 } -// lldbr-check:(simple_struct::NoPadding64) no_padding64 = NoPadding64 { x: -10005.5, y: 10006, z: 10007 } +// lldbg-check:[...]$2 = { x = -10005.5 y = 10006 z = 10007 } +// lldbr-check:(simple_struct::NoPadding64) no_padding64 = { x = -10005.5 y = 10006 z = 10007 } // lldb-command:print no_padding163264 -// lldbg-check:[...]$3 = NoPadding163264 { a: -10008, b: 10009, c: 10010, d: 10011 } -// lldbr-check:(simple_struct::NoPadding163264) no_padding163264 = NoPadding163264 { a: -10008, b: 10009, c: 10010, d: 10011 } +// lldbg-check:[...]$3 = { a = -10008 b = 10009 c = 10010 d = 10011 } +// lldbr-check:(simple_struct::NoPadding163264) no_padding163264 = { a = -10008 b = 10009 c = 10010 d = 10011 } // lldb-command:print internal_padding -// lldbg-check:[...]$4 = InternalPadding { x: 10012, y: -10013 } -// lldbr-check:(simple_struct::InternalPadding) internal_padding = InternalPadding { x: 10012, y: -10013 } +// lldbg-check:[...]$4 = { x = 10012 y = -10013 } +// lldbr-check:(simple_struct::InternalPadding) internal_padding = { x = 10012 y = -10013 } // lldb-command:print padding_at_end -// lldbg-check:[...]$5 = PaddingAtEnd { x: -10014, y: 10015 } -// lldbr-check:(simple_struct::PaddingAtEnd) padding_at_end = PaddingAtEnd { x: -10014, y: 10015 } +// lldbg-check:[...]$5 = { x = -10014 y = 10015 } +// lldbr-check:(simple_struct::PaddingAtEnd) padding_at_end = { x = -10014 y = 10015 } #![allow(unused_variables)] #![allow(dead_code)] diff --git a/src/test/debuginfo/simple-tuple.rs b/src/test/debuginfo/simple-tuple.rs index f7e5b5c982a80..c2db5218e6842 100644 --- a/src/test/debuginfo/simple-tuple.rs +++ b/src/test/debuginfo/simple-tuple.rs @@ -100,28 +100,28 @@ // lldb-command:run // lldb-command:print/d noPadding8 -// lldbg-check:[...]$0 = (-100, 100) -// lldbr-check:((i8, u8)) noPadding8 = { = -100 -100 = 100 100 } +// lldbg-check:[...]$0 = { 0 = -100 1 = 100 } +// lldbr-check:((i8, u8)) noPadding8 = { 0 = -100 1 = 100 } // lldb-command:print noPadding16 -// lldbg-check:[...]$1 = (0, 1, 2) -// lldbr-check:((i16, i16, u16)) noPadding16 = { = 0 = 1 = 2 } +// lldbg-check:[...]$1 = { 0 = 0 1 = 1 2 = 2 } +// lldbr-check:((i16, i16, u16)) noPadding16 = { 0 = 0 1 = 1 2 = 2 } // lldb-command:print noPadding32 -// lldbg-check:[...]$2 = (3, 4.5, 5) -// lldbr-check:((i32, f32, u32)) noPadding32 = { = 3 = 4.5 = 5 } +// lldbg-check:[...]$2 = { 0 = 3 1 = 4.5 2 = 5 } +// lldbr-check:((i32, f32, u32)) noPadding32 = { 0 = 3 1 = 4.5 2 = 5 } // lldb-command:print noPadding64 -// lldbg-check:[...]$3 = (6, 7.5, 8) -// lldbr-check:((i64, f64, u64)) noPadding64 = { = 6 = 7.5 = 8 } +// lldbg-check:[...]$3 = { 0 = 6 1 = 7.5 2 = 8 } +// lldbr-check:((i64, f64, u64)) noPadding64 = { 0 = 6 1 = 7.5 2 = 8 } // lldb-command:print internalPadding1 -// lldbg-check:[...]$4 = (9, 10) -// lldbr-check:((i16, i32)) internalPadding1 = { = 9 = 10 } +// lldbg-check:[...]$4 = { 0 = 9 1 = 10 } +// lldbr-check:((i16, i32)) internalPadding1 = { 0 = 9 1 = 10 } // lldb-command:print internalPadding2 -// lldbg-check:[...]$5 = (11, 12, 13, 14) -// lldbr-check:((i16, i32, u32, u64)) internalPadding2 = { = 11 = 12 = 13 = 14 } +// lldbg-check:[...]$5 = { 0 = 11 1 = 12 2 = 13 3 = 14 } +// lldbr-check:((i16, i32, u32, u64)) internalPadding2 = { 0 = 11 1 = 12 2 = 13 3 = 14 } // lldb-command:print paddingAtEnd -// lldbg-check:[...]$6 = (15, 16) -// lldbr-check:((i32, i16)) paddingAtEnd = { = 15 = 16 } +// lldbg-check:[...]$6 = { 0 = 15 1 = 16 } +// lldbr-check:((i32, i16)) paddingAtEnd = { 0 = 15 1 = 16 } #![allow(unused_variables)] #![allow(dead_code)] diff --git a/src/test/debuginfo/struct-in-struct.rs b/src/test/debuginfo/struct-in-struct.rs index 46340508ae001..a76a4c05d9bdd 100644 --- a/src/test/debuginfo/struct-in-struct.rs +++ b/src/test/debuginfo/struct-in-struct.rs @@ -25,36 +25,36 @@ // lldb-command:run // lldb-command:print three_simple_structs -// lldbg-check:[...]$0 = ThreeSimpleStructs { x: Simple { x: 1 }, y: Simple { x: 2 }, z: Simple { x: 3 } } -// lldbr-check:(struct_in_struct::ThreeSimpleStructs) three_simple_structs = ThreeSimpleStructs { x: Simple { x: 1 }, y: Simple { x: 2 }, z: Simple { x: 3 } } +// lldbg-check:[...]$0 = { x = { x = 1 } y = { x = 2 } z = { x = 3 } } +// lldbr-check:(struct_in_struct::ThreeSimpleStructs) three_simple_structs = { x = { x = 1 } y = { x = 2 } z = { x = 3 } } // lldb-command:print internal_padding_parent -// lldbg-check:[...]$1 = InternalPaddingParent { x: InternalPadding { x: 4, y: 5 }, y: InternalPadding { x: 6, y: 7 }, z: InternalPadding { x: 8, y: 9 } } -// lldbr-check:(struct_in_struct::InternalPaddingParent) internal_padding_parent = InternalPaddingParent { x: InternalPadding { x: 4, y: 5 }, y: InternalPadding { x: 6, y: 7 }, z: InternalPadding { x: 8, y: 9 } } +// lldbg-check:[...]$1 = { x = { x = 4 y = 5 } y = { x = 6 y = 7 } z = { x = 8 y = 9 } } +// lldbr-check:(struct_in_struct::InternalPaddingParent) internal_padding_parent = { x = { x = 4 y = 5 } y = { x = 6 y = 7 } z = { x = 8 y = 9 } } // lldb-command:print padding_at_end_parent -// lldbg-check:[...]$2 = PaddingAtEndParent { x: PaddingAtEnd { x: 10, y: 11 }, y: PaddingAtEnd { x: 12, y: 13 }, z: PaddingAtEnd { x: 14, y: 15 } } -// lldbr-check:(struct_in_struct::PaddingAtEndParent) padding_at_end_parent = PaddingAtEndParent { x: PaddingAtEnd { x: 10, y: 11 }, y: PaddingAtEnd { x: 12, y: 13 }, z: PaddingAtEnd { x: 14, y: 15 } } +// lldbg-check:[...]$2 = { x = { x = 10 y = 11 } y = { x = 12 y = 13 } z = { x = 14 y = 15 } } +// lldbr-check:(struct_in_struct::PaddingAtEndParent) padding_at_end_parent = { x = { x = 10 y = 11 } y = { x = 12 y = 13 } z = { x = 14 y = 15 } } // lldb-command:print mixed -// lldbg-check:[...]$3 = Mixed { x: PaddingAtEnd { x: 16, y: 17 }, y: InternalPadding { x: 18, y: 19 }, z: Simple { x: 20 }, w: 21 } -// lldbr-check:(struct_in_struct::Mixed) mixed = Mixed { x: PaddingAtEnd { x: 16, y: 17 }, y: InternalPadding { x: 18, y: 19 }, z: Simple { x: 20 }, w: 21 } +// lldbg-check:[...]$3 = { x = { x = 16 y = 17 } y = { x = 18 y = 19 } z = { x = 20 } w = 21 } +// lldbr-check:(struct_in_struct::Mixed) mixed = { x = { x = 16 y = 17 } y = { x = 18 y = 19 } z = { x = 20 } w = 21 } // lldb-command:print bag -// lldbg-check:[...]$4 = Bag { x: Simple { x: 22 } } -// lldbr-check:(struct_in_struct::Bag) bag = Bag { x: Simple { x: 22 } } +// lldbg-check:[...]$4 = { x = { x = 22 } } +// lldbr-check:(struct_in_struct::Bag) bag = { x = { x = 22 } } // lldb-command:print bag_in_bag -// lldbg-check:[...]$5 = BagInBag { x: Bag { x: Simple { x: 23 } } } -// lldbr-check:(struct_in_struct::BagInBag) bag_in_bag = BagInBag { x: Bag { x: Simple { x: 23 } } } +// lldbg-check:[...]$5 = { x = { x = { x = 23 } } } +// lldbr-check:(struct_in_struct::BagInBag) bag_in_bag = { x = { x = { x = 23 } } } // lldb-command:print tjo -// lldbg-check:[...]$6 = ThatsJustOverkill { x: BagInBag { x: Bag { x: Simple { x: 24 } } } } -// lldbr-check:(struct_in_struct::ThatsJustOverkill) tjo = ThatsJustOverkill { x: BagInBag { x: Bag { x: Simple { x: 24 } } } } +// lldbg-check:[...]$6 = { x = { x = { x = { x = 24 } } } } +// lldbr-check:(struct_in_struct::ThatsJustOverkill) tjo = { x = { x = { x = { x = 24 } } } } // lldb-command:print tree -// lldbg-check:[...]$7 = Tree { x: Simple { x: 25 }, y: InternalPaddingParent { x: InternalPadding { x: 26, y: 27 }, y: InternalPadding { x: 28, y: 29 }, z: InternalPadding { x: 30, y: 31 } }, z: BagInBag { x: Bag { x: Simple { x: 32 } } } } -// lldbr-check:(struct_in_struct::Tree) tree = Tree { x: Simple { x: 25 }, y: InternalPaddingParent { x: InternalPadding { x: 26, y: 27 }, y: InternalPadding { x: 28, y: 29 }, z: InternalPadding { x: 30, y: 31 } }, z: BagInBag { x: Bag { x: Simple { x: 32 } } } } +// lldbg-check:[...]$7 = { x = { x = 25 } y = { x = { x = 26 y = 27 } y = { x = 28 y = 29 } z = { x = 30 y = 31 } } z = { x = { x = { x = 32 } } } } +// lldbr-check:(struct_in_struct::Tree) tree = { x = { x = 25 } y = { x = { x = 26 y = 27 } y = { x = 28 y = 29 } z = { x = 30 y = 31 } } z = { x = { x = { x = 32 } } } } #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] 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/struct-with-destructor.rs b/src/test/debuginfo/struct-with-destructor.rs index c478e6d22222b..ebae953cb4b04 100644 --- a/src/test/debuginfo/struct-with-destructor.rs +++ b/src/test/debuginfo/struct-with-destructor.rs @@ -28,20 +28,20 @@ // lldb-command:run // lldb-command:print simple -// lldbg-check:[...]$0 = WithDestructor { x: 10, y: 20 } -// lldbr-check:(struct_with_destructor::WithDestructor) simple = WithDestructor { x: 10, y: 20 } +// lldbg-check:[...]$0 = { x = 10 y = 20 } +// lldbr-check:(struct_with_destructor::WithDestructor) simple = { x = 10 y = 20 } // lldb-command:print noDestructor -// lldbg-check:[...]$1 = NoDestructorGuarded { a: NoDestructor { x: 10, y: 20 }, guard: -1 } -// lldbr-check:(struct_with_destructor::NoDestructorGuarded) noDestructor = NoDestructorGuarded { a: NoDestructor { x: 10, y: 20 }, guard: -1 } +// lldbg-check:[...]$1 = { a = { x = 10 y = 20 } guard = -1 } +// lldbr-check:(struct_with_destructor::NoDestructorGuarded) noDestructor = { a = { x = 10 y = 20 } guard = -1 } // lldb-command:print withDestructor -// lldbg-check:[...]$2 = WithDestructorGuarded { a: WithDestructor { x: 10, y: 20 }, guard: -1 } -// lldbr-check:(struct_with_destructor::WithDestructorGuarded) withDestructor = WithDestructorGuarded { a: WithDestructor { x: 10, y: 20 }, guard: -1 } +// lldbg-check:[...]$2 = { a = { x = 10 y = 20 } guard = -1 } +// lldbr-check:(struct_with_destructor::WithDestructorGuarded) withDestructor = { a = { x = 10 y = 20 } guard = -1 } // lldb-command:print nested -// lldbg-check:[...]$3 = NestedOuter { a: NestedInner { a: WithDestructor { x: 7890, y: 9870 } } } -// lldbr-check:(struct_with_destructor::NestedOuter) nested = NestedOuter { a: NestedInner { a: WithDestructor { x: 7890, y: 9870 } } } +// lldbg-check:[...]$3 = { a = { a = { x = 7890 y = 9870 } } } +// lldbr-check:(struct_with_destructor::NestedOuter) nested = { a = { a = { x = 7890 y = 9870 } } } #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/tuple-in-tuple.rs b/src/test/debuginfo/tuple-in-tuple.rs index 5727521f5f542..e0f940ca7a1c1 100644 --- a/src/test/debuginfo/tuple-in-tuple.rs +++ b/src/test/debuginfo/tuple-in-tuple.rs @@ -36,28 +36,28 @@ // lldb-command:run // lldb-command:print no_padding1 -// lldbg-check:[...]$0 = ((0, 1), 2, 3) -// lldbr-check:(((u32, u32), u32, u32)) no_padding1 = { = { = 0 = 1 } = 2 = 3 } +// lldbg-check:[...]$0 = { 0 = { 0 = 0 1 = 1 } 1 = 2 2 = 3 } +// lldbr-check:(((u32, u32), u32, u32)) no_padding1 = { 0 = { 0 = 0 1 = 1 } 1 = 2 2 = 3 } // lldb-command:print no_padding2 -// lldbg-check:[...]$1 = (4, (5, 6), 7) -// lldbr-check:((u32, (u32, u32), u32)) no_padding2 = { = 4 = { = 5 = 6 } = 7 } +// lldbg-check:[...]$1 = { 0 = 4 1 = { 0 = 5 1 = 6 } 2 = 7 } +// lldbr-check:((u32, (u32, u32), u32)) no_padding2 = { 0 = 4 1 = { 0 = 5 1 = 6 } 2 = 7 } // lldb-command:print no_padding3 -// lldbg-check:[...]$2 = (8, 9, (10, 11)) -// lldbr-check:((u32, u32, (u32, u32))) no_padding3 = { = 8 = 9 = { = 10 = 11 } } +// lldbg-check:[...]$2 = { 0 = 8 1 = 9 2 = { 0 = 10 1 = 11 } } +// lldbr-check:((u32, u32, (u32, u32))) no_padding3 = { 0 = 8 1 = 9 2 = { 0 = 10 1 = 11 } } // lldb-command:print internal_padding1 -// lldbg-check:[...]$3 = (12, (13, 14)) -// lldbr-check:((i16, (i32, i32))) internal_padding1 = { = 12 = { = 13 = 14 } } +// lldbg-check:[...]$3 = { 0 = 12 1 = { 0 = 13 1 = 14 } } +// lldbr-check:((i16, (i32, i32))) internal_padding1 = { 0 = 12 1 = { 0 = 13 1 = 14 } } // lldb-command:print internal_padding2 -// lldbg-check:[...]$4 = (15, (16, 17)) -// lldbr-check:((i16, (i16, i32))) internal_padding2 = { = 15 = { = 16 = 17 } } +// lldbg-check:[...]$4 = { 0 = 15 1 = { 0 = 16 1 = 17 } } +// lldbr-check:((i16, (i16, i32))) internal_padding2 = { 0 = 15 1 = { 0 = 16 1 = 17 } } // lldb-command:print padding_at_end1 -// lldbg-check:[...]$5 = (18, (19, 20)) -// lldbr-check:((i32, (i32, i16))) padding_at_end1 = { = 18 = { = 19 = 20 } } +// lldbg-check:[...]$5 = { 0 = 18 1 = { 0 = 19 1 = 20 } } +// lldbr-check:((i32, (i32, i16))) padding_at_end1 = { 0 = 18 1 = { 0 = 19 1 = 20 } } // lldb-command:print padding_at_end2 -// lldbg-check:[...]$6 = ((21, 22), 23) -// lldbr-check:(((i32, i16), i32)) padding_at_end2 = { = { = 21 = 22 } = 23 } +// lldbg-check:[...]$6 = { 0 = { 0 = 21 1 = 22 } 1 = 23 } +// lldbr-check:(((i32, i16), i32)) padding_at_end2 = { 0 = { 0 = 21 1 = 22 } 1 = 23 } #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/tuple-struct.rs b/src/test/debuginfo/tuple-struct.rs index f06ff176acd96..78b6e6a54dbc9 100644 --- a/src/test/debuginfo/tuple-struct.rs +++ b/src/test/debuginfo/tuple-struct.rs @@ -38,28 +38,28 @@ // lldb-command:run // lldb-command:print no_padding16 -// lldbg-check:[...]$0 = NoPadding16(10000, -10001) -// lldbr-check:(tuple_struct::NoPadding16) no_padding16 = { = 10000 = -10001 } +// lldbg-check:[...]$0 = { 0 = 10000 1 = -10001 } +// lldbr-check:(tuple_struct::NoPadding16) no_padding16 = { 0 = 10000 1 = -10001 } // lldb-command:print no_padding32 -// lldbg-check:[...]$1 = NoPadding32(-10002, -10003.5, 10004) -// lldbr-check:(tuple_struct::NoPadding32) no_padding32 = { = -10002 = -10003.5 = 10004 } +// lldbg-check:[...]$1 = { 0 = -10002 1 = -10003.5 2 = 10004 } +// lldbr-check:(tuple_struct::NoPadding32) no_padding32 = { 0 = -10002 1 = -10003.5 2 = 10004 } // lldb-command:print no_padding64 -// lldbg-check:[...]$2 = NoPadding64(-10005.5, 10006, 10007) -// lldbr-check:(tuple_struct::NoPadding64) no_padding64 = { = -10005.5 = 10006 = 10007 } +// lldbg-check:[...]$2 = { 0 = -10005.5 1 = 10006 2 = 10007 } +// lldbr-check:(tuple_struct::NoPadding64) no_padding64 = { 0 = -10005.5 1 = 10006 2 = 10007 } // lldb-command:print no_padding163264 -// lldbg-check:[...]$3 = NoPadding163264(-10008, 10009, 10010, 10011) -// lldbr-check:(tuple_struct::NoPadding163264) no_padding163264 = { = -10008 = 10009 = 10010 = 10011 } +// lldbg-check:[...]$3 = { 0 = -10008 1 = 10009 2 = 10010 3 = 10011 } +// lldbr-check:(tuple_struct::NoPadding163264) no_padding163264 = { 0 = -10008 1 = 10009 2 = 10010 3 = 10011 } // lldb-command:print internal_padding -// lldbg-check:[...]$4 = InternalPadding(10012, -10013) -// lldbr-check:(tuple_struct::InternalPadding) internal_padding = { = 10012 = -10013 } +// lldbg-check:[...]$4 = { 0 = 10012 1 = -10013 } +// lldbr-check:(tuple_struct::InternalPadding) internal_padding = { 0 = 10012 1 = -10013 } // lldb-command:print padding_at_end -// lldbg-check:[...]$5 = PaddingAtEnd(-10014, 10015) -// lldbr-check:(tuple_struct::PaddingAtEnd) padding_at_end = { = -10014 = 10015 } +// lldbg-check:[...]$5 = { 0 = -10014 1 = 10015 } +// lldbr-check:(tuple_struct::PaddingAtEnd) padding_at_end = { 0 = -10014 1 = 10015 } // This test case mainly makes sure that no field names are generated for tuple structs (as opposed // to all fields having the name ""). Otherwise they are handled the same a normal 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/union-smoke.rs b/src/test/debuginfo/union-smoke.rs index 79b4030ee34dc..4d4b6cc96fb57 100644 --- a/src/test/debuginfo/union-smoke.rs +++ b/src/test/debuginfo/union-smoke.rs @@ -19,13 +19,13 @@ // lldb-command:run // lldb-command:print u -// lldbg-check:[...]$0 = U { a: ('\x02', '\x02'), b: 514 } -// lldbr-check:(union_smoke::U) u = { a = { = 2 = 2 } b = 514 } +// lldbg-check:[...]$0 = { a = { 0 = '\x02' 1 = '\x02' } b = 514 } +// lldbr-check:(union_smoke::U) u = { a = { 0 = '\x02' 1 = '\x02' } b = 514 } // Don't test this with rust-enabled lldb for now; see // https://github.com/rust-lang-nursery/lldb/issues/18 // lldbg-command:print union_smoke::SU -// lldbg-check:[...]$1 = U { a: ('\x01', '\x01'), b: 257 } +// lldbg-check:[...]$1 = { a = { 0 = '\x01' 1 = '\x01' } b = 257 } #![allow(unused)] #![feature(omit_gdb_pretty_printer_section)] 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/debuginfo/var-captured-in-nested-closure.rs b/src/test/debuginfo/var-captured-in-nested-closure.rs index a0f0de20d0c97..8ab6d141731ea 100644 --- a/src/test/debuginfo/var-captured-in-nested-closure.rs +++ b/src/test/debuginfo/var-captured-in-nested-closure.rs @@ -50,11 +50,11 @@ // lldbg-check:[...]$1 = 2 // lldbr-check:(isize) constant = 2 // lldb-command:print a_struct -// lldbg-check:[...]$2 = Struct { a: -3, b: 4.5, c: 5 } -// lldbr-check:(var_captured_in_nested_closure::Struct) a_struct = Struct { a: -3, b: 4.5, c: 5 } +// lldbg-check:[...]$2 = { a = -3 b = 4.5 c = 5 } +// lldbr-check:(var_captured_in_nested_closure::Struct) a_struct = { a = -3 b = 4.5 c = 5 } // lldb-command:print *struct_ref -// lldbg-check:[...]$3 = Struct { a: -3, b: 4.5, c: 5 } -// lldbr-check:(var_captured_in_nested_closure::Struct) *struct_ref = Struct { a: -3, b: 4.5, c: 5 } +// lldbg-check:[...]$3 = { a = -3 b = 4.5 c = 5 } +// lldbr-check:(var_captured_in_nested_closure::Struct) *struct_ref = { a = -3 b = 4.5 c = 5 } // lldb-command:print *owned // lldbg-check:[...]$4 = 6 // lldbr-check:(isize) *owned = 6 @@ -70,11 +70,11 @@ // lldbg-check:[...]$7 = 2 // lldbr-check:(isize) constant = 2 // lldb-command:print a_struct -// lldbg-check:[...]$8 = Struct { a: -3, b: 4.5, c: 5 } -// lldbr-check:(var_captured_in_nested_closure::Struct) a_struct = Struct { a: -3, b: 4.5, c: 5 } +// lldbg-check:[...]$8 = { a = -3 b = 4.5 c = 5 } +// lldbr-check:(var_captured_in_nested_closure::Struct) a_struct = { a = -3 b = 4.5 c = 5 } // lldb-command:print *struct_ref -// lldbg-check:[...]$9 = Struct { a: -3, b: 4.5, c: 5 } -// lldbr-check:(var_captured_in_nested_closure::Struct) *struct_ref = Struct { a: -3, b: 4.5, c: 5 } +// lldbg-check:[...]$9 = { a = -3 b = 4.5 c = 5 } +// lldbr-check:(var_captured_in_nested_closure::Struct) *struct_ref = { a = -3 b = 4.5 c = 5 } // lldb-command:print *owned // lldbg-check:[...]$10 = 6 // lldbr-check:(isize) *owned = 6 diff --git a/src/test/debuginfo/var-captured-in-sendable-closure.rs b/src/test/debuginfo/var-captured-in-sendable-closure.rs index b6a3c32ac4767..bd7c2bfe2c3ff 100644 --- a/src/test/debuginfo/var-captured-in-sendable-closure.rs +++ b/src/test/debuginfo/var-captured-in-sendable-closure.rs @@ -27,8 +27,8 @@ // lldbg-check:[...]$0 = 1 // lldbr-check:(isize) constant = 1 // lldb-command:print a_struct -// lldbg-check:[...]$1 = Struct { a: -2, b: 3.5, c: 4 } -// lldbr-check:(var_captured_in_sendable_closure::Struct) a_struct = Struct { a: -2, b: 3.5, c: 4 } +// lldbg-check:[...]$1 = { a = -2 b = 3.5 c = 4 } +// lldbr-check:(var_captured_in_sendable_closure::Struct) a_struct = { a = -2 b = 3.5 c = 4 } // lldb-command:print *owned // lldbg-check:[...]$2 = 5 // lldbr-check:(isize) *owned = 5 diff --git a/src/test/debuginfo/var-captured-in-stack-closure.rs b/src/test/debuginfo/var-captured-in-stack-closure.rs index 2402b5f68518b..f53f8aaa6701e 100644 --- a/src/test/debuginfo/var-captured-in-stack-closure.rs +++ b/src/test/debuginfo/var-captured-in-stack-closure.rs @@ -46,11 +46,11 @@ // lldbg-check:[...]$1 = 2 // lldbr-check:(isize) constant = 2 // lldb-command:print a_struct -// lldbg-check:[...]$2 = Struct { a: -3, b: 4.5, c: 5 } -// lldbr-check:(var_captured_in_stack_closure::Struct) a_struct = Struct { a: -3, b: 4.5, c: 5 } +// lldbg-check:[...]$2 = { a = -3 b = 4.5 c = 5 } +// lldbr-check:(var_captured_in_stack_closure::Struct) a_struct = { a = -3 b = 4.5 c = 5 } // lldb-command:print *struct_ref -// lldbg-check:[...]$3 = Struct { a: -3, b: 4.5, c: 5 } -// lldbr-check:(var_captured_in_stack_closure::Struct) *struct_ref = Struct { a: -3, b: 4.5, c: 5 } +// lldbg-check:[...]$3 = { a = -3 b = 4.5 c = 5 } +// lldbr-check:(var_captured_in_stack_closure::Struct) *struct_ref = { a = -3 b = 4.5 c = 5 } // lldb-command:print *owned // lldbg-check:[...]$4 = 6 // lldbr-check:(isize) *owned = 6 @@ -64,11 +64,11 @@ // lldbg-check:[...]$6 = 2 // lldbr-check:(isize) constant = 2 // lldb-command:print a_struct -// lldbg-check:[...]$7 = Struct { a: -3, b: 4.5, c: 5 } -// lldbr-check:(var_captured_in_stack_closure::Struct) a_struct = Struct { a: -3, b: 4.5, c: 5 } +// lldbg-check:[...]$7 = { a = -3 b = 4.5 c = 5 } +// lldbr-check:(var_captured_in_stack_closure::Struct) a_struct = { a = -3 b = 4.5 c = 5 } // lldb-command:print *struct_ref -// lldbg-check:[...]$8 = Struct { a: -3, b: 4.5, c: 5 } -// lldbr-check:(var_captured_in_stack_closure::Struct) *struct_ref = Struct { a: -3, b: 4.5, c: 5 } +// lldbg-check:[...]$8 = { a = -3 b = 4.5 c = 5 } +// lldbr-check:(var_captured_in_stack_closure::Struct) *struct_ref = { a = -3 b = 4.5 c = 5 } // lldb-command:print *owned // lldbg-check:[...]$9 = 6 // lldbr-check:(isize) *owned = 6 diff --git a/src/test/debuginfo/vec-slices.rs b/src/test/debuginfo/vec-slices.rs index 8d9fc498882d3..c385491bd1d53 100644 --- a/src/test/debuginfo/vec-slices.rs +++ b/src/test/debuginfo/vec-slices.rs @@ -72,28 +72,28 @@ // lldb-command:run // lldb-command:print empty -// lldbg-check:[...]$0 = &[] -// lldbr-check:(&[i64]) empty = &[] +// lldbg-check:[...]$0 = size=0 +// lldbr-check:(&[i64]) empty = size=0 // lldb-command:print singleton -// lldbg-check:[...]$1 = &[1] +// lldbg-check:[...]$1 = size=1 { [0] = 1 } // lldbr-check:(&[i64]) singleton = &[1] // lldb-command:print multiple -// lldbg-check:[...]$2 = &[2, 3, 4, 5] -// lldbr-check:(&[i64]) multiple = &[2, 3, 4, 5] +// lldbg-check:[...]$2 = size=4 { [0] = 2 [1] = 3 [2] = 4 [3] = 5 } +// lldbr-check:(&[i64]) multiple = size=4 { [0] = 2 [1] = 3 [2] = 4 [3] = 5 } // lldb-command:print slice_of_slice -// lldbg-check:[...]$3 = &[3, 4] -// lldbr-check:(&[i64]) slice_of_slice = &[3, 4] +// lldbg-check:[...]$3 = size=2 { [0] = 3 [1] = 4 } +// lldbr-check:(&[i64]) slice_of_slice = size=2 { [0] = 3 [1] = 4 } // lldb-command:print padded_tuple -// lldbg-check:[...]$4 = &[(6, 7), (8, 9)] -// lldbr-check:(&[(i32, i16)]) padded_tuple = { data_ptr = *[...] length = 2 } +// lldbg-check:[...]$4 = size=2 { [0] = { 0 = 6 1 = 7 } [1] = { 0 = 8 1 = 9 } } +// lldbr-check:(&[(i32, i16)]) padded_tuple = size=2 { [0] = { 0 = 6 1 = 7 } [1] = { 0 = 8 1 = 9 } } // lldb-command:print padded_struct -// lldbg-check:[...]$5 = &[AStruct { x: 10, y: 11, z: 12 }, AStruct { x: 13, y: 14, z: 15 }] -// lldbr-check:(&[vec_slices::AStruct]) padded_struct = &[AStruct { x: 10, y: 11, z: 12 }, AStruct { x: 13, y: 14, z: 15 }] +// lldbg-check:[...]$5 = size=2 { [0] = { x = 10 y = 11 z = 12 } [1] = { x = 13 y = 14 z = 15 } } +// lldbr-check:(&[vec_slices::AStruct]) padded_struct = size=2 { [0] = { x = 10 y = 11 z = 12 } [1] = { x = 13 y = 14 z = 15 } } #![allow(dead_code, unused_variables)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/debuginfo/vec.rs b/src/test/debuginfo/vec.rs index b0ee521c145b0..895661816b864 100644 --- a/src/test/debuginfo/vec.rs +++ b/src/test/debuginfo/vec.rs @@ -18,8 +18,8 @@ // lldb-command:run // lldb-command:print a -// lldbg-check:[...]$0 = [1, 2, 3] -// lldbr-check:([i32; 3]) a = [1, 2, 3] +// lldbg-check:[...]$0 = { [0] = 1 [1] = 2 [2] = 3 } +// lldbr-check:([i32; 3]) a = { [0] = 1 [1] = 2 [2] = 3 } #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] diff --git a/src/test/incremental/change_symbol_export_status.rs b/src/test/incremental/change_symbol_export_status.rs index f3de46d99ddc0..9b3b381d6210a 100644 --- a/src/test/incremental/change_symbol_export_status.rs +++ b/src/test/incremental/change_symbol_export_status.rs @@ -2,10 +2,8 @@ // compile-flags: -Zquery-dep-graph #![feature(rustc_attrs)] -#![allow(private_no_mangle_fns)] - -#![rustc_partition_codegened(module="change_symbol_export_status-mod1", cfg="rpass2")] -#![rustc_partition_reused(module="change_symbol_export_status-mod2", cfg="rpass2")] +#![rustc_partition_codegened(module = "change_symbol_export_status-mod1", cfg = "rpass2")] +#![rustc_partition_reused(module = "change_symbol_export_status-mod2", cfg = "rpass2")] // This test case makes sure that a change in symbol visibility is detected by // our dependency tracking. We do this by changing a module's visibility to 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/call_expressions.rs b/src/test/incremental/hashes/call_expressions.rs index 87f108abadd55..3706ab4a02075 100644 --- a/src/test/incremental/hashes/call_expressions.rs +++ b/src/test/incremental/hashes/call_expressions.rs @@ -25,7 +25,7 @@ pub fn change_callee_function() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_callee_function() { callee2(1, 2) @@ -40,7 +40,7 @@ pub fn change_argument_function() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_argument_function() { callee1(1, 3) @@ -57,8 +57,8 @@ mod change_callee_indirectly_function { #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] - #[rustc_clean(label="hir_owner_items", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] + #[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] pub fn change_callee_indirectly_function() { @@ -81,7 +81,7 @@ pub fn change_callee_method() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_callee_method() { let s = Struct; @@ -98,7 +98,7 @@ pub fn change_argument_method() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_argument_method() { let s = Struct; @@ -115,7 +115,7 @@ pub fn change_ufcs_callee_method() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_ufcs_callee_method() { let s = Struct; @@ -132,7 +132,7 @@ pub fn change_argument_method_ufcs() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_argument_method_ufcs() { let s = Struct; @@ -149,9 +149,9 @@ pub fn change_to_ufcs() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] -// One might think this would be expanded in the hir_owner_items/Mir, but it actually +// One might think this would be expanded in the hir_owner_nodes/Mir, but it actually // results in slightly different hir_owner/Mir. pub fn change_to_ufcs() { let s = Struct; @@ -171,7 +171,7 @@ pub mod change_ufcs_callee_indirectly { #[cfg(not(cfail1))] use super::Struct2 as Struct; - #[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] diff --git a/src/test/incremental/hashes/closure_expressions.rs b/src/test/incremental/hashes/closure_expressions.rs index 3d9db340f6375..b1e9ed678c4c5 100644 --- a/src/test/incremental/hashes/closure_expressions.rs +++ b/src/test/incremental/hashes/closure_expressions.rs @@ -21,7 +21,7 @@ pub fn change_closure_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn change_closure_body() { let _ = || 3u32; @@ -37,7 +37,7 @@ pub fn add_parameter() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_parameter() { let x = 0u32; @@ -53,7 +53,7 @@ pub fn change_parameter_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_parameter_pattern() { let _ = |(x,): (u32,)| x; @@ -68,7 +68,7 @@ pub fn add_move() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_move() { let _ = move || 1; @@ -84,8 +84,8 @@ pub fn add_type_ascription_to_parameter() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, typeck_tables_of")] -#[rustc_clean(cfg="cfail3")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner_nodes, typeck_tables_of")] +#[rustc_clean(cfg = "cfail3")] pub fn add_type_ascription_to_parameter() { let closure = |x: u32| x + 1u32; let _: u32 = closure(1); @@ -101,7 +101,7 @@ pub fn change_parameter_type() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_parameter_type() { let closure = |x: u16| (x as u64) + 1; diff --git a/src/test/incremental/hashes/consts.rs b/src/test/incremental/hashes/consts.rs index 8f77bb24f872e..6e0db6a49aae2 100644 --- a/src/test/incremental/hashes/consts.rs +++ b/src/test/incremental/hashes/consts.rs @@ -19,7 +19,7 @@ const CONST_VISIBILITY: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub const CONST_VISIBILITY: u8 = 0; @@ -29,7 +29,7 @@ pub const CONST_VISIBILITY: u8 = 0; const CONST_CHANGE_TYPE_1: i32 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_1: u32 = 0; @@ -39,13 +39,13 @@ const CONST_CHANGE_TYPE_1: u32 = 0; const CONST_CHANGE_TYPE_2: Option = None; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_2: Option = None; // Change value between simple literals -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_VALUE_1: i16 = { #[cfg(cfail1)] @@ -57,7 +57,7 @@ const CONST_CHANGE_VALUE_1: i16 = { // Change value between expressions -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_VALUE_2: i16 = { #[cfg(cfail1)] @@ -67,7 +67,7 @@ const CONST_CHANGE_VALUE_2: i16 = { { 1 + 2 } }; -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_VALUE_3: i16 = { #[cfg(cfail1)] @@ -77,7 +77,7 @@ const CONST_CHANGE_VALUE_3: i16 = { { 2 * 3 } }; -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_VALUE_4: i16 = { #[cfg(cfail1)] @@ -99,11 +99,11 @@ mod const_change_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as Type; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_INDIRECTLY_1: Type = Type; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] const CONST_CHANGE_TYPE_INDIRECTLY_2: Option = None; } diff --git a/src/test/incremental/hashes/enum_constructors.rs b/src/test/incremental/hashes/enum_constructors.rs index 5ad6eeafc7fc3..2c07cbcb2054b 100644 --- a/src/test/incremental/hashes/enum_constructors.rs +++ b/src/test/incremental/hashes/enum_constructors.rs @@ -34,7 +34,7 @@ pub fn change_field_value_struct_like() -> Enum { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_field_value_struct_like() -> Enum { Enum::Struct { @@ -57,7 +57,7 @@ pub fn change_field_order_struct_like() -> Enum { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] // FIXME(michaelwoerister):Interesting. I would have thought that that changes the MIR. And it // would if it were not all constants @@ -96,7 +96,7 @@ pub fn change_constructor_path_struct_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_struct_like() { let _ = Enum2::Struct { @@ -119,7 +119,7 @@ pub fn change_constructor_variant_struct_like() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_variant_struct_like() { let _ = Enum2::Struct2 { @@ -139,7 +139,7 @@ pub mod change_constructor_path_indirectly_struct_like { #[rustc_clean( cfg="cfail2", - except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,\ + except="fn_sig,hir_owner,hir_owner_nodes,optimized_mir,mir_built,\ typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] @@ -161,7 +161,7 @@ pub mod change_constructor_variant_indirectly_struct_like { #[cfg(not(cfail1))] use super::Enum2::Struct2 as Variant; - #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] + #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn function() -> Enum2 { Variant { @@ -180,7 +180,7 @@ pub fn change_field_value_tuple_like() -> Enum { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_field_value_tuple_like() -> Enum { Enum::Tuple(0, 1, 3) @@ -197,7 +197,7 @@ pub fn change_constructor_path_tuple_like() { #[cfg(not(cfail1))] #[rustc_clean( cfg="cfail2", - except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of" + except="hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_tuple_like() { @@ -215,7 +215,7 @@ pub fn change_constructor_variant_tuple_like() { #[cfg(not(cfail1))] #[rustc_clean( cfg="cfail2", - except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of" + except="hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_variant_tuple_like() { @@ -232,7 +232,7 @@ pub mod change_constructor_path_indirectly_tuple_like { #[rustc_clean( cfg="cfail2", - except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,\ + except="fn_sig,hir_owner,hir_owner_nodes,optimized_mir,mir_built,\ typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] @@ -251,7 +251,7 @@ pub mod change_constructor_variant_indirectly_tuple_like { #[cfg(not(cfail1))] use super::Enum2::Tuple2 as Variant; - #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn function() -> Enum2 { Variant(0, 1, 2) @@ -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_items,optimized_mir,mir_built,typeck_tables_of")] +#[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_items,optimized_mir,mir_built")] +#[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; } @@ -309,7 +309,7 @@ pub mod change_constructor_path_indirectly_c_like { #[rustc_clean( cfg="cfail2", - except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,\ + except="fn_sig,hir_owner,hir_owner_nodes,optimized_mir,mir_built,\ typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] @@ -328,7 +328,7 @@ pub mod change_constructor_variant_indirectly_c_like { #[cfg(not(cfail1))] use super::Clike::B as Variant; - #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] + #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn function() -> Clike { Variant diff --git a/src/test/incremental/hashes/enum_defs.rs b/src/test/incremental/hashes/enum_defs.rs index 7be15b4bb155f..624870cc9ec8f 100644 --- a/src/test/incremental/hashes/enum_defs.rs +++ b/src/test/incremental/hashes/enum_defs.rs @@ -26,7 +26,7 @@ enum EnumVisibility { A } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub enum EnumVisibility { A @@ -42,7 +42,7 @@ enum EnumChangeNameCStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeNameCStyleVariant { Variant1, @@ -59,7 +59,7 @@ enum EnumChangeNameTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeNameTupleStyleVariant { Variant1, @@ -76,7 +76,7 @@ enum EnumChangeNameStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeNameStructStyleVariant { Variant1, @@ -93,7 +93,7 @@ enum EnumChangeValueCStyleVariant0 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] enum EnumChangeValueCStyleVariant0 { Variant1, @@ -109,7 +109,7 @@ enum EnumChangeValueCStyleVariant1 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeValueCStyleVariant1 { Variant1, @@ -125,7 +125,7 @@ enum EnumAddCStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddCStyleVariant { Variant1, @@ -142,7 +142,7 @@ enum EnumRemoveCStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumRemoveCStyleVariant { Variant1, @@ -157,7 +157,7 @@ enum EnumAddTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddTupleStyleVariant { Variant1, @@ -174,7 +174,7 @@ enum EnumRemoveTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumRemoveTupleStyleVariant { Variant1, @@ -189,7 +189,7 @@ enum EnumAddStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddStructStyleVariant { Variant1, @@ -206,7 +206,7 @@ enum EnumRemoveStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumRemoveStructStyleVariant { Variant1, @@ -221,7 +221,7 @@ enum EnumChangeFieldTypeTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] enum EnumChangeFieldTypeTupleStyleVariant { Variant1(u32, @@ -238,7 +238,7 @@ enum EnumChangeFieldTypeStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] enum EnumChangeFieldTypeStructStyleVariant { Variant1, @@ -257,7 +257,7 @@ enum EnumChangeFieldNameStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeFieldNameStructStyleVariant { Variant1 { a: u32, c: u32 }, @@ -272,7 +272,7 @@ enum EnumChangeOrderTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] enum EnumChangeOrderTupleStyleVariant { Variant1( @@ -289,7 +289,7 @@ enum EnumChangeFieldOrderStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumChangeFieldOrderStructStyleVariant { Variant1 { b: f32, a: u32 }, @@ -304,7 +304,7 @@ enum EnumAddFieldTupleStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddFieldTupleStyleVariant { Variant1(u32, u32, u32), @@ -319,7 +319,7 @@ enum EnumAddFieldStructStyleVariant { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] enum EnumAddFieldStructStyleVariant { Variant1 { a: u32, b: u32, c: u32 }, @@ -335,7 +335,7 @@ enum EnumAddMustUse { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] #[must_use] enum EnumAddMustUse { @@ -353,7 +353,7 @@ enum EnumAddReprC { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] #[repr(C)] enum EnumAddReprC { @@ -531,7 +531,7 @@ enum EnumSwapUsageTypeParameters { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] enum EnumSwapUsageTypeParameters { Variant1 { @@ -552,7 +552,7 @@ enum EnumSwapUsageLifetimeParameters<'a, 'b> { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] enum EnumSwapUsageLifetimeParameters<'a, 'b> { Variant1 { @@ -577,7 +577,7 @@ mod change_field_type_indirectly_tuple_style { #[cfg(not(cfail1))] use super::ReferencedType2 as FieldType; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] enum TupleStyle { Variant1( @@ -595,7 +595,7 @@ mod change_field_type_indirectly_struct_style { #[cfg(not(cfail1))] use super::ReferencedType2 as FieldType; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] enum StructStyle { Variant1 { @@ -618,7 +618,7 @@ mod change_trait_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,predicates_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,predicates_of")] #[rustc_clean(cfg="cfail3")] enum Enum { Variant1(T) @@ -634,7 +634,7 @@ mod change_trait_bound_indirectly_where { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,predicates_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,predicates_of")] #[rustc_clean(cfg="cfail3")] enum Enum where T: Trait { Variant1(T) diff --git a/src/test/incremental/hashes/exported_vs_not.rs b/src/test/incremental/hashes/exported_vs_not.rs index b546930ea8fcd..4ea58705017ee 100644 --- a/src/test/incremental/hashes/exported_vs_not.rs +++ b/src/test/incremental/hashes/exported_vs_not.rs @@ -7,7 +7,7 @@ #![crate_type="rlib"] // Case 1: The function body is not exported to metadata. If the body changes, -// the hash of the hir_owner_items node should change, but not the hash of +// the hash of the hir_owner_nodes node should change, but not the hash of // either the hir_owner or the Metadata node. #[cfg(cfail1)] @@ -16,7 +16,7 @@ pub fn body_not_exported_to_metadata() -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn body_not_exported_to_metadata() -> u32 { 2 @@ -35,7 +35,7 @@ pub fn body_exported_to_metadata_because_of_inline() -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] #[inline] pub fn body_exported_to_metadata_because_of_inline() -> u32 { @@ -55,7 +55,7 @@ pub fn body_exported_to_metadata_because_of_generic() -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] #[inline] pub fn body_exported_to_metadata_because_of_generic() -> u32 { diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index 3e54dafd9ac0e..d3d5a69c171f9 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; @@ -48,7 +48,7 @@ pub fn change_iteration_variable_name() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_iteration_variable_name() { let mut _x = 0; @@ -71,7 +71,7 @@ pub fn change_iteration_variable_pattern() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_iteration_variable_pattern() { let mut _x = 0; @@ -94,7 +94,7 @@ pub fn change_iterable() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, promoted_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, promoted_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_iterable() { let mut _x = 0; @@ -116,7 +116,7 @@ pub fn add_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_break() { let mut _x = 0; @@ -139,7 +139,7 @@ pub fn add_loop_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label() { let mut _x = 0; @@ -162,7 +162,7 @@ pub fn add_loop_label_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -187,7 +187,7 @@ pub fn change_break_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_break_label() { let mut _x = 0; @@ -212,7 +212,7 @@ pub fn add_loop_label_to_continue() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_continue() { let mut _x = 0; @@ -237,7 +237,7 @@ pub fn change_continue_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_label() { let mut _x = 0; @@ -262,7 +262,7 @@ pub fn change_continue_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_to_break() { let mut _x = 0; diff --git a/src/test/incremental/hashes/function_interfaces.rs b/src/test/incremental/hashes/function_interfaces.rs index bfd536284665d..a6b936fcbcf89 100644 --- a/src/test/incremental/hashes/function_interfaces.rs +++ b/src/test/incremental/hashes/function_interfaces.rs @@ -22,7 +22,7 @@ pub fn add_parameter() {} #[cfg(not(cfail1))] #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" + except = "hir_owner, hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of, fn_sig" )] #[rustc_clean(cfg = "cfail3")] pub fn add_parameter(p: i32) {} @@ -33,7 +33,7 @@ pub fn add_parameter(p: i32) {} pub fn add_return_type() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes")] #[rustc_clean(cfg = "cfail3")] pub fn add_return_type() -> () {} @@ -45,7 +45,7 @@ pub fn type_of_parameter(p: i32) {} #[cfg(not(cfail1))] #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" + except = "hir_owner, hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of, fn_sig" )] #[rustc_clean(cfg = "cfail3")] pub fn type_of_parameter(p: i64) {} @@ -58,7 +58,7 @@ pub fn type_of_parameter_ref(p: &i32) {} #[cfg(not(cfail1))] #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" + except = "hir_owner, hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of, fn_sig" )] #[rustc_clean(cfg = "cfail3")] pub fn type_of_parameter_ref(p: &mut i32) {} @@ -71,7 +71,7 @@ pub fn order_of_parameters(p1: i32, p2: i64) {} #[cfg(not(cfail1))] #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" + except = "hir_owner, hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of, fn_sig" )] #[rustc_clean(cfg = "cfail3")] pub fn order_of_parameters(p2: i64, p1: i32) {} @@ -84,7 +84,7 @@ pub fn make_unsafe() {} #[cfg(not(cfail1))] #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" + except = "hir_owner, hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of, fn_sig" )] #[rustc_clean(cfg = "cfail3")] pub unsafe fn make_unsafe() {} @@ -95,7 +95,7 @@ pub unsafe fn make_unsafe() {} pub fn make_extern() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, typeck_tables_of, fn_sig")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes, typeck_tables_of, fn_sig")] #[rustc_clean(cfg = "cfail3")] pub extern "C" fn make_extern() {} @@ -107,7 +107,7 @@ pub fn type_parameter() {} #[cfg(not(cfail1))] #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, generics_of, type_of, predicates_of" + except = "hir_owner, hir_owner_nodes, generics_of, type_of, predicates_of" )] #[rustc_clean(cfg = "cfail3")] pub fn type_parameter() {} @@ -118,7 +118,7 @@ pub fn type_parameter() {} pub fn lifetime_parameter() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, generics_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes, generics_of")] #[rustc_clean(cfg = "cfail3")] pub fn lifetime_parameter<'a>() {} @@ -128,7 +128,7 @@ pub fn lifetime_parameter<'a>() {} pub fn trait_bound() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn trait_bound() {} @@ -138,7 +138,7 @@ pub fn trait_bound() {} pub fn builtin_bound() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn builtin_bound() {} @@ -150,7 +150,7 @@ pub fn lifetime_bound<'a, T>() {} #[cfg(not(cfail1))] #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, generics_of, type_of, predicates_of" + except = "hir_owner, hir_owner_nodes, generics_of, type_of, predicates_of" )] #[rustc_clean(cfg = "cfail3")] pub fn lifetime_bound<'a, T: 'a>() {} @@ -161,7 +161,7 @@ pub fn lifetime_bound<'a, T: 'a>() {} pub fn second_trait_bound() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn second_trait_bound() {} @@ -171,7 +171,7 @@ pub fn second_trait_bound() {} pub fn second_builtin_bound() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn second_builtin_bound() {} @@ -183,7 +183,7 @@ pub fn second_lifetime_bound<'a, 'b, T: 'a>() {} #[cfg(not(cfail1))] #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, generics_of, type_of, predicates_of" + except = "hir_owner, hir_owner_nodes, generics_of, type_of, predicates_of" )] #[rustc_clean(cfg = "cfail3")] pub fn second_lifetime_bound<'a, 'b, T: 'a + 'b>() {} @@ -194,7 +194,7 @@ pub fn second_lifetime_bound<'a, 'b, T: 'a + 'b>() {} pub fn inline() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes")] #[rustc_clean(cfg = "cfail3")] #[inline] pub fn inline() {} @@ -206,7 +206,7 @@ pub fn inline() {} pub fn inline_never() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes")] #[rustc_clean(cfg = "cfail3")] #[inline(never)] pub fn inline_never() {} @@ -217,7 +217,7 @@ pub fn inline_never() {} pub fn no_mangle() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes")] #[rustc_clean(cfg = "cfail3")] #[no_mangle] pub fn no_mangle() {} @@ -228,7 +228,7 @@ pub fn no_mangle() {} pub fn linkage() {} #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes")] #[rustc_clean(cfg = "cfail3")] #[linkage = "weak_odr"] pub fn linkage() {} @@ -241,7 +241,7 @@ pub fn return_impl_trait() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, typeck_tables_of, fn_sig")] +#[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes, typeck_tables_of, fn_sig")] #[rustc_clean(cfg = "cfail3")] pub fn return_impl_trait() -> impl Clone { 0 @@ -274,7 +274,7 @@ pub mod change_return_type_indirectly { #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" + except = "hir_owner, hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of, fn_sig" )] #[rustc_clean(cfg = "cfail3")] pub fn indirect_return_type() -> ReturnType { @@ -292,7 +292,7 @@ pub mod change_parameter_type_indirectly { #[rustc_clean( cfg = "cfail2", - except = "hir_owner, hir_owner_items, mir_built, optimized_mir, typeck_tables_of, fn_sig" + except = "hir_owner, hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of, fn_sig" )] #[rustc_clean(cfg = "cfail3")] pub fn indirect_parameter_type(p: ParameterType) {} @@ -309,7 +309,7 @@ pub mod change_trait_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] + #[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn indirect_trait_bound(p: T) {} } @@ -322,7 +322,7 @@ pub mod change_trait_bound_indirectly_in_where_clause { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_items, predicates_of")] + #[rustc_clean(cfg = "cfail2", except = "hir_owner, hir_owner_nodes, predicates_of")] #[rustc_clean(cfg = "cfail3")] pub fn indirect_trait_bound_where(p: T) where diff --git a/src/test/incremental/hashes/if_expressions.rs b/src/test/incremental/hashes/if_expressions.rs index 93bdc0322bb71..29b3f1f5b1d83 100644 --- a/src/test/incremental/hashes/if_expressions.rs +++ b/src/test/incremental/hashes/if_expressions.rs @@ -25,7 +25,7 @@ pub fn change_condition(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_condition(x: bool) -> u32 { if !x { @@ -46,7 +46,7 @@ pub fn change_then_branch(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_then_branch(x: bool) -> u32 { if x { @@ -69,7 +69,7 @@ pub fn change_else_branch(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_else_branch(x: bool) -> u32 { if x { @@ -94,7 +94,7 @@ pub fn add_else_branch(x: bool) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_else_branch(x: bool) -> u32 { let mut ret = 1; @@ -120,7 +120,7 @@ pub fn change_condition_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_condition_if_let(x: Option) -> u32 { if let Some(_) = x { @@ -143,7 +143,7 @@ pub fn change_then_branch_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_then_branch_if_let(x: Option) -> u32 { if let Some(x) = x { @@ -166,7 +166,7 @@ pub fn change_else_branch_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_else_branch_if_let(x: Option) -> u32 { if let Some(x) = x { @@ -191,7 +191,7 @@ pub fn add_else_branch_if_let(x: Option) -> u32 { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_else_branch_if_let(x: Option) -> u32 { let mut ret = 1; diff --git a/src/test/incremental/hashes/indexing_expressions.rs b/src/test/incremental/hashes/indexing_expressions.rs index 84c0298918e7e..7a8cbc3566ee9 100644 --- a/src/test/incremental/hashes/indexing_expressions.rs +++ b/src/test/incremental/hashes/indexing_expressions.rs @@ -22,8 +22,8 @@ fn change_simple_index(slice: &[u32]) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn change_simple_index(slice: &[u32]) -> u32 { slice[4] } @@ -39,8 +39,8 @@ fn change_lower_bound(slice: &[u32]) -> &[u32] { #[cfg(not(cfail1))] #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn change_lower_bound(slice: &[u32]) -> &[u32] { &slice[2..5] } @@ -56,8 +56,8 @@ fn change_upper_bound(slice: &[u32]) -> &[u32] { #[cfg(not(cfail1))] #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn change_upper_bound(slice: &[u32]) -> &[u32] { &slice[3..7] } @@ -73,8 +73,8 @@ fn add_lower_bound(slice: &[u32]) -> &[u32] { #[cfg(not(cfail1))] #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn add_lower_bound(slice: &[u32]) -> &[u32] { &slice[3..4] } @@ -90,8 +90,8 @@ fn add_upper_bound(slice: &[u32]) -> &[u32] { #[cfg(not(cfail1))] #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn add_upper_bound(slice: &[u32]) -> &[u32] { &slice[3..7] } @@ -107,8 +107,8 @@ fn change_mutability(slice: &mut [u32]) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn change_mutability(slice: &mut [u32]) -> u32 { (&slice[3..5])[0] } @@ -124,8 +124,8 @@ fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { #[cfg(not(cfail1))] #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { &slice[3..=7] } diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs index c0b80a92df6ee..a9c8457f7f260 100644 --- a/src/test/incremental/hashes/inherent_impls.rs +++ b/src/test/incremental/hashes/inherent_impls.rs @@ -23,7 +23,7 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,associated_item_def_ids")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,associated_item_def_ids")] #[rustc_clean(cfg="cfail3")] impl Foo { #[rustc_clean(cfg="cfail3")] @@ -44,7 +44,7 @@ impl Foo { impl Foo { #[rustc_clean( cfg="cfail2", - except="hir_owner_items,optimized_mir,promoted_mir,mir_built,typeck_tables_of" + except="hir_owner_nodes,optimized_mir,promoted_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn method_body() { @@ -68,7 +68,7 @@ impl Foo { impl Foo { #[rustc_clean( cfg="cfail2", - except="hir_owner_items,optimized_mir,promoted_mir,mir_built,typeck_tables_of" + except="hir_owner_nodes,optimized_mir,promoted_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] #[inline] @@ -85,10 +85,10 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="associated_item,hir_owner,hir_owner_items")] + #[rustc_clean(cfg="cfail2", except="associated_item,hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] fn method_privacy() { } } @@ -100,7 +100,7 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] impl Foo { #[rustc_dirty(cfg="cfail2", except="type_of,predicates_of,promoted_mir")] @@ -120,7 +120,7 @@ impl Foo { impl Foo { #[rustc_clean( cfg="cfail2", - except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of,optimized_mir,mir_built" + except="hir_owner,hir_owner_nodes,fn_sig,typeck_tables_of,optimized_mir,mir_built" )] #[rustc_clean(cfg="cfail3")] pub fn method_selfmutness(&mut self) { } @@ -135,7 +135,7 @@ impl Foo { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,associated_item_def_ids")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,associated_item_def_ids")] #[rustc_clean(cfg="cfail3")] impl Foo { #[rustc_clean(cfg="cfail2")] @@ -160,7 +160,7 @@ impl Foo { impl Foo { #[rustc_clean( cfg="cfail2", - except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of,optimized_mir,mir_built" + except="hir_owner,hir_owner_nodes,fn_sig,typeck_tables_of,optimized_mir,mir_built" )] #[rustc_clean(cfg="cfail3")] pub fn add_method_parameter(&self, _: i32) { } @@ -178,7 +178,7 @@ impl Foo { #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] + #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_method_parameter_name(&self, b: i64) { } } @@ -197,7 +197,7 @@ impl Foo { impl Foo { #[rustc_clean( cfg="cfail2", - except="hir_owner,hir_owner_items,fn_sig,optimized_mir,mir_built,typeck_tables_of")] + except="hir_owner,hir_owner_nodes,fn_sig,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_method_return_type(&self) -> u8 { 0 } } @@ -214,7 +214,7 @@ impl Foo { #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] #[inline] pub fn make_method_inline(&self) -> u8 { 0 } @@ -232,7 +232,7 @@ impl Foo { #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] + #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_method_parameter_order(&self, b: i64, a: i64) { } } @@ -251,7 +251,7 @@ impl Foo { impl Foo { #[rustc_clean( cfg="cfail2", - except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of,optimized_mir,mir_built" + except="hir_owner,hir_owner_nodes,fn_sig,typeck_tables_of,optimized_mir,mir_built" )] #[rustc_clean(cfg="cfail3")] pub unsafe fn make_method_unsafe(&self) { } @@ -269,7 +269,7 @@ impl Foo { #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,fn_sig,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub extern fn make_method_extern(&self) { } } @@ -286,7 +286,7 @@ impl Foo { #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,fn_sig,typeck_tables_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,fn_sig,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub extern "system" fn change_method_calling_convention(&self) { } } @@ -312,7 +312,7 @@ impl Foo { // if we lower generics before the body, then the `HirId` for // things in the body will be affected. So if you start to see // `typeck_tables_of` appear dirty, that might be the cause. -nmatsakis - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_parameter_to_method<'a>(&self) { } } @@ -340,7 +340,7 @@ impl Foo { // appear dirty, that might be the cause. -nmatsakis #[rustc_clean( cfg="cfail2", - except="hir_owner,hir_owner_items,generics_of,predicates_of,type_of", + except="hir_owner,hir_owner_nodes,generics_of,predicates_of,type_of", )] #[rustc_clean(cfg="cfail3")] pub fn add_type_parameter_to_method(&self) { } @@ -360,7 +360,7 @@ impl Foo { impl Foo { #[rustc_clean( cfg="cfail2", - except="hir_owner,hir_owner_items,generics_of,predicates_of,type_of,typeck_tables_of" + except="hir_owner,hir_owner_nodes,generics_of,predicates_of,type_of" )] #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_bound_to_lifetime_param_of_method<'a, 'b: 'a>(&self) { } @@ -387,7 +387,7 @@ impl Foo { // generics before the body, then the `HirId` for things in the // body will be affected. So if you start to see `typeck_tables_of` // appear dirty, that might be the cause. -nmatsakis - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,generics_of,predicates_of,\ + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,generics_of,predicates_of,\ type_of")] #[rustc_clean(cfg="cfail3")] pub fn add_lifetime_bound_to_type_param_of_method<'a, T: 'a>(&self) { } @@ -414,7 +414,7 @@ impl Foo { // generics before the body, then the `HirId` for things in the // body will be affected. So if you start to see `typeck_tables_of` // appear dirty, that might be the cause. -nmatsakis - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,predicates_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,predicates_of")] #[rustc_clean(cfg="cfail3")] pub fn add_trait_bound_to_type_param_of_method(&self) { } } @@ -431,7 +431,7 @@ impl Foo { #[rustc_clean(cfg="cfail2")] #[rustc_clean(cfg="cfail3")] impl Foo { - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] #[no_mangle] pub fn add_no_mangle_to_method(&self) { } @@ -448,7 +448,7 @@ impl Bar { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,generics_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,generics_of")] #[rustc_clean(cfg="cfail3")] impl Bar { #[rustc_clean( @@ -468,7 +468,7 @@ impl Bar { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] impl Bar { #[rustc_clean(cfg="cfail2", except="fn_sig,optimized_mir,mir_built,typeck_tables_of")] @@ -485,7 +485,7 @@ impl Bar { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] impl Bar { #[rustc_clean(cfg="cfail2")] @@ -502,7 +502,7 @@ impl Bar { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] impl Bar { #[rustc_clean(cfg="cfail2")] diff --git a/src/test/incremental/hashes/inline_asm.rs b/src/test/incremental/hashes/inline_asm.rs index a77123110aeda..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,29 +22,29 @@ 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 } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 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,30 +58,30 @@ 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 } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 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,29 +94,29 @@ 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 } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 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,29 +129,29 @@ 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 } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 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,29 +164,29 @@ 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 } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 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,29 +199,29 @@ 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 } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 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/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs index 2d9cf4203dcd3..846bfc6d0e4db 100644 --- a/src/test/incremental/hashes/let_expressions.rs +++ b/src/test/incremental/hashes/let_expressions.rs @@ -22,7 +22,7 @@ pub fn change_name() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir")] + except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_name() { let _y = 2u64; @@ -38,7 +38,7 @@ pub fn add_type() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_nodes,typeck_tables_of,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn add_type() { let _x: u32 = 2u32; @@ -54,7 +54,7 @@ pub fn change_type() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_nodes,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_type() { let _x: u8 = 2; @@ -70,7 +70,7 @@ pub fn change_mutability_of_reference_type() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_nodes,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_reference_type() { let _x: &mut u64; @@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_nodes,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_slot() { let _x: u64 = 0; @@ -102,7 +102,7 @@ pub fn change_simple_binding_to_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_nodes,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_simple_binding_to_pattern() { let (_a, _b) = (0u8, 'x'); @@ -118,7 +118,7 @@ pub fn change_name_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir")] + except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_name_in_pattern() { let (_a, _c) = (1u8, 'y'); @@ -134,7 +134,7 @@ pub fn add_ref_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_nodes,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn add_ref_in_pattern() { let (ref _a, _b) = (1u8, 'y'); @@ -150,7 +150,7 @@ pub fn add_amp_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_nodes,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn add_amp_in_pattern() { let (&_a, _b) = (&1u8, 'y'); @@ -166,7 +166,7 @@ pub fn change_mutability_of_binding_in_pattern() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_nodes,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_binding_in_pattern() { let (mut _a, _b) = (99u8, 'q'); @@ -182,7 +182,7 @@ pub fn add_initializer() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,typeck_tables_of,mir_built,optimized_mir")] + except="hir_owner_nodes,typeck_tables_of,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn add_initializer() { let _x: i16 = 3i16; @@ -198,7 +198,7 @@ pub fn change_initializer() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir")] + except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_initializer() { let _x = 5u16; diff --git a/src/test/incremental/hashes/loop_expressions.rs b/src/test/incremental/hashes/loop_expressions.rs index 7ce43559cfff3..65db89eb976cf 100644 --- a/src/test/incremental/hashes/loop_expressions.rs +++ b/src/test/incremental/hashes/loop_expressions.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; @@ -47,7 +47,7 @@ pub fn add_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_break() { let mut _x = 0; @@ -70,7 +70,7 @@ pub fn add_loop_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label() { let mut _x = 0; @@ -93,7 +93,7 @@ pub fn add_loop_label_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -118,7 +118,7 @@ pub fn change_break_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_break_label() { let mut _x = 0; @@ -143,7 +143,7 @@ pub fn add_loop_label_to_continue() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_continue() { let mut _x = 0; @@ -168,7 +168,7 @@ pub fn change_continue_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_label() { let mut _x = 0; @@ -193,7 +193,7 @@ pub fn change_continue_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_to_break() { let mut _x = 0; diff --git a/src/test/incremental/hashes/match_expressions.rs b/src/test/incremental/hashes/match_expressions.rs index 30934c7c1d16a..033723a4c7796 100644 --- a/src/test/incremental/hashes/match_expressions.rs +++ b/src/test/incremental/hashes/match_expressions.rs @@ -26,7 +26,7 @@ pub fn add_arm(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_arm(x: u32) -> u32 { match x { @@ -51,7 +51,7 @@ pub fn change_order_of_arms(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir")] + except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_order_of_arms(x: u32) -> u32 { match x { @@ -75,7 +75,7 @@ pub fn add_guard_clause(x: u32, y: bool) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_guard_clause(x: u32, y: bool) -> u32 { match x { @@ -99,7 +99,7 @@ pub fn change_guard_clause(x: u32, y: bool) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_guard_clause(x: u32, y: bool) -> u32 { match x { @@ -123,7 +123,7 @@ pub fn add_at_binding(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_at_binding(x: u32) -> u32 { match x { @@ -147,7 +147,7 @@ pub fn change_name_of_at_binding(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir")] + except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_name_of_at_binding(x: u32) -> u32 { match x { @@ -170,7 +170,7 @@ pub fn change_simple_name_to_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_simple_name_to_pattern(x: u32) -> u32 { match (x, x & 1) { @@ -193,7 +193,7 @@ pub fn change_name_in_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir")] + except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_name_in_pattern(x: u32) -> u32 { match (x, x & 1) { @@ -216,7 +216,7 @@ pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_binding_in_pattern(x: u32) -> u32 { match (x, x & 1) { @@ -238,7 +238,7 @@ pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_ref_to_binding_in_pattern(x: u32) -> u32 { match (x, x & 1) { @@ -260,7 +260,7 @@ pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", -except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] +except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_amp_to_binding_in_pattern(x: u32) -> u32 { match (&x, x & 1) { @@ -283,7 +283,7 @@ pub fn change_rhs_of_arm(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir")] + except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_rhs_of_arm(x: u32) -> u32 { match x { @@ -307,7 +307,7 @@ pub fn add_alternative_to_arm(x: u32) -> u32 { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="hir_owner_items,mir_built,optimized_mir,typeck_tables_of")] + except="hir_owner_nodes,mir_built,optimized_mir,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_alternative_to_arm(x: u32) -> u32 { match x { diff --git a/src/test/incremental/hashes/panic_exprs.rs b/src/test/incremental/hashes/panic_exprs.rs index b46d1fac12475..9de2aaa1bfd11 100644 --- a/src/test/incremental/hashes/panic_exprs.rs +++ b/src/test/incremental/hashes/panic_exprs.rs @@ -18,7 +18,7 @@ // Indexing expression -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn indexing(slice: &[u8]) -> u8 { #[cfg(cfail1)] @@ -33,7 +33,7 @@ pub fn indexing(slice: &[u8]) -> u8 { // Arithmetic overflow plus -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_plus(val: i32) -> i32 { #[cfg(cfail1)] @@ -48,7 +48,7 @@ pub fn arithmetic_overflow_plus(val: i32) -> i32 { // Arithmetic overflow minus -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_minus(val: i32) -> i32 { #[cfg(cfail1)] @@ -63,7 +63,7 @@ pub fn arithmetic_overflow_minus(val: i32) -> i32 { // Arithmetic overflow mult -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_mult(val: i32) -> i32 { #[cfg(cfail1)] @@ -78,7 +78,7 @@ pub fn arithmetic_overflow_mult(val: i32) -> i32 { // Arithmetic overflow negation -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn arithmetic_overflow_negation(val: i32) -> i32 { #[cfg(cfail1)] @@ -93,7 +93,7 @@ pub fn arithmetic_overflow_negation(val: i32) -> i32 { // Division by zero -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn division_by_zero(val: i32) -> i32 { #[cfg(cfail1)] @@ -107,7 +107,7 @@ pub fn division_by_zero(val: i32) -> i32 { } // Division by zero -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn mod_by_zero(val: i32) -> i32 { #[cfg(cfail1)] @@ -122,7 +122,7 @@ pub fn mod_by_zero(val: i32) -> i32 { // shift left -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn shift_left(val: i32, shift: usize) -> i32 { #[cfg(cfail1)] @@ -137,7 +137,7 @@ pub fn shift_left(val: i32, shift: usize) -> i32 { // shift right -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn shift_right(val: i32, shift: usize) -> i32 { #[cfg(cfail1)] diff --git a/src/test/incremental/hashes/statics.rs b/src/test/incremental/hashes/statics.rs index 536b79324734c..caf4e12cc4b76 100644 --- a/src/test/incremental/hashes/statics.rs +++ b/src/test/incremental/hashes/statics.rs @@ -21,7 +21,7 @@ static STATIC_VISIBILITY: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub static STATIC_VISIBILITY: u8 = 0; @@ -31,7 +31,7 @@ pub static STATIC_VISIBILITY: u8 = 0; static STATIC_MUTABILITY: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] static mut STATIC_MUTABILITY: u8 = 0; @@ -41,7 +41,7 @@ static mut STATIC_MUTABILITY: u8 = 0; static STATIC_LINKAGE: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] #[linkage="weak_odr"] static STATIC_LINKAGE: u8 = 0; @@ -52,7 +52,7 @@ static STATIC_LINKAGE: u8 = 0; static STATIC_NO_MANGLE: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] #[no_mangle] static STATIC_NO_MANGLE: u8 = 0; @@ -63,7 +63,7 @@ static STATIC_NO_MANGLE: u8 = 0; static STATIC_THREAD_LOCAL: u8 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] #[thread_local] static STATIC_THREAD_LOCAL: u8 = 0; @@ -74,7 +74,7 @@ static STATIC_THREAD_LOCAL: u8 = 0; static STATIC_CHANGE_TYPE_1: i16 = 0; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_1: u64 = 0; @@ -84,13 +84,13 @@ static STATIC_CHANGE_TYPE_1: u64 = 0; static STATIC_CHANGE_TYPE_2: Option = None; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_2: Option = None; // Change value between simple literals -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_VALUE_1: i16 = { #[cfg(cfail1)] @@ -102,7 +102,7 @@ static STATIC_CHANGE_VALUE_1: i16 = { // Change value between expressions -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_VALUE_2: i16 = { #[cfg(cfail1)] @@ -112,7 +112,7 @@ static STATIC_CHANGE_VALUE_2: i16 = { { 1 + 2 } }; -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_VALUE_3: i16 = { #[cfg(cfail1)] @@ -122,7 +122,7 @@ static STATIC_CHANGE_VALUE_3: i16 = { { 2 * 3 } }; -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_VALUE_4: i16 = { #[cfg(cfail1)] @@ -144,11 +144,11 @@ mod static_change_type_indirectly { #[cfg(not(cfail1))] use super::ReferencedType2 as Type; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_INDIRECTLY_1: Type = Type; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items,type_of")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes,type_of")] #[rustc_clean(cfg="cfail3")] static STATIC_CHANGE_TYPE_INDIRECTLY_2: Option = None; } diff --git a/src/test/incremental/hashes/struct_constructors.rs b/src/test/incremental/hashes/struct_constructors.rs index 89b18eefd0646..006b712923b99 100644 --- a/src/test/incremental/hashes/struct_constructors.rs +++ b/src/test/incremental/hashes/struct_constructors.rs @@ -31,7 +31,7 @@ pub fn change_field_value_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_field_value_regular_struct() -> RegularStruct { RegularStruct { @@ -54,7 +54,7 @@ pub fn change_field_order_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_field_order_regular_struct() -> RegularStruct { RegularStruct { @@ -82,7 +82,7 @@ pub fn add_field_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_field_regular_struct() -> RegularStruct { let struct1 = RegularStruct { @@ -117,7 +117,7 @@ pub fn change_field_label_regular_struct() -> RegularStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_field_label_regular_struct() -> RegularStruct { let struct1 = RegularStruct { @@ -152,7 +152,7 @@ pub fn change_constructor_path_regular_struct() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_regular_struct() { let _ = RegularStruct2 { @@ -173,7 +173,7 @@ pub mod change_constructor_path_indirectly_regular_struct { #[rustc_clean( cfg="cfail2", - except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,typeck_tables_of" + except="fn_sig,hir_owner,hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn function() -> Struct { @@ -196,7 +196,7 @@ pub fn change_field_value_tuple_struct() -> TupleStruct { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,optimized_mir,mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_field_value_tuple_struct() -> TupleStruct { TupleStruct(0, 1, 3) @@ -213,7 +213,7 @@ pub fn change_constructor_path_tuple_struct() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items,mir_built,typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,mir_built,typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn change_constructor_path_tuple_struct() { let _ = TupleStruct2(0, 1, 2); @@ -230,7 +230,7 @@ pub mod change_constructor_path_indirectly_tuple_struct { #[rustc_clean( cfg="cfail2", - except="fn_sig,hir_owner,hir_owner_items,optimized_mir,mir_built,typeck_tables_of" + except="fn_sig,hir_owner,hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of" )] #[rustc_clean(cfg="cfail3")] pub fn function() -> Struct { diff --git a/src/test/incremental/hashes/struct_defs.rs b/src/test/incremental/hashes/struct_defs.rs index fa08b7ec1ed3a..a42bd9261f95d 100644 --- a/src/test/incremental/hashes/struct_defs.rs +++ b/src/test/incremental/hashes/struct_defs.rs @@ -25,12 +25,12 @@ pub struct LayoutPacked; #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -42,12 +42,12 @@ struct LayoutC; #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -62,12 +62,12 @@ struct TupleStructFieldType(i32); #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -85,12 +85,12 @@ struct TupleStructAddField(i32); #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -107,12 +107,12 @@ struct TupleStructFieldVisibility(char); #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -126,12 +126,12 @@ struct RecordStructFieldType { x: f32 } #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -149,12 +149,12 @@ struct RecordStructFieldName { x: f32 } #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -168,12 +168,12 @@ struct RecordStructAddField { x: f32 } #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -189,12 +189,12 @@ struct RecordStructFieldVisibility { x: f32 } #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -210,12 +210,12 @@ struct AddLifetimeParameter<'a>(&'a f32, &'a f64); #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_dirty(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -229,12 +229,12 @@ struct AddLifetimeParameterBound<'a, 'b>(&'a f32, &'b f64); #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -248,12 +248,12 @@ struct AddLifetimeParameterBoundWhereClause<'a, 'b>(&'a f32, &'b f64); #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -270,12 +270,12 @@ struct AddTypeParameter(T1, T1); #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="type_of", cfg="cfail2")] #[rustc_dirty(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -294,12 +294,12 @@ struct AddTypeParameterBound(T); #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -313,12 +313,12 @@ struct AddTypeParameterBoundWhereClause(T); #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -333,12 +333,12 @@ struct AddTypeParameterBoundWhereClause( // addresses taken into account by the hashing algorithm). // Note: there is no #[cfg(...)], so this is ALWAYS compiled #[rustc_clean(label="hir_owner", cfg="cfail2")] -#[rustc_clean(label="hir_owner_items", cfg="cfail2")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -352,12 +352,12 @@ struct Visibility; #[cfg(not(cfail1))] #[rustc_dirty(label="hir_owner", cfg="cfail2")] -#[rustc_dirty(label="hir_owner_items", cfg="cfail2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] -#[rustc_clean(label="hir_owner_items", cfg="cfail3")] +#[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -374,12 +374,12 @@ mod tuple_struct_change_field_type_indirectly { use super::ReferencedType2 as FieldType; #[rustc_dirty(label="hir_owner", cfg="cfail2")] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] - #[rustc_clean(label="hir_owner_items", cfg="cfail3")] + #[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -397,12 +397,12 @@ mod record_struct_change_field_type_indirectly { use super::ReferencedType2 as FieldType; #[rustc_dirty(label="hir_owner", cfg="cfail2")] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_clean(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] - #[rustc_clean(label="hir_owner_items", cfg="cfail3")] + #[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -425,12 +425,12 @@ mod change_trait_bound_indirectly { use super::ReferencedTrait2 as Trait; #[rustc_dirty(label="hir_owner", cfg="cfail2")] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] - #[rustc_clean(label="hir_owner_items", cfg="cfail3")] + #[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] @@ -445,12 +445,12 @@ mod change_trait_bound_indirectly_in_where_clause { use super::ReferencedTrait2 as Trait; #[rustc_dirty(label="hir_owner", cfg="cfail2")] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_clean(label="type_of", cfg="cfail2")] #[rustc_clean(label="generics_of", cfg="cfail2")] #[rustc_dirty(label="predicates_of", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] - #[rustc_clean(label="hir_owner_items", cfg="cfail3")] + #[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[rustc_clean(label="type_of", cfg="cfail3")] #[rustc_clean(label="generics_of", cfg="cfail3")] #[rustc_clean(label="predicates_of", cfg="cfail3")] diff --git a/src/test/incremental/hashes/trait_defs.rs b/src/test/incremental/hashes/trait_defs.rs index df41b73f92c42..aa39ea88e0ec0 100644 --- a/src/test/incremental/hashes/trait_defs.rs +++ b/src/test/incremental/hashes/trait_defs.rs @@ -140,8 +140,8 @@ trait TraitChangeMethodParameterName { #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] - #[rustc_clean(label="hir_owner_items", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] + #[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn with_default(y: i32) {} } @@ -260,8 +260,8 @@ trait TraitChangeModeSelfOwnToMut: Sized { trait TraitChangeModeSelfOwnToMut: Sized { #[rustc_dirty(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] - #[rustc_clean(label="hir_owner_items", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] + #[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn method(mut self) {} } diff --git a/src/test/incremental/hashes/trait_impls.rs b/src/test/incremental/hashes/trait_impls.rs index 70e066870b6b4..ddec6ff8eecdd 100644 --- a/src/test/incremental/hashes/trait_impls.rs +++ b/src/test/incremental/hashes/trait_impls.rs @@ -64,8 +64,8 @@ impl ChangeMethodBodyTrait for Foo { impl ChangeMethodBodyTrait for Foo { #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] - #[rustc_clean(label="hir_owner_items", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] + #[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] fn method_name() { () } @@ -91,8 +91,8 @@ impl ChangeMethodBodyTraitInlined for Foo { impl ChangeMethodBodyTraitInlined for Foo { #[rustc_clean(label="hir_owner", cfg="cfail2")] #[rustc_clean(label="hir_owner", cfg="cfail3")] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] - #[rustc_clean(label="hir_owner_items", cfg="cfail3")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] + #[rustc_clean(label="hir_owner_nodes", cfg="cfail3")] #[inline] fn method_name() { panic!() diff --git a/src/test/incremental/hashes/type_defs.rs b/src/test/incremental/hashes/type_defs.rs index bbe1514ba9fd4..495445670c006 100644 --- a/src/test/incremental/hashes/type_defs.rs +++ b/src/test/incremental/hashes/type_defs.rs @@ -24,7 +24,7 @@ type ChangePrimitiveType = i32; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type ChangePrimitiveType = i64; @@ -35,7 +35,7 @@ type ChangePrimitiveType = i64; type ChangeMutability = &'static i32; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type ChangeMutability = &'static mut i32; @@ -46,7 +46,7 @@ type ChangeMutability = &'static mut i32; type ChangeLifetime<'a> = (&'static i32, &'a i32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type ChangeLifetime<'a> = (&'a i32, &'a i32); @@ -60,7 +60,7 @@ struct Struct2; type ChangeTypeStruct = Struct1; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type ChangeTypeStruct = Struct2; @@ -71,7 +71,7 @@ type ChangeTypeStruct = Struct2; type ChangeTypeTuple = (u32, u64); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type ChangeTypeTuple = (u32, i64); @@ -91,7 +91,7 @@ enum Enum2 { type ChangeTypeEnum = Enum1; #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type ChangeTypeEnum = Enum2; @@ -102,7 +102,7 @@ type ChangeTypeEnum = Enum2; type AddTupleField = (i32, i64); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type AddTupleField = (i32, i64, i16); @@ -113,7 +113,7 @@ type AddTupleField = (i32, i64, i16); type ChangeNestedTupleField = (i32, (i64, i16)); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type ChangeNestedTupleField = (i32, (i64, i8)); @@ -124,7 +124,7 @@ type ChangeNestedTupleField = (i32, (i64, i8)); type AddTypeParam = (T1, T1); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type AddTypeParam = (T1, T2); @@ -135,7 +135,7 @@ type AddTypeParam = (T1, T2); type AddTypeParamBound = (T1, u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type AddTypeParamBound = (T1, u32); @@ -146,7 +146,7 @@ type AddTypeParamBound = (T1, u32); type AddTypeParamBoundWhereClause where T1: Clone = (T1, u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type AddTypeParamBoundWhereClause where T1: Clone+Copy = (T1, u32); @@ -157,7 +157,7 @@ type AddTypeParamBoundWhereClause where T1: Clone+Copy = (T1, u32); type AddLifetimeParam<'a> = (&'a u32, &'a u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type AddLifetimeParam<'a, 'b> = (&'a u32, &'b u32); @@ -168,7 +168,7 @@ type AddLifetimeParam<'a, 'b> = (&'a u32, &'b u32); type AddLifetimeParamBound<'a, 'b> = (&'a u32, &'b u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type AddLifetimeParamBound<'a, 'b: 'a> = (&'a u32, &'b u32); @@ -181,7 +181,7 @@ where 'b: 'a = (&'a u32, &'b u32, &'c u32); #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type AddLifetimeParamBoundWhereClause<'a, 'b, 'c> where 'b: 'a, @@ -200,7 +200,7 @@ mod change_trait_bound_indirectly { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type ChangeTraitBoundIndirectly = (T, u32); } @@ -214,7 +214,7 @@ mod change_trait_bound_indirectly_in_where_clause { #[cfg(not(cfail1))] use super::ReferencedTrait2 as Trait; - #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_items")] + #[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] type ChangeTraitBoundIndirectly where T : Trait = (T, u32); } diff --git a/src/test/incremental/hashes/unary_and_binary_exprs.rs b/src/test/incremental/hashes/unary_and_binary_exprs.rs index 9b63003482fe5..c8b53c27b02c8 100644 --- a/src/test/incremental/hashes/unary_and_binary_exprs.rs +++ b/src/test/incremental/hashes/unary_and_binary_exprs.rs @@ -21,7 +21,7 @@ pub fn const_negation() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn const_negation() -> i32 { -1 @@ -36,7 +36,7 @@ pub fn const_bitwise_not() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn const_bitwise_not() -> i32 { !99 @@ -51,7 +51,7 @@ pub fn var_negation(x: i32, y: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn var_negation(x: i32, y: i32) -> i32 { -y @@ -66,7 +66,7 @@ pub fn var_bitwise_not(x: i32, y: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn var_bitwise_not(x: i32, y: i32) -> i32 { !y @@ -81,7 +81,7 @@ pub fn var_deref(x: &i32, y: &i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn var_deref(x: &i32, y: &i32) -> i32 { *y @@ -96,7 +96,7 @@ pub fn first_const_add() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn first_const_add() -> i32 { 2 + 3 @@ -111,7 +111,7 @@ pub fn second_const_add() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn second_const_add() -> i32 { 1 + 3 @@ -126,7 +126,7 @@ pub fn first_var_add(a: i32, b: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn first_var_add(a: i32, b: i32) -> i32 { b + 2 @@ -141,7 +141,7 @@ pub fn second_var_add(a: i32, b: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn second_var_add(a: i32, b: i32) -> i32 { 1 + b @@ -156,7 +156,7 @@ pub fn plus_to_minus(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn plus_to_minus(a: i32) -> i32 { 1 - a @@ -171,7 +171,7 @@ pub fn plus_to_mult(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn plus_to_mult(a: i32) -> i32 { 1 * a @@ -186,7 +186,7 @@ pub fn plus_to_div(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn plus_to_div(a: i32) -> i32 { 1 / a @@ -201,7 +201,7 @@ pub fn plus_to_mod(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn plus_to_mod(a: i32) -> i32 { 1 % a @@ -216,7 +216,7 @@ pub fn and_to_or(a: bool, b: bool) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn and_to_or(a: bool, b: bool) -> bool { a || b @@ -231,7 +231,7 @@ pub fn bitwise_and_to_bitwise_or(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_bitwise_or(a: i32) -> i32 { 1 | a @@ -246,7 +246,7 @@ pub fn bitwise_and_to_bitwise_xor(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_bitwise_xor(a: i32) -> i32 { 1 ^ a @@ -261,7 +261,7 @@ pub fn bitwise_and_to_lshift(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_lshift(a: i32) -> i32 { a << 1 @@ -276,7 +276,7 @@ pub fn bitwise_and_to_rshift(a: i32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn bitwise_and_to_rshift(a: i32) -> i32 { a >> 1 @@ -291,7 +291,7 @@ pub fn eq_to_uneq(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_uneq(a: i32) -> bool { a != 1 @@ -306,7 +306,7 @@ pub fn eq_to_lt(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_lt(a: i32) -> bool { a < 1 @@ -321,7 +321,7 @@ pub fn eq_to_gt(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_gt(a: i32) -> bool { a > 1 @@ -336,7 +336,7 @@ pub fn eq_to_le(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_le(a: i32) -> bool { a <= 1 @@ -351,7 +351,7 @@ pub fn eq_to_ge(a: i32) -> bool { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn eq_to_ge(a: i32) -> bool { a >= 1 @@ -368,7 +368,7 @@ pub fn type_cast(a: u8) -> u64 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built,typeck_tables_of", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn type_cast(a: u8) -> u64 { let b = a as u32; @@ -385,7 +385,7 @@ pub fn value_cast(a: u32) -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn value_cast(a: u32) -> i32 { 2 as i32 @@ -403,7 +403,7 @@ pub fn place() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn place() -> i32 { let mut x = 10; @@ -423,7 +423,7 @@ pub fn rvalue() -> i32 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn rvalue() -> i32 { let mut x = 10; @@ -440,7 +440,7 @@ pub fn index_to_slice(s: &[u8], i: usize, j: usize) -> u8 { } #[cfg(not(cfail1))] -#[rustc_clean(except="hir_owner_items,optimized_mir,mir_built", cfg="cfail2")] +#[rustc_clean(except="hir_owner_nodes,optimized_mir,mir_built", cfg="cfail2")] #[rustc_clean(cfg="cfail3")] pub fn index_to_slice(s: &[u8], i: usize, j: usize) -> u8 { s[j] diff --git a/src/test/incremental/hashes/while_let_loops.rs b/src/test/incremental/hashes/while_let_loops.rs index 908f60440fa5b..36e0fcdbe74d1 100644 --- a/src/test/incremental/hashes/while_let_loops.rs +++ b/src/test/incremental/hashes/while_let_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; @@ -48,7 +48,7 @@ pub fn change_loop_condition() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_condition() { let mut _x = 0; @@ -70,7 +70,7 @@ pub fn add_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_break() { let mut _x = 0; @@ -93,7 +93,7 @@ pub fn add_loop_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label() { let mut _x = 0; @@ -116,7 +116,7 @@ pub fn add_loop_label_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -141,7 +141,7 @@ pub fn change_break_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_break_label() { let mut _x = 0; @@ -166,7 +166,7 @@ pub fn add_loop_label_to_continue() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_continue() { let mut _x = 0; @@ -191,7 +191,7 @@ pub fn change_continue_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_label() { let mut _x = 0; @@ -216,7 +216,7 @@ pub fn change_continue_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_to_break() { let mut _x = 0; diff --git a/src/test/incremental/hashes/while_loops.rs b/src/test/incremental/hashes/while_loops.rs index 365ec5fa567d9..83f09bd7be614 100644 --- a/src/test/incremental/hashes/while_loops.rs +++ b/src/test/incremental/hashes/while_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; @@ -48,7 +48,7 @@ pub fn change_loop_condition() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_condition() { let mut _x = 0; @@ -70,7 +70,7 @@ pub fn add_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir, typeck_tables_of")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir, typeck_tables_of")] #[rustc_clean(cfg="cfail3")] pub fn add_break() { let mut _x = 0; @@ -93,7 +93,7 @@ pub fn add_loop_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label() { let mut _x = 0; @@ -116,7 +116,7 @@ pub fn add_loop_label_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_break() { let mut _x = 0; @@ -141,7 +141,7 @@ pub fn change_break_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_break_label() { let mut _x = 0; @@ -166,7 +166,7 @@ pub fn add_loop_label_to_continue() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")] #[rustc_clean(cfg="cfail3")] pub fn add_loop_label_to_continue() { let mut _x = 0; @@ -191,7 +191,7 @@ pub fn change_continue_label() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_label() { let mut _x = 0; @@ -216,7 +216,7 @@ pub fn change_continue_to_break() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="hir_owner_items, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, mir_built, optimized_mir")] #[rustc_clean(cfg="cfail3")] pub fn change_continue_to_break() { let mut _x = 0; diff --git a/src/test/incremental/ich_method_call_trait_scope.rs b/src/test/incremental/ich_method_call_trait_scope.rs index a9ec66346acb0..847bce7ef90b0 100644 --- a/src/test/incremental/ich_method_call_trait_scope.rs +++ b/src/test/incremental/ich_method_call_trait_scope.rs @@ -27,14 +27,14 @@ mod mod3 { use Trait2; #[rustc_clean(label="hir_owner", cfg="rpass2")] - #[rustc_clean(label="hir_owner_items", cfg="rpass2")] + #[rustc_clean(label="hir_owner_nodes", cfg="rpass2")] #[rustc_dirty(label="typeck_tables_of", cfg="rpass2")] fn bar() { ().method(); } #[rustc_clean(label="hir_owner", cfg="rpass2")] - #[rustc_clean(label="hir_owner_items", cfg="rpass2")] + #[rustc_clean(label="hir_owner_nodes", cfg="rpass2")] #[rustc_clean(label="typeck_tables_of", cfg="rpass2")] fn baz() { 22; // no method call, traits in scope don't matter diff --git a/src/test/incremental/ich_nested_items.rs b/src/test/incremental/ich_nested_items.rs index a9232190eef48..aabdaa664112f 100644 --- a/src/test/incremental/ich_nested_items.rs +++ b/src/test/incremental/ich_nested_items.rs @@ -8,7 +8,7 @@ #![feature(rustc_attrs)] #[rustc_clean(label = "hir_owner", cfg = "cfail2")] -#[rustc_dirty(label = "hir_owner_items", cfg = "cfail2")] +#[rustc_dirty(label = "hir_owner_nodes", cfg = "cfail2")] pub fn foo() { #[cfg(cfail1)] pub fn baz() {} // order is different... @@ -17,7 +17,7 @@ pub fn foo() { // the parent node, which is the statement holding this item. Changing the position of // `bar` in `foo` will update that reference and make `hir_owner(bar)` dirty. #[rustc_dirty(label = "hir_owner", cfg = "cfail2")] - #[rustc_clean(label = "hir_owner_items", cfg = "cfail2")] + #[rustc_clean(label = "hir_owner_nodes", cfg = "cfail2")] pub fn bar() {} // but that doesn't matter. #[cfg(cfail2)] diff --git a/src/test/incremental/ich_resolve_results.rs b/src/test/incremental/ich_resolve_results.rs index c4674faabf5d6..19df2972f8942 100644 --- a/src/test/incremental/ich_resolve_results.rs +++ b/src/test/incremental/ich_resolve_results.rs @@ -29,17 +29,17 @@ mod mod3 { use mod2::Foo; #[rustc_clean(label="hir_owner", cfg="rpass2")] - #[rustc_clean(label="hir_owner_items", cfg="rpass2")] + #[rustc_clean(label="hir_owner_nodes", cfg="rpass2")] #[rustc_clean(label="hir_owner", cfg="rpass3")] - #[rustc_dirty(label="hir_owner_items", cfg="rpass3")] + #[rustc_dirty(label="hir_owner_nodes", cfg="rpass3")] fn in_expr() { Foo(0); } #[rustc_clean(label="hir_owner", cfg="rpass2")] - #[rustc_clean(label="hir_owner_items", cfg="rpass2")] + #[rustc_clean(label="hir_owner_nodes", cfg="rpass2")] #[rustc_clean(label="hir_owner", cfg="rpass3")] - #[rustc_dirty(label="hir_owner_items", cfg="rpass3")] + #[rustc_dirty(label="hir_owner_nodes", cfg="rpass3")] fn in_type() { test::(); } diff --git a/src/test/incremental/issue-72386.rs b/src/test/incremental/issue-72386.rs new file mode 100644 index 0000000000000..3dc7f502a5992 --- /dev/null +++ b/src/test/incremental/issue-72386.rs @@ -0,0 +1,22 @@ +// revisions: rpass1 cfail1 rpass3 +// only-x86_64 +// Regression test for issue #72386 +// Checks that we don't ICE when switching to an invalid register +// and back again + +#![feature(asm)] + +#[cfg(any(rpass1, rpass3))] +fn main() { + unsafe { + asm!("nop") + } +} + +#[cfg(cfail1)] +fn main() { + unsafe { + asm!("nop",out("invalid_reg")_) + //[cfail1]~^ ERROR invalid register + } +} 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/source_loc_macros.rs b/src/test/incremental/source_loc_macros.rs index a360a66a64b24..f18d2fdaf0a01 100644 --- a/src/test/incremental/source_loc_macros.rs +++ b/src/test/incremental/source_loc_macros.rs @@ -8,25 +8,25 @@ #![feature(rustc_attrs)] #[rustc_clean(label="hir_owner", cfg="rpass2")] -#[rustc_clean(label="hir_owner_items", cfg="rpass2")] +#[rustc_clean(label="hir_owner_nodes", cfg="rpass2")] fn line_same() { let _ = line!(); } #[rustc_clean(label="hir_owner", cfg="rpass2")] -#[rustc_clean(label="hir_owner_items", cfg="rpass2")] +#[rustc_clean(label="hir_owner_nodes", cfg="rpass2")] fn col_same() { let _ = column!(); } #[rustc_clean(label="hir_owner", cfg="rpass2")] -#[rustc_clean(label="hir_owner_items", cfg="rpass2")] +#[rustc_clean(label="hir_owner_nodes", cfg="rpass2")] fn file_same() { let _ = file!(); } #[rustc_clean(label="hir_owner", cfg="rpass2")] -#[rustc_dirty(label="hir_owner_items", cfg="rpass2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="rpass2")] fn line_different() { #[cfg(rpass1)] { @@ -39,7 +39,7 @@ fn line_different() { } #[rustc_clean(label="hir_owner", cfg="rpass2")] -#[rustc_dirty(label="hir_owner_items", cfg="rpass2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="rpass2")] fn col_different() { #[cfg(rpass1)] { diff --git a/src/test/incremental/spans_significant_w_debuginfo.rs b/src/test/incremental/spans_significant_w_debuginfo.rs index b87d829132b29..aff2be830fff8 100644 --- a/src/test/incremental/spans_significant_w_debuginfo.rs +++ b/src/test/incremental/spans_significant_w_debuginfo.rs @@ -13,5 +13,5 @@ pub fn main() {} #[cfg(rpass2)] #[rustc_dirty(label="hir_owner", cfg="rpass2")] -#[rustc_dirty(label="hir_owner_items", cfg="rpass2")] +#[rustc_dirty(label="hir_owner_nodes", cfg="rpass2")] pub fn main() {} diff --git a/src/test/incremental/string_constant.rs b/src/test/incremental/string_constant.rs index 11a42262c16d2..cc35f3bdf299b 100644 --- a/src/test/incremental/string_constant.rs +++ b/src/test/incremental/string_constant.rs @@ -18,7 +18,7 @@ pub mod x { } #[cfg(cfail2)] - #[rustc_dirty(label="hir_owner_items", cfg="cfail2")] + #[rustc_dirty(label="hir_owner_nodes", cfg="cfail2")] #[rustc_dirty(label="optimized_mir", cfg="cfail2")] pub fn x() { println!("{}", "2"); 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..aeb38f3f91068 --- /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: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:20:5: 20:25 +| 12: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:23:12: 23:20 +| 13: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*const ^0) } at $DIR/address-of.rs:23:12: 23:20 +| 14: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:24:12: 24:28 +| 15: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32; 10]) } at $DIR/address-of.rs:24:12: 24:28 +| 16: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:25:12: 25:27 +| 17: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*const dyn std::marker::Send) } at $DIR/address-of.rs:25:12: 25:27 +| 18: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:26:12: 26:24 +| 19: Canonical { max_universe: U0, variables: [], value: Ty(*const [i32]) } at $DIR/address-of.rs:26:12: 26:24 +| 20: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:28:5: 28:16 +| 21: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:30:5: 30:23 +| 22: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:33:12: 33:18 +| 23: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: Ty(*mut ^0) } at $DIR/address-of.rs:33:12: 33:18 +| 24: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32; 10]) } at $DIR/address-of.rs:34:12: 34:26 +| 25: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32; 10]) } at $DIR/address-of.rs:34:12: 34:26 +| 26: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:35:12: 35:25 +| 27: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: Ty(*mut dyn std::marker::Send) } at $DIR/address-of.rs:35:12: 35:25 +| 28: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32]) } at $DIR/address-of.rs:36:12: 36:22 +| 29: Canonical { max_universe: U0, variables: [], value: Ty(*mut [i32]) } at $DIR/address-of.rs:36:12: 36:22 +| +fn address_of_reborrow() -> () { + let mut _0: (); // return place in scope 0 at $DIR/address-of.rs:3:26: 3:26 + let _1: &[i32; 10]; // in scope 0 at $DIR/address-of.rs:4:9: 4:10 + let _2: [i32; 10]; // in scope 0 at $DIR/address-of.rs:4:14: 4:21 + let mut _4: [i32; 10]; // in scope 0 at $DIR/address-of.rs:5:22: 5:29 + let _5: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:7:5: 7:18 + let mut _6: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:7:5: 7:18 + let _7: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:8:5: 8:26 + let _8: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:9:5: 9:25 + let mut _9: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:9:5: 9:25 + let mut _10: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:9:5: 9:6 + let _11: *const [i32]; // in scope 0 at $DIR/address-of.rs:10:5: 10:22 + let mut _12: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:10:5: 10:6 + let _13: *const i32; // in scope 0 at $DIR/address-of.rs:11:5: 11:20 + let mut _14: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:11:5: 11:6 + let mut _18: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:15:30: 15:31 + let mut _20: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:16:27: 16:28 + let _21: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:18:5: 18:18 + let mut _22: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:18:5: 18:18 + let _23: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:19:5: 19:26 + let _24: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:20:5: 20:25 + let mut _25: *const dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:20:5: 20:25 + let mut _26: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:20:5: 20:6 + let _27: *const [i32]; // in scope 0 at $DIR/address-of.rs:21:5: 21:22 + let mut _28: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:21:5: 21:6 + let mut _32: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:25:30: 25:31 + let mut _34: *const [i32; 10]; // in scope 0 at $DIR/address-of.rs:26:27: 26:28 + let _35: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:28:5: 28:16 + let mut _36: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:28:5: 28:16 + let _37: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:29:5: 29:24 + let _38: *mut dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:30:5: 30:23 + let mut _39: *mut dyn std::marker::Send; // in scope 0 at $DIR/address-of.rs:30:5: 30:23 + let mut _40: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:30:5: 30:6 + let _41: *mut [i32]; // in scope 0 at $DIR/address-of.rs:31:5: 31:20 + let mut _42: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:31:5: 31:6 + let mut _46: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:35:28: 35:29 + let mut _48: *mut [i32; 10]; // in scope 0 at $DIR/address-of.rs:36:25: 36:26 + scope 1 { + debug y => _1; // in scope 1 at $DIR/address-of.rs:4:9: 4:10 + let mut _3: &mut [i32; 10]; // in scope 1 at $DIR/address-of.rs:5:9: 5:14 + scope 2 { + debug z => _3; // in scope 2 at $DIR/address-of.rs:5:9: 5:14 + let _15: *const [i32; 10] as UserTypeProjection { base: UserType(2), projs: [] }; // in scope 2 at $DIR/address-of.rs:13:9: 13:10 + scope 3 { + debug p => _15; // in scope 3 at $DIR/address-of.rs:13:9: 13:10 + let _16: *const [i32; 10] as UserTypeProjection { base: UserType(4), projs: [] }; // in scope 3 at $DIR/address-of.rs:14:9: 14:10 + scope 4 { + debug p => _16; // in scope 4 at $DIR/address-of.rs:14:9: 14:10 + let _17: *const dyn std::marker::Send as UserTypeProjection { base: UserType(6), projs: [] }; // in scope 4 at $DIR/address-of.rs:15:9: 15:10 + scope 5 { + debug p => _17; // in scope 5 at $DIR/address-of.rs:15:9: 15:10 + let _19: *const [i32] as UserTypeProjection { base: UserType(8), projs: [] }; // in scope 5 at $DIR/address-of.rs:16:9: 16:10 + scope 6 { + debug p => _19; // in scope 6 at $DIR/address-of.rs:16:9: 16:10 + let _29: *const [i32; 10] as UserTypeProjection { base: UserType(12), projs: [] }; // in scope 6 at $DIR/address-of.rs:23:9: 23:10 + scope 7 { + debug p => _29; // in scope 7 at $DIR/address-of.rs:23:9: 23:10 + let _30: *const [i32; 10] as UserTypeProjection { base: UserType(14), projs: [] }; // in scope 7 at $DIR/address-of.rs:24:9: 24:10 + scope 8 { + debug p => _30; // in scope 8 at $DIR/address-of.rs:24:9: 24:10 + let _31: *const dyn std::marker::Send as UserTypeProjection { base: UserType(16), projs: [] }; // in scope 8 at $DIR/address-of.rs:25:9: 25:10 + scope 9 { + debug p => _31; // in scope 9 at $DIR/address-of.rs:25:9: 25:10 + let _33: *const [i32] as UserTypeProjection { base: UserType(18), projs: [] }; // in scope 9 at $DIR/address-of.rs:26:9: 26:10 + scope 10 { + debug p => _33; // in scope 10 at $DIR/address-of.rs:26:9: 26:10 + let _43: *mut [i32; 10] as UserTypeProjection { base: UserType(22), projs: [] }; // in scope 10 at $DIR/address-of.rs:33:9: 33:10 + scope 11 { + debug p => _43; // in scope 11 at $DIR/address-of.rs:33:9: 33:10 + let _44: *mut [i32; 10] as UserTypeProjection { base: UserType(24), projs: [] }; // in scope 11 at $DIR/address-of.rs:34:9: 34:10 + scope 12 { + debug p => _44; // in scope 12 at $DIR/address-of.rs:34:9: 34:10 + let _45: *mut dyn std::marker::Send as UserTypeProjection { base: UserType(26), projs: [] }; // in scope 12 at $DIR/address-of.rs:35:9: 35:10 + scope 13 { + debug p => _45; // in scope 13 at $DIR/address-of.rs:35:9: 35:10 + let _47: *mut [i32] as UserTypeProjection { base: UserType(28), projs: [] }; // in scope 13 at $DIR/address-of.rs:36:9: 36:10 + scope 14 { + debug p => _47; // in scope 14 at $DIR/address-of.rs:36:9: 36:10 + } + } + } + } + } + } + } + } + } + } + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/address-of.rs:4:9: 4:10 + StorageLive(_2); // scope 0 at $DIR/address-of.rs:4:14: 4:21 + _2 = [const 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 096f98bade25a..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.EraseRegions.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.EraseRegions.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..d39b9b8a3b444 --- /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; // 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..381c1ca6f22ef --- /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; // 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..de423cd907afe --- /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: bb2, unwind: bb5]; // scope 4 at $DIR/basic_assignment.rs:23:5: 23:11 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/basic_assignment.rs:10:1: 24:2 + } + + bb2: { + drop(_6) -> [return: bb6, unwind: bb4]; // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 + } + + bb3 (cleanup): { + drop(_4) -> bb1; // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb4 (cleanup): { + drop(_5) -> bb3; // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb5 (cleanup): { + drop(_6) -> bb4; // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 + } + + bb6: { + StorageDead(_6); // scope 4 at $DIR/basic_assignment.rs:23:19: 23:20 + _0 = const (); // scope 0 at $DIR/basic_assignment.rs:10:11: 24:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/basic_assignment.rs:10:11: 24:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_5) -> [return: bb7, unwind: bb3]; // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb7: { + StorageDead(_5); // scope 3 at $DIR/basic_assignment.rs:24:1: 24:2 + drop(_4) -> [return: bb8, unwind: bb1]; // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 + } + + bb8: { + StorageDead(_4); // scope 2 at $DIR/basic_assignment.rs:24:1: 24:2 + StorageDead(_2); // scope 1 at $DIR/basic_assignment.rs:24:1: 24:2 + StorageDead(_1); // scope 0 at $DIR/basic_assignment.rs:24:1: 24:2 + return; // scope 0 at $DIR/basic_assignment.rs:24:2: 24:2 + } +} diff --git a/src/test/mir-opt/box_expr.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..259501c7de951 --- /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: bb2, unwind: bb3]; // scope 0 at $DIR/box_expr.rs:7:17: 7:25 + // ty::Const + // + ty: fn() -> S {S::new} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/box_expr.rs:7:17: 7:23 + // + literal: Const { ty: fn() -> S {S::new}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/box_expr.rs:6:1: 9:2 + } + + bb2: { + _1 = move _2; // scope 0 at $DIR/box_expr.rs:7:13: 7:25 + drop(_2) -> bb4; // scope 0 at $DIR/box_expr.rs:7:24: 7:25 + } + + bb3 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/box_expr.rs:7:24: 7:25 + } + + bb4: { + StorageDead(_2); // scope 0 at $DIR/box_expr.rs:7:24: 7:25 + StorageLive(_3); // scope 1 at $DIR/box_expr.rs:8:5: 8:12 + StorageLive(_4); // scope 1 at $DIR/box_expr.rs:8:10: 8:11 + _4 = move _1; // scope 1 at $DIR/box_expr.rs:8:10: 8:11 + _3 = const std::mem::drop::>(move _4) -> [return: bb5, unwind: bb7]; // scope 1 at $DIR/box_expr.rs:8:5: 8:12 + // ty::Const + // + ty: fn(std::boxed::Box) {std::mem::drop::>} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/box_expr.rs:8:5: 8:9 + // + literal: Const { ty: fn(std::boxed::Box) {std::mem::drop::>}, val: Value(Scalar()) } + } + + bb5: { + StorageDead(_4); // scope 1 at $DIR/box_expr.rs:8:11: 8:12 + StorageDead(_3); // scope 1 at $DIR/box_expr.rs:8:12: 8:13 + _0 = const (); // scope 0 at $DIR/box_expr.rs:6:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/box_expr.rs:6:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_1) -> bb8; // scope 0 at $DIR/box_expr.rs:9:1: 9:2 + } + + bb6 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/box_expr.rs:9:1: 9:2 + } + + bb7 (cleanup): { + drop(_4) -> bb6; // scope 1 at $DIR/box_expr.rs:8:11: 8:12 + } + + bb8: { + StorageDead(_1); // scope 0 at $DIR/box_expr.rs:9:1: 9:2 + return; // scope 0 at $DIR/box_expr.rs:9:2: 9:2 + } +} diff --git a/src/test/mir-opt/byte_slice.rs b/src/test/mir-opt/byte_slice.rs index 7edfa3e1124db..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.EraseRegions.after.mir -// ... -// _1 = const b"foo"; -// ... -// _2 = [const 5u8, const 120u8]; -// ... -// END rustc.main.EraseRegions.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 f6f7d0910911c..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; -static mut BAR: *const &'static i32 = [&Y].as_ptr(); +// EMIT_MIR rustc.BAR.PromoteTemps.diff +// EMIT_MIR rustc.BAR-promoted[0].ConstProp.after.mir +static mut BAR: *const &i32 = [&Y].as_ptr(); -static mut FOO: *const &'static i32 = [unsafe { &X }].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 Scalar(alloc1+0) : &i32; -// _4 = &(*_5); -// _3 = [move _4]; -// _2 = &_3; -// _1 = move _2 as &[&'static 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 Scalar(alloc0+0) : &i32; -// _4 = &(*_5); -// _3 = [move _4]; -// _2 = &_3; -// _1 = move _2 as &[&'static 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 &[&'static 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 &[&'static 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..5c192979a8696 --- /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: bb2, unwind: bb1]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44 + // ty::Const + // + ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const-promotion-extern-static.rs:9:36: 9:42 + // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45 + } + + bb2: { +- StorageDead(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44 +- StorageDead(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44 + return; // scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45 + } +- } +- +- alloc0 (static: Y, size: 4, align: 4) { +- 2a 00 00 00 │ *... + } + diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.FOO-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..649cea6493e45 --- /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: bb2, unwind: bb1]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55 + // ty::Const + // + ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const-promotion-extern-static.rs:13:47: 13:53 + // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::::as_ptr}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56 + } + + bb2: { +- StorageDead(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55 +- StorageDead(_3); // scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55 + return; // scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56 + } + } +- +- alloc2 (extern static: X) + diff --git a/src/test/mir-opt/const_allocation.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..d02906132e296 --- /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; // 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..4fe3f08955894 --- /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; // 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..de06e5334e040 --- /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 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..7052c8387041b --- /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 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..7ceec94d81e76 --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/32bit/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/bad_op_unsafe_oob_for_slices.rs:4:11: 4:11 + let _1: *const [i32]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + let mut _2: *const [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _3: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _4: [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:26: 5:35 + let _6: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + let mut _7: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _8: bool; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _9: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + scope 2 { + let _5: i32; // in scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + scope 3 { + debug _b => _5; // in scope 3 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _9 = const main::promoted[0]; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // ty::Const + // + ty: &[i32; 3] + // + val: Unevaluated(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 + 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..483a6f232ef79 --- /dev/null +++ b/src/test/mir-opt/const_prop/bad_op_unsafe_oob_for_slices/64bit/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/bad_op_unsafe_oob_for_slices.rs:4:11: 4:11 + let _1: *const [i32]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + let mut _2: *const [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _3: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + let _4: [i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:26: 5:35 + let _6: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:23: 7:24 + let mut _7: usize; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _8: bool; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:18: 7:25 + let mut _9: &[i32; 3]; // in scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + scope 2 { + let _5: i32; // in scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + scope 3 { + debug _b => _5; // in scope 3 at $DIR/bad_op_unsafe_oob_for_slices.rs:7:13: 7:15 + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:9: 5:10 + StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + _9 = const main::promoted[0]; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35 + // ty::Const + // + ty: &[i32; 3] + // + val: Unevaluated(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 + 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..16f937f3e7b5e --- /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: bb2, unwind: bb1]; // scope 0 at $DIR/boxes.rs:12:26: 12:27 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/boxes.rs:11:1: 13:2 + } + + bb2: { + StorageDead(_3); // scope 0 at $DIR/boxes.rs:12:26: 12:27 + _0 = const (); // scope 0 at $DIR/boxes.rs:11:11: 13:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/boxes.rs:11:11: 13:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/boxes.rs:13:1: 13:2 + return; // scope 0 at $DIR/boxes.rs:13:2: 13:2 + } + } + diff --git a/src/test/mir-opt/const_prop/cast.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..f9f7d543d21f5 --- /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; // 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 new file mode 100644 index 0000000000000..1071590dd9e51 --- /dev/null +++ b/src/test/mir-opt/const_prop/control-flow-simplification.rs @@ -0,0 +1,20 @@ +// compile-flags: -Zmir-opt-level=1 + +trait NeedsDrop:Sized{ + const NEEDS:bool=std::mem::needs_drop::(); +} + +impl NeedsDrop for This{} + +// EMIT_MIR rustc.hello.ConstProp.diff +// EMIT_MIR rustc.hello.PreCodegen.before.mir +fn hello(){ + if ::NEEDS { + panic!() + } +} + +pub fn main() { + hello::<()>(); + hello::>(); +} 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 667d21fc14ee4..13e8eb3e44e1a 100644 --- a/src/test/mir-opt/const_prop/discriminant.rs +++ b/src/test/mir-opt/const_prop/discriminant.rs @@ -1,53 +1,12 @@ // compile-flags: -O +// FIXME(wesleywiser): Ideally, we could const-prop away all of this and just be left with +// `let x = 42` but that doesn't work because const-prop doesn't support `Operand::Indirect` +// and `InterpCx::eval_place()` always forces an allocation which creates the `Indirect`. +// Fixing either of those will allow us to const-prop this away. + +// 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 Scalar(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..1c873f53f378a --- /dev/null +++ b/src/test/mir-opt/const_prop/discriminant/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/discriminant.rs:10:11: 10:11 + let _1: i32; // in scope 0 at $DIR/discriminant.rs:11:9: 11:10 + let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:11:13: 11:64 + let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:11:34: 11:44 + let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:11:21: 11:31 + scope 1 { + debug x => _1; // in scope 1 at $DIR/discriminant.rs:11:9: 11:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/discriminant.rs:11:9: 11:10 + StorageLive(_2); // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + StorageLive(_3); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 +- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 ++ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 + // ty::Const +- // + ty: bool ++ // + ty: std::option::Option + // + val: Value(Scalar(0x01)) + // mir::Constant +- // + span: $DIR/discriminant.rs:11:39: 11:43 +- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } +- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:11:21: 11:31 +- switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ // + span: $DIR/discriminant.rs:11:34: 11:44 ++ // + literal: Const { ty: std::option::Option, val: Value(Scalar(0x01)) } ++ _4 = const 1isize; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ // ty::Const ++ // + ty: isize ++ // + val: Value(Scalar(0x00000001)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:11:21: 11:31 ++ // + literal: Const { ty: isize, val: Value(Scalar(0x00000001)) } ++ switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ // ty::Const ++ // + ty: isize ++ // + val: Value(Scalar(0x00000001)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:11:21: 11:31 ++ // + literal: Const { ty: isize, val: Value(Scalar(0x00000001)) } + } + + bb1: { + _2 = const 10i32; // scope 0 at $DIR/discriminant.rs:11:59: 11:61 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000000a)) + // mir::Constant + // + span: $DIR/discriminant.rs:11:59: 11:61 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + } + + bb2: { + switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:11:26: 11:30 + } + + bb3: { + _2 = const 42i32; // scope 0 at $DIR/discriminant.rs:11:47: 11:49 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/discriminant.rs:11:47: 11:49 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + } + + bb4: { + _1 = Add(move _2, const 0i32); // scope 0 at $DIR/discriminant.rs:11:13: 11:68 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/discriminant.rs:11:67: 11:68 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + StorageDead(_2); // scope 0 at $DIR/discriminant.rs:11:67: 11:68 + StorageDead(_3); // scope 0 at $DIR/discriminant.rs:11:68: 11:69 + _0 = const (); // scope 0 at $DIR/discriminant.rs:10:11: 12:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/discriminant.rs:10:11: 12:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/discriminant.rs:12:1: 12:2 + return; // scope 0 at $DIR/discriminant.rs:12:2: 12:2 + } + } + diff --git a/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/discriminant/64bit/rustc.main.ConstProp.diff new file mode 100644 index 0000000000000..75b4b7e5a62ba --- /dev/null +++ b/src/test/mir-opt/const_prop/discriminant/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/discriminant.rs:10:11: 10:11 + let _1: i32; // in scope 0 at $DIR/discriminant.rs:11:9: 11:10 + let mut _2: i32; // in scope 0 at $DIR/discriminant.rs:11:13: 11:64 + let mut _3: std::option::Option; // in scope 0 at $DIR/discriminant.rs:11:34: 11:44 + let mut _4: isize; // in scope 0 at $DIR/discriminant.rs:11:21: 11:31 + scope 1 { + debug x => _1; // in scope 1 at $DIR/discriminant.rs:11:9: 11:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/discriminant.rs:11:9: 11:10 + StorageLive(_2); // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + StorageLive(_3); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 +- _3 = std::option::Option::::Some(const true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 ++ _3 = const std::option::Option::::Some(true); // scope 0 at $DIR/discriminant.rs:11:34: 11:44 + // ty::Const +- // + ty: bool ++ // + ty: std::option::Option + // + val: Value(Scalar(0x01)) + // mir::Constant +- // + span: $DIR/discriminant.rs:11:39: 11:43 +- // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } +- _4 = discriminant(_3); // scope 0 at $DIR/discriminant.rs:11:21: 11:31 +- switchInt(move _4) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ // + span: $DIR/discriminant.rs:11:34: 11:44 ++ // + literal: Const { ty: std::option::Option, val: Value(Scalar(0x01)) } ++ _4 = const 1isize; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ // ty::Const ++ // + ty: isize ++ // + val: Value(Scalar(0x0000000000000001)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:11:21: 11:31 ++ // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000001)) } ++ switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1]; // scope 0 at $DIR/discriminant.rs:11:21: 11:31 ++ // ty::Const ++ // + ty: isize ++ // + val: Value(Scalar(0x0000000000000001)) ++ // mir::Constant ++ // + span: $DIR/discriminant.rs:11:21: 11:31 ++ // + literal: Const { ty: isize, val: Value(Scalar(0x0000000000000001)) } + } + + bb1: { + _2 = const 10i32; // scope 0 at $DIR/discriminant.rs:11:59: 11:61 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000000a)) + // mir::Constant + // + span: $DIR/discriminant.rs:11:59: 11:61 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000000a)) } + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + } + + bb2: { + switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3]; // scope 0 at $DIR/discriminant.rs:11:26: 11:30 + } + + bb3: { + _2 = const 42i32; // scope 0 at $DIR/discriminant.rs:11:47: 11:49 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002a)) + // mir::Constant + // + span: $DIR/discriminant.rs:11:47: 11:49 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + goto -> bb4; // scope 0 at $DIR/discriminant.rs:11:13: 11:64 + } + + bb4: { + _1 = Add(move _2, const 0i32); // scope 0 at $DIR/discriminant.rs:11:13: 11:68 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/discriminant.rs:11:67: 11:68 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + StorageDead(_2); // scope 0 at $DIR/discriminant.rs:11:67: 11:68 + StorageDead(_3); // scope 0 at $DIR/discriminant.rs:11:68: 11:69 + _0 = const (); // scope 0 at $DIR/discriminant.rs:10:11: 12:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/discriminant.rs:10:11: 12:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/discriminant.rs:12:1: 12:2 + return; // scope 0 at $DIR/discriminant.rs:12:2: 12:2 + } + } + diff --git a/src/test/mir-opt/const_prop/indirect.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 30c75303b3e53..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 Scalar() : (); -// _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..0d703068d41f4 --- /dev/null +++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref/rustc.main.ConstProp.diff @@ -0,0 +1,60 @@ +- // 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 ++ // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) } + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x0000002b)) + // mir::Constant +- // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:22: 5:24 ++ // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25 + // + literal: Const { ty: i32, val: Value(Scalar(0x0000002b)) } + StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10 + _2 = &mut _1; // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:13: 6:19 + ((*_2).1: i32) = const 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..f6bb72baea419 --- /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); // 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..e0b9fbe04c387 --- /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); // 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 693ef78398558..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 Scalar(alloc0+0) : &u8; -// _2 = (*_3); -// ... -// _5 = const Scalar(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..b1c9e22913935 --- /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; // 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..29555b03a8b8e --- /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; // 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/const_prop_miscompile.rs b/src/test/mir-opt/const_prop_miscompile.rs new file mode 100644 index 0000000000000..043b22870f49e --- /dev/null +++ b/src/test/mir-opt/const_prop_miscompile.rs @@ -0,0 +1,22 @@ +#![feature(raw_ref_op)] + +// EMIT_MIR rustc.foo.ConstProp.diff +fn foo() { + let mut u = (1,); + *&mut u.0 = 5; + let y = { u.0 } == 5; +} + +// EMIT_MIR rustc.bar.ConstProp.diff +fn bar() { + let mut v = (1,); + unsafe { + *&raw mut v.0 = 5; + } + let y = { v.0 } == 5; +} + +fn main() { + foo(); + bar(); +} diff --git a/src/test/mir-opt/const_prop_miscompile/rustc.bar.ConstProp.diff b/src/test/mir-opt/const_prop_miscompile/rustc.bar.ConstProp.diff new file mode 100644 index 0000000000000..c87f67bf9f587 --- /dev/null +++ b/src/test/mir-opt/const_prop_miscompile/rustc.bar.ConstProp.diff @@ -0,0 +1,75 @@ +- // MIR for `bar` before ConstProp ++ // MIR for `bar` after ConstProp + + fn bar() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_prop_miscompile.rs:11:10: 11:10 + let mut _1: (i32,); // in scope 0 at $DIR/const_prop_miscompile.rs:12:9: 12:14 + let _2: (); // in scope 0 at $DIR/const_prop_miscompile.rs:13:5: 15:6 + let mut _3: *mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:14:10: 14:22 + let mut _5: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:16:13: 16:20 + scope 1 { + debug v => _1; // in scope 1 at $DIR/const_prop_miscompile.rs:12:9: 12:14 + let _4: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:16:9: 16:10 + scope 2 { + } + scope 3 { + debug y => _4; // in scope 3 at $DIR/const_prop_miscompile.rs:16:9: 16:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:12:9: 12:14 +- _1 = (const 1i32,); // scope 0 at $DIR/const_prop_miscompile.rs:12:17: 12:21 ++ _1 = const (1i32,); // scope 0 at $DIR/const_prop_miscompile.rs:12:17: 12:21 + // ty::Const +- // + ty: i32 ++ // + ty: (i32,) + // + val: Value(Scalar(0x00000001)) + // mir::Constant +- // + span: $DIR/const_prop_miscompile.rs:12:18: 12:19 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } ++ // + span: $DIR/const_prop_miscompile.rs:12:17: 12:21 ++ // + literal: Const { ty: (i32,), val: Value(Scalar(0x00000001)) } + StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:13:5: 15:6 + StorageLive(_3); // scope 2 at $DIR/const_prop_miscompile.rs:14:10: 14:22 + _3 = &raw mut (_1.0: i32); // scope 2 at $DIR/const_prop_miscompile.rs:14:10: 14:22 + (*_3) = const 5i32; // scope 2 at $DIR/const_prop_miscompile.rs:14:9: 14:26 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000005)) + // mir::Constant + // + span: $DIR/const_prop_miscompile.rs:14:25: 14:26 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } + StorageDead(_3); // scope 2 at $DIR/const_prop_miscompile.rs:14:26: 14:27 + _2 = const (); // scope 2 at $DIR/const_prop_miscompile.rs:13:5: 15:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_prop_miscompile.rs:13:5: 15:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/const_prop_miscompile.rs:15:5: 15:6 + StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:16:9: 16:10 + StorageLive(_5); // scope 1 at $DIR/const_prop_miscompile.rs:16:13: 16:20 + _5 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:16:15: 16:18 + _4 = Eq(move _5, const 5i32); // scope 1 at $DIR/const_prop_miscompile.rs:16:13: 16:25 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000005)) + // mir::Constant + // + span: $DIR/const_prop_miscompile.rs:16:24: 16:25 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } + StorageDead(_5); // scope 1 at $DIR/const_prop_miscompile.rs:16:24: 16:25 + _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:11:10: 17:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_prop_miscompile.rs:11:10: 17:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:17:1: 17:2 + StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:17:1: 17:2 + return; // scope 0 at $DIR/const_prop_miscompile.rs:17:2: 17:2 + } + } + diff --git a/src/test/mir-opt/const_prop_miscompile/rustc.foo.ConstProp.diff b/src/test/mir-opt/const_prop_miscompile/rustc.foo.ConstProp.diff new file mode 100644 index 0000000000000..8a6850d2fe3ad --- /dev/null +++ b/src/test/mir-opt/const_prop_miscompile/rustc.foo.ConstProp.diff @@ -0,0 +1,63 @@ +- // MIR for `foo` before ConstProp ++ // MIR for `foo` after ConstProp + + fn foo() -> () { + let mut _0: (); // return place in scope 0 at $DIR/const_prop_miscompile.rs:4:10: 4:10 + let mut _1: (i32,); // in scope 0 at $DIR/const_prop_miscompile.rs:5:9: 5:14 + let mut _2: &mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:6:6: 6:14 + let mut _4: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:7:13: 7:20 + scope 1 { + debug u => _1; // in scope 1 at $DIR/const_prop_miscompile.rs:5:9: 5:14 + let _3: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:7:9: 7:10 + scope 2 { + debug y => _3; // in scope 2 at $DIR/const_prop_miscompile.rs:7:9: 7:10 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:5:9: 5:14 +- _1 = (const 1i32,); // scope 0 at $DIR/const_prop_miscompile.rs:5:17: 5:21 ++ _1 = const (1i32,); // scope 0 at $DIR/const_prop_miscompile.rs:5:17: 5:21 + // ty::Const +- // + ty: i32 ++ // + ty: (i32,) + // + val: Value(Scalar(0x00000001)) + // mir::Constant +- // + span: $DIR/const_prop_miscompile.rs:5:18: 5:19 +- // + literal: Const { ty: i32, val: Value(Scalar(0x00000001)) } ++ // + span: $DIR/const_prop_miscompile.rs:5:17: 5:21 ++ // + literal: Const { ty: (i32,), val: Value(Scalar(0x00000001)) } + StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:6:6: 6:14 + _2 = &mut (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:6:6: 6:14 + (*_2) = const 5i32; // scope 1 at $DIR/const_prop_miscompile.rs:6:5: 6:18 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000005)) + // mir::Constant + // + span: $DIR/const_prop_miscompile.rs:6:17: 6:18 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } + StorageDead(_2); // scope 1 at $DIR/const_prop_miscompile.rs:6:18: 6:19 + StorageLive(_3); // scope 1 at $DIR/const_prop_miscompile.rs:7:9: 7:10 + StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:7:13: 7:20 + _4 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:7:15: 7:18 + _3 = Eq(move _4, const 5i32); // scope 1 at $DIR/const_prop_miscompile.rs:7:13: 7:25 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000005)) + // mir::Constant + // + span: $DIR/const_prop_miscompile.rs:7:24: 7:25 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000005)) } + StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:7:24: 7:25 + _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:4:10: 8:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/const_prop_miscompile.rs:4:10: 8:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_3); // scope 1 at $DIR/const_prop_miscompile.rs:8:1: 8:2 + StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:8:1: 8:2 + return; // scope 0 at $DIR/const_prop_miscompile.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/copy_propagation.rs b/src/test/mir-opt/copy_propagation.rs index 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..3c1c02da42ff8 --- /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: { + falseEdge -> [real: bb9, imaginary: bb1]; // scope 0 at $DIR/exponential-or.rs:8:9: 8:79 + } + + bb9: { + StorageLive(_7); // scope 0 at $DIR/exponential-or.rs:8:10: 8:21 + _7 = (_1.0: u32); // scope 0 at $DIR/exponential-or.rs:8:10: 8:21 + StorageLive(_8); // scope 0 at $DIR/exponential-or.rs:8:57: 8:78 + _8 = (_1.3: u32); // scope 0 at $DIR/exponential-or.rs:8:57: 8:78 + StorageLive(_9); // scope 1 at $DIR/exponential-or.rs:8:83: 8:84 + _9 = _7; // scope 1 at $DIR/exponential-or.rs:8:83: 8:84 + StorageLive(_10); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + _10 = _8; // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + _0 = BitXor(move _9, move _10); // scope 1 at $DIR/exponential-or.rs:8:83: 8:88 + StorageDead(_10); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + StorageDead(_9); // scope 1 at $DIR/exponential-or.rs:8:87: 8:88 + StorageDead(_8); // scope 0 at $DIR/exponential-or.rs:8: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/fn-ptr-shim.rs b/src/test/mir-opt/fn-ptr-shim.rs new file mode 100644 index 0000000000000..08413c9f6fceb --- /dev/null +++ b/src/test/mir-opt/fn-ptr-shim.rs @@ -0,0 +1,15 @@ +// compile-flags: -Zmir-opt-level=0 -Zvalidate-mir + +// Tests that the `` shim does not create a `Call` terminator with a `Self` callee +// (as only `FnDef` and `FnPtr` callees are allowed in MIR). + +// EMIT_MIR rustc.ops-function-Fn-call.AddMovesForPackedDrops.before.mir +fn main() { + call(noop as fn()); +} + +fn noop() {} + +fn call(f: F) { + f(); +} diff --git a/src/test/mir-opt/fn-ptr-shim/rustc.ops-function-Fn-call.AddMovesForPackedDrops.before.mir b/src/test/mir-opt/fn-ptr-shim/rustc.ops-function-Fn-call.AddMovesForPackedDrops.before.mir new file mode 100644 index 0000000000000..4ecc331afaeb9 --- /dev/null +++ b/src/test/mir-opt/fn-ptr-shim/rustc.ops-function-Fn-call.AddMovesForPackedDrops.before.mir @@ -0,0 +1,13 @@ +// MIR for `std::ops::Fn::call` before AddMovesForPackedDrops + +fn std::ops::Fn::call(_1: *const fn(), _2: Args) -> >::Output { + let mut _0: >::Output; // return place in scope 0 at $SRC_DIR/libcore/ops/function.rs:LL:COL + + bb0: { + _0 = move (*_1)() -> bb1; // scope 0 at $SRC_DIR/libcore/ops/function.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/libcore/ops/function.rs:LL:COL + } +} diff --git a/src/test/mir-opt/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..3e7083ff62ecd --- /dev/null +++ b/src/test/mir-opt/generator-drop-cleanup/rustc.main-{{closure}}.generator_drop.0.mir @@ -0,0 +1,80 @@ +// 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: u32; // in scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + scope 1 { + debug _s => (((*_1) as variant#3).0: std::string::String); // in scope 1 at $DIR/generator-drop-cleanup.rs:11:13: 11:15 + } + scope 2 { + let mut _6: std::vec::Vec; // in scope 2 at $DIR/generator-drop-cleanup.rs:11:18: 11:31 + scope 3 { + } + } + + bb0: { + _9 = discriminant((*_1)); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + switchInt(move _9) -> [0u32: bb7, 3u32: bb11, otherwise: bb12]; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb2 (cleanup): { + nop; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + goto -> bb8; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb3: { + StorageDead(_5); // scope 1 at $DIR/generator-drop-cleanup.rs:12:13: 12:14 + StorageDead(_4); // scope 1 at $DIR/generator-drop-cleanup.rs:12:14: 12:15 + drop((((*_1) as variant#3).0: std::string::String)) -> [return: bb4, unwind: bb2]; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb4: { + nop; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + goto -> bb9; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb5: { + return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb6: { + return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb7: { + goto -> bb10; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb8 (cleanup): { + goto -> bb1; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb9: { + goto -> bb5; // scope 0 at $DIR/generator-drop-cleanup.rs:13:5: 13:6 + } + + bb10: { + goto -> bb6; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb11: { + StorageLive(_4); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + StorageLive(_5); // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + goto -> bb3; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } + + bb12: { + return; // scope 0 at $DIR/generator-drop-cleanup.rs:10:15: 13:6 + } +} diff --git a/src/test/mir-opt/generator-storage-dead-unwind.rs b/src/test/mir-opt/generator-storage-dead-unwind.rs index 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..06645860d842d --- /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: bb2, drop: bb4]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:9: 25:14 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:16: 28:6 + } + + bb2: { + StorageDead(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:13: 25:14 + StorageDead(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:14: 25:15 + StorageLive(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 + StorageLive(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 + _8 = move _3; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:14: 26:15 + _7 = const take::(move _8) -> [return: bb7, unwind: bb9]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:9: 26:16 + // ty::Const + // + ty: fn(Foo) {take::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:26:9: 26:13 + // + literal: Const { ty: fn(Foo) {take::}, val: Value(Scalar()) } + } + + bb3 (cleanup): { + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> bb1; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb4: { + StorageDead(_6); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:13: 25:14 + StorageDead(_5); // scope 2 at $DIR/generator-storage-dead-unwind.rs:25:14: 25:15 + StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_3) -> [return: bb5, unwind: bb3]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb5: { + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> [return: bb6, unwind: bb1]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb6: { + generator_drop; // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:16: 28:6 + } + + bb7: { + StorageDead(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16 + StorageDead(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:16: 26:17 + StorageLive(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 + StorageLive(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 + _10 = move _4; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:14: 27:15 + _9 = const take::(move _10) -> [return: bb10, unwind: bb11]; // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:9: 27:16 + // ty::Const + // + ty: fn(Bar) {take::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:27:9: 27:13 + // + literal: Const { ty: fn(Bar) {take::}, val: Value(Scalar()) } + } + + bb8 (cleanup): { + StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> bb1; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb9 (cleanup): { + StorageDead(_8); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:15: 26:16 + StorageDead(_7); // scope 2 at $DIR/generator-storage-dead-unwind.rs:26:16: 26:17 + goto -> bb8; // scope 2 at $DIR/generator-storage-dead-unwind.rs:1:1: 1:1 + } + + bb10: { + StorageDead(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:15: 27:16 + StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17 + _0 = const (); // scope 0 at $DIR/generator-storage-dead-unwind.rs:22:19: 28:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/generator-storage-dead-unwind.rs:22:19: 28:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_4); // scope 1 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + StorageDead(_3); // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + drop(_1) -> [return: bb12, unwind: bb1]; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:5: 28:6 + } + + bb11 (cleanup): { + StorageDead(_10); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:15: 27:16 + StorageDead(_9); // scope 2 at $DIR/generator-storage-dead-unwind.rs:27:16: 27:17 + goto -> bb8; // scope 2 at $DIR/generator-storage-dead-unwind.rs:1:1: 1:1 + } + + bb12: { + return; // scope 0 at $DIR/generator-storage-dead-unwind.rs:28:6: 28:6 + } +} diff --git a/src/test/mir-opt/generator-tiny.rs b/src/test/mir-opt/generator-tiny.rs new file mode 100644 index 0000000000000..c86e2865ca8a4 --- /dev/null +++ b/src/test/mir-opt/generator-tiny.rs @@ -0,0 +1,26 @@ +//! Tests that generators that cannot return or unwind don't have unnecessary +//! panic branches. + +// compile-flags: -C panic=abort +// no-prefer-dynamic + +#![feature(generators, generator_trait)] + +struct HasDrop; + +impl Drop for HasDrop { + fn drop(&mut self) {} +} + +fn callee() {} + +// EMIT_MIR rustc.main-{{closure}}.generator_resume.0.mir +fn main() { + let _gen = |_x: u8| { + let _d = HasDrop; + loop { + yield; + callee(); + } + }; +} 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..c73dea5f8fde6 --- /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: u32; // in scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + scope 1 { + debug _d => (((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}])) as variant#3).0: HasDrop); // in scope 1 at $DIR/generator-tiny.rs:20:13: 20:15 + } + + bb0: { + _11 = discriminant((*(_1.0: &mut [generator@$DIR/generator-tiny.rs:19:16: 25:6 {u8, HasDrop, ()}]))); // scope 0 at $DIR/generator-tiny.rs:19:16: 25:6 + switchInt(move _11) -> [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=<
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..f5d8b84812a3e --- /dev/null +++ b/src/test/mir-opt/graphviz/rustc.main.mir_map.0.dot @@ -0,0 +1,10 @@ +digraph Mir_0_3 { + graph [fontname="monospace"]; + node [fontname="monospace"]; + edge [fontname="monospace"]; + label=>; + bb0__0_3 [shape="none", label=<
0
_0 = const ()
goto
>]; + bb1__0_3 [shape="none", label=<
1
resume
>]; + bb2__0_3 [shape="none", label=<
2
return
>]; + bb0__0_3 -> bb2__0_3 [label=""]; +} diff --git a/src/test/mir-opt/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..50913de98b506 --- /dev/null +++ b/src/test/mir-opt/inline/inline-into-box-place/32bit/rustc.main.Inline.diff @@ -0,0 +1,82 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-into-box-place.rs:7:11: 7:11 + let _1: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + let mut _2: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + let mut _3: (); // in scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 ++ let mut _4: &mut std::vec::Vec; // in scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/inline-into-box-place.rs:8:9: 8:11 + } ++ scope 2 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + StorageLive(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + _2 = Box(std::vec::Vec); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 +- (*_2) = const std::vec::Vec::::new() -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ _4 = &mut (*_2); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec:: { ptr: std::ptr::Unique:: { pointer: {0x4 as *const u32}, _marker: std::marker::PhantomData:: }, cap: 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 (cleanup): { +- resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 +- } +- +- bb2: { ++ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL ++ // + user_ty: UserType(0) ++ // + literal: Const { ty: alloc::raw_vec::RawVec, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, size: Size { raw: 8 }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ ((*_4).1: usize) = const 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: bb3, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 ++ drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + } + +- bb3: { +- StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 +- return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 ++ bb1 (cleanup): { ++ resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 + } + +- bb4 (cleanup): { +- _3 = const alloc::alloc::box_free::>(move (_2.0: std::ptr::Unique>)) -> bb1; // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 +- // ty::Const +- // + ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>} +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:42: 8:43 +- // + literal: Const { ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>}, val: Value(Scalar()) } ++ bb2: { ++ StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 ++ return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/inline/inline-into-box-place/64bit/rustc.main.Inline.diff b/src/test/mir-opt/inline/inline-into-box-place/64bit/rustc.main.Inline.diff new file mode 100644 index 0000000000000..7a1b6460c5bb3 --- /dev/null +++ b/src/test/mir-opt/inline/inline-into-box-place/64bit/rustc.main.Inline.diff @@ -0,0 +1,82 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-into-box-place.rs:7:11: 7:11 + let _1: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + let mut _2: std::boxed::Box>; // in scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + let mut _3: (); // in scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 ++ let mut _4: &mut std::vec::Vec; // in scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + scope 1 { + debug _x => _1; // in scope 1 at $DIR/inline-into-box-place.rs:8:9: 8:11 + } ++ scope 2 { ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-into-box-place.rs:8:9: 8:11 + StorageLive(_2); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 + _2 = Box(std::vec::Vec); // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 +- (*_2) = const std::vec::Vec::::new() -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ _4 = &mut (*_2); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ ((*_4).0: alloc::raw_vec::RawVec) = const alloc::raw_vec::RawVec:: { ptr: std::ptr::Unique:: { pointer: {0x4 as *const u32}, _marker: std::marker::PhantomData:: }, cap: 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 (cleanup): { +- resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 +- } +- +- bb2: { ++ // + span: $SRC_DIR/liballoc/vec.rs:LL:COL ++ // + user_ty: UserType(0) ++ // + literal: Const { ty: alloc::raw_vec::RawVec, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [65535], len: Size { raw: 16 } }, size: Size { raw: 16 }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ ((*_4).1: usize) = const 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: bb3, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 ++ drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 + } + +- bb3: { +- StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 +- return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 ++ bb1 (cleanup): { ++ resume; // scope 0 at $DIR/inline-into-box-place.rs:7:1: 9:2 + } + +- bb4 (cleanup): { +- _3 = const alloc::alloc::box_free::>(move (_2.0: std::ptr::Unique>)) -> bb1; // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 +- // ty::Const +- // + ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>} +- // + val: Value(Scalar()) +- // mir::Constant +- // + span: $DIR/inline-into-box-place.rs:8:42: 8:43 +- // + literal: Const { ty: unsafe fn(std::ptr::Unique>) {alloc::alloc::box_free::>}, val: Value(Scalar()) } ++ bb2: { ++ StorageDead(_1); // scope 0 at $DIR/inline-into-box-place.rs:9:1: 9:2 ++ return; // scope 0 at $DIR/inline-into-box-place.rs:9:2: 9:2 + } + } + diff --git a/src/test/mir-opt/inline/inline-retag.rs b/src/test/mir-opt/inline/inline-retag.rs index 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..44f412c2e2674 --- /dev/null +++ b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.a.Inline.after.mir @@ -0,0 +1,30 @@ +// MIR for `a` after Inline + +fn a(_1: &mut [T]) -> &mut [T] { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:13: 2:14 + let mut _0: &mut [T]; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:2:29: 2:37 + let mut _2: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + let mut _3: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + let mut _4: &mut [T]; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 + scope 1 { + debug self => _4; // in scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + let mut _5: &mut [T]; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 + _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:6 + StorageLive(_5); // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + _5 = &mut (*_4); // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + _3 = &mut (*_5); // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + StorageDead(_5); // scope 1 at $SRC_DIR/libcore/convert/mod.rs:LL:COL + _2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:14: 3:15 + _0 = &mut (*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:3:5: 3:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:1: 4:2 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:1: 4:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:4:2: 4:2 + } +} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir new file mode 100644 index 0000000000000..48e48f989bd94 --- /dev/null +++ b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.b.Inline.after.mir @@ -0,0 +1,34 @@ +// MIR for `b` after Inline + +fn b(_1: &mut std::boxed::Box) -> &mut T { + debug x => _1; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:13: 7:14 + let mut _0: &mut T; // return place in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:7:32: 7:38 + let mut _2: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + let mut _3: &mut T; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + let mut _4: &mut std::boxed::Box; // in scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 + scope 1 { + debug self => _4; // in scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + let mut _5: &mut T; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + let mut _6: &mut T; // in scope 1 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageLive(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageLive(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 + _4 = &mut (*_1); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:6 + StorageLive(_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + StorageLive(_6); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + _6 = &mut (*(*_4)); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + _5 = &mut (*_6); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + _3 = &mut (*_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + StorageDead(_6); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + StorageDead(_5); // scope 1 at $SRC_DIR/liballoc/boxed.rs:LL:COL + _2 = &mut (*_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageDead(_4); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:14: 8:15 + _0 = &mut (*_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:8:5: 8:15 + StorageDead(_3); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:1: 9:2 + StorageDead(_2); // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:1: 9:2 + return; // scope 0 at $DIR/issue-58867-inline-as-ref-as-mut.rs:9:2: 9:2 + } +} diff --git a/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.c.Inline.after.mir b/src/test/mir-opt/inline/issue-58867-inline-as-ref-as-mut/rustc.c.Inline.after.mir 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/instrument_coverage.rs b/src/test/mir-opt/instrument_coverage.rs new file mode 100644 index 0000000000000..3fe010ef68fc3 --- /dev/null +++ b/src/test/mir-opt/instrument_coverage.rs @@ -0,0 +1,20 @@ +// Test that the initial version of Rust coverage injects count_code_region() placeholder calls, +// at the top of each function. The placeholders are later converted into LLVM instrprof.increment +// intrinsics, during codegen. + +// needs-profiler-support +// compile-flags: -Zinstrument-coverage +// EMIT_MIR rustc.main.InstrumentCoverage.diff +// EMIT_MIR rustc.bar.InstrumentCoverage.diff +fn main() { + loop { + if bar() { + break; + } + } +} + +#[inline(never)] +fn bar() -> bool { + true +} diff --git a/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff new file mode 100644 index 0000000000000..1e64379aa0e4b --- /dev/null +++ b/src/test/mir-opt/instrument_coverage/rustc.bar.InstrumentCoverage.diff @@ -0,0 +1,41 @@ +- // MIR for `bar` before InstrumentCoverage ++ // MIR for `bar` after InstrumentCoverage + + fn bar() -> bool { + let mut _0: bool; // return place in scope 0 at $DIR/instrument_coverage.rs:18:13: 18:17 ++ let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2 + + bb0: { ++ StorageLive(_1); // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2 ++ _1 = const std::intrinsics::count_code_region(const 0u32) -> bb2; // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2 ++ // ty::Const ++ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} ++ // + val: Value(Scalar()) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:18:1: 18:1 ++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000000)) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:18:1: 18:1 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } ++ } ++ ++ bb1 (cleanup): { ++ resume; // scope 0 at $DIR/instrument_coverage.rs:18:1: 20:2 ++ } ++ ++ bb2: { ++ StorageDead(_1); // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9 + _0 = const true; // scope 0 at $DIR/instrument_coverage.rs:19:5: 19:9 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:19:5: 19:9 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + return; // scope 0 at $DIR/instrument_coverage.rs:20:2: 20:2 + } + } + diff --git a/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff new file mode 100644 index 0000000000000..82d21467827eb --- /dev/null +++ b/src/test/mir-opt/instrument_coverage/rustc.main.InstrumentCoverage.diff @@ -0,0 +1,82 @@ +- // MIR for `main` before InstrumentCoverage ++ // MIR for `main` after InstrumentCoverage + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/instrument_coverage.rs:9:11: 9:11 + let mut _1: (); // in scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 + let mut _2: bool; // in scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 + let mut _3: !; // in scope 0 at $DIR/instrument_coverage.rs:11:18: 13:10 ++ let mut _4: (); // in scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 + + bb0: { +- falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 ++ StorageLive(_4); // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 ++ _4 = const std::intrinsics::count_code_region(const 0u32) -> bb7; // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 ++ // ty::Const ++ // + ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region} ++ // + val: Value(Scalar()) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:9:1: 9:1 ++ // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) {std::intrinsics::count_code_region}, val: Value(Scalar()) } ++ // ty::Const ++ // + ty: u32 ++ // + val: Value(Scalar(0x00000000)) ++ // mir::Constant ++ // + span: $DIR/instrument_coverage.rs:9:1: 9:1 ++ // + literal: Const { ty: u32, val: Value(Scalar(0x00000000)) } + } + + bb1: { + StorageLive(_2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 + _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 + // ty::Const + // + ty: fn() -> bool {bar} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:11:12: 11:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + + bb2 (cleanup): { + resume; // scope 0 at $DIR/instrument_coverage.rs:9:1: 15:2 + } + + bb3: { + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/instrument_coverage.rs:11:12: 11:17 + switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10 + } + + bb4: { + falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10 + } + + bb5: { + _1 = const (); // scope 0 at $DIR/instrument_coverage.rs:11:9: 13:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:11:9: 13:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6 + goto -> bb0; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 + } + + bb6: { + _0 = const (); // scope 0 at $DIR/instrument_coverage.rs:12:13: 12:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/instrument_coverage.rs:12:13: 12:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/instrument_coverage.rs:14:5: 14:6 + return; // scope 0 at $DIR/instrument_coverage.rs:15:2: 15:2 ++ } ++ ++ bb7: { ++ StorageDead(_4); // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 ++ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/instrument_coverage.rs:10:5: 14:6 + } + } + diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs index 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..7b58dc1f624a5 --- /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 -> bb2; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-38669.rs:4:1: 12:2 + } + + bb2: { + falseUnwind -> [real: bb3, cleanup: bb1]; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 + } + + bb3: { + StorageLive(_3); // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + StorageLive(_4); // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 + _4 = _1; // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 + FakeRead(ForMatchedPlace, _4); // scope 1 at $DIR/issue-38669.rs:7:12: 7:24 + switchInt(_4) -> [false: bb5, otherwise: bb4]; // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + } + + bb4: { + falseEdge -> [real: bb6, imaginary: bb5]; // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + } + + bb5: { + _3 = const (); // scope 1 at $DIR/issue-38669.rs:7:9: 9:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-38669.rs:7:9: 9:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_4); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + StorageDead(_3); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + _1 = const true; // scope 1 at $DIR/issue-38669.rs:10:9: 10:28 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-38669.rs:10:24: 10:28 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _2 = const (); // scope 1 at $DIR/issue-38669.rs:6:10: 11:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-38669.rs:6:10: 11:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb2; // scope 1 at $DIR/issue-38669.rs:6:5: 11:6 + } + + bb6: { + _0 = const (); // scope 1 at $DIR/issue-38669.rs:8:13: 8:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-38669.rs:8:13: 8:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_4); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + StorageDead(_3); // scope 1 at $DIR/issue-38669.rs:9:9: 9:10 + StorageDead(_1); // scope 0 at $DIR/issue-38669.rs:12:1: 12:2 + return; // scope 0 at $DIR/issue-38669.rs:12:2: 12:2 + } +} diff --git a/src/test/mir-opt/issue-41110.rs b/src/test/mir-opt/issue-41110.rs index 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..77763f2d3a0d1 --- /dev/null +++ b/src/test/mir-opt/issue-41110/rustc.main.ElaborateDrops.after.mir @@ -0,0 +1,117 @@ +// MIR for `main` after ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-41110.rs:7:11: 7:11 + let _1: (); // in scope 0 at $DIR/issue-41110.rs:8:9: 8:10 + let mut _2: S; // in scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + let mut _3: S; // in scope 0 at $DIR/issue-41110.rs:8:21: 8:27 + let mut _4: S; // in scope 0 at $DIR/issue-41110.rs:8:21: 8:22 + let mut _5: bool; // in scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + scope 1 { + debug x => _1; // in scope 1 at $DIR/issue-41110.rs:8:9: 8:10 + } + + bb0: { + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:9: 8:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:9: 8:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageLive(_1); // scope 0 at $DIR/issue-41110.rs:8:9: 8:10 + StorageLive(_2); // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + _5 = const true; // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:13: 8:14 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _2 = S; // scope 0 at $DIR/issue-41110.rs:8:13: 8:14 + StorageLive(_3); // scope 0 at $DIR/issue-41110.rs:8:21: 8:27 + StorageLive(_4); // scope 0 at $DIR/issue-41110.rs:8:21: 8:22 + _4 = S; // scope 0 at $DIR/issue-41110.rs:8:21: 8:22 + _3 = const S::id(move _4) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue-41110.rs:8:21: 8:27 + // ty::Const + // + ty: fn(S) -> S {S::id} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:23: 8:25 + // + literal: Const { ty: fn(S) -> S {S::id}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41110.rs:7:1: 9:2 + } + + bb2: { + StorageDead(_4); // scope 0 at $DIR/issue-41110.rs:8:26: 8:27 + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:13: 8:28 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:13: 8:28 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _1 = const S::other(move _2, move _3) -> [return: bb6, unwind: bb5]; // scope 0 at $DIR/issue-41110.rs:8:13: 8:28 + // ty::Const + // + ty: fn(S, S) {S::other} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:15: 8:20 + // + literal: Const { ty: fn(S, S) {S::other}, val: Value(Scalar()) } + } + + bb3 (cleanup): { + goto -> bb9; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb4 (cleanup): { + goto -> bb3; // scope 0 at $DIR/issue-41110.rs:8:26: 8:27 + } + + bb5 (cleanup): { + goto -> bb3; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb6: { + StorageDead(_3); // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:27: 8:28 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_2); // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + _0 = const (); // scope 0 at $DIR/issue-41110.rs:7:11: 9:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:7:11: 9:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/issue-41110.rs:9:1: 9:2 + return; // scope 0 at $DIR/issue-41110.rs:9:2: 9:2 + } + + bb7 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb8 (cleanup): { + _5 = const false; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:8:27: 8:28 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + goto -> bb7; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } + + bb9 (cleanup): { + switchInt(_5) -> [false: bb1, otherwise: bb8]; // scope 0 at $DIR/issue-41110.rs:8:27: 8:28 + } +} diff --git a/src/test/mir-opt/issue-41110/rustc.test.ElaborateDrops.after.mir b/src/test/mir-opt/issue-41110/rustc.test.ElaborateDrops.after.mir new file mode 100644 index 0000000000000..a99846bd15daf --- /dev/null +++ b/src/test/mir-opt/issue-41110/rustc.test.ElaborateDrops.after.mir @@ -0,0 +1,145 @@ +// MIR for `test` after ElaborateDrops + +fn test() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-41110.rs:14:15: 14:15 + let _1: S; // in scope 0 at $DIR/issue-41110.rs:15:9: 15:10 + let _3: (); // in scope 0 at $DIR/issue-41110.rs:17:5: 17:12 + let mut _4: S; // in scope 0 at $DIR/issue-41110.rs:17:10: 17:11 + let mut _5: S; // in scope 0 at $DIR/issue-41110.rs:18:9: 18:10 + let mut _6: bool; // in scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + scope 1 { + debug u => _1; // in scope 1 at $DIR/issue-41110.rs:15:9: 15:10 + let mut _2: S; // in scope 1 at $DIR/issue-41110.rs:16:9: 16:14 + scope 2 { + debug v => _2; // in scope 2 at $DIR/issue-41110.rs:16:9: 16:14 + } + } + + bb0: { + _6 = const false; // scope 0 at $DIR/issue-41110.rs:15:9: 15:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:15:9: 15:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageLive(_1); // scope 0 at $DIR/issue-41110.rs:15:9: 15:10 + _6 = const true; // scope 0 at $DIR/issue-41110.rs:15:13: 15:14 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41110.rs:15:13: 15:14 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _1 = S; // scope 0 at $DIR/issue-41110.rs:15:13: 15:14 + StorageLive(_2); // scope 1 at $DIR/issue-41110.rs:16:9: 16:14 + _2 = S; // scope 1 at $DIR/issue-41110.rs:16:17: 16:18 + StorageLive(_3); // scope 2 at $DIR/issue-41110.rs:17:5: 17:12 + StorageLive(_4); // scope 2 at $DIR/issue-41110.rs:17:10: 17:11 + _4 = move _2; // scope 2 at $DIR/issue-41110.rs:17:10: 17:11 + _3 = const std::mem::drop::(move _4) -> [return: bb2, unwind: bb5]; // scope 2 at $DIR/issue-41110.rs:17:5: 17:12 + // ty::Const + // + ty: fn(S) {std::mem::drop::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:17:5: 17:9 + // + literal: Const { ty: fn(S) {std::mem::drop::}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41110.rs:14:1: 19:2 + } + + bb2: { + StorageDead(_4); // scope 2 at $DIR/issue-41110.rs:17:11: 17:12 + StorageDead(_3); // scope 2 at $DIR/issue-41110.rs:17:12: 17:13 + StorageLive(_5); // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + _6 = const false; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:18:9: 18:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _5 = move _1; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + goto -> bb12; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + } + + bb3 (cleanup): { + goto -> bb15; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb4 (cleanup): { + goto -> bb3; // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb5 (cleanup): { + goto -> bb4; // scope 2 at $DIR/issue-41110.rs:17:11: 17:12 + } + + bb6: { + goto -> bb8; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + } + + bb7 (cleanup): { + goto -> bb4; // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + } + + bb8: { + StorageDead(_5); // scope 2 at $DIR/issue-41110.rs:18:9: 18:10 + _0 = const (); // scope 0 at $DIR/issue-41110.rs:14:15: 19:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41110.rs:14:15: 19:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_2) -> [return: bb9, unwind: bb3]; // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb9: { + StorageDead(_2); // scope 1 at $DIR/issue-41110.rs:19:1: 19:2 + goto -> bb10; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb10: { + _6 = const false; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:19:1: 19:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_1); // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + return; // scope 0 at $DIR/issue-41110.rs:19:2: 19:2 + } + + bb11 (cleanup): { + _2 = move _5; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + goto -> bb7; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + } + + bb12: { + _2 = move _5; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + goto -> bb6; // scope 2 at $DIR/issue-41110.rs:18:5: 18:6 + } + + bb13 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb14 (cleanup): { + _6 = const false; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41110.rs:19:1: 19:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + goto -> bb13; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } + + bb15 (cleanup): { + switchInt(_6) -> [false: bb1, otherwise: bb14]; // scope 0 at $DIR/issue-41110.rs:19:1: 19:2 + } +} diff --git a/src/test/mir-opt/issue-41697.rs b/src/test/mir-opt/issue-41697.rs index 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..d263b2515f17a --- /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: bb2, unwind: bb1]; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb2: { + _0 = move (_1.0: usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + return; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } +} diff --git a/src/test/mir-opt/issue-41697/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..6c00f49fb75b1 --- /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: bb2, unwind: bb1]; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } + + bb2: { + _0 = move (_1.0: usize); // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + return; // scope 0 at $DIR/issue-41697.rs:18:19: 18:22 + } +} diff --git a/src/test/mir-opt/issue-41888.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..ce940273c3ef5 --- /dev/null +++ b/src/test/mir-opt/issue-41888/rustc.main.ElaborateDrops.after.mir @@ -0,0 +1,268 @@ +// MIR for `main` after ElaborateDrops + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-41888.rs:6:11: 6:11 + let _1: E; // in scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + let mut _2: bool; // in scope 0 at $DIR/issue-41888.rs:8:8: 8:14 + let mut _3: E; // in scope 0 at $DIR/issue-41888.rs:9:13: 9:20 + let mut _4: K; // in scope 0 at $DIR/issue-41888.rs:9:18: 9:19 + let mut _5: isize; // in scope 0 at $DIR/issue-41888.rs:10:16: 10:24 + let mut _7: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _8: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _9: bool; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _10: isize; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + let mut _11: isize; // in scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + scope 1 { + debug e => _1; // in scope 1 at $DIR/issue-41888.rs:7:9: 7:10 + let _6: K; // in scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + scope 2 { + debug _k => _6; // in scope 2 at $DIR/issue-41888.rs:10:21: 10:23 + } + } + + bb0: { + _9 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:7:9: 7:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _7 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:7:9: 7:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _8 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:7:9: 7:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageLive(_1); // scope 0 at $DIR/issue-41888.rs:7:9: 7:10 + StorageLive(_2); // scope 1 at $DIR/issue-41888.rs:8:8: 8:14 + _2 = const cond() -> [return: bb2, unwind: bb3]; // scope 1 at $DIR/issue-41888.rs:8:8: 8:14 + // ty::Const + // + ty: fn() -> bool {cond} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41888.rs:8:8: 8:12 + // + literal: Const { ty: fn() -> bool {cond}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-41888.rs:6:1: 15:2 + } + + bb2: { + switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 + } + + bb3 (cleanup): { + goto -> bb1; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb4: { + _0 = const (); // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41888.rs:8:5: 14:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb11; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6 + } + + bb5: { + StorageLive(_3); // scope 1 at $DIR/issue-41888.rs:9:13: 9:20 + StorageLive(_4); // scope 1 at $DIR/issue-41888.rs:9:18: 9:19 + _4 = K; // scope 1 at $DIR/issue-41888.rs:9:18: 9:19 + _3 = E::F(move _4); // scope 1 at $DIR/issue-41888.rs:9:13: 9:20 + StorageDead(_4); // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + goto -> bb14; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + } + + bb6: { + goto -> bb8; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + } + + bb7 (cleanup): { + goto -> bb3; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + } + + bb8: { + StorageDead(_3); // scope 1 at $DIR/issue-41888.rs:9:19: 9:20 + _5 = discriminant(_1); // scope 1 at $DIR/issue-41888.rs:10:16: 10:24 + switchInt(move _5) -> [0isize: bb10, otherwise: bb9]; // scope 1 at $DIR/issue-41888.rs:10:16: 10:24 + } + + bb9: { + _0 = const (); // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41888.rs:10:9: 13:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb11; // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 + } + + bb10: { + StorageLive(_6); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + _9 = const false; // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:10:21: 10:23 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _6 = move ((_1 as F).0: K); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23 + _0 = const (); // scope 2 at $DIR/issue-41888.rs:10:29: 13:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-41888.rs:10:29: 13:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_6); // scope 1 at $DIR/issue-41888.rs:13:9: 13:10 + goto -> bb11; // scope 1 at $DIR/issue-41888.rs:10:9: 13:10 + } + + bb11: { + goto -> bb21; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb12: { + _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:15:1: 15:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _8 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:15:1: 15:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + _9 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:15:1: 15:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + StorageDead(_2); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + return; // scope 0 at $DIR/issue-41888.rs:15:2: 15:2 + } + + bb13 (cleanup): { + _7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _1 = move _3; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + goto -> bb7; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + } + + bb14: { + _7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-41888.rs:9:9: 9:10 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + _1 = move _3; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + goto -> bb6; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10 + } + + bb15: { + _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:15:1: 15:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + goto -> bb12; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb16 (cleanup): { + _7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/issue-41888.rs:15:1: 15:2 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + goto -> bb1; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb17 (cleanup): { + goto -> bb16; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb18: { + drop(_1) -> [return: bb15, unwind: bb16]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb19 (cleanup): { + drop(_1) -> bb16; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb20: { + _10 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + switchInt(move _10) -> [0isize: bb15, otherwise: bb18]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb21: { + switchInt(_7) -> [false: bb15, otherwise: bb20]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb22 (cleanup): { + _11 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + switchInt(move _11) -> [0isize: bb17, otherwise: bb19]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } + + bb23 (cleanup): { + switchInt(_7) -> [false: bb16, otherwise: bb22]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2 + } +} diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs index 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..7299a683a9f0b --- /dev/null +++ b/src/test/mir-opt/issue-49232/rustc.main.mir_map.0.mir @@ -0,0 +1,128 @@ +// MIR for `main` 0 mir_map + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-49232.rs:5:11: 5:11 + let mut _1: (); // in scope 0 at $DIR/issue-49232.rs:5:1: 15:2 + let _2: i32; // in scope 0 at $DIR/issue-49232.rs:7:13: 7:19 + let mut _3: bool; // in scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + let mut _4: !; // in scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + let _5: (); // in scope 0 at $DIR/issue-49232.rs:13:9: 13:22 + let mut _6: &i32; // in scope 0 at $DIR/issue-49232.rs:13:14: 13:21 + scope 1 { + debug beacon => _2; // in scope 1 at $DIR/issue-49232.rs:7:13: 7:19 + } + + bb0: { + goto -> bb1; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 + } + + bb1: { + falseUnwind -> [real: bb3, cleanup: bb4]; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 + } + + bb2: { + goto -> bb14; // scope 0 at $DIR/issue-49232.rs:15:2: 15:2 + } + + bb3: { + StorageLive(_2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19 + StorageLive(_3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + _3 = const true; // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/issue-49232.rs:8:19: 8:23 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + FakeRead(ForMatchedPlace, _3); // scope 0 at $DIR/issue-49232.rs:8:19: 8:23 + switchInt(_3) -> [false: bb5, otherwise: bb6]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22 + } + + bb4 (cleanup): { + resume; // scope 0 at $DIR/issue-49232.rs:5:1: 15:2 + } + + bb5: { + falseEdge -> [real: bb7, imaginary: bb6]; // scope 0 at $DIR/issue-49232.rs:9:17: 9:22 + } + + bb6: { + _0 = const (); // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-49232.rs:10:25: 10:30 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb8; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + } + + bb7: { + _2 = const 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 -> bb12; // scope 0 at $DIR/issue-49232.rs:8:13: 11:14 + } + + bb8: { + StorageDead(_3); // scope 0 at $DIR/issue-49232.rs:12:10: 12:11 + goto -> bb9; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + } + + bb9: { + StorageDead(_2); // scope 0 at $DIR/issue-49232.rs:14:5: 14:6 + goto -> bb2; // scope 0 at $DIR/issue-49232.rs:10:25: 10:30 + } + + bb10: { + _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 + } + + bb11: { + goto -> bb12; // scope 0 at $DIR/issue-49232.rs:8:13: 11:14 + } + + bb12: { + FakeRead(ForLet, _2); // scope 0 at $DIR/issue-49232.rs:7:13: 7:19 + StorageDead(_3); // scope 0 at $DIR/issue-49232.rs:12:10: 12:11 + StorageLive(_5); // scope 1 at $DIR/issue-49232.rs:13:9: 13:22 + StorageLive(_6); // scope 1 at $DIR/issue-49232.rs:13:14: 13:21 + _6 = &_2; // scope 1 at $DIR/issue-49232.rs:13:14: 13:21 + _5 = const std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb4]; // scope 1 at $DIR/issue-49232.rs:13:9: 13:22 + // ty::Const + // + ty: fn(&i32) {std::mem::drop::<&i32>} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-49232.rs:13:9: 13:13 + // + literal: Const { ty: fn(&i32) {std::mem::drop::<&i32>}, val: Value(Scalar()) } + } + + bb13: { + StorageDead(_6); // scope 1 at $DIR/issue-49232.rs:13:21: 13:22 + StorageDead(_5); // scope 1 at $DIR/issue-49232.rs:13:22: 13:23 + _1 = const (); // scope 0 at $DIR/issue-49232.rs:6:10: 14:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-49232.rs:6:10: 14:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/issue-49232.rs:14:5: 14:6 + goto -> bb1; // scope 0 at $DIR/issue-49232.rs:6:5: 14:6 + } + + bb14: { + return; // scope 0 at $DIR/issue-49232.rs:15:2: 15:2 + } +} diff --git a/src/test/mir-opt/issue-62289.rs b/src/test/mir-opt/issue-62289.rs index 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..0b8b03961f2a0 --- /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: bb2, unwind: bb3]; // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + // ty::Const + // + ty: fn(std::option::Option) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-62289.rs:9:15: 9:20 + // + literal: Const { ty: fn(std::option::Option) -> std::result::Result< as std::ops::Try>::Ok, as std::ops::Try>::Error> { as std::ops::Try>::into_result}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-62289.rs:8:1: 10:2 + } + + bb2: { + StorageDead(_4); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + _5 = discriminant(_3); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + switchInt(move _5) -> [0isize: bb4, 1isize: bb6, otherwise: bb5]; // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + } + + bb3 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + } + + bb4: { + StorageLive(_10); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + _10 = ((_3 as Ok).0: u32); // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + (*_2) = _10; // scope 4 at $DIR/issue-62289.rs:9:15: 9:20 + StorageDead(_10); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + _1 = move _2; // scope 0 at $DIR/issue-62289.rs:9:10: 9:21 + drop(_2) -> [return: bb12, unwind: bb11]; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + } + + bb5: { + unreachable; // scope 0 at $DIR/issue-62289.rs:9:15: 9:20 + } + + bb6: { + StorageLive(_6); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + _6 = ((_3 as Err).0: std::option::NoneError); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + StorageLive(_8); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + StorageLive(_9); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + _9 = _6; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + _8 = const >::from(move _9) -> [return: bb8, unwind: bb3]; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + // ty::Const + // + ty: fn(std::option::NoneError) -> std::option::NoneError {>::from} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-62289.rs:9:19: 9:20 + // + literal: Const { ty: fn(std::option::NoneError) -> std::option::NoneError {>::from}, val: Value(Scalar()) } + } + + bb7: { + return; // scope 0 at $DIR/issue-62289.rs:10:2: 10:2 + } + + bb8: { + StorageDead(_9); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + _0 = const > as std::ops::Try>::from_error(move _8) -> [return: bb9, unwind: bb3]; // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + // ty::Const + // + ty: fn(> as std::ops::Try>::Error) -> std::option::Option> {> as std::ops::Try>::from_error} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-62289.rs:9:15: 9:20 + // + literal: Const { ty: fn(> as std::ops::Try>::Error) -> std::option::Option> {> as std::ops::Try>::from_error}, val: Value(Scalar()) } + } + + bb9: { + StorageDead(_8); // scope 2 at $DIR/issue-62289.rs:9:19: 9:20 + StorageDead(_6); // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + drop(_2) -> bb10; // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + } + + bb10: { + StorageDead(_2); // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + StorageDead(_1); // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + StorageDead(_3); // scope 0 at $DIR/issue-62289.rs:10:1: 10:2 + goto -> bb7; // scope 0 at $DIR/issue-62289.rs:9:19: 9:20 + } + + bb11 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + } + + bb12: { + StorageDead(_2); // scope 0 at $DIR/issue-62289.rs:9:20: 9:21 + _0 = std::option::Option::>::Some(move _1); // scope 0 at $DIR/issue-62289.rs:9:5: 9:22 + drop(_1) -> bb13; // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + } + + bb13: { + StorageDead(_1); // scope 0 at $DIR/issue-62289.rs:9:21: 9:22 + StorageDead(_3); // scope 0 at $DIR/issue-62289.rs:10:1: 10:2 + goto -> bb7; // scope 0 at $DIR/issue-62289.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/issue-72181-1.rs b/src/test/mir-opt/issue-72181-1.rs new file mode 100644 index 0000000000000..6d65f847a2c63 --- /dev/null +++ b/src/test/mir-opt/issue-72181-1.rs @@ -0,0 +1,21 @@ +// compile-flags: -Z mir-opt-level=1 +// Regression test for #72181, this ICE requires `-Z mir-opt-level=1` flags. + +#![feature(never_type)] +#![allow(unused, invalid_value)] + +enum Void {} + +// EMIT_MIR rustc.f.mir_map.0.mir +fn f(v: Void) -> ! { + match v {} +} + +// EMIT_MIR rustc.main.mir_map.0.mir +fn main() { + let v: Void = unsafe { + std::mem::transmute::<(), Void>(()) + }; + + f(v); +} diff --git a/src/test/mir-opt/issue-72181-1/rustc.f.mir_map.0.mir b/src/test/mir-opt/issue-72181-1/rustc.f.mir_map.0.mir new file mode 100644 index 0000000000000..1821365898e53 --- /dev/null +++ b/src/test/mir-opt/issue-72181-1/rustc.f.mir_map.0.mir @@ -0,0 +1,37 @@ +// MIR for `f` 0 mir_map + +fn f(_1: Void) -> ! { + debug v => _1; // in scope 0 at $DIR/issue-72181-1.rs:10:6: 10:7 + let mut _0: !; // return place in scope 0 at $DIR/issue-72181-1.rs:10:18: 10:19 + let mut _2: !; // in scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2 + let mut _3: !; // in scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2 + StorageLive(_3); // scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15 + FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12 + unreachable; // scope 0 at $DIR/issue-72181-1.rs:11:11: 11:12 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181-1.rs:10:1: 12:2 + } + + bb2: { + unreachable; // scope 0 at $DIR/issue-72181-1.rs:11:5: 11:15 + } + + bb3: { + StorageDead(_3); // scope 0 at $DIR/issue-72181-1.rs:11:14: 11:15 + unreachable; // scope 0 at $DIR/issue-72181-1.rs:10:20: 12:2 + } + + bb4: { + StorageDead(_2); // scope 0 at $DIR/issue-72181-1.rs:12:1: 12:2 + goto -> bb5; // scope 0 at $DIR/issue-72181-1.rs:12:2: 12:2 + } + + bb5: { + return; // scope 0 at $DIR/issue-72181-1.rs:12:2: 12:2 + } +} diff --git a/src/test/mir-opt/issue-72181-1/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-72181-1/rustc.main.mir_map.0.mir new file mode 100644 index 0000000000000..b87d0294fb87b --- /dev/null +++ b/src/test/mir-opt/issue-72181-1/rustc.main.mir_map.0.mir @@ -0,0 +1,67 @@ +// MIR for `main` 0 mir_map + +| User Type Annotations +| 0: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16 +| 1: Canonical { max_universe: U0, variables: [], value: Ty(Void) } at $DIR/issue-72181-1.rs:16:12: 16:16 +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-72181-1.rs:15:11: 15:11 + let mut _1: !; // in scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2 + let _2: Void as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10 + let mut _3: (); // in scope 0 at $DIR/issue-72181-1.rs:17:41: 17:43 + let _4: !; // in scope 0 at $DIR/issue-72181-1.rs:20:5: 20:9 + let mut _5: Void; // in scope 0 at $DIR/issue-72181-1.rs:20:7: 20:8 + scope 1 { + debug v => _2; // in scope 1 at $DIR/issue-72181-1.rs:16:9: 16:10 + } + scope 2 { + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10 + StorageLive(_3); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43 + _3 = (); // scope 2 at $DIR/issue-72181-1.rs:17:41: 17:43 + _2 = const std::intrinsics::transmute::<(), Void>(move _3) -> [return: bb2, unwind: bb1]; // scope 2 at $DIR/issue-72181-1.rs:17:9: 17:44 + // ty::Const + // + ty: unsafe extern "rust-intrinsic" fn(()) -> Void {std::intrinsics::transmute::<(), Void>} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-72181-1.rs:17:9: 17:40 + // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Void {std::intrinsics::transmute::<(), Void>}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181-1.rs:15:1: 21:2 + } + + bb2: { + StorageDead(_3); // scope 2 at $DIR/issue-72181-1.rs:17:43: 17:44 + FakeRead(ForLet, _2); // scope 0 at $DIR/issue-72181-1.rs:16:9: 16:10 + AscribeUserType(_2, o, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at $DIR/issue-72181-1.rs:16:12: 16:16 + StorageLive(_4); // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9 + StorageLive(_5); // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8 + _5 = move _2; // scope 1 at $DIR/issue-72181-1.rs:20:7: 20:8 + const f(move _5) -> bb1; // scope 1 at $DIR/issue-72181-1.rs:20:5: 20:9 + // ty::Const + // + ty: fn(Void) -> ! {f} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-72181-1.rs:20:5: 20:6 + // + literal: Const { ty: fn(Void) -> ! {f}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_5); // scope 1 at $DIR/issue-72181-1.rs:20:8: 20:9 + StorageDead(_4); // scope 1 at $DIR/issue-72181-1.rs:20:9: 20:10 + StorageDead(_2); // scope 0 at $DIR/issue-72181-1.rs:21:1: 21:2 + unreachable; // scope 0 at $DIR/issue-72181-1.rs:15:11: 21:2 + } + + bb4: { + goto -> bb5; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2 + } + + bb5: { + return; // scope 0 at $DIR/issue-72181-1.rs:21:2: 21:2 + } +} diff --git a/src/test/mir-opt/issue-72181.rs b/src/test/mir-opt/issue-72181.rs new file mode 100644 index 0000000000000..9373ce12032b9 --- /dev/null +++ b/src/test/mir-opt/issue-72181.rs @@ -0,0 +1,28 @@ +// compile-flags: -Z mir-opt-level=1 +// Regression test for #72181, this ICE requires `-Z mir-opt-level=1` flags. + +use std::mem; + +#[derive(Copy, Clone)] +enum Never {} + +union Foo { + a: u64, + b: Never +} + +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.foo.mir_map.0.mir +fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 } + +// EMIT_MIR rustc.bar.mir_map.0.mir +fn bar([(_, x)]: [(Never, u32); 1]) -> u32 { x } + +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.main.mir_map.0.mir +fn main() { + let _ = mem::size_of::(); + + let f = [Foo { a: 42 }, Foo { a: 10 }]; + let _ = unsafe { f[0].a }; +} diff --git a/src/test/mir-opt/issue-72181/32bit/rustc.bar.mir_map.0.mir b/src/test/mir-opt/issue-72181/32bit/rustc.bar.mir_map.0.mir new file mode 100644 index 0000000000000..29654c2b1f83b --- /dev/null +++ b/src/test/mir-opt/issue-72181/32bit/rustc.bar.mir_map.0.mir @@ -0,0 +1,25 @@ +// MIR for `bar` 0 mir_map + +fn bar(_1: [(Never, u32); 1]) -> u32 { + let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:19:40: 19:43 + let _2: u32; // in scope 0 at $DIR/issue-72181.rs:19:13: 19:14 + scope 1 { + debug x => _2; // in scope 1 at $DIR/issue-72181.rs:19:13: 19:14 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:19:13: 19:14 + _2 = (_1[0 of 1].1: u32); // scope 0 at $DIR/issue-72181.rs:19:13: 19:14 + _0 = _2; // scope 1 at $DIR/issue-72181.rs:19:46: 19:47 + StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:19:48: 19:49 + goto -> bb2; // scope 0 at $DIR/issue-72181.rs:19:49: 19:49 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:19:1: 19:49 + } + + bb2: { + return; // scope 0 at $DIR/issue-72181.rs:19:49: 19:49 + } +} diff --git a/src/test/mir-opt/issue-72181/32bit/rustc.foo.mir_map.0.mir b/src/test/mir-opt/issue-72181/32bit/rustc.foo.mir_map.0.mir new file mode 100644 index 0000000000000..776eb61a5264f --- /dev/null +++ b/src/test/mir-opt/issue-72181/32bit/rustc.foo.mir_map.0.mir @@ -0,0 +1,37 @@ +// MIR for `foo` 0 mir_map + +fn foo(_1: [(Never, u32); 1]) -> u32 { + debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:16:8: 16:10 + let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:16:34: 16:37 + let _2: usize; // in scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + _2 = const 0usize; // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/issue-72181.rs:16:43: 16:44 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } + _3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + _4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + assert(move _4, "index out of bounds: the len is {} but the index is {}", move _3, _2) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:16:1: 16:49 + } + + bb2: { + _0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:16:40: 16:47 + StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:16:48: 16:49 + goto -> bb3; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 + } + + bb3: { + return; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 + } +} diff --git a/src/test/mir-opt/issue-72181/32bit/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-72181/32bit/rustc.main.mir_map.0.mir new file mode 100644 index 0000000000000..aa44dcd8eaee3 --- /dev/null +++ b/src/test/mir-opt/issue-72181/32bit/rustc.main.mir_map.0.mir @@ -0,0 +1,93 @@ +// MIR for `main` 0 mir_map + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-72181.rs:23:11: 23:11 + let mut _1: usize; // in scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + let mut _3: Foo; // in scope 0 at $DIR/issue-72181.rs:26:14: 26:27 + let mut _4: Foo; // in scope 0 at $DIR/issue-72181.rs:26:29: 26:42 + let mut _5: u64; // in scope 0 at $DIR/issue-72181.rs:27:13: 27:30 + let _6: usize; // in scope 0 at $DIR/issue-72181.rs:27:24: 27:25 + let mut _7: usize; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 + let mut _8: bool; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 + scope 1 { + let _2: [Foo; 2]; // in scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + scope 2 { + debug f => _2; // in scope 2 at $DIR/issue-72181.rs:26:9: 26:10 + scope 3 { + } + scope 4 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + _1 = const std::mem::size_of::() -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + // ty::Const + // + ty: fn() -> usize {std::mem::size_of::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-72181.rs:24:13: 24:32 + // + literal: Const { ty: fn() -> usize {std::mem::size_of::}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:23:1: 28:2 + } + + bb2: { + StorageDead(_1); // scope 0 at $DIR/issue-72181.rs:24:34: 24:35 + StorageLive(_2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + StorageLive(_3); // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 + _3 = Foo { a: const 42u64 }; // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 + // ty::Const + // + ty: u64 + // + val: Value(Scalar(0x000000000000002a)) + // mir::Constant + // + span: $DIR/issue-72181.rs:26:23: 26:25 + // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000002a)) } + StorageLive(_4); // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 + _4 = Foo { a: const 10u64 }; // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 + // ty::Const + // + ty: u64 + // + val: Value(Scalar(0x000000000000000a)) + // mir::Constant + // + span: $DIR/issue-72181.rs:26:38: 26:40 + // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000000a)) } + _2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:26:13: 26:43 + StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 + StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 + FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:27:13: 27:30 + StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 + _6 = const 0usize; // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/issue-72181.rs:27:24: 27:25 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } + _7 = Len(_2); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + _8 = Lt(_6, _7); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> [success: bb3, unwind: bb1]; // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + } + + bb3: { + _5 = (_2[_6].0: u64); // scope 4 at $DIR/issue-72181.rs:27:22: 27:28 + StorageDead(_6); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 + StorageDead(_5); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 + _0 = const (); // scope 0 at $DIR/issue-72181.rs:23:11: 28:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-72181.rs:23:11: 28:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/issue-72181.rs:28:1: 28:2 + goto -> bb4; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 + } + + bb4: { + return; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 + } +} diff --git a/src/test/mir-opt/issue-72181/64bit/rustc.bar.mir_map.0.mir b/src/test/mir-opt/issue-72181/64bit/rustc.bar.mir_map.0.mir new file mode 100644 index 0000000000000..29654c2b1f83b --- /dev/null +++ b/src/test/mir-opt/issue-72181/64bit/rustc.bar.mir_map.0.mir @@ -0,0 +1,25 @@ +// MIR for `bar` 0 mir_map + +fn bar(_1: [(Never, u32); 1]) -> u32 { + let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:19:40: 19:43 + let _2: u32; // in scope 0 at $DIR/issue-72181.rs:19:13: 19:14 + scope 1 { + debug x => _2; // in scope 1 at $DIR/issue-72181.rs:19:13: 19:14 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:19:13: 19:14 + _2 = (_1[0 of 1].1: u32); // scope 0 at $DIR/issue-72181.rs:19:13: 19:14 + _0 = _2; // scope 1 at $DIR/issue-72181.rs:19:46: 19:47 + StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:19:48: 19:49 + goto -> bb2; // scope 0 at $DIR/issue-72181.rs:19:49: 19:49 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:19:1: 19:49 + } + + bb2: { + return; // scope 0 at $DIR/issue-72181.rs:19:49: 19:49 + } +} diff --git a/src/test/mir-opt/issue-72181/64bit/rustc.foo.mir_map.0.mir b/src/test/mir-opt/issue-72181/64bit/rustc.foo.mir_map.0.mir new file mode 100644 index 0000000000000..639019eaf9ccc --- /dev/null +++ b/src/test/mir-opt/issue-72181/64bit/rustc.foo.mir_map.0.mir @@ -0,0 +1,37 @@ +// MIR for `foo` 0 mir_map + +fn foo(_1: [(Never, u32); 1]) -> u32 { + debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:16:8: 16:10 + let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:16:34: 16:37 + let _2: usize; // in scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + _2 = const 0usize; // scope 0 at $DIR/issue-72181.rs:16:43: 16:44 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/issue-72181.rs:16:43: 16:44 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + _3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + _4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + assert(move _4, "index out of bounds: the len is {} but the index is {}", move _3, _2) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:16:40: 16:45 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:16:1: 16:49 + } + + bb2: { + _0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:16:40: 16:47 + StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:16:48: 16:49 + goto -> bb3; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 + } + + bb3: { + return; // scope 0 at $DIR/issue-72181.rs:16:49: 16:49 + } +} diff --git a/src/test/mir-opt/issue-72181/64bit/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-72181/64bit/rustc.main.mir_map.0.mir new file mode 100644 index 0000000000000..4098e0e295c5d --- /dev/null +++ b/src/test/mir-opt/issue-72181/64bit/rustc.main.mir_map.0.mir @@ -0,0 +1,93 @@ +// MIR for `main` 0 mir_map + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-72181.rs:23:11: 23:11 + let mut _1: usize; // in scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + let mut _3: Foo; // in scope 0 at $DIR/issue-72181.rs:26:14: 26:27 + let mut _4: Foo; // in scope 0 at $DIR/issue-72181.rs:26:29: 26:42 + let mut _5: u64; // in scope 0 at $DIR/issue-72181.rs:27:13: 27:30 + let _6: usize; // in scope 0 at $DIR/issue-72181.rs:27:24: 27:25 + let mut _7: usize; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 + let mut _8: bool; // in scope 0 at $DIR/issue-72181.rs:27:22: 27:26 + scope 1 { + let _2: [Foo; 2]; // in scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + scope 2 { + debug f => _2; // in scope 2 at $DIR/issue-72181.rs:26:9: 26:10 + scope 3 { + } + scope 4 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + _1 = const std::mem::size_of::() -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:24:13: 24:34 + // ty::Const + // + ty: fn() -> usize {std::mem::size_of::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-72181.rs:24:13: 24:32 + // + literal: Const { ty: fn() -> usize {std::mem::size_of::}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:23:1: 28:2 + } + + bb2: { + StorageDead(_1); // scope 0 at $DIR/issue-72181.rs:24:34: 24:35 + StorageLive(_2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + StorageLive(_3); // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 + _3 = Foo { a: const 42u64 }; // scope 1 at $DIR/issue-72181.rs:26:14: 26:27 + // ty::Const + // + ty: u64 + // + val: Value(Scalar(0x000000000000002a)) + // mir::Constant + // + span: $DIR/issue-72181.rs:26:23: 26:25 + // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000002a)) } + StorageLive(_4); // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 + _4 = Foo { a: const 10u64 }; // scope 1 at $DIR/issue-72181.rs:26:29: 26:42 + // ty::Const + // + ty: u64 + // + val: Value(Scalar(0x000000000000000a)) + // mir::Constant + // + span: $DIR/issue-72181.rs:26:38: 26:40 + // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000000a)) } + _2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:26:13: 26:43 + StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 + StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:26:42: 26:43 + FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:26:9: 26:10 + StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:27:13: 27:30 + StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 + _6 = const 0usize; // scope 4 at $DIR/issue-72181.rs:27:24: 27:25 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/issue-72181.rs:27:24: 27:25 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + _7 = Len(_2); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + _8 = Lt(_6, _7); // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> [success: bb3, unwind: bb1]; // scope 4 at $DIR/issue-72181.rs:27:22: 27:26 + } + + bb3: { + _5 = (_2[_6].0: u64); // scope 4 at $DIR/issue-72181.rs:27:22: 27:28 + StorageDead(_6); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 + StorageDead(_5); // scope 2 at $DIR/issue-72181.rs:27:30: 27:31 + _0 = const (); // scope 0 at $DIR/issue-72181.rs:23:11: 28:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-72181.rs:23:11: 28:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/issue-72181.rs:28:1: 28:2 + goto -> bb4; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 + } + + bb4: { + return; // scope 0 at $DIR/issue-72181.rs:28:2: 28:2 + } +} diff --git a/src/test/mir-opt/issue-72181/rustc.bar.mir_map.0.mir b/src/test/mir-opt/issue-72181/rustc.bar.mir_map.0.mir new file mode 100644 index 0000000000000..3b6dc46d055cd --- /dev/null +++ b/src/test/mir-opt/issue-72181/rustc.bar.mir_map.0.mir @@ -0,0 +1,25 @@ +// MIR for `bar` 0 mir_map + +fn bar(_1: [(Never, u32); 1]) -> u32 { + let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:18:40: 18:43 + let _2: u32; // in scope 0 at $DIR/issue-72181.rs:18:13: 18:14 + scope 1 { + debug x => _2; // in scope 1 at $DIR/issue-72181.rs:18:13: 18:14 + } + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:18:13: 18:14 + _2 = (_1[0 of 1].1: u32); // scope 0 at $DIR/issue-72181.rs:18:13: 18:14 + _0 = _2; // scope 1 at $DIR/issue-72181.rs:18:46: 18:47 + StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:18:48: 18:49 + goto -> bb2; // scope 0 at $DIR/issue-72181.rs:18:49: 18:49 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:18:1: 18:49 + } + + bb2: { + return; // scope 0 at $DIR/issue-72181.rs:18:49: 18:49 + } +} diff --git a/src/test/mir-opt/issue-72181/rustc.foo.mir_map.0.mir b/src/test/mir-opt/issue-72181/rustc.foo.mir_map.0.mir new file mode 100644 index 0000000000000..2941e282cf42b --- /dev/null +++ b/src/test/mir-opt/issue-72181/rustc.foo.mir_map.0.mir @@ -0,0 +1,37 @@ +// MIR for `foo` 0 mir_map + +fn foo(_1: [(Never, u32); 1]) -> u32 { + debug xs => _1; // in scope 0 at $DIR/issue-72181.rs:15:8: 15:10 + let mut _0: u32; // return place in scope 0 at $DIR/issue-72181.rs:15:34: 15:37 + let _2: usize; // in scope 0 at $DIR/issue-72181.rs:15:43: 15:44 + let mut _3: usize; // in scope 0 at $DIR/issue-72181.rs:15:40: 15:45 + let mut _4: bool; // in scope 0 at $DIR/issue-72181.rs:15:40: 15:45 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/issue-72181.rs:15:43: 15:44 + _2 = const 0usize; // scope 0 at $DIR/issue-72181.rs:15:43: 15:44 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/issue-72181.rs:15:43: 15:44 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + _3 = Len(_1); // scope 0 at $DIR/issue-72181.rs:15:40: 15:45 + _4 = Lt(_2, _3); // scope 0 at $DIR/issue-72181.rs:15:40: 15:45 + assert(move _4, "index out of bounds: the len is {} but the index is {}", move _3, _2) -> [success: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:15:40: 15:45 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:15:1: 15:49 + } + + bb2: { + _0 = (_1[_2].1: u32); // scope 0 at $DIR/issue-72181.rs:15:40: 15:47 + StorageDead(_2); // scope 0 at $DIR/issue-72181.rs:15:48: 15:49 + goto -> bb3; // scope 0 at $DIR/issue-72181.rs:15:49: 15:49 + } + + bb3: { + return; // scope 0 at $DIR/issue-72181.rs:15:49: 15:49 + } +} diff --git a/src/test/mir-opt/issue-72181/rustc.main.mir_map.0.mir b/src/test/mir-opt/issue-72181/rustc.main.mir_map.0.mir new file mode 100644 index 0000000000000..65f4de0e23545 --- /dev/null +++ b/src/test/mir-opt/issue-72181/rustc.main.mir_map.0.mir @@ -0,0 +1,93 @@ +// MIR for `main` 0 mir_map + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/issue-72181.rs:21:11: 21:11 + let mut _1: usize; // in scope 0 at $DIR/issue-72181.rs:22:13: 22:34 + let mut _3: Foo; // in scope 0 at $DIR/issue-72181.rs:24:14: 24:27 + let mut _4: Foo; // in scope 0 at $DIR/issue-72181.rs:24:29: 24:42 + let mut _5: u64; // in scope 0 at $DIR/issue-72181.rs:25:13: 25:30 + let _6: usize; // in scope 0 at $DIR/issue-72181.rs:25:24: 25:25 + let mut _7: usize; // in scope 0 at $DIR/issue-72181.rs:25:22: 25:26 + let mut _8: bool; // in scope 0 at $DIR/issue-72181.rs:25:22: 25:26 + scope 1 { + let _2: [Foo; 2]; // in scope 1 at $DIR/issue-72181.rs:24:9: 24:10 + scope 2 { + debug f => _2; // in scope 2 at $DIR/issue-72181.rs:24:9: 24:10 + scope 3 { + } + scope 4 { + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/issue-72181.rs:22:13: 22:34 + _1 = const std::mem::size_of::() -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/issue-72181.rs:22:13: 22:34 + // ty::Const + // + ty: fn() -> usize {std::mem::size_of::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-72181.rs:22:13: 22:32 + // + literal: Const { ty: fn() -> usize {std::mem::size_of::}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/issue-72181.rs:21:1: 26:2 + } + + bb2: { + StorageDead(_1); // scope 0 at $DIR/issue-72181.rs:22:34: 22:35 + StorageLive(_2); // scope 1 at $DIR/issue-72181.rs:24:9: 24:10 + StorageLive(_3); // scope 1 at $DIR/issue-72181.rs:24:14: 24:27 + _3 = Foo { a: const 42u64 }; // scope 1 at $DIR/issue-72181.rs:24:14: 24:27 + // ty::Const + // + ty: u64 + // + val: Value(Scalar(0x000000000000002a)) + // mir::Constant + // + span: $DIR/issue-72181.rs:24:23: 24:25 + // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000002a)) } + StorageLive(_4); // scope 1 at $DIR/issue-72181.rs:24:29: 24:42 + _4 = Foo { a: const 10u64 }; // scope 1 at $DIR/issue-72181.rs:24:29: 24:42 + // ty::Const + // + ty: u64 + // + val: Value(Scalar(0x000000000000000a)) + // mir::Constant + // + span: $DIR/issue-72181.rs:24:38: 24:40 + // + literal: Const { ty: u64, val: Value(Scalar(0x000000000000000a)) } + _2 = [move _3, move _4]; // scope 1 at $DIR/issue-72181.rs:24:13: 24:43 + StorageDead(_4); // scope 1 at $DIR/issue-72181.rs:24:42: 24:43 + StorageDead(_3); // scope 1 at $DIR/issue-72181.rs:24:42: 24:43 + FakeRead(ForLet, _2); // scope 1 at $DIR/issue-72181.rs:24:9: 24:10 + StorageLive(_5); // scope 2 at $DIR/issue-72181.rs:25:13: 25:30 + StorageLive(_6); // scope 4 at $DIR/issue-72181.rs:25:24: 25:25 + _6 = const 0usize; // scope 4 at $DIR/issue-72181.rs:25:24: 25:25 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/issue-72181.rs:25:24: 25:25 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + _7 = Len(_2); // scope 4 at $DIR/issue-72181.rs:25:22: 25:26 + _8 = Lt(_6, _7); // scope 4 at $DIR/issue-72181.rs:25:22: 25:26 + assert(move _8, "index out of bounds: the len is {} but the index is {}", move _7, _6) -> [success: bb3, unwind: bb1]; // scope 4 at $DIR/issue-72181.rs:25:22: 25:26 + } + + bb3: { + _5 = (_2[_6].0: u64); // scope 4 at $DIR/issue-72181.rs:25:22: 25:28 + StorageDead(_6); // scope 2 at $DIR/issue-72181.rs:25:30: 25:31 + StorageDead(_5); // scope 2 at $DIR/issue-72181.rs:25:30: 25:31 + _0 = const (); // scope 0 at $DIR/issue-72181.rs:21:11: 26:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/issue-72181.rs:21:11: 26:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/issue-72181.rs:26:1: 26:2 + goto -> bb4; // scope 0 at $DIR/issue-72181.rs:26:2: 26:2 + } + + bb4: { + return; // scope 0 at $DIR/issue-72181.rs:26:2: 26:2 + } +} diff --git a/src/test/mir-opt/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..7046ebb793466 --- /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: bb3, otherwise: bb2]; // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/loop_test.rs:6:1: 17:2 + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb3]; // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + } + + bb3: { + _1 = const (); // scope 0 at $DIR/loop_test.rs:10:5: 12:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/loop_test.rs:10:5: 12:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + StorageDead(_1); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + StorageLive(_4); // scope 0 at $DIR/loop_test.rs:13:5: 16:6 + goto -> bb5; // scope 0 at $DIR/loop_test.rs:13:5: 16:6 + } + + bb4: { + _0 = const (); // scope 0 at $DIR/loop_test.rs:11:9: 11:15 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/loop_test.rs:11:9: 11:15 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + StorageDead(_1); // scope 0 at $DIR/loop_test.rs:12:5: 12:6 + return; // scope 0 at $DIR/loop_test.rs:17:2: 17:2 + } + + bb5: { + falseUnwind -> [real: bb6, cleanup: bb1]; // scope 0 at $DIR/loop_test.rs:13:5: 16:6 + } + + bb6: { + StorageLive(_6); // scope 0 at $DIR/loop_test.rs:14:13: 14:14 + _6 = const 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 -> bb5; // scope 0 at $DIR/loop_test.rs:15:9: 15:17 + } +} diff --git a/src/test/mir-opt/match-arm-scopes.rs b/src/test/mir-opt/match-arm-scopes.rs index 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..856248e90d495 --- /dev/null +++ b/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.ElaborateDrops.after.mir @@ -0,0 +1,235 @@ +// MIR for `complicated_match` after ElaborateDrops + +fn complicated_match(_1: bool, _2: (bool, bool, std::string::String)) -> i32 { + debug cond => _1; // in scope 0 at $DIR/match-arm-scopes.rs:14:22: 14:26 + debug items => _2; // in scope 0 at $DIR/match-arm-scopes.rs:14:34: 14:39 + let mut _0: i32; // return place in scope 0 at $DIR/match-arm-scopes.rs:14:66: 14:69 + let mut _3: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + let mut _4: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + let _5: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + let _6: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + let _7: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + let _8: &std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + let mut _9: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + let mut _10: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + let mut _11: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + let mut _12: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + let mut _13: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + let mut _14: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + let _15: bool; // in scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + let _16: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + scope 1 { + debug a => _5; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 + debug a => _6; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 + debug s => _7; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 + debug s => _8; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 + } + scope 2 { + debug b => _15; // in scope 2 at $DIR/match-arm-scopes.rs:17:16: 17:17 + debug t => _16; // in scope 2 at $DIR/match-arm-scopes.rs:17:19: 17:20 + } + + bb0: { + switchInt((_2.0: bool)) -> [false: bb6, otherwise: bb2]; // scope 0 at $DIR/match-arm-scopes.rs:16:10: 16:15 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/match-arm-scopes.rs:14:1: 19:2 + } + + bb2: { + switchInt((_2.1: bool)) -> [false: bb14, otherwise: bb3]; // scope 0 at $DIR/match-arm-scopes.rs:16:29: 16:34 + } + + bb3: { + switchInt((_2.0: bool)) -> [false: bb4, otherwise: bb21]; // scope 0 at $DIR/match-arm-scopes.rs:17:10: 17:14 + } + + bb4: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 + goto -> bb20; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb5: { + _0 = const 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: bb19, unwind: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + } + + bb6: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + _6 = &(_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + StorageLive(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + StorageLive(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + _10 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + switchInt(_10) -> [false: bb7, otherwise: bb8]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb7: { + _9 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 + StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + switchInt(move _9) -> [false: bb13, otherwise: bb12]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb8: { + _0 = const 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 + 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 -> bb11; // scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + } + + bb9: { + return; // scope 0 at $DIR/match-arm-scopes.rs:19:2: 19:2 + } + + bb10 (cleanup): { + goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb11: { + drop(_2) -> [return: bb9, unwind: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb12: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16: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 -> bb5; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb13: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16: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 + } + + bb14: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + _6 = &(_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + StorageLive(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + StorageLive(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + _13 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + switchInt(_13) -> [false: bb15, otherwise: bb16]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb15: { + _12 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 + StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + switchInt(move _12) -> [false: bb18, otherwise: bb17]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb16: { + _0 = const 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 + 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 -> bb11; // scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + } + + bb17: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16: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 -> bb5; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb18: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16: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 -> bb3; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb19: { + StorageDead(_7); // scope 0 at $DIR/match-arm-scopes.rs:16: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 -> bb23; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb20: { + _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: bb22, unwind: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + } + + bb21: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + goto -> bb20; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb22: { + StorageDead(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + StorageDead(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + goto -> bb23; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb23: { + goto -> bb29; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb24 (cleanup): { + goto -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb25 (cleanup): { + goto -> bb24; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb26: { + goto -> bb9; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb27 (cleanup): { + goto -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb28 (cleanup): { + goto -> bb27; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb29: { + goto -> bb26; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } +} diff --git a/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.SimplifyCfg-initial.after.mir b/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.SimplifyCfg-initial.after.mir new file mode 100644 index 0000000000000..1f6b2c982fee0 --- /dev/null +++ b/src/test/mir-opt/match-arm-scopes/rustc.complicated_match.SimplifyCfg-initial.after.mir @@ -0,0 +1,246 @@ +// MIR for `complicated_match` after SimplifyCfg-initial + +fn complicated_match(_1: bool, _2: (bool, bool, std::string::String)) -> i32 { + debug cond => _1; // in scope 0 at $DIR/match-arm-scopes.rs:14:22: 14:26 + debug items => _2; // in scope 0 at $DIR/match-arm-scopes.rs:14:34: 14:39 + let mut _0: i32; // return place in scope 0 at $DIR/match-arm-scopes.rs:14:66: 14:69 + let mut _3: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + let mut _4: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + let _5: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + let _6: &bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + let _7: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + let _8: &std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + let mut _9: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + let mut _10: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + let mut _11: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + let mut _12: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + let mut _13: bool; // in scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + let mut _14: !; // in scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + let _15: bool; // in scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + let _16: std::string::String; // in scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + scope 1 { + debug a => _5; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 + debug a => _6; // in scope 1 at $DIR/match-arm-scopes.rs:16:17: 16:18 + debug s => _7; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 + debug s => _8; // in scope 1 at $DIR/match-arm-scopes.rs:16:20: 16:21 + } + scope 2 { + debug b => _15; // in scope 2 at $DIR/match-arm-scopes.rs:17:16: 17:17 + debug t => _16; // in scope 2 at $DIR/match-arm-scopes.rs:17:19: 17:20 + } + + bb0: { + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/match-arm-scopes.rs:16:10: 16:15 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/match-arm-scopes.rs:14:1: 19:2 + } + + bb2: { + falseEdge -> [real: bb9, imaginary: bb4]; // scope 0 at $DIR/match-arm-scopes.rs:16:9: 16:22 + } + + bb3: { + switchInt((_2.1: bool)) -> [false: bb4, otherwise: bb5]; // scope 0 at $DIR/match-arm-scopes.rs:16:29: 16:34 + } + + bb4: { + falseEdge -> [real: bb18, imaginary: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:16:25: 16:38 + } + + bb5: { + switchInt((_2.0: bool)) -> [false: bb7, otherwise: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:17:10: 17:14 + } + + bb6: { + falseEdge -> [real: bb26, imaginary: bb7]; // scope 0 at $DIR/match-arm-scopes.rs:17:9: 17:21 + } + + bb7: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:32: 17:33 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:35: 17:36 + goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb8: { + _0 = const 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: bb24, unwind: bb14]; // scope 0 at $DIR/match-arm-scopes.rs:16:78: 16:79 + } + + bb9: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + _6 = &(_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:17: 16:18 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:20: 16:21 + _3 = &shallow (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + _4 = &shallow (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + StorageLive(_9); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + StorageLive(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + _10 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + FakeRead(ForMatchedPlace, _10); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + switchInt(_10) -> [false: bb11, otherwise: bb10]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb10: { + falseEdge -> [real: bb12, imaginary: bb11]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb11: { + _9 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 + StorageDead(_10); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + switchInt(move _9) -> [false: bb17, otherwise: bb16]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb12: { + _0 = const 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 + 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 -> bb15; // scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + } + + bb13: { + return; // scope 0 at $DIR/match-arm-scopes.rs:19:2: 19:2 + } + + bb14 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb15: { + drop(_2) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } + + bb16: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16: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 -> bb8; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb17: { + StorageDead(_9); // scope 0 at $DIR/match-arm-scopes.rs:16: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 + falseEdge -> [real: bb3, imaginary: bb4]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb18: { + StorageLive(_6); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + _6 = &(_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:16:26: 16:27 + StorageLive(_8); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + _8 = &(_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:16:36: 16:37 + _3 = &shallow (_2.0: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + _4 = &shallow (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:15:11: 15:16 + StorageLive(_12); // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + StorageLive(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + _13 = _1; // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + FakeRead(ForMatchedPlace, _13); // scope 0 at $DIR/match-arm-scopes.rs:16:45: 16:49 + switchInt(_13) -> [false: bb20, otherwise: bb19]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb19: { + falseEdge -> [real: bb21, imaginary: bb20]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb20: { + _12 = (*_6); // scope 0 at $DIR/match-arm-scopes.rs:16:70: 16:71 + StorageDead(_13); // scope 0 at $DIR/match-arm-scopes.rs:16:72: 16:73 + switchInt(move _12) -> [false: bb23, otherwise: bb22]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb21: { + _0 = const 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 + 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 -> bb15; // scope 0 at $DIR/match-arm-scopes.rs:16:52: 16:60 + } + + bb22: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16: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 -> bb8; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb23: { + StorageDead(_12); // scope 0 at $DIR/match-arm-scopes.rs:16: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 + falseEdge -> [real: bb5, imaginary: bb6]; // scope 0 at $DIR/match-arm-scopes.rs:16:42: 16:73 + } + + bb24: { + StorageDead(_7); // scope 0 at $DIR/match-arm-scopes.rs:16: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 -> bb28; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb25: { + _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: bb27, unwind: bb14]; // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + } + + bb26: { + StorageLive(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + _15 = (_2.1: bool); // scope 0 at $DIR/match-arm-scopes.rs:17:16: 17:17 + StorageLive(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + _16 = move (_2.2: std::string::String); // scope 0 at $DIR/match-arm-scopes.rs:17:19: 17:20 + goto -> bb25; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb27: { + StorageDead(_16); // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + StorageDead(_15); // scope 0 at $DIR/match-arm-scopes.rs:17:42: 17:43 + goto -> bb28; // scope 0 at $DIR/match-arm-scopes.rs:15:5: 18:6 + } + + bb28: { + drop(_2) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/match-arm-scopes.rs:19:1: 19:2 + } +} diff --git a/src/test/mir-opt/match_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..3e1dec697b76f --- /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: bb2, 1isize: bb3, otherwise: bb5]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/match_false_edges.rs:14:1: 20:2 + } + + bb2: { + _1 = (const 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 -> bb11; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + } + + bb3: { + falseEdge -> [real: bb6, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:16:9: 16:16 + } + + bb4: { + falseEdge -> [real: bb10, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:17:9: 17:16 + } + + bb5: { + unreachable; // scope 0 at $DIR/match_false_edges.rs:15:19: 15:27 + } + + bb6: { + StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + _11 = const full_tested_match::promoted[0]; // scope 0 at $DIR/match_false_edges.rs:16:14: 16:15 + // ty::Const + // + ty: &std::option::Option + // + val: Unevaluated(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: bb7, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + // ty::Const + // + ty: fn() -> bool {guard} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:16:20: 16:25 + // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } + } + + bb7: { + switchInt(move _7) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + } + + bb8: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:16: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 -> bb11; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + } + + bb9: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:16:37: 16:38 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:16:37: 16:38 + goto -> bb4; // scope 0 at $DIR/match_false_edges.rs:16:20: 16:27 + } + + bb10: { + StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 + _9 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:17:14: 17:15 + StorageLive(_10); // scope 3 at $DIR/match_false_edges.rs:17:24: 17:25 + _10 = _9; // scope 3 at $DIR/match_false_edges.rs:17:24: 17:25 + _1 = (const 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 -> bb11; // scope 0 at $DIR/match_false_edges.rs:15:13: 19:6 + } + + bb11: { + StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:19:6: 19:7 + StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:19:6: 19:7 + _0 = const (); // scope 0 at $DIR/match_false_edges.rs:14:28: 20:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:14:28: 20:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/match_false_edges.rs:20:2: 20:2 + } +} diff --git a/src/test/mir-opt/match_false_edges/rustc.full_tested_match2.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges/rustc.full_tested_match2.PromoteTemps.before.mir new file mode 100644 index 0000000000000..4e6dc6e13ff62 --- /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: bb2, 1isize: bb3, otherwise: bb5]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/match_false_edges.rs:25:1: 31:2 + } + + bb2: { + falseEdge -> [real: bb10, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:28:9: 28:13 + } + + bb3: { + falseEdge -> [real: bb6, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:27:9: 27:16 + } + + bb4: { + StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 + _9 = ((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:29:14: 29:15 + StorageLive(_10); // scope 3 at $DIR/match_false_edges.rs:29:24: 29:25 + _10 = _9; // scope 3 at $DIR/match_false_edges.rs:29:24: 29:25 + _1 = (const 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 -> bb11; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + } + + bb5: { + unreachable; // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + } + + bb6: { + StorageLive(_6); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + _6 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:27:14: 27:15 + _4 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:26:19: 26:27 + StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + _7 = const guard() -> [return: bb7, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + // ty::Const + // + ty: fn() -> bool {guard} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:27:20: 27:25 + // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } + } + + bb7: { + switchInt(move _7) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + } + + bb8: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:27: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 -> bb11; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + } + + bb9: { + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:27:37: 27:38 + StorageDead(_6); // scope 0 at $DIR/match_false_edges.rs:27:37: 27:38 + falseEdge -> [real: bb4, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:27:20: 27:27 + } + + bb10: { + _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 -> bb11; // scope 0 at $DIR/match_false_edges.rs:26:13: 30:6 + } + + bb11: { + StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:30:6: 30:7 + StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:30:6: 30:7 + _0 = const (); // scope 0 at $DIR/match_false_edges.rs:25:29: 31:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:25:29: 31:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/match_false_edges.rs:31:2: 31:2 + } +} diff --git a/src/test/mir-opt/match_false_edges/rustc.main.PromoteTemps.before.mir b/src/test/mir-opt/match_false_edges/rustc.main.PromoteTemps.before.mir new file mode 100644 index 0000000000000..b54058ca73f6f --- /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: bb3, otherwise: bb2]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/match_false_edges.rs:34:1: 41:2 + } + + bb2: { + falseEdge -> [real: bb10, imaginary: bb5]; // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + } + + bb3: { + falseEdge -> [real: bb6, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:36:9: 36:17 + } + + bb4: { + StorageLive(_14); // scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 + _14 = _2; // scope 0 at $DIR/match_false_edges.rs:39:9: 39:11 + _1 = const 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 -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb5: { + falseEdge -> [real: bb11, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:38:9: 38:16 + } + + bb6: { + StorageLive(_7); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _7 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:36:14: 36:16 + _5 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + StorageLive(_8); // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + _8 = const guard() -> [return: bb7, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + // ty::Const + // + ty: fn() -> bool {guard} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:36:21: 36:26 + // + literal: Const { ty: fn() -> bool {guard}, val: Value(Scalar()) } + } + + bb7: { + switchInt(move _8) -> [false: bb9, otherwise: bb8]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + } + + bb8: { + StorageDead(_8); // scope 0 at $DIR/match_false_edges.rs:36: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 -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb9: { + StorageDead(_8); // scope 0 at $DIR/match_false_edges.rs:36:33: 36:34 + StorageDead(_7); // scope 0 at $DIR/match_false_edges.rs:36:33: 36:34 + falseEdge -> [real: bb2, imaginary: bb2]; // scope 0 at $DIR/match_false_edges.rs:36:21: 36:28 + } + + bb10: { + StorageLive(_9); // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + _9 = _2; // scope 0 at $DIR/match_false_edges.rs:37:9: 37:11 + _1 = const 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 -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb11: { + StorageLive(_11); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _11 = &((_2 as Some).0: i32); // scope 0 at $DIR/match_false_edges.rs:38:14: 38:15 + _5 = &shallow _2; // scope 0 at $DIR/match_false_edges.rs:35:19: 35:26 + StorageLive(_12); // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + StorageLive(_13); // scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 + _13 = (*_11); // scope 0 at $DIR/match_false_edges.rs:38:27: 38:28 + _12 = const guard2(move _13) -> [return: bb12, unwind: bb1]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + // ty::Const + // + ty: fn(i32) -> bool {guard2} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:38:20: 38:26 + // + literal: Const { ty: fn(i32) -> bool {guard2}, val: Value(Scalar()) } + } + + bb12: { + StorageDead(_13); // scope 0 at $DIR/match_false_edges.rs:38:28: 38:29 + switchInt(move _12) -> [false: bb14, otherwise: bb13]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + } + + bb13: { + StorageDead(_12); // scope 0 at $DIR/match_false_edges.rs:38: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 -> bb15; // scope 0 at $DIR/match_false_edges.rs:35:13: 40:6 + } + + bb14: { + StorageDead(_12); // scope 0 at $DIR/match_false_edges.rs:38:34: 38:35 + StorageDead(_11); // scope 0 at $DIR/match_false_edges.rs:38:34: 38:35 + falseEdge -> [real: bb4, imaginary: bb4]; // scope 0 at $DIR/match_false_edges.rs:38:20: 38:29 + } + + bb15: { + StorageDead(_2); // scope 0 at $DIR/match_false_edges.rs:40:6: 40:7 + StorageDead(_1); // scope 0 at $DIR/match_false_edges.rs:40:6: 40:7 + _0 = const (); // scope 0 at $DIR/match_false_edges.rs:34:11: 41:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/match_false_edges.rs:34:11: 41:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/match_false_edges.rs:41:2: 41:2 + } +} diff --git a/src/test/mir-opt/match_test.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..5996496406a9f --- /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: { + falseEdge -> [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: { + falseEdge -> [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: { + falseEdge -> [real: bb13, imaginary: bb3]; // scope 2 at $DIR/match_test.rs:15:9: 15:11 + } + + bb9: { + _8 = &shallow _1; // scope 2 at $DIR/match_test.rs:12:11: 12:12 + StorageLive(_9); // scope 2 at $DIR/match_test.rs:13:18: 13:19 + _9 = _2; // scope 2 at $DIR/match_test.rs:13:18: 13:19 + switchInt(move _9) -> [false: bb11, otherwise: bb10]; // scope 2 at $DIR/match_test.rs:13:18: 13:19 + } + + bb10: { + StorageDead(_9); // scope 2 at $DIR/match_test.rs:13: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 + falseEdge -> [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..e3f113fea2851 --- /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], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#0r, '_#1r} +| '_#1r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#1r} +| '_#2r | U0 | {} +| '_#3r | U0 | {bb2[0..=8], bb3[0], bb5[0..=2]} +| '_#4r | U0 | {bb2[1..=8], bb3[0], bb5[0..=2]} +| '_#5r | U0 | {bb2[4..=8], bb3[0], bb5[0..=2]} +| +| Inference Constraints +| '_#0r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} +| '_#1r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} +| '_#3r live at {bb2[0]} +| '_#4r live at {bb2[1..=3]} +| '_#5r live at {bb2[4..=8], bb3[0], bb5[0..=2]} +| '_#3r: '_#4r due to Assignment at Single(bb2[0]) +| '_#4r: '_#5r due to Assignment at Single(bb2[3]) +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11 + let mut _1: [usize; Const { ty: usize, val: Value(Scalar(0x00000003)) }]; // in scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _3: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + let mut _4: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _5: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _7: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + let _8: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + let mut _9: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + let _10: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + scope 1 { + debug v => _1; // in scope 1 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _2: &'_#4r usize; // in scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + scope 2 { + debug p => _2; // in scope 2 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + let _6: &'_#5r usize; // in scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + scope 3 { + debug q => _6; // in scope 3 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + } + } + } + + bb0: { + StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + _1 = [const Const(Value(Scalar(0x00000001)): usize), const Const(Value(Scalar(0x00000002)): usize), const Const(Value(Scalar(0x00000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000001)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:18: 17:19 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000001)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000002)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:21: 17:22 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000003)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:24: 17:25 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000003)) } + FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _3 = const Const(Value(Scalar(0x00000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:18:16: 18:17 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000000)) } + _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> [success: bb2, unwind: bb1]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + } + + bb1 (cleanup): { + resume; // bb1[0]: scope 0 at $DIR/region-subtyping-basic.rs:16:1: 25:2 + } + + bb2: { + _2 = &'_#3r _1[_3]; // bb2[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18 + FakeRead(ForLet, _2); // bb2[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_6); // bb2[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + _6 = _2; // bb2[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 + FakeRead(ForLet, _6); // bb2[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + StorageLive(_7); // bb2[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + _7 = const Const(Value(Scalar(0x01)): bool); // bb2[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:20:8: 20:12 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + FakeRead(ForMatchedPlace, _7); // bb2[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + switchInt(_7) -> [Const(Value(Scalar(0x00)): bool): bb4, otherwise: bb3]; // bb2[8]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb3: { + falseEdge -> [real: bb5, imaginary: bb4]; // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb4: { + StorageLive(_10); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + _10 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x00000016)): usize)) -> [return: bb7, unwind: bb1]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + // ty::Const + // + ty: fn(usize) -> bool {use_x} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x00000016)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:15: 23:17 + // + literal: Const { ty: usize, val: Value(Scalar(0x00000016)) } + } + + bb5: { + StorageLive(_8); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + StorageLive(_9); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _9 = (*_6); // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _8 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb6, unwind: bb1]; // bb5[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + // ty::Const + // + ty: fn(usize) -> bool {use_x} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + } + + bb6: { + StorageDead(_9); // bb6[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 + StorageDead(_8); // bb6[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 + _0 = const Const(Value(Scalar()): ()); // bb6[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:20:13: 22:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb8; // bb6[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb7: { + StorageDead(_10); // bb7[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 + _0 = const Const(Value(Scalar()): ()); // bb7[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:22:12: 24:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb8; // bb7[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb8: { + StorageDead(_6); // bb8[0]: scope 2 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_3); // bb8[1]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_2); // bb8[2]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_1); // bb8[3]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_7); // bb8[4]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + return; // bb8[5]: scope 0 at $DIR/region-subtyping-basic.rs:25:2: 25:2 + } +} diff --git a/src/test/mir-opt/nll/region-subtyping-basic/64bit/rustc.main.nll.0.mir b/src/test/mir-opt/nll/region-subtyping-basic/64bit/rustc.main.nll.0.mir new file mode 100644 index 0000000000000..a69952ff07f34 --- /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], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#0r, '_#1r} +| '_#1r | U0 | {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5], '_#1r} +| '_#2r | U0 | {} +| '_#3r | U0 | {bb2[0..=8], bb3[0], bb5[0..=2]} +| '_#4r | U0 | {bb2[1..=8], bb3[0], bb5[0..=2]} +| '_#5r | U0 | {bb2[4..=8], bb3[0], bb5[0..=2]} +| +| Inference Constraints +| '_#0r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} +| '_#1r live at {bb0[0..=8], bb1[0], bb2[0..=8], bb3[0], bb4[0..=1], bb5[0..=3], bb6[0..=3], bb7[0..=2], bb8[0..=5]} +| '_#3r live at {bb2[0]} +| '_#4r live at {bb2[1..=3]} +| '_#5r live at {bb2[4..=8], bb3[0], bb5[0..=2]} +| '_#3r: '_#4r due to Assignment at Single(bb2[0]) +| '_#4r: '_#5r due to Assignment at Single(bb2[3]) +| +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/region-subtyping-basic.rs:16:11: 16:11 + let mut _1: [usize; Const { ty: usize, val: Value(Scalar(0x0000000000000003)) }]; // in scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _3: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + let mut _4: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _5: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + let mut _7: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + let _8: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + let mut _9: usize; // in scope 0 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + let _10: bool; // in scope 0 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + scope 1 { + debug v => _1; // in scope 1 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + let _2: &'_#4r usize; // in scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + scope 2 { + debug p => _2; // in scope 2 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + let _6: &'_#5r usize; // in scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + scope 3 { + debug q => _6; // in scope 3 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + } + } + } + + bb0: { + StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + _1 = [const Const(Value(Scalar(0x0000000000000001)): usize), const Const(Value(Scalar(0x0000000000000002)): usize), const Const(Value(Scalar(0x0000000000000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000001)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:18: 17:19 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000001)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000002)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:21: 17:22 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000003)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:17:24: 17:25 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000003)) } + FakeRead(ForLet, _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 + StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _3 = const Const(Value(Scalar(0x0000000000000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:18:16: 18:17 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000000)) } + _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> [success: bb2, unwind: bb1]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 + } + + bb1 (cleanup): { + resume; // bb1[0]: scope 0 at $DIR/region-subtyping-basic.rs:16:1: 25:2 + } + + bb2: { + _2 = &'_#3r _1[_3]; // bb2[0]: scope 1 at $DIR/region-subtyping-basic.rs:18:13: 18:18 + FakeRead(ForLet, _2); // bb2[1]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 + StorageLive(_6); // bb2[2]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + _6 = _2; // bb2[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 + FakeRead(ForLet, _6); // bb2[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 + StorageLive(_7); // bb2[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + _7 = const Const(Value(Scalar(0x01)): bool); // bb2[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:20:8: 20:12 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + FakeRead(ForMatchedPlace, _7); // bb2[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + switchInt(_7) -> [Const(Value(Scalar(0x00)): bool): bb4, otherwise: bb3]; // bb2[8]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb3: { + falseEdge -> [real: bb5, imaginary: bb4]; // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb4: { + StorageLive(_10); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + _10 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x0000000000000016)): usize)) -> [return: bb7, unwind: bb1]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + // ty::Const + // + ty: fn(usize) -> bool {use_x} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + // ty::Const + // + ty: usize + // + val: Value(Scalar(0x0000000000000016)) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:23:15: 23:17 + // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000016)) } + } + + bb5: { + StorageLive(_8); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + StorageLive(_9); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _9 = (*_6); // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 + _8 = const Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb6, unwind: bb1]; // bb5[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + // ty::Const + // + ty: fn(usize) -> bool {use_x} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 + // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } + } + + bb6: { + StorageDead(_9); // bb6[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 + StorageDead(_8); // bb6[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 + _0 = const Const(Value(Scalar()): ()); // bb6[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:20:13: 22:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb8; // bb6[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb7: { + StorageDead(_10); // bb7[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 + _0 = const Const(Value(Scalar()): ()); // bb7[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/region-subtyping-basic.rs:22:12: 24:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + goto -> bb8; // bb7[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 + } + + bb8: { + StorageDead(_6); // bb8[0]: scope 2 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_3); // bb8[1]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_2); // bb8[2]: scope 1 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_1); // bb8[3]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + StorageDead(_7); // bb8[4]: scope 0 at $DIR/region-subtyping-basic.rs:25:1: 25:2 + return; // bb8[5]: scope 0 at $DIR/region-subtyping-basic.rs:25:2: 25:2 + } +} diff --git a/src/test/mir-opt/no-drop-for-inactive-variant.rs b/src/test/mir-opt/no-drop-for-inactive-variant.rs index f906761684526..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::<&'static 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..eb6911735a59e --- /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: bb2, 1isize: bb4, otherwise: bb3]; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:9:9: 9:16 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:7:1: 12:2 + } + + bb2: { + StorageLive(_4); // scope 0 at $SRC_DIR/libstd/macros.rs:LL:COL + const std::rt::begin_panic::<&str>(const "explicit panic") -> bb5; // 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 }) } + } + + bb3: { + unreachable; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:8:11: 8:14 + } + + bb4: { + 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 + } + + bb5 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/no-drop-for-inactive-variant.rs:12: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..0af213e425fe4 --- /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) -> bb2; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 + // ty::Const + // + ty: for<'r> fn(&'r str) -> std::string::String {::to_string} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:9:23: 9:32 + // + literal: Const { ty: for<'r> fn(&'r str) -> std::string::String {::to_string}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/no-spurious-drop-after-call.rs:8:1: 10:2 + } + + bb2: { + StorageDead(_3); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:33: 9:34 + _1 = const std::mem::drop::(move _2) -> [return: bb3, unwind: bb4]; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:5: 9:35 + // ty::Const + // + ty: fn(std::string::String) {std::mem::drop::} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:9:5: 9:19 + // + literal: Const { ty: fn(std::string::String) {std::mem::drop::}, val: Value(Scalar()) } + } + + bb3: { + StorageDead(_2); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:34: 9:35 + StorageDead(_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:35: 9:36 + StorageDead(_1); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:35: 9:36 + _0 = const (); // scope 0 at $DIR/no-spurious-drop-after-call.rs:8:11: 10:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/no-spurious-drop-after-call.rs:8:11: 10:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + return; // scope 0 at $DIR/no-spurious-drop-after-call.rs:10:2: 10:2 + } + + bb4 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:34: 9:35 + } +} diff --git a/src/test/mir-opt/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..4511470f3a50f --- /dev/null +++ b/src/test/mir-opt/nrvo-simple/rustc.nrvo.RenameReturnPlace.diff @@ -0,0 +1,46 @@ +- // 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 + StorageLive(_5); // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 + StorageLive(_6); // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 +- _6 = &mut _2; // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 ++ _6 = &mut _0; // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 + _5 = &mut (*_6); // scope 1 at $DIR/nrvo-simple.rs:4:10: 4:18 + _3 = move _1(move _5) -> bb1; // scope 1 at $DIR/nrvo-simple.rs:4:5: 4:19 + } + + bb1: { + StorageDead(_5); // scope 1 at $DIR/nrvo-simple.rs:4:18: 4:19 + StorageDead(_6); // scope 1 at $DIR/nrvo-simple.rs:4:19: 4:20 + StorageDead(_3); // scope 1 at $DIR/nrvo-simple.rs:4:19: 4:20 +- _0 = _2; // scope 1 at $DIR/nrvo-simple.rs:5:5: 5:8 +- StorageDead(_2); // scope 0 at $DIR/nrvo-simple.rs:6:1: 6:2 + return; // scope 0 at $DIR/nrvo-simple.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/packed-struct-drop-aligned.rs b/src/test/mir-opt/packed-struct-drop-aligned.rs index 113f81c441f7c..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.EraseRegions.before.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.EraseRegions.before.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..21dab9ab92394 --- /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 (cleanup): { + resume; // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:1: 8:2 + } + + bb2: { + StorageDead(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + return; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:2: 8:2 + } + + bb3 (cleanup): { + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_1) -> bb1; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } + + bb4: { + StorageDead(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + StorageDead(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + _0 = const (); // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } +} diff --git a/src/test/mir-opt/packed-struct-drop-aligned/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/packed-struct-drop-aligned/64bit/rustc.main.SimplifyCfg-elaborate-drops.after.mir new file mode 100644 index 0000000000000..cf46f74c16df3 --- /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 (cleanup): { + resume; // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:1: 8:2 + } + + bb2: { + StorageDead(_1); // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + return; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:2: 8:2 + } + + bb3 (cleanup): { + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + drop(_1) -> bb1; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } + + bb4: { + StorageDead(_6); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:5: 7:8 + StorageDead(_4); // scope 1 at $DIR/packed-struct-drop-aligned.rs:7:28: 7:29 + _0 = const (); // scope 0 at $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/packed-struct-drop-aligned.rs:5:11: 8:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_1) -> [return: bb2, unwind: bb1]; // scope 0 at $DIR/packed-struct-drop-aligned.rs:8:1: 8:2 + } +} diff --git a/src/test/mir-opt/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 294fe247c38be..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 1c88a9e4d5a32..eba0f567c4a08 100644 --- a/src/test/mir-opt/retag.rs +++ b/src/test/mir-opt/retag.rs @@ -6,16 +6,26 @@ 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 { x } - fn foo_shr<'x>(&self, x: &'x i32) -> &'x i32 { x } + fn foo<'x>(&self, x: &'x mut i32) -> &'x mut i32 { + x + } + fn foo_shr<'x>(&self, x: &'x i32) -> &'x i32 { + x + } } +// 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; { @@ -27,7 +37,10 @@ fn main() { } // Also test closures - let c: fn(&i32) -> &i32 = |x: &i32| -> &i32 { let _y = x; x }; + let c: fn(&i32) -> &i32 = |x: &i32| -> &i32 { + let _y = x; + x + }; let _w = c(&x); // need to call `foo_shr` or it doesn't even get generated @@ -36,94 +49,3 @@ fn main() { // escape-to-raw (shr) let _w = _w as *const _; } - -// END RUST SOURCE -// START rustc.{{impl}}-foo.EraseRegions.after.mir -// bb0: { -// Retag([fn entry] _1); -// Retag([fn entry] _2); -// ... -// _0 = &mut (*_3); -// Retag(_0); -// ... -// return; -// } -// END rustc.{{impl}}-foo.EraseRegions.after.mir -// START rustc.{{impl}}-foo_shr.EraseRegions.after.mir -// bb0: { -// Retag([fn entry] _1); -// Retag([fn entry] _2); -// ... -// _0 = _2; -// Retag(_0); -// ... -// return; -// } -// END rustc.{{impl}}-foo_shr.EraseRegions.after.mir -// START rustc.main.EraseRegions.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.EraseRegions.after.mir -// START rustc.main-{{closure}}.EraseRegions.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}}.EraseRegions.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..c8c5da37abe32 --- /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; // 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: bb2, unwind: bb3]; // scope 1 at $DIR/retag.rs:32:17: 32:36 + // ty::Const + // + ty: for<'r, 'x> fn(&'r Test, &'x mut i32) -> &'x mut i32 {Test::foo} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/retag.rs:32:25: 32:28 + // + literal: Const { ty: for<'r, 'x> fn(&'r Test, &'x mut i32) -> &'x mut i32 {Test::foo}, val: Value(Scalar()) } + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/retag.rs:29:1: 51:2 + } + + bb2: { + Retag(_3); // scope 1 at $DIR/retag.rs:32:17: 32:36 + StorageDead(_6); // scope 1 at $DIR/retag.rs:32:35: 32:36 + StorageDead(_4); // scope 1 at $DIR/retag.rs:32:35: 32:36 + StorageDead(_7); // scope 1 at $DIR/retag.rs:32:36: 32:37 + drop(_5) -> [return: bb4, unwind: bb1]; // scope 1 at $DIR/retag.rs:32:36: 32:37 + } + + bb3 (cleanup): { + drop(_5) -> bb1; // scope 1 at $DIR/retag.rs:32:36: 32:37 + } + + bb4: { + StorageDead(_5); // scope 1 at $DIR/retag.rs:32:36: 32:37 + StorageLive(_8); // scope 2 at $DIR/retag.rs:33:13: 33:14 + StorageLive(_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 + _9 = move _3; // scope 2 at $DIR/retag.rs:33:19: 33:20 + Retag(_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 + _8 = &mut (*_9); // scope 2 at $DIR/retag.rs:33:19: 33:20 + Retag(_8); // scope 2 at $DIR/retag.rs:33:19: 33:20 + StorageDead(_9); // scope 2 at $DIR/retag.rs:33:22: 33:23 + StorageLive(_10); // scope 3 at $DIR/retag.rs:34:13: 34:14 + _10 = move _8; // scope 3 at $DIR/retag.rs:34:17: 34:18 + Retag(_10); // scope 3 at $DIR/retag.rs:34:17: 34:18 + StorageLive(_11); // scope 4 at $DIR/retag.rs:36:13: 36:15 + StorageLive(_12); // scope 4 at $DIR/retag.rs:36:18: 36:29 + _12 = &raw mut (*_10); // scope 4 at $DIR/retag.rs:36:18: 36:19 + Retag([raw] _12); // scope 4 at $DIR/retag.rs:36:18: 36:19 + _11 = _12; // scope 4 at $DIR/retag.rs:36:18: 36:29 + StorageDead(_12); // scope 4 at $DIR/retag.rs:36:29: 36:30 + _2 = const (); // scope 1 at $DIR/retag.rs:31:5: 37:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/retag.rs:31:5: 37:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_11); // scope 4 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_10); // scope 3 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_8); // scope 2 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_3); // scope 1 at $DIR/retag.rs:37:5: 37:6 + StorageDead(_2); // scope 1 at $DIR/retag.rs:37:5: 37:6 + StorageLive(_13); // scope 1 at $DIR/retag.rs:40:9: 40:10 + StorageLive(_14); // scope 1 at $DIR/retag.rs:40:31: 43:6 + _14 = [closure@main::{{closure}}#0]; // scope 1 at $DIR/retag.rs:40:31: 43:6 + // closure + // + def_id: DefId(0:14 ~ retag[317d]::main[0]::{{closure}}[0]) + // + substs: [ + // i8, + // for<'r> extern "rust-call" fn((&'r i32,)) -> &'r i32, + // (), + // ] + Retag(_14); // scope 1 at $DIR/retag.rs:40:31: 43:6 + _13 = move _14 as for<'r> fn(&'r i32) -> &'r i32 (Pointer(ClosureFnPointer(Normal))); // scope 1 at $DIR/retag.rs:40:31: 43:6 + StorageDead(_14); // scope 1 at $DIR/retag.rs:43:5: 43:6 + StorageLive(_15); // scope 6 at $DIR/retag.rs:44:9: 44:11 + StorageLive(_16); // scope 6 at $DIR/retag.rs:44:14: 44:15 + _16 = _13; // scope 6 at $DIR/retag.rs:44:14: 44:15 + StorageLive(_17); // scope 6 at $DIR/retag.rs:44:16: 44:18 + StorageLive(_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 + _18 = &_1; // scope 6 at $DIR/retag.rs:44:16: 44:18 + Retag(_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 + _17 = &(*_18); // scope 6 at $DIR/retag.rs:44:16: 44:18 + Retag(_17); // scope 6 at $DIR/retag.rs:44:16: 44:18 + _15 = move _16(move _17) -> bb5; // scope 6 at $DIR/retag.rs:44:14: 44:19 + } + + bb5: { + Retag(_15); // scope 6 at $DIR/retag.rs:44:14: 44:19 + StorageDead(_17); // scope 6 at $DIR/retag.rs:44:18: 44:19 + StorageDead(_16); // scope 6 at $DIR/retag.rs:44:18: 44:19 + StorageDead(_18); // scope 6 at $DIR/retag.rs:44:19: 44:20 + StorageLive(_19); // scope 7 at $DIR/retag.rs:47:5: 47:24 + StorageLive(_20); // scope 7 at $DIR/retag.rs:47:5: 47:12 + StorageLive(_21); // scope 7 at $DIR/retag.rs:47:5: 47:12 + _21 = Test(const 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: bb6, unwind: bb7]; // scope 7 at $DIR/retag.rs:47:5: 47:24 + // ty::Const + // + ty: for<'r, 'x> fn(&'r Test, &'x i32) -> &'x i32 {Test::foo_shr} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/retag.rs:47:13: 47:20 + // + literal: Const { ty: for<'r, 'x> fn(&'r Test, &'x i32) -> &'x i32 {Test::foo_shr}, val: Value(Scalar()) } + } + + bb6: { + Retag(_19); // scope 7 at $DIR/retag.rs:47:5: 47:24 + StorageDead(_22); // scope 7 at $DIR/retag.rs:47:23: 47:24 + StorageDead(_20); // scope 7 at $DIR/retag.rs:47:23: 47:24 + StorageDead(_23); // scope 7 at $DIR/retag.rs:47:24: 47:25 + drop(_21) -> [return: bb8, unwind: bb1]; // scope 7 at $DIR/retag.rs:47:24: 47:25 + } + + bb7 (cleanup): { + drop(_21) -> bb1; // scope 7 at $DIR/retag.rs:47:24: 47:25 + } + + bb8: { + StorageDead(_21); // scope 7 at $DIR/retag.rs:47:24: 47:25 + StorageDead(_19); // scope 7 at $DIR/retag.rs:47:24: 47:25 + StorageLive(_25); // scope 7 at $DIR/retag.rs:50:9: 50:11 + StorageLive(_26); // scope 7 at $DIR/retag.rs:50:14: 50:28 + _26 = &raw const (*_15); // scope 7 at $DIR/retag.rs:50:14: 50:16 + Retag([raw] _26); // scope 7 at $DIR/retag.rs:50:14: 50:16 + _25 = _26; // scope 7 at $DIR/retag.rs:50:14: 50:28 + StorageDead(_26); // scope 7 at $DIR/retag.rs:50:28: 50:29 + _0 = const (); // scope 0 at $DIR/retag.rs:29:11: 51:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/retag.rs:29:11: 51:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_25); // scope 7 at $DIR/retag.rs:51:1: 51:2 + StorageDead(_15); // scope 6 at $DIR/retag.rs:51:1: 51:2 + StorageDead(_13); // scope 1 at $DIR/retag.rs:51:1: 51:2 + StorageDead(_1); // scope 0 at $DIR/retag.rs:51:1: 51:2 + return; // scope 0 at $DIR/retag.rs:51:2: 51:2 + } +} diff --git a/src/test/mir-opt/retag/rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir b/src/test/mir-opt/retag/rustc.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.mir 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..cc2738b5e50a3 --- /dev/null +++ b/src/test/mir-opt/simple-match/32bit/rustc.match_bool.mir_map.0.mir @@ -0,0 +1,49 @@ +// MIR for `match_bool` 0 mir_map + +fn match_bool(_1: bool) -> usize { + debug x => _1; // in scope 0 at $DIR/simple-match.rs:5:15: 5:16 + let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32 + + bb0: { + FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12 + switchInt(_1) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/simple-match.rs:5:1: 10:2 + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb3]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb3: { + _0 = const 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 -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb4: { + _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 -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb5: { + goto -> bb6; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } + + bb6: { + return; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/simple-match/64bit/rustc.match_bool.mir_map.0.mir b/src/test/mir-opt/simple-match/64bit/rustc.match_bool.mir_map.0.mir new file mode 100644 index 0000000000000..309041abef9be --- /dev/null +++ b/src/test/mir-opt/simple-match/64bit/rustc.match_bool.mir_map.0.mir @@ -0,0 +1,49 @@ +// MIR for `match_bool` 0 mir_map + +fn match_bool(_1: bool) -> usize { + debug x => _1; // in scope 0 at $DIR/simple-match.rs:5:15: 5:16 + let mut _0: usize; // return place in scope 0 at $DIR/simple-match.rs:5:27: 5:32 + + bb0: { + FakeRead(ForMatchedPlace, _1); // scope 0 at $DIR/simple-match.rs:6:11: 6:12 + switchInt(_1) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/simple-match.rs:5:1: 10:2 + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb3]; // scope 0 at $DIR/simple-match.rs:7:9: 7:13 + } + + bb3: { + _0 = const 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 -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb4: { + _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 -> bb5; // scope 0 at $DIR/simple-match.rs:6:5: 9:6 + } + + bb5: { + goto -> bb6; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } + + bb6: { + return; // scope 0 at $DIR/simple-match.rs:10:2: 10:2 + } +} diff --git a/src/test/mir-opt/simplify-arm-identity.rs b/src/test/mir-opt/simplify-arm-identity.rs index 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..94759dca038b1 --- /dev/null +++ b/src/test/mir-opt/simplify-arm-identity/32bit/rustc.main.SimplifyArmIdentity.diff @@ -0,0 +1,81 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:17:11: 17:11 + let _1: Src; // in scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + scope 1 { + debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + scope 2 { + } + scope 3 { + debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + ((_1 as Foo).0: u8) = const 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 = const Dst::Foo(0u8); // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + // ty::Const + // + ty: Dst + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:21:21: 21:32 + // + literal: Const { ty: Dst, val: Value(Scalar(0x00)) } + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb2: { + unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:19:24: 19:25 + } + + bb3: { + StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:34: 20:35 + StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20: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..ba21f16b685d4 --- /dev/null +++ b/src/test/mir-opt/simplify-arm-identity/64bit/rustc.main.SimplifyArmIdentity.diff @@ -0,0 +1,81 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:17:11: 17:11 + let _1: Src; // in scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:20:9: 20:20 + let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + scope 1 { + debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + scope 2 { + } + scope 3 { + debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:18:9: 18:10 + ((_1 as Foo).0: u8) = const 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 = const Dst::Foo(0u8); // scope 1 at $DIR/simplify-arm-identity.rs:21:21: 21:32 + // ty::Const + // + ty: Dst + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:21:21: 21:32 + // + literal: Const { ty: Dst, val: Value(Scalar(0x00)) } + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 22:6 + } + + bb2: { + unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:19:24: 19:25 + } + + bb3: { + StorageLive(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:20:18: 20:19 + StorageLive(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + _5 = _4; // scope 3 at $DIR/simplify-arm-identity.rs:20:33: 20:34 + ((_2 as Foo).0: u8) = move _5; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:20:24: 20:35 + StorageDead(_5); // scope 3 at $DIR/simplify-arm-identity.rs:20:34: 20:35 + StorageDead(_4); // scope 1 at $DIR/simplify-arm-identity.rs:20: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..e7373391b79c7 --- /dev/null +++ b/src/test/mir-opt/simplify-arm-identity/rustc.main.SimplifyArmIdentity.diff @@ -0,0 +1,70 @@ +- // MIR for `main` before SimplifyArmIdentity ++ // MIR for `main` after SimplifyArmIdentity + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify-arm-identity.rs:16:11: 16:11 + let _1: Src as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at $DIR/simplify-arm-identity.rs:17:9: 17:10 + let mut _2: Dst; // in scope 0 at $DIR/simplify-arm-identity.rs:18:18: 21:6 + let mut _3: isize; // in scope 0 at $DIR/simplify-arm-identity.rs:19:9: 19:20 + let mut _5: u8; // in scope 0 at $DIR/simplify-arm-identity.rs:19:33: 19:34 + scope 1 { + debug e => _1; // in scope 1 at $DIR/simplify-arm-identity.rs:17:9: 17:10 + let _4: u8; // in scope 1 at $DIR/simplify-arm-identity.rs:19:18: 19:19 + scope 2 { + } + scope 3 { + debug x => _4; // in scope 3 at $DIR/simplify-arm-identity.rs:19:18: 19:19 + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/simplify-arm-identity.rs:17:9: 17:10 + ((_1 as Foo).0: u8) = const 0u8; // scope 0 at $DIR/simplify-arm-identity.rs:17:18: 17:29 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:17:27: 17:28 + // + literal: Const { ty: u8, val: Value(Scalar(0x00)) } + discriminant(_1) = 0; // scope 0 at $DIR/simplify-arm-identity.rs:17:18: 17:29 + StorageLive(_2); // scope 1 at $DIR/simplify-arm-identity.rs:18:18: 21:6 + _3 = discriminant(_1); // scope 1 at $DIR/simplify-arm-identity.rs:19:9: 19:20 + switchInt(move _3) -> [0isize: bb3, 1isize: bb1, otherwise: bb2]; // scope 1 at $DIR/simplify-arm-identity.rs:19:9: 19:20 + } + + bb1: { + _2 = const Dst::Foo(0u8); // bb1[0]: scope 1 at $DIR/simplify-arm-identity.rs:20:21: 20:32 + // ty::Const + // + ty: Dst + // + val: Value(Scalar(0x00)) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:20:21: 20:32 + // + literal: Const { ty: Dst, val: Value(Scalar(0x00)) } + goto -> bb4; // bb1[1]: scope 1 at $DIR/simplify-arm-identity.rs:18:18: 21:6 + } + + bb2: { + unreachable; // scope 1 at $DIR/simplify-arm-identity.rs:18:24: 18:25 + } + + bb3: { + _4 = ((_1 as Foo).0: u8); // scope 1 at $DIR/simplify-arm-identity.rs:19:18: 19:19 + ((_2 as Foo).0: u8) = move _4; // scope 3 at $DIR/simplify-arm-identity.rs:19:24: 19:35 + discriminant(_2) = 0; // scope 3 at $DIR/simplify-arm-identity.rs:19:24: 19:35 + goto -> bb4; // scope 1 at $DIR/simplify-arm-identity.rs:18:18: 21:6 + } + + bb4: { + StorageDead(_2); // scope 1 at $DIR/simplify-arm-identity.rs:21:6: 21:7 + _0 = const (); // scope 0 at $DIR/simplify-arm-identity.rs:16:11: 22:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm-identity.rs:16:11: 22:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_1); // scope 0 at $DIR/simplify-arm-identity.rs:22:1: 22:2 + return; // scope 0 at $DIR/simplify-arm-identity.rs:22:2: 22:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm.rs b/src/test/mir-opt/simplify-arm.rs 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..8bb28206964f1 --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id.SimplifyArmIdentity.diff @@ -0,0 +1,44 @@ +- // 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 + 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..1226b4feaf41f --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id.SimplifyBranchSame.diff @@ -0,0 +1,44 @@ +- // 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: { + 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 + 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..5a5d67b36d9a5 --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyArmIdentity.diff @@ -0,0 +1,56 @@ +- // 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 + 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 + 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..e82865162615e --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id_result.SimplifyBranchSame.diff @@ -0,0 +1,56 @@ +- // 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 + } + + 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 + 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 + goto -> bb4; // scope 0 at $DIR/simplify-arm.rs:17:5: 20:6 + } + + bb4: { + return; // scope 0 at $DIR/simplify-arm.rs:21:2: 21:2 + } + } + diff --git a/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyArmIdentity.diff b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyArmIdentity.diff new file mode 100644 index 0000000000000..44f475346e016 --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyArmIdentity.diff @@ -0,0 +1,108 @@ +- // 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 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 + StorageLive(_11); // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 + _11 = _2; // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 + ((_0 as Ok).0: u8) = move _11; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 + discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 + StorageDead(_11); // scope 1 at $DIR/simplify-arm.rs:25:9: 25:10 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb5; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb3: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + } + + bb4: { + StorageLive(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageLive(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageLive(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _9 = _6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _8 = const >::from(move _9) -> bb6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + // ty::Const + // + ty: fn(i32) -> i32 {>::from} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:14: 24:15 + // + literal: Const { ty: fn(i32) -> i32 {>::from}, val: Value(Scalar()) } + } + + bb5: { + return; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb6: { + StorageDead(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _0 = const as std::ops::Try>::from_error(move _8) -> bb7; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + // ty::Const + // + ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:13: 24:15 + // + literal: Const { ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error}, val: Value(Scalar()) } + } + + bb7: { + StorageDead(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageDead(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb5; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + } + } + diff --git a/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyBranchSame.diff b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyBranchSame.diff new file mode 100644 index 0000000000000..c91c55dfb04c4 --- /dev/null +++ b/src/test/mir-opt/simplify-arm/rustc.id_try.SimplifyBranchSame.diff @@ -0,0 +1,108 @@ +- // 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: { + StorageLive(_10); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + _10 = ((_3 as Ok).0: u8); // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + _2 = _10; // scope 5 at $DIR/simplify-arm.rs:24:13: 24:15 + StorageDead(_10); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 + StorageLive(_11); // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 + _11 = _2; // scope 1 at $DIR/simplify-arm.rs:25:8: 25:9 + ((_0 as Ok).0: u8) = move _11; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 + discriminant(_0) = 0; // scope 1 at $DIR/simplify-arm.rs:25:5: 25:10 + StorageDead(_11); // scope 1 at $DIR/simplify-arm.rs:25:9: 25:10 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb5; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb3: { + unreachable; // scope 0 at $DIR/simplify-arm.rs:24:13: 24:15 + } + + bb4: { + StorageLive(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + _6 = ((_3 as Err).0: i32); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageLive(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageLive(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _9 = _6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _8 = const >::from(move _9) -> bb6; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + // ty::Const + // + ty: fn(i32) -> i32 {>::from} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:14: 24:15 + // + literal: Const { ty: fn(i32) -> i32 {>::from}, val: Value(Scalar()) } + } + + bb5: { + return; // scope 0 at $DIR/simplify-arm.rs:26:2: 26:2 + } + + bb6: { + StorageDead(_9); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + _0 = const as std::ops::Try>::from_error(move _8) -> bb7; // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + // ty::Const + // + ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify-arm.rs:24:13: 24:15 + // + literal: Const { ty: fn( as std::ops::Try>::Error) -> std::result::Result { as std::ops::Try>::from_error}, val: Value(Scalar()) } + } + + bb7: { + StorageDead(_8); // scope 3 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageDead(_6); // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + StorageDead(_3); // scope 0 at $DIR/simplify-arm.rs:24:15: 24:16 + StorageDead(_2); // scope 0 at $DIR/simplify-arm.rs:26:1: 26:2 + goto -> bb5; // scope 0 at $DIR/simplify-arm.rs:24:14: 24:15 + } + } + diff --git a/src/test/mir-opt/simplify-locals-fixedpoint.rs b/src/test/mir-opt/simplify-locals-fixedpoint.rs 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 6f03438ff7234..48cee3c30d2da 100644 --- a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs +++ b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs @@ -1,89 +1,17 @@ // compile-flags: -C overflow-checks=no -fn use_zst(_: ((), ())) { } +fn use_zst(_: ((), ())) {} struct Temp { - x: u8 + x: u8, } -fn use_u8(_: u8) { } +fn use_u8(_: u8) {} +// EMIT_MIR rustc.main.SimplifyLocals.diff fn main() { let ((), ()) = ((), ()); use_zst(((), ())); - use_u8((Temp { x : 40 }).x + 2); + 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 Scalar() : (); -// StorageLive(_3); -// _3 = const Scalar() : (); -// _1 = const Scalar() : ((), ()); -// StorageDead(_3); -// StorageDead(_2); -// StorageDead(_1); -// StorageLive(_4); -// StorageLive(_6); -// _6 = const Scalar() : (); -// StorageLive(_7); -// _7 = const Scalar() : (); -// StorageDead(_7); -// StorageDead(_6); -// _4 = const use_zst(const Scalar() : ((), ())) -> bb1; -// } -// bb1: { -// StorageDead(_4); -// StorageLive(_8); -// StorageLive(_10); -// StorageLive(_11); -// _11 = const Scalar(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 Scalar() : ((), ())) -> 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..7047b542aa607 --- /dev/null +++ b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads.rs @@ -0,0 +1,13 @@ +fn map(x: Option>) -> Option> { + match x { + None => None, + Some(x) => Some(x), + } +} + +fn main() { + map(None); +} + +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// EMIT_MIR rustc.map.SimplifyLocals.diff diff --git a/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/32bit/rustc.map.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/32bit/rustc.map.SimplifyLocals.diff new file mode 100644 index 0000000000000..2f78671763d51 --- /dev/null +++ b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/32bit/rustc.map.SimplifyLocals.diff @@ -0,0 +1,42 @@ +- // 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: { + _0 = const std::option::Option::>::None; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:17: 3:21 + // ty::Const + // + ty: std::option::Option> + // + val: Value(Scalar(0x00000000)) + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:17: 3:21 + // + literal: Const { ty: std::option::Option>, val: Value(Scalar(0x00000000)) } + goto -> bb3; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:2:5: 5:6 + } + + bb3: { +- _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-locals-removes-unused-discriminant-reads/64bit/rustc.map.SimplifyLocals.diff b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/64bit/rustc.map.SimplifyLocals.diff new file mode 100644 index 0000000000000..a97fa98a7b09e --- /dev/null +++ b/src/test/mir-opt/simplify-locals-removes-unused-discriminant-reads/64bit/rustc.map.SimplifyLocals.diff @@ -0,0 +1,42 @@ +- // 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: { + _0 = const std::option::Option::>::None; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:17: 3:21 + // ty::Const + // + ty: std::option::Option> + // + val: Value(Scalar(0x0000000000000000)) + // mir::Constant + // + span: $DIR/simplify-locals-removes-unused-discriminant-reads.rs:3:17: 3:21 + // + literal: Const { ty: std::option::Option>, val: Value(Scalar(0x0000000000000000)) } + goto -> bb3; // scope 0 at $DIR/simplify-locals-removes-unused-discriminant-reads.rs:2:5: 5:6 + } + + bb3: { +- _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..3b472ed3a0376 --- /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() -> bb3; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 ++ _2 = const bar() -> bb1; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + // ty::Const + // + ty: fn() -> bool {bar} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:12: 7:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + +- bb2 (cleanup): { +- resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 ++ bb1: { ++ switchInt(_2) -> [false: bb2, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + } + +- bb3: { +- nop; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 +- } +- +- bb4: { +- goto -> bb6; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 +- } +- +- bb5: { ++ bb2: { + _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:9: 9:10 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 + goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 + } + +- bb6: { ++ bb3: { + _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:8:13: 8:18 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 + return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-initial.diff b/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-initial.diff new file mode 100644 index 0000000000000..1ba05b1cb3881 --- /dev/null +++ b/src/test/mir-opt/simplify_cfg/rustc.main.SimplifyCfg-initial.diff @@ -0,0 +1,101 @@ +- // MIR for `main` before SimplifyCfg-initial ++ // MIR for `main` after SimplifyCfg-initial + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11 + let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 + let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 + + bb0: { +- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 ++ falseUnwind -> [real: bb1, cleanup: bb2]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 + } + + bb1: { +- falseUnwind -> [real: bb3, cleanup: bb4]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 +- } +- +- bb2: { +- goto -> bb13; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 +- } +- +- bb3: { + StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- _2 = const bar() -> [return: bb5, unwind: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 ++ _2 = const bar() -> [return: bb3, unwind: bb2]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 + // ty::Const + // + ty: fn() -> bool {bar} + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:12: 7:15 + // + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar()) } + } + +- bb4 (cleanup): { ++ bb2 (cleanup): { + resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2 + } + +- bb5: { ++ bb3: { + FakeRead(ForMatchedPlace, _2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17 +- switchInt(_2) -> [false: bb7, otherwise: bb6]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ switchInt(_2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + } + +- bb6: { +- falseEdge -> [real: bb8, imaginary: bb7]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ bb4: { ++ falseEdge -> [real: bb6, imaginary: bb5]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + } + +- bb7: { ++ bb5: { + _1 = const (); // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:7:9: 9:10 + // + literal: Const { ty: (), val: Value(Scalar()) } +- goto -> bb12; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 ++ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 ++ goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 + } + +- bb8: { ++ bb6: { + _0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/simplify_cfg.rs:8:13: 8:18 + // + literal: Const { ty: (), val: Value(Scalar()) } +- goto -> bb9; // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 +- } +- +- bb9: { + StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 +- goto -> bb2; // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18 +- } +- +- bb10: { +- unreachable; // scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10 +- } +- +- bb11: { +- goto -> bb12; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10 +- } +- +- bb12: { +- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:10:5: 10:6 +- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6 +- } +- +- bb13: { + return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2 + } + } + diff --git a/src/test/mir-opt/simplify_if.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..97050122ca96e --- /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:6:14: 6:15 + } + + bb3: { + return; // scope 0 at $DIR/simplify_try.rs:8:2: 8:2 + } + } + diff --git a/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyBranchSame.after.mir b/src/test/mir-opt/simplify_try/rustc.try_identity.SimplifyBranchSame.after.mir 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..7d3537d094347 --- /dev/null +++ b/src/test/mir-opt/simplify_try_if_let/rustc.{{impl}}-append.SimplifyArmIdentity.diff @@ -0,0 +1,126 @@ +- // 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 + 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 5dc15286bab50..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: &'static Foo; -// let _1: &'static Foo; -// let _2: Foo; -// let mut _3: &'static [(u32, u32)]; -// let mut _4: &'static [(u32, u32); 42]; -// let _5: &'static [(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 &'static [(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..62b7535f2b575 --- /dev/null +++ b/src/test/mir-opt/storage_live_dead_in_statics/rustc.XXX.mir_map.0.mir @@ -0,0 +1,670 @@ +// 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 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:1: 23:3 + } +} diff --git a/src/test/mir-opt/storage_ranges.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/tls-access.rs b/src/test/mir-opt/tls-access.rs new file mode 100644 index 0000000000000..4f3f6b1b3ac02 --- /dev/null +++ b/src/test/mir-opt/tls-access.rs @@ -0,0 +1,13 @@ +#![feature(thread_local)] + +#[thread_local] +static mut FOO: u8 = 3; + +fn main() { + unsafe { + let a = &FOO; + FOO = 42; + } +} + +// EMIT_MIR rustc.main.SimplifyCfg-final.after.mir diff --git a/src/test/mir-opt/tls-access/rustc.main.SimplifyCfg-final.after.mir b/src/test/mir-opt/tls-access/rustc.main.SimplifyCfg-final.after.mir new file mode 100644 index 0000000000000..e4798c2e32407 --- /dev/null +++ b/src/test/mir-opt/tls-access/rustc.main.SimplifyCfg-final.after.mir @@ -0,0 +1,40 @@ +// MIR for `main` after SimplifyCfg-final + +fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/tls-access.rs:6:11: 6:11 + let _2: *mut u8; // in scope 0 at $DIR/tls-access.rs:8:18: 8:21 + let mut _3: *mut u8; // in scope 0 at $DIR/tls-access.rs:9:9: 9:12 + scope 1 { + let _1: &u8; // in scope 1 at $DIR/tls-access.rs:8:13: 8:14 + scope 2 { + debug a => _1; // in scope 2 at $DIR/tls-access.rs:8:13: 8:14 + } + } + + bb0: { + StorageLive(_1); // scope 1 at $DIR/tls-access.rs:8:13: 8:14 + StorageLive(_2); // scope 1 at $DIR/tls-access.rs:8:18: 8:21 + _2 = &/*tls*/ mut FOO; // scope 1 at $DIR/tls-access.rs:8:18: 8:21 + _1 = &(*_2); // scope 1 at $DIR/tls-access.rs:8:17: 8:21 + StorageLive(_3); // scope 2 at $DIR/tls-access.rs:9:9: 9:12 + _3 = &/*tls*/ mut FOO; // scope 2 at $DIR/tls-access.rs:9:9: 9:12 + (*_3) = const 42u8; // scope 2 at $DIR/tls-access.rs:9:9: 9:17 + // ty::Const + // + ty: u8 + // + val: Value(Scalar(0x2a)) + // mir::Constant + // + span: $DIR/tls-access.rs:9:15: 9:17 + // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) } + StorageDead(_3); // scope 2 at $DIR/tls-access.rs:9:17: 9:18 + _0 = const (); // scope 1 at $DIR/tls-access.rs:7:5: 10:6 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/tls-access.rs:7:5: 10:6 + // + literal: Const { ty: (), val: Value(Scalar()) } + StorageDead(_2); // scope 1 at $DIR/tls-access.rs:10:5: 10:6 + StorageDead(_1); // scope 1 at $DIR/tls-access.rs:10:5: 10:6 + return; // scope 0 at $DIR/tls-access.rs:11:2: 11:2 + } +} diff --git a/src/test/mir-opt/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..de29cd61019f1 --- /dev/null +++ b/src/test/mir-opt/uniform_array_move_out/rustc.move_out_by_subslice.mir_map.0.mir @@ -0,0 +1,117 @@ +// 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: bb4, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/uniform_array_move_out.rs:10:1: 13:2 + } + + bb2 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb3 (cleanup): { + drop(_3) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + } + + bb4: { + StorageDead(_3); // scope 0 at $DIR/uniform_array_move_out.rs:11:18: 11:19 + StorageLive(_4); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + StorageLive(_5); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + _5 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:11:21: 11:26 + (*_5) = const 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: bb7, unwind: bb5]; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + } + + bb5 (cleanup): { + drop(_4) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb6 (cleanup): { + drop(_5) -> bb5; // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + } + + bb7: { + StorageDead(_5); // scope 0 at $DIR/uniform_array_move_out.rs:11:25: 11:26 + _1 = [move _2, move _4]; // scope 0 at $DIR/uniform_array_move_out.rs:11:13: 11:27 + drop(_4) -> [return: bb8, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb8: { + StorageDead(_4); // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + drop(_2) -> [return: bb9, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + } + + bb9: { + StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:11:26: 11:27 + FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:11:9: 11:10 + StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + _6 = move _1[0..2]; // scope 1 at $DIR/uniform_array_move_out.rs:12:10: 12:17 + _0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:10:27: 13:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/uniform_array_move_out.rs:10:27: 13:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_6) -> [return: bb12, unwind: bb10]; // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb10 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb11 (cleanup): { + drop(_6) -> bb10; // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb12: { + StorageDead(_6); // scope 1 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + drop(_1) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + } + + bb13: { + StorageDead(_1); // scope 0 at $DIR/uniform_array_move_out.rs:13:1: 13:2 + goto -> bb14; // scope 0 at $DIR/uniform_array_move_out.rs:13:2: 13:2 + } + + bb14: { + return; // scope 0 at $DIR/uniform_array_move_out.rs:13:2: 13:2 + } +} diff --git a/src/test/mir-opt/uniform_array_move_out/rustc.move_out_from_end.mir_map.0.mir b/src/test/mir-opt/uniform_array_move_out/rustc.move_out_from_end.mir_map.0.mir new file mode 100644 index 0000000000000..aeab0e892ae8b --- /dev/null +++ b/src/test/mir-opt/uniform_array_move_out/rustc.move_out_from_end.mir_map.0.mir @@ -0,0 +1,117 @@ +// 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: bb4, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/uniform_array_move_out.rs:4:1: 7:2 + } + + bb2 (cleanup): { + drop(_2) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb3 (cleanup): { + drop(_3) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + } + + bb4: { + StorageDead(_3); // scope 0 at $DIR/uniform_array_move_out.rs:5:18: 5:19 + StorageLive(_4); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + StorageLive(_5); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + _5 = Box(i32); // scope 0 at $DIR/uniform_array_move_out.rs:5:21: 5:26 + (*_5) = const 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: bb7, unwind: bb5]; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + } + + bb5 (cleanup): { + drop(_4) -> bb2; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb6 (cleanup): { + drop(_5) -> bb5; // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + } + + bb7: { + StorageDead(_5); // scope 0 at $DIR/uniform_array_move_out.rs:5:25: 5:26 + _1 = [move _2, move _4]; // scope 0 at $DIR/uniform_array_move_out.rs:5:13: 5:27 + drop(_4) -> [return: bb8, unwind: bb2]; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb8: { + StorageDead(_4); // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + drop(_2) -> [return: bb9, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + } + + bb9: { + StorageDead(_2); // scope 0 at $DIR/uniform_array_move_out.rs:5:26: 5:27 + FakeRead(ForLet, _1); // scope 0 at $DIR/uniform_array_move_out.rs:5:9: 5:10 + StorageLive(_6); // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + _6 = move _1[1 of 2]; // scope 1 at $DIR/uniform_array_move_out.rs:6:14: 6:16 + _0 = const (); // scope 0 at $DIR/uniform_array_move_out.rs:4:24: 7:2 + // ty::Const + // + ty: () + // + val: Value(Scalar()) + // mir::Constant + // + span: $DIR/uniform_array_move_out.rs:4:24: 7:2 + // + literal: Const { ty: (), val: Value(Scalar()) } + drop(_6) -> [return: bb12, unwind: bb10]; // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb10 (cleanup): { + drop(_1) -> bb1; // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb11 (cleanup): { + drop(_6) -> bb10; // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb12: { + StorageDead(_6); // scope 1 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + drop(_1) -> [return: bb13, unwind: bb1]; // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + } + + bb13: { + StorageDead(_1); // scope 0 at $DIR/uniform_array_move_out.rs:7:1: 7:2 + goto -> bb14; // scope 0 at $DIR/uniform_array_move_out.rs:7:2: 7:2 + } + + bb14: { + return; // scope 0 at $DIR/uniform_array_move_out.rs:7:2: 7:2 + } +} diff --git a/src/test/mir-opt/uninhabited-enum.rs b/src/test/mir-opt/uninhabited-enum.rs index 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..c800ccb1ae51f --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/32bit/rustc.E-V-{{constant}}.mir_map.0.mir @@ -0,0 +1,20 @@ +// 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 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + } +} diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.Test-X-{{constructor}}.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/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..28f14399a6309 --- /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,46 @@ +// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops + +fn std::intrinsics::drop_in_place(_1: *mut std::vec::Vec) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _2: &mut std::vec::Vec; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _3: (); // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb7; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb3: { + goto -> bb1; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + goto -> bb2; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb5 (cleanup): { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> bb4; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb6: { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> [return: bb3, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb7: { + _2 = &mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _3 = const as std::ops::Drop>::drop(move _2) -> [return: bb6, unwind: bb5]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop} + // + val: Value(Scalar()) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop}, val: Value(Scalar()) } + } +} diff --git a/src/test/mir-opt/unusual-item-types/32bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/32bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir new file mode 100644 index 0000000000000..f4a5cc0b3279a --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/32bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir @@ -0,0 +1,20 @@ +// 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 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 + } +} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.E-V-{{constant}}.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/64bit/rustc.E-V-{{constant}}.mir_map.0.mir new file mode 100644 index 0000000000000..e635cd2b01bbd --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/64bit/rustc.E-V-{{constant}}.mir_map.0.mir @@ -0,0 +1,20 @@ +// 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 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/unusual-item-types.rs:22:9: 22:10 + } +} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.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..28f14399a6309 --- /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,46 @@ +// MIR for `std::intrinsics::drop_in_place` before AddMovesForPackedDrops + +fn std::intrinsics::drop_in_place(_1: *mut std::vec::Vec) -> () { + let mut _0: (); // return place in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _2: &mut std::vec::Vec; // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + let mut _3: (); // in scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + + bb0: { + goto -> bb7; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb1: { + return; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb2 (cleanup): { + resume; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb3: { + goto -> bb1; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb4 (cleanup): { + goto -> bb2; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb5 (cleanup): { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> bb4; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb6: { + drop(((*_1).0: alloc::raw_vec::RawVec)) -> [return: bb3, unwind: bb4]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + } + + bb7: { + _2 = &mut (*_1); // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + _3 = const as std::ops::Drop>::drop(move _2) -> [return: bb6, unwind: bb5]; // scope 0 at $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // ty::Const + // + ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop} + // + val: Value(Scalar()) + // mir::Constant + // + span: $SRC_DIR/libcore/ptr/mod.rs:LL:COL + // + literal: Const { ty: for<'r> fn(&'r mut std::vec::Vec) { as std::ops::Drop>::drop}, val: Value(Scalar()) } + } +} diff --git a/src/test/mir-opt/unusual-item-types/64bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir b/src/test/mir-opt/unusual-item-types/64bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir new file mode 100644 index 0000000000000..f4a5cc0b3279a --- /dev/null +++ b/src/test/mir-opt/unusual-item-types/64bit/rustc.{{impl}}-ASSOCIATED_CONSTANT.mir_map.0.mir @@ -0,0 +1,20 @@ +// 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 + } + + bb1 (cleanup): { + resume; // scope 0 at $DIR/unusual-item-types.rs:10:5: 10:40 + } +} diff --git a/src/test/mir-opt/while-storage.rs b/src/test/mir-opt/while-storage.rs index 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..3ddf82c2fb2c9 --- /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:12:13: 12:18 + } + + bb7: { + StorageDead(_2); // scope 0 at $DIR/while-storage.rs:14:5: 14:6 + return; // scope 0 at $DIR/while-storage.rs:15:2: 15:2 + } +} diff --git a/src/test/pretty/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..b3d188dd70881 --- /dev/null +++ b/src/test/pretty/asm.pp @@ -0,0 +1,34 @@ +#![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 +// only-x86_64 + +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") _); + asm!("inst1\ninst2"); + asm!("inst1 {0}, 42\ninst2 {1}, 24", in(reg) a, out(reg) b); + asm!("inst2 {1}, 24\ninst1 {0}, 42", in(reg) a, out(reg) b); + asm!("inst1 {0}, 42\ninst2 {1}, 24", in(reg) a, out(reg) b); + asm!("inst1\ninst2"); + asm!("inst1\ninst2"); + asm!("inst1\n\tinst2"); + asm!("inst1\ninst2\ninst3\ninst4"); + } +} diff --git a/src/test/pretty/asm.rs b/src/test/pretty/asm.rs new file mode 100644 index 0000000000000..33f25e5216b4e --- /dev/null +++ b/src/test/pretty/asm.rs @@ -0,0 +1,29 @@ +#![feature(asm)] + +// pretty-mode:expanded +// pp-exact:asm.pp +// only-x86_64 + +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") _); + asm!("inst1", "inst2"); + asm!("inst1 {}, 42", "inst2 {}, 24", in(reg) a, out(reg) b); + asm!("inst2 {1}, 24", "inst1 {0}, 42", in(reg) a, out(reg) b); + asm!("inst1 {}, 42", "inst2 {name}, 24", in(reg) a, name = out(reg) b); + asm!("inst1 +inst2"); + asm!("inst1\ninst2"); + asm!("inst1\n\tinst2"); + asm!("inst1\ninst2", "inst3\ninst4"); + } +} diff --git a/src/test/pretty/attr-literals.rs b/src/test/pretty/attr-literals.rs index 9db7e27b16103..44d2c5db3e668 100644 --- a/src/test/pretty/attr-literals.rs +++ b/src/test/pretty/attr-literals.rs @@ -5,7 +5,7 @@ #![feature(rustc_attrs)] fn main() { - #![rustc_dummy("hi", 1, 2, 1.012, pi = 3.14, bye, name ("John"))] + #![rustc_dummy("hi", 1, 2, 1.012, pi = 3.14, bye, name("John"))] #[rustc_dummy = 8] fn f() { } diff --git a/src/test/pretty/delimited-token-groups.rs b/src/test/pretty/delimited-token-groups.rs index 7bbb7dc911f93..66de0fc6cf7fa 100644 --- a/src/test/pretty/delimited-token-groups.rs +++ b/src/test/pretty/delimited-token-groups.rs @@ -7,7 +7,7 @@ macro_rules! mac { ($ ($ tt : tt) *) => () } mac! { struct S { field1 : u8, field2 : u16, } impl Clone for S { - fn clone () -> S + fn clone() -> S { panic ! () ; @@ -16,9 +16,8 @@ mac! { } mac! { - a - (aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa - aaaaaaaa aaaaaaaa) a + a(aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa + aaaaaaaa aaaaaaaa) a [aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa] a { diff --git a/src/test/pretty/issue-12590-a.rs b/src/test/pretty/issue-12590-a.rs index 1a9e85c42d8fb..ca1fef83cffc5 100644 --- a/src/test/pretty/issue-12590-a.rs +++ b/src/test/pretty/issue-12590-a.rs @@ -1,4 +1,5 @@ // pp-exact +// pretty-compare-only // The next line should not be expanded diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index 8aa4cdeb5394e..ee7586bae820a 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -34,29 +34,29 @@ ((::alloc::fmt::format as for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((::core::fmt::Arguments::new_v1 as - fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments::<'_>::new_v1})((&([("test" - as - &'static str)] - as - [&str; 1]) - as - &[&str; 1]), - (&(match (() - as - ()) - { - () - => - ([] - as - [std::fmt::ArgumentV1<'_>; 0]), - } - as - [std::fmt::ArgumentV1<'_>; 0]) - as - &[std::fmt::ArgumentV1<'_>; 0])) + fn(&[&str], &[std::fmt::ArgumentV1]) -> std::fmt::Arguments {std::fmt::Arguments::new_v1})((&([("test" + as + &str)] + as + [&str; 1]) + as + &[&str; 1]), + (&(match (() + as + ()) + { + () + => + ([] + as + [std::fmt::ArgumentV1; 0]), + } + as + [std::fmt::ArgumentV1; 0]) + as + &[std::fmt::ArgumentV1; 0])) as - std::fmt::Arguments<'_>)) + std::fmt::Arguments)) as std::string::String); (res as std::string::String) } as std::string::String); diff --git a/src/test/pretty/issue-68710-field-attr-proc-mac-lost.rs b/src/test/pretty/issue-68710-field-attr-proc-mac-lost.rs index 643ca761aac32..031a482595932 100644 --- a/src/test/pretty/issue-68710-field-attr-proc-mac-lost.rs +++ b/src/test/pretty/issue-68710-field-attr-proc-mac-lost.rs @@ -12,5 +12,5 @@ const C: C = #[cfg(debug_assertions)] field: 0, - #[cfg(not (debug_assertions))] + #[cfg(not(debug_assertions))] field: 1,}; 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-44216-add-instant.rs b/src/test/run-fail/issue-44216-add-instant.rs deleted file mode 100644 index 76ad0a3d41bf8..0000000000000 --- a/src/test/run-fail/issue-44216-add-instant.rs +++ /dev/null @@ -1,8 +0,0 @@ -// error-pattern:overflow - -use std::time::{Instant, Duration}; - -fn main() { - let now = Instant::now(); - let _ = now + Duration::from_secs(u64::max_value()); -} diff --git a/src/test/run-fail/issue-44216-add-system-time.rs b/src/test/run-fail/issue-44216-add-system-time.rs deleted file mode 100644 index aa861f7d5993e..0000000000000 --- a/src/test/run-fail/issue-44216-add-system-time.rs +++ /dev/null @@ -1,8 +0,0 @@ -// error-pattern:overflow - -use std::time::{Duration, SystemTime}; - -fn main() { - let now = SystemTime::now(); - let _ = now + Duration::from_secs(u64::max_value()); -} diff --git a/src/test/run-fail/issue-44216-sub-instant.rs b/src/test/run-fail/issue-44216-sub-instant.rs deleted file mode 100644 index 8bc1f47ae2d96..0000000000000 --- a/src/test/run-fail/issue-44216-sub-instant.rs +++ /dev/null @@ -1,8 +0,0 @@ -// error-pattern:overflow - -use std::time::{Instant, Duration}; - -fn main() { - let now = Instant::now(); - let _ = now - Duration::from_secs(u64::max_value()); -} diff --git a/src/test/run-fail/issue-44216-sub-system-time.rs b/src/test/run-fail/issue-44216-sub-system-time.rs deleted file mode 100644 index 37ab0e7c3f99f..0000000000000 --- a/src/test/run-fail/issue-44216-sub-system-time.rs +++ /dev/null @@ -1,8 +0,0 @@ -// error-pattern:overflow - -use std::time::{Duration, SystemTime}; - -fn main() { - let now = SystemTime::now(); - let _ = now - Duration::from_secs(u64::max_value()); -} 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 07ef2424cc880..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,35 +1,38 @@ #![feature(rustc_private)] -extern crate rustc; -extern crate rustc_codegen_utils; +extern crate rustc_codegen_ssa; +extern crate rustc_errors; +extern crate rustc_middle; #[macro_use] extern crate rustc_data_structures; -extern crate rustc_hir; -extern crate rustc_target; extern crate rustc_driver; +extern crate rustc_hir; +extern crate rustc_session; extern crate rustc_span; +extern crate rustc_symbol_mangling; +extern crate rustc_target; -use std::any::Any; -use std::sync::Arc; -use std::path::Path; -use rustc_span::symbol::Symbol; -use rustc::session::Session; -use rustc::session::config::OutputFilenames; -use rustc::ty::TyCtxt; -use rustc::ty::query::Providers; -use rustc::middle::cstore::{EncodedMetadata, MetadataLoader, MetadataLoaderDyn}; -use rustc::dep_graph::DepGraph; -use rustc::util::common::ErrorReported; -use rustc_codegen_utils::codegen_backend::CodegenBackend; -use rustc_data_structures::sync::MetadataRef; +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; pub struct NoLlvmMetadataLoader; impl MetadataLoader for NoLlvmMetadataLoader { fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { - let buf = std::fs::read(filename).map_err(|e| format!("metadata file open err: {:?}", e))?; + let buf = + std::fs::read(filename).map_err(|e| format!("metadata file open err: {:?}", e))?; let buf: OwningRef, [u8]> = OwningRef::new(buf); Ok(rustc_erase_owner!(buf.map_owner_box())) } @@ -47,13 +50,13 @@ impl CodegenBackend for TheBackend { } fn provide(&self, providers: &mut Providers) { - rustc_codegen_utils::symbol_names::provide(providers); + 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) { @@ -77,7 +80,8 @@ impl CodegenBackend for TheBackend { _sess: &Session, _dep_graph: &DepGraph, ) -> Result, ErrorReported> { - let crate_name = ongoing_codegen.downcast::() + let crate_name = ongoing_codegen + .downcast::() .expect("in join_codegen: ongoing_codegen is not a Symbol"); Ok(crate_name) } @@ -88,17 +92,15 @@ impl CodegenBackend for TheBackend { codegen_results: Box, outputs: &OutputFilenames, ) -> Result<(), ErrorReported> { + use rustc_session::{config::CrateType, output::out_filename}; use std::io::Write; - use rustc::session::config::CrateType; - use rustc_codegen_utils::link::out_filename; - let crate_name = codegen_results.downcast::() - .expect("in link: codegen_results is not a Symbol"); + let crate_name = + codegen_results.downcast::().expect("in link: codegen_results is not a Symbol"); for &crate_type in sess.opts.crate_types.iter() { if crate_type != CrateType::Rlib { sess.fatal(&format!("Crate type is {:?}", crate_type)); } - let output_name = - out_filename(sess, crate_type, &outputs, &*crate_name.as_str()); + let output_name = out_filename(sess, crate_type, &outputs, &*crate_name.as_str()); let mut out_file = ::std::fs::File::create(output_name).unwrap(); write!(out_file, "This has been \"compiled\" successfully.").unwrap(); } diff --git a/src/test/run-make-fulldeps/incr-add-rust-src-component/Makefile b/src/test/run-make-fulldeps/incr-add-rust-src-component/Makefile new file mode 100644 index 0000000000000..50ff3dd56ce92 --- /dev/null +++ b/src/test/run-make-fulldeps/incr-add-rust-src-component/Makefile @@ -0,0 +1,44 @@ +-include ../tools.mk + +# rust-lang/rust#70924: Test that if we add rust-src component in between two +# incremetnal compiles, the compiler does not ICE on the second. + +# This test uses `ln -s` rather than copying to save testing time, but its +# usage doesn't work on windows. So ignore windows. + +# ignore-windows + +SYSROOT:=$(shell $(RUSTC) --print sysroot) +FAKEROOT=$(TMPDIR)/fakeroot +INCR=$(TMPDIR)/incr + +# Make a local copy of the sysroot; then remove the rust-src part of it, if +# present, for the *first* build. Then put in a facsimile of the rust-src +# component for the second build, in order to expose the ICE from issue #70924. +# +# Note that it is much easier to just do `cp -a $(SYSROOT)/* $(FAKEROOT)` as a +# first step, but I am concerned that would be too expensive in a unit test +# compared to making symbolic links. +# +# Anyway, the pattern you'll see here is: For every prefix in +# root/lib/rustlib/src, link all of prefix parent content, then remove the +# prefix, then loop on the next prefix. This way, we basically create a copy of +# the context around root/lib/rustlib/src, and can freely add/remove the src +# component itself. +all: + mkdir $(FAKEROOT) + ln -s $(SYSROOT)/* $(FAKEROOT) + rm -f $(FAKEROOT)/lib + mkdir $(FAKEROOT)/lib + ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib + rm -f $(FAKEROOT)/lib/rustlib + mkdir $(FAKEROOT)/lib/rustlib + ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib + rm -f $(FAKEROOT)/lib/rustlib/src + mkdir $(FAKEROOT)/lib/rustlib/src + ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src + rm -f $(FAKEROOT)/lib/rustlib/src/rust + $(RUSTC) --sysroot $(FAKEROOT) -C incremental=$(INCR) main.rs + mkdir -p $(FAKEROOT)/lib/rustlib/src/rust/src/libstd + touch $(FAKEROOT)/lib/rustlib/src/rust/src/libstd/lib.rs + $(RUSTC) --sysroot $(FAKEROOT) -C incremental=$(INCR) main.rs diff --git a/src/test/run-make-fulldeps/incr-add-rust-src-component/main.rs b/src/test/run-make-fulldeps/incr-add-rust-src-component/main.rs new file mode 100644 index 0000000000000..f6320bcb04aa8 --- /dev/null +++ b/src/test/run-make-fulldeps/incr-add-rust-src-component/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello World"); +} 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/issue-19371/foo.rs b/src/test/run-make-fulldeps/issue-19371/foo.rs index 12da64fc88f18..af84faa7511c6 100644 --- a/src/test/run-make-fulldeps/issue-19371/foo.rs +++ b/src/test/run-make-fulldeps/issue-19371/foo.rs @@ -1,13 +1,12 @@ #![feature(rustc_private)] -extern crate rustc; extern crate rustc_interface; extern crate rustc_driver; +extern crate rustc_session; extern crate rustc_span; -use rustc::session::DiagnosticOutput; -use rustc::session::config::{Input, Options, - OutputType, OutputTypes}; +use rustc_session::DiagnosticOutput; +use rustc_session::config::{Input, Options, OutputType, OutputTypes}; use rustc_interface::interface; use rustc_span::source_map::FileName; 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/long-linker-command-lines/foo.rs b/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs index 96fb16b1fcc8f..f313798de215b 100644 --- a/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs +++ b/src/test/run-make-fulldeps/long-linker-command-lines/foo.rs @@ -90,7 +90,7 @@ fn main() { } let linker_args = read_linker_args(&ok); - for mut arg in linker_args.split('S') { + for arg in linker_args.split('S') { expected_libs.remove(arg); } 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/pretty-expanded/input.rs b/src/test/run-make-fulldeps/pretty-expanded/input.rs index 3cbabc5b46061..af3d75b3bf216 100644 --- a/src/test/run-make-fulldeps/pretty-expanded/input.rs +++ b/src/test/run-make-fulldeps/pretty-expanded/input.rs @@ -2,7 +2,7 @@ // #13544 -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; #[derive(RustcEncodable)] pub struct A; #[derive(RustcEncodable)] pub struct B(isize); diff --git a/src/test/run-make-fulldeps/profile/Makefile b/src/test/run-make-fulldeps/profile/Makefile index c12712590e48f..04d382b475ed2 100644 --- a/src/test/run-make-fulldeps/profile/Makefile +++ b/src/test/run-make-fulldeps/profile/Makefile @@ -7,3 +7,6 @@ all: $(call RUN,test) || exit 1 [ -e "$(TMPDIR)/test.gcno" ] || (echo "No .gcno file"; exit 1) [ -e "$(TMPDIR)/test.gcda" ] || (echo "No .gcda file"; exit 1) + $(RUSTC) -g -Z profile -Z profile-emit=$(TMPDIR)/abc/abc.gcda test.rs + $(call RUN,test) || exit 1 + [ -e "$(TMPDIR)/abc/abc.gcda" ] || (echo "gcda file not emitted to defined path"; exit 1) diff --git a/src/test/run-make-fulldeps/reproducible-build-2/Makefile b/src/test/run-make-fulldeps/reproducible-build-2/Makefile index fc912efed5e3c..fd94516fbbaf6 100644 --- a/src/test/run-make-fulldeps/reproducible-build-2/Makefile +++ b/src/test/run-make-fulldeps/reproducible-build-2/Makefile @@ -2,7 +2,6 @@ # ignore-musl # ignore-windows -# ignore-macos (rust-lang/rust#66568) # Objects are reproducible but their path is not. all: \ @@ -21,7 +20,7 @@ sysroot: rm -rf $(TMPDIR) && mkdir $(TMPDIR) $(RUSTC) reproducible-build-aux.rs $(RUSTC) reproducible-build.rs --crate-type rlib --sysroot $(shell $(RUSTC) --print sysroot) --remap-path-prefix=$(shell $(RUSTC) --print sysroot)=/sysroot - cp -r $(shell $(RUSTC) --print sysroot) $(TMPDIR)/sysroot + cp -R $(shell $(RUSTC) --print sysroot) $(TMPDIR)/sysroot cp $(TMPDIR)/libreproducible_build.rlib $(TMPDIR)/libfoo.rlib $(RUSTC) reproducible-build.rs --crate-type rlib --sysroot $(TMPDIR)/sysroot --remap-path-prefix=$(TMPDIR)/sysroot=/sysroot cmp "$(TMPDIR)/libreproducible_build.rlib" "$(TMPDIR)/libfoo.rlib" || exit 1 diff --git a/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile index 35317dca1e824..b11d4c4cab7cf 100644 --- a/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-cdylib-link/Makefile @@ -1,5 +1,5 @@ # needs-sanitizer-support -# only-x86_64 +# needs-sanitizer-address # only-linux -include ../tools.mk @@ -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..c2ebd2a6d8cac 100644 --- a/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-dylib-link/Makefile @@ -1,5 +1,5 @@ # needs-sanitizer-support -# only-x86_64 +# needs-sanitizer-address # only-linux -include ../tools.mk @@ -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/sanitizer-staticlib-link/Makefile b/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile index f56475b441f1a..5ceff16471cee 100644 --- a/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile +++ b/src/test/run-make-fulldeps/sanitizer-staticlib-link/Makefile @@ -1,5 +1,5 @@ # needs-sanitizer-support -# only-x86_64 +# needs-sanitizer-address # only-linux -include ../tools.mk diff --git a/src/test/run-make-fulldeps/save-analysis-fail/foo.rs b/src/test/run-make-fulldeps/save-analysis-fail/foo.rs index e042210ac79b0..5d504ced65e33 100644 --- a/src/test/run-make-fulldeps/save-analysis-fail/foo.rs +++ b/src/test/run-make-fulldeps/save-analysis-fail/foo.rs @@ -2,13 +2,13 @@ #![feature(box_syntax)] #![feature(rustc_private)] -extern crate graphviz; +extern crate rustc_graphviz; // A simple rust project extern crate krate2; extern crate krate2 as krate3; -use graphviz::RenderOption; +use rustc_graphviz::RenderOption; use std::collections::{HashMap,HashSet}; use std::cell::RefCell; use std::io::Write; diff --git a/src/test/run-make-fulldeps/save-analysis/foo.rs b/src/test/run-make-fulldeps/save-analysis/foo.rs index bc0209dc5832a..789ab686e3f8e 100644 --- a/src/test/run-make-fulldeps/save-analysis/foo.rs +++ b/src/test/run-make-fulldeps/save-analysis/foo.rs @@ -4,13 +4,13 @@ #![feature(associated_type_defaults)] #![feature(external_doc)] -extern crate graphviz; +extern crate rustc_graphviz; // A simple rust project extern crate krate2; extern crate krate2 as krate3; -use graphviz::RenderOption; +use rustc_graphviz::RenderOption; use std::collections::{HashMap,HashSet}; use std::cell::RefCell; use std::io::Write; @@ -27,7 +27,7 @@ use std::char::from_u32; static uni: &'static str = "Les Miséééééééérables"; static yy: usize = 25; -static bob: Option = None; +static bob: Option = None; // buglink test - see issue #1337. @@ -418,7 +418,7 @@ impl Error + 'static + Send { ::is::(self) } } -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; #[derive(Clone, Copy, Hash, RustcEncodable, RustcDecodable, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] struct AllDerives(i32); 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/target-specs/my-awesome-platform.json b/src/test/run-make-fulldeps/target-specs/my-awesome-platform.json index 8d028280a8da7..00de3de05f07a 100644 --- a/src/test/run-make-fulldeps/target-specs/my-awesome-platform.json +++ b/src/test/run-make-fulldeps/target-specs/my-awesome-platform.json @@ -1,5 +1,5 @@ { - "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", + "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128", "linker-flavor": "gcc", "llvm-target": "i686-unknown-linux-gnu", "target-endian": "little", diff --git a/src/test/run-make-fulldeps/target-specs/my-x86_64-unknown-linux-gnu-platform.json b/src/test/run-make-fulldeps/target-specs/my-x86_64-unknown-linux-gnu-platform.json index 48040ae3da0ef..6d5e964ed4fee 100644 --- a/src/test/run-make-fulldeps/target-specs/my-x86_64-unknown-linux-gnu-platform.json +++ b/src/test/run-make-fulldeps/target-specs/my-x86_64-unknown-linux-gnu-platform.json @@ -1,6 +1,6 @@ { "pre-link-args": {"gcc": ["-m64"]}, - "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "linker-flavor": "gcc", "llvm-target": "x86_64-unknown-linux-gnu", "target-endian": "little", 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/env-dep-info/Makefile b/src/test/run-make/env-dep-info/Makefile new file mode 100644 index 0000000000000..2be0b4b324b08 --- /dev/null +++ b/src/test/run-make/env-dep-info/Makefile @@ -0,0 +1,8 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + EXISTING_ENV=1 EXISTING_OPT_ENV=1 $(RUSTC) --emit dep-info main.rs + $(CGREP) "# env-dep:EXISTING_ENV=1" < $(TMPDIR)/main.d + $(CGREP) "# env-dep:EXISTING_OPT_ENV=1" < $(TMPDIR)/main.d + $(CGREP) "# env-dep:NONEXISTENT_OPT_ENV" < $(TMPDIR)/main.d + $(CGREP) "# env-dep:ESCAPE\nESCAPE\\" < $(TMPDIR)/main.d diff --git a/src/test/run-make/env-dep-info/main.rs b/src/test/run-make/env-dep-info/main.rs new file mode 100644 index 0000000000000..a25246bac792c --- /dev/null +++ b/src/test/run-make/env-dep-info/main.rs @@ -0,0 +1,6 @@ +fn main() { + env!("EXISTING_ENV"); + option_env!("EXISTING_OPT_ENV"); + option_env!("NONEXISTENT_OPT_ENV"); + option_env!("ESCAPE\nESCAPE\\"); +} diff --git a/src/test/run-make/static-pie/Makefile b/src/test/run-make/static-pie/Makefile new file mode 100644 index 0000000000000..1d3cc82138927 --- /dev/null +++ b/src/test/run-make/static-pie/Makefile @@ -0,0 +1,15 @@ +-include ../../run-make-fulldeps/tools.mk + +# only-x86_64-unknown-linux-musl + +# How to manually run this +# $ ./x.py test --target x86_64-unknown-linux-musl src/test/run-make/static-pie + +all: + $(RUSTC) --target $(TARGET) -C target-feature=+crt-static test-aslr.rs + # Check that no dynamic interpreter is set + ! readelf -l $(call RUN_BINFILE,test-aslr) | $(CGREP) INTERP + # Check that we have a dynamic executable + readelf -l $(call RUN_BINFILE,test-aslr) | $(CGREP) DYNAMIC + # Check for address space layout randomization + $(call RUN,test-aslr) --test-aslr diff --git a/src/test/run-make/static-pie/test-aslr.rs b/src/test/run-make/static-pie/test-aslr.rs new file mode 100644 index 0000000000000..f28e00f7f4cf9 --- /dev/null +++ b/src/test/run-make/static-pie/test-aslr.rs @@ -0,0 +1,43 @@ +const NUM_RUNS: usize = 10; + +fn run_self(exe: &str) -> usize { + use std::process::Command; + let mut set = std::collections::HashSet::new(); + + let mut cmd = Command::new(exe); + cmd.arg("--report"); + (0..NUM_RUNS).for_each(|_| { + set.insert(cmd.output().expect("failed to execute process").stdout); + }); + set.len() +} + +fn main() { + let mut args = std::env::args(); + let arg0 = args.next().unwrap(); + match args.next() { + Some(s) if s.eq("--report") => { + println!("main = {:#?}", &main as *const _); + } + Some(s) if s.eq("--test-no-aslr") => { + let cnt = run_self(&arg0); + if cnt != 1 { + eprintln!("FAIL: {} most likely ASLR", arg0); + std::process::exit(1); + } + println!("PASS: {} does no ASLR", arg0); + } + Some(s) if s.eq("--test-aslr") => { + let cnt = run_self(&arg0); + if cnt != NUM_RUNS { + eprintln!("FAIL: {} most likely no ASLR", arg0); + std::process::exit(1); + } + println!("PASS: {} does ASLR", arg0); + } + Some(_) | None => { + println!("Usage: {} --test-no-aslr | --test-aslr", arg0); + std::process::exit(1); + } + } +} 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-std/return-specific-literal.js b/src/test/rustdoc-js-std/return-specific-literal.js new file mode 100644 index 0000000000000..c7c347240b751 --- /dev/null +++ b/src/test/rustdoc-js-std/return-specific-literal.js @@ -0,0 +1,10 @@ +const QUERY = 'struct:"string"'; + +const EXPECTED = { + 'in_args': [ + { 'path': 'std::string::String', 'name': 'ne' }, + ], + 'returned': [ + { 'path': 'std::string::String', 'name': 'add' }, + ], +}; diff --git a/src/test/rustdoc-js-std/return-specific.js b/src/test/rustdoc-js-std/return-specific.js new file mode 100644 index 0000000000000..d9a910553b8de --- /dev/null +++ b/src/test/rustdoc-js-std/return-specific.js @@ -0,0 +1,10 @@ +const QUERY = 'struct:string'; + +const EXPECTED = { + 'in_args': [ + { 'path': 'std::string::String', 'name': 'ne' }, + ], + 'returned': [ + { 'path': 'std::string::String', 'name': 'add' }, + ], +}; diff --git a/src/test/rustdoc-js/doc-alias-filter-out.js b/src/test/rustdoc-js/doc-alias-filter-out.js new file mode 100644 index 0000000000000..46a089d06ebef --- /dev/null +++ b/src/test/rustdoc-js/doc-alias-filter-out.js @@ -0,0 +1,9 @@ +// exact-check + +const QUERY = 'true'; + +const FILTER_CRATE = 'some_other_crate'; + +const EXPECTED = { + 'others': [], +}; diff --git a/src/test/rustdoc-js/doc-alias-filter-out.rs b/src/test/rustdoc-js/doc-alias-filter-out.rs new file mode 100644 index 0000000000000..815e8cedd16da --- /dev/null +++ b/src/test/rustdoc-js/doc-alias-filter-out.rs @@ -0,0 +1,4 @@ +#![feature(doc_alias)] + +#[doc(alias = "true")] +pub struct Foo; diff --git a/src/test/rustdoc-js/doc-alias-filter.js b/src/test/rustdoc-js/doc-alias-filter.js new file mode 100644 index 0000000000000..4b1e2e2970479 --- /dev/null +++ b/src/test/rustdoc-js/doc-alias-filter.js @@ -0,0 +1,17 @@ +// exact-check + +const QUERY = 'true'; + +const FILTER_CRATE = 'doc_alias_filter'; + +const EXPECTED = { + 'others': [ + { + 'path': 'doc_alias_filter', + 'name': 'Foo', + 'alias': 'true', + 'href': '../doc_alias_filter/struct.Foo.html', + 'is_alias': true + }, + ], +}; diff --git a/src/test/rustdoc-js/doc-alias-filter.rs b/src/test/rustdoc-js/doc-alias-filter.rs new file mode 100644 index 0000000000000..8887f8c2b0149 --- /dev/null +++ b/src/test/rustdoc-js/doc-alias-filter.rs @@ -0,0 +1,7 @@ +#![feature(doc_alias)] + +#[doc(alias = "true")] +pub struct Foo; + +#[doc(alias = "false")] +pub struct Bar; 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/check-doc-alias-attr.rs b/src/test/rustdoc-ui/check-doc-alias-attr.rs new file mode 100644 index 0000000000000..2f01099107d9e --- /dev/null +++ b/src/test/rustdoc-ui/check-doc-alias-attr.rs @@ -0,0 +1,9 @@ +#![feature(doc_alias)] + +#[doc(alias = "foo")] // ok! +pub struct Bar; + +#[doc(alias)] //~ ERROR +#[doc(alias = 0)] //~ ERROR +#[doc(alias("bar"))] //~ ERROR +pub struct Foo; diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.stderr b/src/test/rustdoc-ui/check-doc-alias-attr.stderr new file mode 100644 index 0000000000000..480acc821aaa8 --- /dev/null +++ b/src/test/rustdoc-ui/check-doc-alias-attr.stderr @@ -0,0 +1,20 @@ +error: doc alias attribute expects a string: #[doc(alias = "0")] + --> $DIR/check-doc-alias-attr.rs:6:7 + | +LL | #[doc(alias)] + | ^^^^^ + +error: doc alias attribute expects a string: #[doc(alias = "0")] + --> $DIR/check-doc-alias-attr.rs:7:7 + | +LL | #[doc(alias = 0)] + | ^^^^^^^^^ + +error: doc alias attribute expects a string: #[doc(alias = "0")] + --> $DIR/check-doc-alias-attr.rs:8:7 + | +LL | #[doc(alias("bar"))] + | ^^^^^^^^^^^^ + +error: aborting due to 3 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/deny-missing-docs-crate.stderr b/src/test/rustdoc-ui/deny-missing-docs-crate.stderr index f0a13b70b977f..821e6b99f7b8f 100644 --- a/src/test/rustdoc-ui/deny-missing-docs-crate.stderr +++ b/src/test/rustdoc-ui/deny-missing-docs-crate.stderr @@ -1,4 +1,4 @@ -error: missing documentation for crate +error: missing documentation for the crate --> $DIR/deny-missing-docs-crate.rs:1:1 | LL | / #![deny(missing_docs)] 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/doctest-output.rs b/src/test/rustdoc-ui/doctest-output.rs new file mode 100644 index 0000000000000..f812263c25265 --- /dev/null +++ b/src/test/rustdoc-ui/doctest-output.rs @@ -0,0 +1,15 @@ +// compile-flags:--test --test-args=--test-threads=1 +// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" +// check-pass + +//! ``` +//! assert_eq!(1 + 1, 2); +//! ``` + +pub mod foo { + + /// ``` + /// assert_eq!(1 + 1, 2); + /// ``` + pub fn bar() {} +} diff --git a/src/test/rustdoc-ui/doctest-output.stdout b/src/test/rustdoc-ui/doctest-output.stdout new file mode 100644 index 0000000000000..9a55bf5019692 --- /dev/null +++ b/src/test/rustdoc-ui/doctest-output.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test $DIR/doctest-output.rs - (line 5) ... ok +test $DIR/doctest-output.rs - foo::bar (line 11) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out + 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/issue-61732.rs b/src/test/rustdoc-ui/issue-61732.rs index d4835c092248e..4bd8efeaa3b97 100644 --- a/src/test/rustdoc-ui/issue-61732.rs +++ b/src/test/rustdoc-ui/issue-61732.rs @@ -1,4 +1,4 @@ // This previously triggered an ICE. pub(in crate::r#mod) fn main() {} -//~^ ERROR expected module, found unresolved item +//~^ ERROR failed to resolve: maybe a missing crate `r#mod` diff --git a/src/test/rustdoc-ui/issue-61732.stderr b/src/test/rustdoc-ui/issue-61732.stderr index 6c8ba48864df0..8213422491120 100644 --- a/src/test/rustdoc-ui/issue-61732.stderr +++ b/src/test/rustdoc-ui/issue-61732.stderr @@ -1,11 +1,11 @@ -error[E0577]: expected module, found unresolved item `crate::r#mod` - --> $DIR/issue-61732.rs:3:8 +error[E0433]: failed to resolve: maybe a missing crate `r#mod`? + --> $DIR/issue-61732.rs:3:15 | LL | pub(in crate::r#mod) fn main() {} - | ^^^^^^^^^^^^ not a module + | ^^^^^ maybe a missing crate `r#mod`? error: Compilation failed, aborting rustdoc error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0577`. +For more information about this error, try `rustc --explain E0433`. 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-compile-fail3.stderr b/src/test/rustdoc-ui/test-compile-fail3.stderr index 7a2f1815ed8e5..fab801b3beaea 100644 --- a/src/test/rustdoc-ui/test-compile-fail3.stderr +++ b/src/test/rustdoc-ui/test-compile-fail3.stderr @@ -1,4 +1,4 @@ -error: unterminated double quote string +error[E0765]: unterminated double quote string --> $DIR/test-compile-fail3.rs:3:1 | 3 | "fail @@ -6,3 +6,4 @@ error: unterminated double quote string error: aborting due to previous error +For more information about this error, try `rustc --explain E0765`. 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/unparseable-doc-test.stdout b/src/test/rustdoc-ui/unparseable-doc-test.stdout index 4ea6455d3aa4c..29cb22e2e4b09 100644 --- a/src/test/rustdoc-ui/unparseable-doc-test.stdout +++ b/src/test/rustdoc-ui/unparseable-doc-test.stdout @@ -5,7 +5,7 @@ test $DIR/unparseable-doc-test.rs - foo (line 6) ... FAILED failures: ---- $DIR/unparseable-doc-test.rs - foo (line 6) stdout ---- -error: unterminated double quote string +error[E0765]: unterminated double quote string --> $DIR/unparseable-doc-test.rs:8:1 | LL | "unterminated @@ -13,6 +13,7 @@ LL | "unterminated error: aborting due to previous error +For more information about this error, try `rustc --explain E0765`. Couldn't compile the test. failures: 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/attributes.rs b/src/test/rustdoc/attributes.rs index d5772e183bcb2..e9cd3514a07e2 100644 --- a/src/test/rustdoc/attributes.rs +++ b/src/test/rustdoc/attributes.rs @@ -16,6 +16,6 @@ pub enum Foo { Bar, } -// @has foo/struct.Repr.html '//*[@class="docblock attributes top-attr"]' '#[repr(C, align (8))]' +// @has foo/struct.Repr.html '//*[@class="docblock attributes top-attr"]' '#[repr(C, align(8))]' #[repr(C, align(8))] pub struct Repr; 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/intra-link-proc-macro-macro.rs b/src/test/rustdoc/auxiliary/intra-link-proc-macro-macro.rs new file mode 100644 index 0000000000000..04a431d99026e --- /dev/null +++ b/src/test/rustdoc/auxiliary/intra-link-proc-macro-macro.rs @@ -0,0 +1,35 @@ +// force-host +// no-prefer-dynamic +// compile-flags: --crate-type proc-macro + +#![crate_type="proc-macro"] +#![crate_name="intra_link_proc_macro_macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_derive(DeriveA)] +pub fn a_derive(input: TokenStream) -> TokenStream { + input +} + +#[proc_macro_derive(DeriveB)] +pub fn b_derive(input: TokenStream) -> TokenStream { + input +} + +#[proc_macro_derive(DeriveTrait)] +pub fn trait_derive(input: TokenStream) -> TokenStream { + input +} + +#[proc_macro_attribute] +pub fn attr_a(input: TokenStream, _args: TokenStream) -> TokenStream { + input +} + +#[proc_macro_attribute] +pub fn attr_b(input: TokenStream, _args: TokenStream) -> TokenStream { + input +} 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/auxiliary/issue-73061.rs b/src/test/rustdoc/auxiliary/issue-73061.rs new file mode 100644 index 0000000000000..e05a3bc6d9180 --- /dev/null +++ b/src/test/rustdoc/auxiliary/issue-73061.rs @@ -0,0 +1,17 @@ +//edition:2018 + +#![feature(type_alias_impl_trait)] + +pub trait Foo { + type X: std::future::Future; + fn x(&self) -> Self::X; +} + +pub struct F; + +impl Foo for F { + type X = impl std::future::Future; + fn x(&self) -> Self::X { + async {} + } +} diff --git a/src/test/rustdoc/codeblock-title.rs b/src/test/rustdoc/codeblock-title.rs index 2f77929c74e37..b59b21111b009 100644 --- a/src/test/rustdoc/codeblock-title.rs +++ b/src/test/rustdoc/codeblock-title.rs @@ -4,6 +4,7 @@ // @has foo/fn.bar.html '//*[@class="tooltip compile_fail"]/span' "This example deliberately fails to compile" // @has foo/fn.bar.html '//*[@class="tooltip ignore"]/span' "This example is not tested" +// @has foo/fn.bar.html '//*[@class="tooltip should_panic"]/span' "This example panics" /// foo /// @@ -15,6 +16,10 @@ /// goo(); /// ``` /// +/// ```should_panic +/// hoo(); +/// ``` +/// /// ``` /// let x = 0; /// ``` 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/crate-version.rs b/src/test/rustdoc/crate-version.rs index 9ea84ac031211..893af5c61332d 100644 --- a/src/test/rustdoc/crate-version.rs +++ b/src/test/rustdoc/crate-version.rs @@ -1,3 +1,3 @@ -// compile-flags: --crate-version=1.3.37 -Z unstable-options +// compile-flags: --crate-version=1.3.37 // @has 'crate_version/index.html' '//div[@class="block version"]/p' 'Version 1.3.37' 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-proc-macro.rs b/src/test/rustdoc/intra-link-proc-macro.rs new file mode 100644 index 0000000000000..7b6ea5d60f853 --- /dev/null +++ b/src/test/rustdoc/intra-link-proc-macro.rs @@ -0,0 +1,27 @@ +// aux-build:intra-link-proc-macro-macro.rs +// build-aux-docs +#![deny(intra_doc_link_resolution_failure)] + +extern crate intra_link_proc_macro_macro; + + +pub use intra_link_proc_macro_macro::{DeriveA, attr_a}; +use intra_link_proc_macro_macro::{DeriveB, attr_b}; + +// @has intra_link_proc_macro/struct.Foo.html +// @has - '//a/@href' '../intra_link_proc_macro/derive.DeriveA.html' +// @has - '//a/@href' '../intra_link_proc_macro/attr.attr_a.html' +// @has - '//a/@href' '../intra_link_proc_macro/trait.DeriveTrait.html' +// @has - '//a/@href' '../intra_link_proc_macro_macro/derive.DeriveB.html' +// @has - '//a/@href' '../intra_link_proc_macro_macro/attr.attr_b.html' +/// Link to [DeriveA], [attr_a], [DeriveB], [attr_b], [DeriveTrait] +pub struct Foo; + +// @has intra_link_proc_macro/struct.Bar.html +// @has - '//a/@href' '../intra_link_proc_macro/derive.DeriveA.html' +// @has - '//a/@href' '../intra_link_proc_macro/attr.attr_a.html' +/// Link to [deriveA](derive@DeriveA) [attr](macro@attr_a) +pub struct Bar; + +// this should not cause ambiguity errors +pub trait DeriveTrait {} diff --git a/src/test/rustdoc/intra-link-self.rs b/src/test/rustdoc/intra-link-self.rs index acf975f5c738e..97752d5cfcb5c 100644 --- a/src/test/rustdoc/intra-link-self.rs +++ b/src/test/rustdoc/intra-link-self.rs @@ -1,5 +1,7 @@ #![crate_name = "foo"] +// ignore-tidy-linelength + // @has foo/index.html '//a/@href' '../foo/struct.Foo.html#method.new' // @has foo/struct.Foo.html '//a/@href' '../foo/struct.Foo.html#method.new' @@ -27,3 +29,89 @@ impl Bar { unimplemented!() } } + +pub struct MyStruct { + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#structfield.struct_field' + + /// [`struct_field`] + /// + /// [`struct_field`]: Self::struct_field + pub struct_field: u8, +} + +pub enum MyEnum { + // @has foo/enum.MyEnum.html '//a/@href' '../foo/enum.MyEnum.html#EnumVariant.v' + + /// [`EnumVariant`] + /// + /// [`EnumVariant`]: Self::EnumVariant + EnumVariant, +} + +pub union MyUnion { + // @has foo/union.MyUnion.html '//a/@href' '../foo/union.MyUnion.html#structfield.union_field' + + /// [`union_field`] + /// + /// [`union_field`]: Self::union_field + pub union_field: f32, +} + +pub trait MyTrait { + // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedtype.AssoType' + + /// [`AssoType`] + /// + /// [`AssoType`]: Self::AssoType + type AssoType; + + // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedconstant.ASSO_CONST' + + /// [`ASSO_CONST`] + /// + /// [`ASSO_CONST`]: Self::ASSO_CONST + const ASSO_CONST: i32 = 1; + + // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#method.asso_fn' + + /// [`asso_fn`] + /// + /// [`asso_fn`]: Self::asso_fn + fn asso_fn() {} +} + +impl MyStruct { + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.for_impl' + + /// [`for_impl`] + /// + /// [`for_impl`]: Self::for_impl + pub fn for_impl() { + unimplemented!() + } +} + +impl MyTrait for MyStruct { + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' + + /// [`AssoType`] + /// + /// [`AssoType`]: Self::AssoType + type AssoType = u32; + + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' + + /// [`ASSO_CONST`] + /// + /// [`ASSO_CONST`]: Self::ASSO_CONST + const ASSO_CONST: i32 = 10; + + // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.asso_fn' + + /// [`asso_fn`] + /// + /// [`asso_fn`]: Self::asso_fn + fn asso_fn() { + unimplemented!() + } +} 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/issue-72340.rs b/src/test/rustdoc/issue-72340.rs new file mode 100644 index 0000000000000..6ed3bfbe3e54b --- /dev/null +++ b/src/test/rustdoc/issue-72340.rs @@ -0,0 +1,19 @@ +#![crate_name = "foo"] + +pub struct Body; + +impl Body { + pub fn empty() -> Self { + Body + } + +} + +impl Default for Body { + // @has foo/struct.Body.html '//a/@href' '../foo/struct.Body.html#method.empty' + + /// Returns [`Body::empty()`](Body::empty). + fn default() -> Body { + Body::empty() + } +} diff --git a/src/test/rustdoc/issue-73061-cross-crate-opaque-assoc-type.rs b/src/test/rustdoc/issue-73061-cross-crate-opaque-assoc-type.rs new file mode 100644 index 0000000000000..2700f2370eec8 --- /dev/null +++ b/src/test/rustdoc/issue-73061-cross-crate-opaque-assoc-type.rs @@ -0,0 +1,14 @@ +// Regression test for ICE #73061 + +// aux-build:issue-73061.rs + +extern crate issue_73061; + +pub struct Z; + +impl issue_73061::Foo for Z { + type X = ::X; + fn x(&self) -> Self::X { + issue_73061::F.x() + } +} 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/sanitizer-option.rs b/src/test/rustdoc/sanitizer-option.rs index 6af9ed3e33f66..a79b37ee08210 100644 --- a/src/test/rustdoc/sanitizer-option.rs +++ b/src/test/rustdoc/sanitizer-option.rs @@ -1,4 +1,5 @@ // needs-sanitizer-support +// needs-sanitizer-address // compile-flags: --test -Z sanitizer=address // // #43031: Verify that rustdoc passes `-Z` options to rustc. Use an extern diff --git a/src/test/rustdoc/show-const-contents.rs b/src/test/rustdoc/show-const-contents.rs index 064c026e6a078..814339e198f95 100644 --- a/src/test/rustdoc/show-const-contents.rs +++ b/src/test/rustdoc/show-const-contents.rs @@ -28,8 +28,8 @@ pub const CONST_CALC_I32: i32 = 42 + 1; // @!has show_const_contents/constant.CONST_REF_I32.html '; //' pub const CONST_REF_I32: &'static i32 = &42; -// @has show_const_contents/constant.CONST_I32_MAX.html '= i32::max_value(); // 2_147_483_647i32' -pub const CONST_I32_MAX: i32 = i32::max_value(); +// @has show_const_contents/constant.CONST_I32_MAX.html '= i32::MAX; // 2_147_483_647i32' +pub const CONST_I32_MAX: i32 = i32::MAX; // @!has show_const_contents/constant.UNIT.html '= ();' // @!has show_const_contents/constant.UNIT.html '; //' @@ -51,16 +51,16 @@ 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 { ($T:ident) => ( - pub const MIN: $T = $T::min_value(); + pub const MIN: $T = $T::MIN; ) } -// @has show_const_contents/constant.MIN.html '= i16::min_value(); // -32_768i16' +// @has show_const_contents/constant.MIN.html '= i16::MIN; // -32_768i16' int_module!(i16); // @has show_const_contents/constant.ESCAPE.html //pre '= r#""#;' 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/synthetic_auto/overflow.rs b/src/test/rustdoc/synthetic_auto/overflow.rs new file mode 100644 index 0000000000000..546b3e07793e1 --- /dev/null +++ b/src/test/rustdoc/synthetic_auto/overflow.rs @@ -0,0 +1,35 @@ +// Tests that we don't fail with an overflow error for certain +// strange types +// See https://github.com/rust-lang/rust/pull/72936#issuecomment-643676915 + +pub trait Interner { + type InternedType; +} + +struct RustInterner<'tcx> { + foo: &'tcx () +} + +impl<'tcx> Interner for RustInterner<'tcx> { + type InternedType = Box>; +} + +enum TyData { + FnDef(I::InternedType) +} + +struct VariableKind(I::InternedType); + +// @has overflow/struct.BoundVarsCollector.html +// @has - '//code' "impl<'tcx> Send for BoundVarsCollector<'tcx>" +pub struct BoundVarsCollector<'tcx> { + val: VariableKind> +} + +fn is_send() {} + +struct MyInterner<'tcx> { + val: &'tcx () +} + +fn main() {} 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 8917693d45eeb..0000000000000 --- a/src/test/ui-fulldeps/auxiliary/linkage-visibility.rs +++ /dev/null @@ -1,37 +0,0 @@ -// ignore-musl - dlsym doesn't see symbols without "-C link-arg=-Wl,--export-dynamic" - -#![feature(rustc_private)] - -// We're testing linkage visibility; the compiler warns us, but we want to -// do the runtime check that these functions aren't exported. -#![allow(private_no_mangle_fns)] - -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/compiler-calls.rs b/src/test/ui-fulldeps/compiler-calls.rs index bd9113c7079ea..e97dcab6ae560 100644 --- a/src/test/ui-fulldeps/compiler-calls.rs +++ b/src/test/ui-fulldeps/compiler-calls.rs @@ -3,6 +3,7 @@ // ignore-cross-compile // ignore-stage1 +// ignore-remote #![feature(rustc_private)] diff --git a/src/test/ui-fulldeps/derive-no-std-not-supported.rs b/src/test/ui-fulldeps/derive-no-std-not-supported.rs index d09b1922a7ba5..1299d82d9c4b9 100644 --- a/src/test/ui-fulldeps/derive-no-std-not-supported.rs +++ b/src/test/ui-fulldeps/derive-no-std-not-supported.rs @@ -4,7 +4,7 @@ #![feature(rustc_private)] #![no_std] -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; #[derive(RustcEncodable)] struct Bar { diff --git a/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs b/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs index 29c1b8fb0da97..2b349ae9556c3 100644 --- a/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs +++ b/src/test/ui-fulldeps/deriving-encodable-decodable-box.rs @@ -5,7 +5,7 @@ #![feature(box_syntax)] #![feature(rustc_private)] -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; use rustc_serialize::{Encodable, Decodable}; use rustc_serialize::json; diff --git a/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs b/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs index fe608890bbd4d..c2aecbdc16793 100644 --- a/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs +++ b/src/test/ui-fulldeps/deriving-encodable-decodable-cell-refcell.rs @@ -7,7 +7,7 @@ #![feature(rustc_private)] -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; use std::cell::{Cell, RefCell}; use rustc_serialize::{Encodable, Decodable}; diff --git a/src/test/ui-fulldeps/deriving-global.rs b/src/test/ui-fulldeps/deriving-global.rs index d7cc98fed2595..5ba34a7af6bf5 100644 --- a/src/test/ui-fulldeps/deriving-global.rs +++ b/src/test/ui-fulldeps/deriving-global.rs @@ -2,7 +2,7 @@ #![feature(rustc_private)] -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; mod submod { // if any of these are implemented without global calls for any diff --git a/src/test/ui-fulldeps/deriving-hygiene.rs b/src/test/ui-fulldeps/deriving-hygiene.rs index b1bdfaceb887d..85ef217e7671e 100644 --- a/src/test/ui-fulldeps/deriving-hygiene.rs +++ b/src/test/ui-fulldeps/deriving-hygiene.rs @@ -2,7 +2,7 @@ #![allow(non_upper_case_globals)] #![feature(rustc_private)] -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; pub const other: u8 = 1; pub const f: u8 = 1; diff --git a/src/test/ui-fulldeps/dropck-tarena-cycle-checked.rs b/src/test/ui-fulldeps/dropck-tarena-cycle-checked.rs index a7e8131342f55..fabcd727482dc 100644 --- a/src/test/ui-fulldeps/dropck-tarena-cycle-checked.rs +++ b/src/test/ui-fulldeps/dropck-tarena-cycle-checked.rs @@ -8,9 +8,9 @@ #![feature(rustc_private)] -extern crate arena; +extern crate rustc_arena; -use arena::TypedArena; +use rustc_arena::TypedArena; use std::cell::Cell; use id::Id; diff --git a/src/test/ui-fulldeps/dropck-tarena-cycle-checked.stderr b/src/test/ui-fulldeps/dropck-tarena-cycle-checked.stderr index 7009c0bba6981..98c1a22bd1de0 100644 --- a/src/test/ui-fulldeps/dropck-tarena-cycle-checked.stderr +++ b/src/test/ui-fulldeps/dropck-tarena-cycle-checked.stderr @@ -7,7 +7,7 @@ LL | } | - | | | `arena` dropped here while still borrowed - | borrow might be used here, when `arena` is dropped and runs the `Drop` code for type `arena::TypedArena` + | borrow might be used here, when `arena` is dropped and runs the `Drop` code for type `rustc_arena::TypedArena` error: aborting due to previous error diff --git a/src/test/ui-fulldeps/dropck-tarena-unsound-drop.rs b/src/test/ui-fulldeps/dropck-tarena-unsound-drop.rs index e454f44d1af6b..86485a9887fbe 100644 --- a/src/test/ui-fulldeps/dropck-tarena-unsound-drop.rs +++ b/src/test/ui-fulldeps/dropck-tarena-unsound-drop.rs @@ -11,9 +11,9 @@ #![feature(rustc_private)] -extern crate arena; +extern crate rustc_arena; -use arena::TypedArena; +use rustc_arena::TypedArena; trait HasId { fn count(&self) -> usize; } diff --git a/src/test/ui-fulldeps/dropck-tarena-unsound-drop.stderr b/src/test/ui-fulldeps/dropck-tarena-unsound-drop.stderr index 319848b989634..22c7487e8f516 100644 --- a/src/test/ui-fulldeps/dropck-tarena-unsound-drop.stderr +++ b/src/test/ui-fulldeps/dropck-tarena-unsound-drop.stderr @@ -7,7 +7,7 @@ LL | } | - | | | `arena` dropped here while still borrowed - | borrow might be used here, when `arena` is dropped and runs the `Drop` code for type `arena::TypedArena` + | borrow might be used here, when `arena` is dropped and runs the `Drop` code for type `rustc_arena::TypedArena` error: aborting due to previous error diff --git a/src/test/ui-fulldeps/dropck_tarena_sound_drop.rs b/src/test/ui-fulldeps/dropck_tarena_sound_drop.rs index cf188d9efa3b2..c5b9efee8e730 100644 --- a/src/test/ui-fulldeps/dropck_tarena_sound_drop.rs +++ b/src/test/ui-fulldeps/dropck_tarena_sound_drop.rs @@ -12,9 +12,9 @@ #![allow(unstable)] #![feature(rustc_private)] -extern crate arena; +extern crate rustc_arena; -use arena::TypedArena; +use rustc_arena::TypedArena; trait HasId { fn count(&self) -> usize; } diff --git a/src/test/ui-fulldeps/empty-struct-braces-derive.rs b/src/test/ui-fulldeps/empty-struct-braces-derive.rs index 68b407423aa68..fc85765eea4a5 100644 --- a/src/test/ui-fulldeps/empty-struct-braces-derive.rs +++ b/src/test/ui-fulldeps/empty-struct-braces-derive.rs @@ -3,7 +3,7 @@ #![feature(rustc_private)] -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug, RustcEncodable, RustcDecodable)] diff --git a/src/test/ui-fulldeps/extern-mod-syntax.rs b/src/test/ui-fulldeps/extern-mod-syntax.rs index 258ab0dbe9562..a90ab7ac4388e 100644 --- a/src/test/ui-fulldeps/extern-mod-syntax.rs +++ b/src/test/ui-fulldeps/extern-mod-syntax.rs @@ -3,8 +3,8 @@ #![allow(unused_imports)] #![feature(rustc_private)] -extern crate serialize; -use serialize::json::Object; +extern crate rustc_serialize; +use rustc_serialize::json::Object; pub fn main() { println!("Hello world!"); 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..973294e985f7a 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,13 +32,12 @@ 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::` TyKind::Placeholder(..) => (), //~ ERROR usage of `ty::TyKind::` TyKind::Infer(..) => (), //~ ERROR usage of `ty::TyKind::` - TyKind::Error => (), //~ ERROR usage of `ty::TyKind::` + TyKind::Error(_) => (), //~ ERROR usage of `ty::TyKind::` } if let ty::Int(int_ty) = kind {} 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..d6e4c85c190d5 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 => (), +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-11881.rs b/src/test/ui-fulldeps/issue-11881.rs index bd046a6cdee5f..7b0abba4d16c8 100644 --- a/src/test/ui-fulldeps/issue-11881.rs +++ b/src/test/ui-fulldeps/issue-11881.rs @@ -6,7 +6,7 @@ #![feature(rustc_private)] -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; use std::io::Cursor; use std::io::prelude::*; diff --git a/src/test/ui-fulldeps/issue-14021.rs b/src/test/ui-fulldeps/issue-14021.rs index 49fa4492fa1ee..1898b12c70385 100644 --- a/src/test/ui-fulldeps/issue-14021.rs +++ b/src/test/ui-fulldeps/issue-14021.rs @@ -4,11 +4,10 @@ #![allow(unused_imports)] #![feature(rustc_private)] -extern crate serialize; -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; -use serialize::{Encodable, Decodable}; -use serialize::json; +use rustc_serialize::{Encodable, Decodable}; +use rustc_serialize::json; #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)] struct UnitLikeStruct; 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-15924.rs b/src/test/ui-fulldeps/issue-15924.rs index ec33de12ebbee..e3d4b0eb4460d 100644 --- a/src/test/ui-fulldeps/issue-15924.rs +++ b/src/test/ui-fulldeps/issue-15924.rs @@ -6,11 +6,11 @@ #![feature(rustc_private)] -extern crate serialize; +extern crate rustc_serialize; use std::fmt; -use serialize::{Encoder, Encodable}; -use serialize::json; +use rustc_serialize::{Encoder, Encodable}; +use rustc_serialize::json; struct Foo { v: T, diff --git a/src/test/ui-fulldeps/issue-24972.rs b/src/test/ui-fulldeps/issue-24972.rs index 0d354aac13787..51e134fbf8871 100644 --- a/src/test/ui-fulldeps/issue-24972.rs +++ b/src/test/ui-fulldeps/issue-24972.rs @@ -3,9 +3,9 @@ #![allow(dead_code)] #![feature(rustc_private)] -extern crate serialize; +extern crate rustc_serialize; -use serialize::{Encodable, Decodable}; +use rustc_serialize::{Encodable, Decodable}; use std::fmt::Display; pub trait Entity : Decodable + Encodable + Sized { diff --git a/src/test/ui-fulldeps/issue-2804.rs b/src/test/ui-fulldeps/issue-2804.rs index a5345bbcd1465..3d5922d155f26 100644 --- a/src/test/ui-fulldeps/issue-2804.rs +++ b/src/test/ui-fulldeps/issue-2804.rs @@ -4,10 +4,10 @@ #![allow(dead_code)] #![feature(rustc_private)] -extern crate serialize; +extern crate rustc_serialize; use std::collections::HashMap; -use serialize::json::{self, Json}; +use rustc_serialize::json::{self, Json}; use std::option; enum object { 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/issue-4016.rs b/src/test/ui-fulldeps/issue-4016.rs index fb84acbe64525..96157c2f426c0 100644 --- a/src/test/ui-fulldeps/issue-4016.rs +++ b/src/test/ui-fulldeps/issue-4016.rs @@ -4,9 +4,9 @@ #![feature(rustc_private)] -extern crate serialize; +extern crate rustc_serialize; -use serialize::{json, Decodable}; +use rustc_serialize::{json, Decodable}; trait JD : Decodable {} diff --git a/src/test/ui-fulldeps/issue-4036.rs b/src/test/ui-fulldeps/issue-4036.rs index 9c9d39142681a..05a7a30140a44 100644 --- a/src/test/ui-fulldeps/issue-4036.rs +++ b/src/test/ui-fulldeps/issue-4036.rs @@ -6,9 +6,9 @@ #![feature(rustc_private)] -extern crate serialize; +extern crate rustc_serialize; -use serialize::{json, Decodable}; +use rustc_serialize::{json, Decodable}; pub fn main() { let json = json::from_str("[1]").unwrap(); 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-pass-macros.rs b/src/test/ui-fulldeps/lint-pass-macros.rs new file mode 100644 index 0000000000000..b3c2a542792f0 --- /dev/null +++ b/src/test/ui-fulldeps/lint-pass-macros.rs @@ -0,0 +1,26 @@ +// compile-flags: -Z unstable-options +// check-pass + +#![feature(rustc_private)] + +extern crate rustc_session; + +use rustc_session::lint::{LintArray, LintPass}; +use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; + +declare_lint! { + pub TEST_LINT, + Allow, + "test" +} + +struct Foo; + +struct Bar<'a>(&'a u32); + +impl_lint_pass!(Foo => [TEST_LINT]); +impl_lint_pass!(Bar<'_> => [TEST_LINT]); + +declare_lint_pass!(Baz => [TEST_LINT]); + +fn main() {} 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/mod_dir_path_canonicalized.rs b/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs index 1046355a3433f..ff7bbafe7c212 100644 --- a/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs +++ b/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs @@ -1,6 +1,7 @@ // run-pass // Testing that a librustc_ast can parse modules with canonicalized base path // ignore-cross-compile +// ignore-remote #![feature(rustc_private)] @@ -28,6 +29,6 @@ fn parse() { let path = Path::new(file!()); let path = path.canonicalize().unwrap(); - let mut parser = new_parser_from_file(&parse_session, &path); + let mut parser = new_parser_from_file(&parse_session, &path, None); let _ = parser.parse_crate_mod(); } 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..6da26e6cfbe41 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; @@ -55,6 +56,7 @@ fn expr(kind: ExprKind) -> P { kind, span: DUMMY_SP, attrs: ThinVec::new(), + tokens: None }) } @@ -82,9 +84,9 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { 2 => { let seg = PathSegment::from_ident(Ident::from_str("x")); iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( - seg.clone(), vec![e, make_x()]))); + seg.clone(), vec![e, make_x()], DUMMY_SP))); iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( - seg.clone(), vec![make_x(), e]))); + seg.clone(), vec![make_x(), e], DUMMY_SP))); }, 3..=8 => { let op = Spanned { @@ -199,6 +201,7 @@ impl MutVisitor for AddParens { kind: ExprKind::Paren(e), span: DUMMY_SP, attrs: ThinVec::new(), + tokens: None }) }); } diff --git a/src/test/ui-fulldeps/regions-mock-tcx.rs b/src/test/ui-fulldeps/regions-mock-tcx.rs index 524c94a8555ec..30e6272324068 100644 --- a/src/test/ui-fulldeps/regions-mock-tcx.rs +++ b/src/test/ui-fulldeps/regions-mock-tcx.rs @@ -11,12 +11,12 @@ #![feature(rustc_private, libc)] -extern crate arena; +extern crate rustc_arena; extern crate libc; use TypeStructure::{TypeInt, TypeFunction}; use AstKind::{ExprInt, ExprVar, ExprLambda}; -use arena::TypedArena; +use rustc_arena::TypedArena; use std::collections::HashMap; use std::mem; diff --git a/src/test/ui-fulldeps/rustc_encodable_hygiene.rs b/src/test/ui-fulldeps/rustc_encodable_hygiene.rs index 42a6153465c29..b49135cb60b29 100644 --- a/src/test/ui-fulldeps/rustc_encodable_hygiene.rs +++ b/src/test/ui-fulldeps/rustc_encodable_hygiene.rs @@ -4,7 +4,7 @@ #[allow(dead_code)] -extern crate serialize as rustc_serialize; +extern crate rustc_serialize; #[derive(RustcDecodable, RustcEncodable,Debug)] struct A { 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..32864dba4587e 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, @@ -38,6 +38,7 @@ pub struct LargeStruct(i64, i64, i64, i64, i64, i64, i64, i64); #[cfg(target_arch = "x86_64")] #[inline(never)] +#[allow(improper_ctypes_definitions)] pub extern "sysv64" fn large_struct_by_val(mut foo: LargeStruct) -> LargeStruct { foo.0 *= 1; foo.1 *= 2; @@ -54,34 +55,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/align-with-extern-c-fn.rs b/src/test/ui/align-with-extern-c-fn.rs index 09abe4fbf7e09..f77f40998de01 100644 --- a/src/test/ui/align-with-extern-c-fn.rs +++ b/src/test/ui/align-with-extern-c-fn.rs @@ -10,6 +10,7 @@ #[repr(align(16))] pub struct A(i64); +#[allow(improper_ctypes_definitions)] pub extern "C" fn foo(x: A) {} fn main() { 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/annotate-snippet/auxiliary/multispan.rs b/src/test/ui/annotate-snippet/auxiliary/multispan.rs new file mode 100644 index 0000000000000..c05d15643dbb1 --- /dev/null +++ b/src/test/ui/annotate-snippet/auxiliary/multispan.rs @@ -0,0 +1,37 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(proc_macro_diagnostic, proc_macro_span, proc_macro_def_site)] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, Span, Diagnostic}; + +fn parse(input: TokenStream) -> Result<(), Diagnostic> { + let mut hi_spans = vec![]; + for tree in input { + if let TokenTree::Ident(ref ident) = tree { + if ident.to_string() == "hi" { + hi_spans.push(ident.span()); + } + } + } + + if !hi_spans.is_empty() { + return Err(Span::def_site() + .error("hello to you, too!") + .span_note(hi_spans, "found these 'hi's")); + } + + Ok(()) +} + +#[proc_macro] +pub fn hello(input: TokenStream) -> TokenStream { + if let Err(diag) = parse(input) { + diag.emit(); + } + + TokenStream::new() +} diff --git a/src/test/ui/annotate-snippet/missing-type.stderr b/src/test/ui/annotate-snippet/missing-type.stderr index 806acf0bed5d1..c16f022a77fa3 100644 --- a/src/test/ui/annotate-snippet/missing-type.stderr +++ b/src/test/ui/annotate-snippet/missing-type.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `Iter` in this scope - --> $DIR/missing-type.rs:4:11 + --> $DIR/missing-type.rs:4:12 | LL | let x: Iter; | ^^^^ not found in this scope diff --git a/src/test/ui/annotate-snippet/multispan.rs b/src/test/ui/annotate-snippet/multispan.rs new file mode 100644 index 0000000000000..325252d7716f6 --- /dev/null +++ b/src/test/ui/annotate-snippet/multispan.rs @@ -0,0 +1,28 @@ +// aux-build:multispan.rs +// compile-flags: --error-format human-annotate-rs + +#![feature(proc_macro_hygiene)] + +extern crate multispan; + +use multispan::hello; + +fn main() { + // This one emits no error. + hello!(); + + // Exactly one 'hi'. + hello!(hi); //~ ERROR hello to you, too! + + // Now two, back to back. + hello!(hi hi); //~ ERROR hello to you, too! + + // Now three, back to back. + hello!(hi hi hi); //~ ERROR hello to you, too! + + // Now several, with spacing. + hello!(hi hey hi yo hi beep beep hi hi); //~ ERROR hello to you, too! + hello!(hi there, hi how are you? hi... hi.); //~ ERROR hello to you, too! + hello!(whoah. hi di hi di ho); //~ ERROR hello to you, too! + hello!(hi good hi and good bye); //~ ERROR hello to you, too! +} diff --git a/src/test/ui/annotate-snippet/multispan.stderr b/src/test/ui/annotate-snippet/multispan.stderr new file mode 100644 index 0000000000000..4ac31e32ba7cf --- /dev/null +++ b/src/test/ui/annotate-snippet/multispan.stderr @@ -0,0 +1,42 @@ +error: hello to you, too! + --> $DIR/multispan.rs:15:5 + | +LL | hello!(hi); + | ^^^^^^^^^^^ + | +error: hello to you, too! + --> $DIR/multispan.rs:18:5 + | +LL | hello!(hi hi); + | ^^^^^^^^^^^^^^ + | +error: hello to you, too! + --> $DIR/multispan.rs:21:5 + | +LL | hello!(hi hi hi); + | ^^^^^^^^^^^^^^^^^ + | +error: hello to you, too! + --> $DIR/multispan.rs:24:5 + | +LL | hello!(hi hey hi yo hi beep beep hi hi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +error: hello to you, too! + --> $DIR/multispan.rs:25:5 + | +LL | hello!(hi there, hi how are you? hi... hi.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +error: hello to you, too! + --> $DIR/multispan.rs:26:5 + | +LL | hello!(whoah. hi di hi di ho); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +error: hello to you, too! + --> $DIR/multispan.rs:27:5 + | +LL | hello!(hi good hi and good bye); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | 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..21ce8c6236d06 --- /dev/null +++ b/src/test/ui/asm/bad-template.rs @@ -0,0 +1,28 @@ +// 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 + asm!("", in(reg) 0, in(reg) 1); + //~^ ERROR multiple unused asm arguments + } +} diff --git a/src/test/ui/asm/bad-template.stderr b/src/test/ui/asm/bad-template.stderr new file mode 100644 index 0000000000000..1aea7467ed0a7 --- /dev/null +++ b/src/test/ui/asm/bad-template.stderr @@ -0,0 +1,102 @@ +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 + | + = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"` + +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 + | + = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {a} */"` + +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 + | + = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {a} */"` + +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: multiple unused asm arguments + --> $DIR/bad-template.rs:25:18 + | +LL | asm!("", in(reg) 0, in(reg) 1); + | ^^^^^^^^^ ^^^^^^^^^ argument never used + | | + | argument never used + | + = help: if these arguments are intentionally unused, consider using them in an asm comment: `"/* {0} {1} */"` + +error: aborting due to 11 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/duplicate-options.fixed b/src/test/ui/asm/duplicate-options.fixed new file mode 100644 index 0000000000000..f4672a50fd0f4 --- /dev/null +++ b/src/test/ui/asm/duplicate-options.fixed @@ -0,0 +1,26 @@ +// only-x86_64 +// run-rustfix + +#![feature(asm)] + +fn main() { + unsafe { + asm!("", options(nomem, )); + //~^ ERROR the `nomem` option was already provided + asm!("", options(att_syntax, )); + //~^ ERROR the `att_syntax` option was already provided + asm!("", options(nostack, att_syntax), options()); + //~^ ERROR the `nostack` option was already provided + asm!("", options(nostack, ), options(), options()); + //~^ ERROR the `nostack` option was already provided + //~| ERROR the `nostack` option was already provided + //~| ERROR the `nostack` option was already provided + asm!( + "", + options(nomem, noreturn), + options(att_syntax, ), //~ ERROR the `noreturn` option was already provided + options( nostack), //~ ERROR the `nomem` option was already provided + options(), //~ ERROR the `noreturn` option was already provided + ); + } +} diff --git a/src/test/ui/asm/duplicate-options.rs b/src/test/ui/asm/duplicate-options.rs new file mode 100644 index 0000000000000..80292d7521a9a --- /dev/null +++ b/src/test/ui/asm/duplicate-options.rs @@ -0,0 +1,26 @@ +// only-x86_64 +// run-rustfix + +#![feature(asm)] + +fn main() { + unsafe { + asm!("", options(nomem, nomem)); + //~^ ERROR the `nomem` option was already provided + asm!("", options(att_syntax, att_syntax)); + //~^ ERROR the `att_syntax` option was already provided + asm!("", options(nostack, att_syntax), options(nostack)); + //~^ ERROR the `nostack` option was already provided + asm!("", options(nostack, nostack), options(nostack), options(nostack)); + //~^ ERROR the `nostack` option was already provided + //~| ERROR the `nostack` option was already provided + //~| ERROR the `nostack` option was already provided + asm!( + "", + options(nomem, noreturn), + options(att_syntax, noreturn), //~ ERROR the `noreturn` option was already provided + options(nomem, nostack), //~ ERROR the `nomem` option was already provided + options(noreturn), //~ ERROR the `noreturn` option was already provided + ); + } +} diff --git a/src/test/ui/asm/duplicate-options.stderr b/src/test/ui/asm/duplicate-options.stderr new file mode 100644 index 0000000000000..cd8d743e031a5 --- /dev/null +++ b/src/test/ui/asm/duplicate-options.stderr @@ -0,0 +1,56 @@ +error: the `nomem` option was already provided + --> $DIR/duplicate-options.rs:8:33 + | +LL | asm!("", options(nomem, nomem)); + | ^^^^^ this option was already provided + +error: the `att_syntax` option was already provided + --> $DIR/duplicate-options.rs:10:38 + | +LL | asm!("", options(att_syntax, att_syntax)); + | ^^^^^^^^^^ this option was already provided + +error: the `nostack` option was already provided + --> $DIR/duplicate-options.rs:12:56 + | +LL | asm!("", options(nostack, att_syntax), options(nostack)); + | ^^^^^^^ this option was already provided + +error: the `nostack` option was already provided + --> $DIR/duplicate-options.rs:14:35 + | +LL | asm!("", options(nostack, nostack), options(nostack), options(nostack)); + | ^^^^^^^ this option was already provided + +error: the `nostack` option was already provided + --> $DIR/duplicate-options.rs:14:53 + | +LL | asm!("", options(nostack, nostack), options(nostack), options(nostack)); + | ^^^^^^^ this option was already provided + +error: the `nostack` option was already provided + --> $DIR/duplicate-options.rs:14:71 + | +LL | asm!("", options(nostack, nostack), options(nostack), options(nostack)); + | ^^^^^^^ this option was already provided + +error: the `noreturn` option was already provided + --> $DIR/duplicate-options.rs:21:33 + | +LL | options(att_syntax, noreturn), + | ^^^^^^^^ this option was already provided + +error: the `nomem` option was already provided + --> $DIR/duplicate-options.rs:22:21 + | +LL | options(nomem, nostack), + | ^^^^^ this option was already provided + +error: the `noreturn` option was already provided + --> $DIR/duplicate-options.rs:23:21 + | +LL | options(noreturn), + | ^^^^^^^^ this option was already provided + +error: aborting due to 9 previous errors + 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/issue-72570.rs b/src/test/ui/asm/issue-72570.rs new file mode 100644 index 0000000000000..678534657cb31 --- /dev/null +++ b/src/test/ui/asm/issue-72570.rs @@ -0,0 +1,12 @@ +// compile-flags: -Zsave-analysis +// only-x86_64 +// Also test for #72960 + +#![feature(asm)] + +fn main() { + unsafe { + asm!("", in("invalid") "".len()); + //~^ ERROR: invalid register `invalid`: unknown register + } +} diff --git a/src/test/ui/asm/issue-72570.stderr b/src/test/ui/asm/issue-72570.stderr new file mode 100644 index 0000000000000..fa5792688b252 --- /dev/null +++ b/src/test/ui/asm/issue-72570.stderr @@ -0,0 +1,8 @@ +error: invalid register `invalid`: unknown register + --> $DIR/issue-72570.rs:9:18 + | +LL | asm!("", in("invalid") "".len()); + | ^^^^^^^^^^^^^^^^^^^^^^ + +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..538a3fde8fdeb --- /dev/null +++ b/src/test/ui/asm/parse-error.rs @@ -0,0 +1,59 @@ +// 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 operand, options, or additional template string + asm!("{}", in foo); + //~^ ERROR expected `(`, found `foo` + asm!("{}", in(reg foo)); + //~^ ERROR expected `)`, found `foo` + asm!("{}", in(reg)); + //~^ ERROR expected expression, found end of macro arguments + asm!("{}", inout(=) foo => bar); + //~^ ERROR expected register class or explicit register + asm!("{}", inout(reg) foo =>); + //~^ ERROR expected expression, found end of macro arguments + asm!("{}", in(reg) foo => bar); + //~^ ERROR expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` + asm!("{}", sym foo + bar); + //~^ ERROR 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(), 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 + asm!("", options(), ""); + //~^ ERROR expected one of + asm!("{}", in(reg) foo, "{}", out(reg) foo); + //~^ ERROR expected one of + asm!(format!("{{{}}}", 0), in(reg) foo); + //~^ ERROR asm template must be a string literal + asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); + //~^ ERROR asm template must be a string literal + } +} diff --git a/src/test/ui/asm/parse-error.stderr b/src/test/ui/asm/parse-error.stderr new file mode 100644 index 0000000000000..dfbfc0abe3472 --- /dev/null +++ b/src/test/ui/asm/parse-error.stderr @@ -0,0 +1,168 @@ +error: requires at least a template string argument + --> $DIR/parse-error.rs:9:9 + | +LL | asm!(); + | ^^^^^^^ + +error: asm template must be a string literal + --> $DIR/parse-error.rs:11:14 + | +LL | asm!(foo); + | ^^^ + +error: expected token: `,` + --> $DIR/parse-error.rs:13:19 + | +LL | asm!("{}" foo); + | ^^^ expected `,` + +error: expected operand, options, or additional template string + --> $DIR/parse-error.rs:15:20 + | +LL | asm!("{}", foo); + | ^^^ expected operand, options, or additional template string + +error: expected `(`, found `foo` + --> $DIR/parse-error.rs:17:23 + | +LL | asm!("{}", in foo); + | ^^^ expected `(` + +error: expected `)`, found `foo` + --> $DIR/parse-error.rs:19:27 + | +LL | asm!("{}", in(reg foo)); + | ^^^ expected `)` + +error: expected expression, found end of macro arguments + --> $DIR/parse-error.rs:21:27 + | +LL | asm!("{}", in(reg)); + | ^ expected expression + +error: expected register class or explicit register + --> $DIR/parse-error.rs:23:26 + | +LL | asm!("{}", inout(=) foo => bar); + | ^ + +error: expected expression, found end of macro arguments + --> $DIR/parse-error.rs:25:37 + | +LL | asm!("{}", inout(reg) foo =>); + | ^ expected expression + +error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` + --> $DIR/parse-error.rs:27:32 + | +LL | asm!("{}", in(reg) foo => bar); + | ^^ expected one of 7 possible tokens + +error: 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: arguments are not allowed after options + --> $DIR/parse-error.rs:37:31 + | +LL | asm!("{}", options(), const foo); + | --------- ^^^^^^^^^ argument + | | + | previous options + +error: duplicate argument named `a` + --> $DIR/parse-error.rs:39:36 + | +LL | asm!("{a}", a = const foo, a = const bar); + | ------------- ^^^^^^^^^^^^^ duplicate argument + | | + | previously here + +error: argument never used + --> $DIR/parse-error.rs:39:36 + | +LL | asm!("{a}", a = const foo, a = const bar); + | ^^^^^^^^^^^^^ argument never used + | + = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"` + +error: explicit register arguments cannot have names + --> $DIR/parse-error.rs:42:18 + | +LL | asm!("", a = in("eax") foo); + | ^^^^^^^^^^^^^^^^^ + +error: named arguments cannot follow explicit register arguments + --> $DIR/parse-error.rs:44: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:46: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:48:36 + | +LL | asm!("{1}", in("eax") foo, const bar); + | ------------- ^^^^^^^^^ positional argument + | | + | explicit register argument + +error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `""` + --> $DIR/parse-error.rs:50:29 + | +LL | asm!("", options(), ""); + | ^^ expected one of 8 possible tokens + +error: expected one of `const`, `in`, `inlateout`, `inout`, `lateout`, `options`, `out`, or `sym`, found `"{}"` + --> $DIR/parse-error.rs:52:33 + | +LL | asm!("{}", in(reg) foo, "{}", out(reg) foo); + | ^^^^ expected one of 8 possible tokens + +error: asm template must be a string literal + --> $DIR/parse-error.rs:54:14 + | +LL | asm!(format!("{{{}}}", 0), in(reg) foo); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: asm template must be a string literal + --> $DIR/parse-error.rs:56:21 + | +LL | asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 25 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..01d8fd34b68a8 --- /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 the legacy LLVM-style asm! syntax is no longer supported + llvm_asm!("" : "=r" (y)); + //~^ ERROR the legacy LLVM-style 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..e25895b723049 --- /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 the legacy LLVM-style asm! syntax is no longer supported + asm!("" : "=r" (y)); + //~^ ERROR the legacy LLVM-style 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..334499c6fd897 --- /dev/null +++ b/src/test/ui/asm/rustfix-asm.stderr @@ -0,0 +1,24 @@ +error: the legacy LLVM-style asm! syntax is no longer supported + --> $DIR/rustfix-asm.rs:10:9 + | +LL | asm!("" :: "r" (x)); + | ----^^^^^^^^^^^^^^^^ + | | + | help: replace with: `llvm_asm!` + | + = note: consider migrating to the new asm! syntax specified in RFC 2873 + = note: alternatively, switch to llvm_asm! to keep your code working as it is + +error: the legacy LLVM-style asm! syntax is no longer supported + --> $DIR/rustfix-asm.rs:12:9 + | +LL | asm!("" : "=r" (y)); + | ----^^^^^^^^^^^^^^^^ + | | + | help: replace with: `llvm_asm!` + | + = note: consider migrating to the new asm! syntax specified in RFC 2873 + = note: alternatively, switch to llvm_asm! to keep your code working as it is + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/asm/srcloc.rs b/src/test/ui/asm/srcloc.rs new file mode 100644 index 0000000000000..1477e3dd5665c --- /dev/null +++ b/src/test/ui/asm/srcloc.rs @@ -0,0 +1,124 @@ +// no-system-llvm +// only-x86_64 +// build-fail + +#![feature(asm)] + +// Checks that inline asm errors are mapped to the correct line in the source code. + +fn main() { + unsafe { + asm!("invalid_instruction"); + //~^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(" + invalid_instruction + "); + //~^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(r#" + invalid_instruction + "#); + //~^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(" + mov eax, eax + invalid_instruction + mov eax, eax + "); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(r#" + mov eax, eax + invalid_instruction + mov eax, eax + "#); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!(concat!("invalid", "_", "instruction")); + //~^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!("movaps %xmm3, (%esi, 2)", options(att_syntax)); + //~^ WARN: scale factor without index register is ignored + + asm!( + "invalid_instruction", + ); + //~^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!( + "mov eax, eax", + "invalid_instruction", + "mov eax, eax", + ); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!( + "mov eax, eax\n", + "invalid_instruction", + "mov eax, eax", + ); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!( + "mov eax, eax", + concat!("invalid", "_", "instruction"), + "mov eax, eax", + ); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + asm!( + concat!("mov eax", ", ", "eax"), + concat!("invalid", "_", "instruction"), + concat!("mov eax", ", ", "eax"), + ); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction' + + // Make sure template strings get separated + asm!( + "invalid_instruction1", + "invalid_instruction2", + ); + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1' + //~^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2' + + asm!( + concat!( + "invalid", "_", "instruction1", "\n", + "invalid", "_", "instruction2", + ), + ); + //~^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1' + //~^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2' + + asm!( + concat!( + "invalid", "_", "instruction1", "\n", + "invalid", "_", "instruction2", + ), + concat!( + "invalid", "_", "instruction3", "\n", + "invalid", "_", "instruction4", + ), + ); + //~^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1' + //~^^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2' + //~^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction3' + //~^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction4' + + asm!( + concat!( + "invalid", "_", "instruction1", "\n", + "invalid", "_", "instruction2", "\n", + ), + concat!( + "invalid", "_", "instruction3", "\n", + "invalid", "_", "instruction4", "\n", + ), + ); + //~^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction1' + //~^^^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction2' + //~^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction3' + //~^^^^^^^^ ERROR: invalid instruction mnemonic 'invalid_instruction4' + } +} diff --git a/src/test/ui/asm/srcloc.stderr b/src/test/ui/asm/srcloc.stderr new file mode 100644 index 0000000000000..b62c8948289dd --- /dev/null +++ b/src/test/ui/asm/srcloc.stderr @@ -0,0 +1,290 @@ +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:11:15 + | +LL | asm!("invalid_instruction"); + | ^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:15:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> :3:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:20:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> :3:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:26:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> :4:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:33:13 + | +LL | invalid_instruction + | ^ + | +note: instantiated into assembly here + --> :4:13 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:38:14 + | +LL | asm!(concat!("invalid", "_", "instruction")); + | ^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +warning: scale factor without index register is ignored + --> $DIR/srcloc.rs:41:15 + | +LL | asm!("movaps %xmm3, (%esi, 2)", options(att_syntax)); + | ^ + | +note: instantiated into assembly here + --> :1:23 + | +LL | movaps %xmm3, (%esi, 2) + | ^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:45:14 + | +LL | "invalid_instruction", + | ^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:51:14 + | +LL | "invalid_instruction", + | ^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:58:14 + | +LL | "invalid_instruction", + | ^ + | +note: instantiated into assembly here + --> :4:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:65:13 + | +LL | concat!("invalid", "_", "instruction"), + | ^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction' + --> $DIR/srcloc.rs:72:13 + | +LL | concat!("invalid", "_", "instruction"), + | ^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction + | ^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction1' + --> $DIR/srcloc.rs:79:14 + | +LL | "invalid_instruction1", + | ^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction1 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction2' + --> $DIR/srcloc.rs:80:14 + | +LL | "invalid_instruction2", + | ^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction2 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction1' + --> $DIR/srcloc.rs:86:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction1 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction2' + --> $DIR/srcloc.rs:86:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction2 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction1' + --> $DIR/srcloc.rs:95:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction1 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction2' + --> $DIR/srcloc.rs:95:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction2 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction3' + --> $DIR/srcloc.rs:99:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :4:1 + | +LL | invalid_instruction3 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction4' + --> $DIR/srcloc.rs:99:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :5:1 + | +LL | invalid_instruction4 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction1' + --> $DIR/srcloc.rs:110:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :2:2 + | +LL | invalid_instruction1 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction2' + --> $DIR/srcloc.rs:110:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :3:1 + | +LL | invalid_instruction2 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction3' + --> $DIR/srcloc.rs:114:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :5:1 + | +LL | invalid_instruction3 + | ^^^^^^^^^^^^^^^^^^^^ + +error: invalid instruction mnemonic 'invalid_instruction4' + --> $DIR/srcloc.rs:114:13 + | +LL | concat!( + | ^ + | +note: instantiated into assembly here + --> :6:1 + | +LL | invalid_instruction4 + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 23 previous errors; 1 warning emitted + diff --git a/src/test/ui/asm/sym.rs b/src/test/ui/asm/sym.rs new file mode 100644 index 0000000000000..8cff16aa75f69 --- /dev/null +++ b/src/test/ui/asm/sym.rs @@ -0,0 +1,80 @@ +// no-system-llvm +// only-x86_64 +// only-linux +// run-pass + +#![feature(asm, track_caller, thread_local)] + +extern "C" fn f1() -> i32 { + 111 +} + +// The compiler will generate a shim to hide the caller location parameter. +#[track_caller] +fn f2() -> i32 { + 222 +} + +macro_rules! call { + ($func:path) => { + unsafe { + let result: i32; + asm!("call {}", sym $func, + out("rax") result, + out("rcx") _, out("rdx") _, out("rdi") _, 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") _, + ); + result + } + } +} + +macro_rules! static_addr { + ($s:expr) => { + unsafe { + let result: *const u32; + // LEA performs a RIP-relative address calculation and returns the address + asm!("lea {}, [rip + {}]", out(reg) result, sym $s); + result + } + } +} +macro_rules! static_tls_addr { + ($s:expr) => { + unsafe { + let result: *const u32; + asm!( + " + # Load TLS base address + mov {out}, qword ptr fs:[0] + # Calculate the address of sym in the TLS block. The @tpoff + # relocation gives the offset of the symbol from the start + # of the TLS block. + lea {out}, [{out} + {sym}@tpoff] + ", + out = out(reg) result, + sym = sym $s + ); + result + } + } +} + +static S1: u32 = 111; +#[thread_local] +static S2: u32 = 222; + +fn main() { + assert_eq!(call!(f1), 111); + assert_eq!(call!(f2), 222); + assert_eq!(static_addr!(S1), &S1 as *const u32); + assert_eq!(static_tls_addr!(S2), &S2 as *const u32); + std::thread::spawn(|| { + assert_eq!(static_addr!(S1), &S1 as *const u32); + assert_eq!(static_tls_addr!(S2), &S2 as *const u32); + }); +} 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/assoc-lang-items.rs b/src/test/ui/assoc-lang-items.rs new file mode 100644 index 0000000000000..23453d201a72f --- /dev/null +++ b/src/test/ui/assoc-lang-items.rs @@ -0,0 +1,21 @@ +#![feature(lang_items)] + +trait Foo { + #[lang = "dummy_lang_item_1"] //~ ERROR definition + fn foo() {} + + #[lang = "dummy_lang_item_2"] //~ ERROR definition + fn bar(); + + #[lang = "dummy_lang_item_3"] //~ ERROR definition + type MyType; +} + +struct Bar; + +impl Bar { + #[lang = "dummy_lang_item_4"] //~ ERROR definition + fn test() {} +} + +fn main() {} diff --git a/src/test/ui/assoc-lang-items.stderr b/src/test/ui/assoc-lang-items.stderr new file mode 100644 index 0000000000000..040792fb1cd5f --- /dev/null +++ b/src/test/ui/assoc-lang-items.stderr @@ -0,0 +1,27 @@ +error[E0522]: definition of an unknown language item: `dummy_lang_item_1` + --> $DIR/assoc-lang-items.rs:4:5 + | +LL | #[lang = "dummy_lang_item_1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_1` + +error[E0522]: definition of an unknown language item: `dummy_lang_item_2` + --> $DIR/assoc-lang-items.rs:7:5 + | +LL | #[lang = "dummy_lang_item_2"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_2` + +error[E0522]: definition of an unknown language item: `dummy_lang_item_3` + --> $DIR/assoc-lang-items.rs:10:5 + | +LL | #[lang = "dummy_lang_item_3"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_3` + +error[E0522]: definition of an unknown language item: `dummy_lang_item_4` + --> $DIR/assoc-lang-items.rs:17:5 + | +LL | #[lang = "dummy_lang_item_4"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_4` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0522`. diff --git a/src/test/ui/associated-const/associated-const-ambiguity-report.stderr b/src/test/ui/associated-const/associated-const-ambiguity-report.stderr index 92a8d19021a2c..c5e32afbbce40 100644 --- a/src/test/ui/associated-const/associated-const-ambiguity-report.stderr +++ b/src/test/ui/associated-const/associated-const-ambiguity-report.stderr @@ -1,8 +1,8 @@ error[E0034]: multiple applicable items in scope - --> $DIR/associated-const-ambiguity-report.rs:17:16 + --> $DIR/associated-const-ambiguity-report.rs:17:23 | LL | const X: i32 = ::ID; - | ^^^^^^^^^ multiple `ID` found + | ^^ multiple `ID` found | note: candidate #1 is defined in an impl of the trait `Foo` for the type `i32` --> $DIR/associated-const-ambiguity-report.rs:10:5 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-private-impl.stderr b/src/test/ui/associated-const/associated-const-private-impl.stderr index 7af55174b1905..1b9d7ac7e6c3f 100644 --- a/src/test/ui/associated-const/associated-const-private-impl.stderr +++ b/src/test/ui/associated-const/associated-const-private-impl.stderr @@ -1,8 +1,8 @@ error[E0624]: associated constant `ID` is private - --> $DIR/associated-const-private-impl.rs:13:19 + --> $DIR/associated-const-private-impl.rs:13:30 | LL | assert_eq!(1, bar1::Foo::ID); - | ^^^^^^^^^^^^^ + | ^^ private associated constant 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..1b4326ea56aaa --- /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 optimizing MIR for `::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..8efa56a9a2e63 --- /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 optimizing MIR for `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..78ce1a28a3fdc --- /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 optimizing MIR for `::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..8b5c5219430b6 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; @@ -108,18 +108,12 @@ type TAW3 where T: Iterator = T; type ETAI1> = impl Copy; //~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] //~| ERROR could not find defining uses -//~| ERROR could not find defining uses -//~| ERROR could not find defining uses type ETAI2> = impl Copy; //~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] //~| ERROR could not find defining uses -//~| ERROR could not find defining uses -//~| ERROR could not find defining uses type ETAI3> = impl Copy; //~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] //~| ERROR could not find defining uses -//~| ERROR could not find defining uses -//~| ERROR could not find defining uses type ETAI4 = impl Iterator; //~^ ERROR the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified [E0719] //~| ERROR could not find defining uses diff --git a/src/test/ui/associated-type-bounds/duplicate.stderr b/src/test/ui/associated-type-bounds/duplicate.stderr index 82b2d32d09d57..712211e60cbac 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 @@ -222,30 +223,6 @@ LL | fn FAPIT3(_: impl Iterator) {} | | | `Item` bound here first -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:62:42 - | -LL | fn FRPIT1() -> impl Iterator { iter::empty() } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:64:42 - | -LL | fn FRPIT2() -> impl Iterator { iter::empty() } - | ---------- ^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - -error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:66:45 - | -LL | fn FRPIT3() -> impl Iterator { iter::empty() } - | ------------- ^^^^^^^^^^^^^ re-bound here - | | - | `Item` bound here first - error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified --> $DIR/duplicate.rs:75:39 | @@ -366,12 +343,6 @@ LL | type TAW3 where T: Iterator = T; | | | `Item` bound here first -error: could not find defining uses - --> $DIR/duplicate.rs:108:1 - | -LL | type ETAI1> = impl Copy; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified --> $DIR/duplicate.rs:108:36 | @@ -380,14 +351,38 @@ LL | type ETAI1> = impl Copy; | | | `Item` bound here first +error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified + --> $DIR/duplicate.rs:62:42 + | +LL | fn FRPIT1() -> impl Iterator { iter::empty() } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified + --> $DIR/duplicate.rs:64:42 + | +LL | fn FRPIT2() -> impl Iterator { iter::empty() } + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified + --> $DIR/duplicate.rs:66:45 + | +LL | fn FRPIT3() -> impl Iterator { iter::empty() } + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + error: could not find defining uses - --> $DIR/duplicate.rs:113:1 + --> $DIR/duplicate.rs:108:51 | -LL | type ETAI2> = impl Copy; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type ETAI1> = impl Copy; + | ^^^^^^^^^ error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:113:36 + --> $DIR/duplicate.rs:111:36 | LL | type ETAI2> = impl Copy; | ---------- ^^^^^^^^^^ re-bound here @@ -395,13 +390,13 @@ LL | type ETAI2> = impl Copy; | `Item` bound here first error: could not find defining uses - --> $DIR/duplicate.rs:118:1 + --> $DIR/duplicate.rs:111:51 | -LL | type ETAI3> = impl Copy; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | type ETAI2> = impl Copy; + | ^^^^^^^^^ error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:118:39 + --> $DIR/duplicate.rs:114:39 | LL | type ETAI3> = impl Copy; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -409,13 +404,19 @@ LL | type ETAI3> = impl Copy; | `Item` bound here first error: could not find defining uses - --> $DIR/duplicate.rs:123:1 + --> $DIR/duplicate.rs:114:57 + | +LL | type ETAI3> = impl Copy; + | ^^^^^^^^^ + +error: could not find defining uses + --> $DIR/duplicate.rs:117:14 | LL | type ETAI4 = impl Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:123:40 + --> $DIR/duplicate.rs:117:40 | LL | type ETAI4 = impl Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -423,13 +424,13 @@ LL | type ETAI4 = impl Iterator; | `Item` bound here first error: could not find defining uses - --> $DIR/duplicate.rs:128:1 + --> $DIR/duplicate.rs:122:14 | LL | type ETAI5 = impl Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:128:40 + --> $DIR/duplicate.rs:122:40 | LL | type ETAI5 = impl Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -437,13 +438,13 @@ LL | type ETAI5 = impl Iterator; | `Item` bound here first error: could not find defining uses - --> $DIR/duplicate.rs:133:1 + --> $DIR/duplicate.rs:127:14 | LL | type ETAI6 = impl Iterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:133:43 + --> $DIR/duplicate.rs:127:43 | LL | type ETAI6 = impl Iterator; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -451,7 +452,7 @@ LL | type ETAI6 = impl Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:139:36 + --> $DIR/duplicate.rs:133:36 | LL | trait TRI1> {} | ---------- ^^^^^^^^^^ re-bound here @@ -459,7 +460,7 @@ LL | trait TRI1> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:141:36 + --> $DIR/duplicate.rs:135:36 | LL | trait TRI2> {} | ---------- ^^^^^^^^^^ re-bound here @@ -467,7 +468,7 @@ LL | trait TRI2> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:143:39 + --> $DIR/duplicate.rs:137:39 | LL | trait TRI3> {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -475,7 +476,7 @@ LL | trait TRI3> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:145:34 + --> $DIR/duplicate.rs:139:34 | LL | trait TRS1: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -483,7 +484,7 @@ LL | trait TRS1: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:147:34 + --> $DIR/duplicate.rs:141:34 | LL | trait TRS2: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -491,7 +492,7 @@ LL | trait TRS2: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:149:37 + --> $DIR/duplicate.rs:143:37 | LL | trait TRS3: Iterator {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -499,7 +500,7 @@ LL | trait TRS3: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:151:45 + --> $DIR/duplicate.rs:145:45 | LL | trait TRW1 where T: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -507,7 +508,7 @@ LL | trait TRW1 where T: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:153:45 + --> $DIR/duplicate.rs:147:45 | LL | trait TRW2 where T: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -515,7 +516,7 @@ LL | trait TRW2 where T: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:155:48 + --> $DIR/duplicate.rs:149:48 | LL | trait TRW3 where T: Iterator {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -523,7 +524,7 @@ LL | trait TRW3 where T: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:157:46 + --> $DIR/duplicate.rs:151:46 | LL | trait TRSW1 where Self: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -531,7 +532,7 @@ LL | trait TRSW1 where Self: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:157:46 + --> $DIR/duplicate.rs:151:46 | LL | trait TRSW1 where Self: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -539,7 +540,7 @@ LL | trait TRSW1 where Self: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:160:46 + --> $DIR/duplicate.rs:154:46 | LL | trait TRSW2 where Self: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -547,7 +548,7 @@ LL | trait TRSW2 where Self: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:160:46 + --> $DIR/duplicate.rs:154:46 | LL | trait TRSW2 where Self: Iterator {} | ---------- ^^^^^^^^^^ re-bound here @@ -555,7 +556,7 @@ LL | trait TRSW2 where Self: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:163:49 + --> $DIR/duplicate.rs:157:49 | LL | trait TRSW3 where Self: Iterator {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -563,7 +564,7 @@ LL | trait TRSW3 where Self: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:163:49 + --> $DIR/duplicate.rs:157:49 | LL | trait TRSW3 where Self: Iterator {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -571,7 +572,7 @@ LL | trait TRSW3 where Self: Iterator {} | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:166:43 + --> $DIR/duplicate.rs:160:43 | LL | trait TRA1 { type A: Iterator; } | ---------- ^^^^^^^^^^ re-bound here @@ -579,7 +580,7 @@ LL | trait TRA1 { type A: Iterator; } | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:168:43 + --> $DIR/duplicate.rs:162:43 | LL | trait TRA2 { type A: Iterator; } | ---------- ^^^^^^^^^^ re-bound here @@ -587,7 +588,7 @@ LL | trait TRA2 { type A: Iterator; } | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:170:46 + --> $DIR/duplicate.rs:164:46 | LL | trait TRA3 { type A: Iterator; } | ------------- ^^^^^^^^^^^^^ re-bound here @@ -595,7 +596,7 @@ LL | trait TRA3 { type A: Iterator; } | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:173:40 + --> $DIR/duplicate.rs:167:40 | LL | type TADyn1 = dyn Iterator; | ---------- ^^^^^^^^^^ re-bound here @@ -603,7 +604,7 @@ LL | type TADyn1 = dyn Iterator; | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:177:44 + --> $DIR/duplicate.rs:171:44 | LL | type TADyn2 = Box>; | ---------- ^^^^^^^^^^ re-bound here @@ -611,7 +612,7 @@ LL | type TADyn2 = Box>; | `Item` bound here first error[E0719]: the value of the associated type `Item` (from trait `std::iter::Iterator`) is already specified - --> $DIR/duplicate.rs:181:43 + --> $DIR/duplicate.rs:175:43 | LL | type TADyn3 = dyn Iterator; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -619,113 +620,77 @@ LL | type TADyn3 = dyn Iterator; | `Item` bound here first error: could not find defining uses - --> $DIR/duplicate.rs:108:24 - | -LL | type ETAI1> = impl Copy; - | ^^^^^^^^^^ - -error: could not find defining uses - --> $DIR/duplicate.rs:108:36 - | -LL | type ETAI1> = impl Copy; - | ^^^^^^^^^^ - -error: could not find defining uses - --> $DIR/duplicate.rs:113:24 - | -LL | type ETAI2> = impl Copy; - | ^^^^^^^^^^ - -error: could not find defining uses - --> $DIR/duplicate.rs:113:36 - | -LL | type ETAI2> = impl Copy; - | ^^^^^^^^^^ - -error: could not find defining uses - --> $DIR/duplicate.rs:118:24 - | -LL | type ETAI3> = impl Copy; - | ^^^^^^^^^^^^^ - -error: could not find defining uses - --> $DIR/duplicate.rs:118:39 - | -LL | type ETAI3> = impl Copy; - | ^^^^^^^^^^^^^ - -error: could not find defining uses - --> $DIR/duplicate.rs:123:28 + --> $DIR/duplicate.rs:117:28 | LL | type ETAI4 = impl Iterator; | ^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:123:40 + --> $DIR/duplicate.rs:117:40 | LL | type ETAI4 = impl Iterator; | ^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:128:28 + --> $DIR/duplicate.rs:122:28 | LL | type ETAI5 = impl Iterator; | ^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:128:40 + --> $DIR/duplicate.rs:122:40 | LL | type ETAI5 = impl Iterator; | ^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:133:28 + --> $DIR/duplicate.rs:127:28 | LL | type ETAI6 = impl Iterator; | ^^^^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:133:43 + --> $DIR/duplicate.rs:127:43 | LL | type ETAI6 = impl Iterator; | ^^^^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:173:28 + --> $DIR/duplicate.rs:167:28 | LL | type TADyn1 = dyn Iterator; | ^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:173:40 + --> $DIR/duplicate.rs:167:40 | LL | type TADyn1 = dyn Iterator; | ^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:177:32 + --> $DIR/duplicate.rs:171:32 | LL | type TADyn2 = Box>; | ^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:177:44 + --> $DIR/duplicate.rs:171:44 | LL | type TADyn2 = Box>; | ^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:181:28 + --> $DIR/duplicate.rs:175:28 | LL | type TADyn3 = dyn Iterator; | ^^^^^^^^^^^^^ error: could not find defining uses - --> $DIR/duplicate.rs:181:43 + --> $DIR/duplicate.rs:175:43 | LL | type TADyn3 = dyn Iterator; | ^^^^^^^^^^^^^ -error: aborting due to 96 previous errors +error: aborting due to 90 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-1.stderr b/src/test/ui/associated-types/associated-types-eq-1.stderr index 66c5f34644c01..53a45cf4e4f4d 100644 --- a/src/test/ui/associated-types/associated-types-eq-1.stderr +++ b/src/test/ui/associated-types/associated-types-eq-1.stderr @@ -4,7 +4,16 @@ error[E0412]: cannot find type `A` in this scope LL | fn foo2(x: I) { | - similarly named type parameter `I` defined here LL | let _: A = x.boo(); - | ^ help: a type parameter with a similar name exists: `I` + | ^ + | +help: a type parameter with a similar name exists + | +LL | let _: I = x.boo(); + | ^ +help: you might be missing a type parameter + | +LL | fn foo2(x: I) { + | ^^^ 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.nll.stderr b/src/test/ui/associated-types/associated-types-eq-hr.nll.stderr new file mode 100644 index 0000000000000..25e9f726ba53a --- /dev/null +++ b/src/test/ui/associated-types/associated-types-eq-hr.nll.stderr @@ -0,0 +1,33 @@ +error[E0271]: type mismatch resolving `for<'x> >::A == &'x isize` + --> $DIR/associated-types-eq-hr.rs:87:5 + | +LL | fn foo() + | --- required by a bound in this +LL | where +LL | T: for<'x> TheTrait<&'x isize, A = &'x isize>, + | ------------- required by this bound in `foo` +... +LL | foo::(); + | ^^^^^^^^^^^^^^^^^ expected `isize`, found `usize` + | + = note: expected reference `&isize` + found reference `&usize` + +error[E0271]: type mismatch resolving `for<'x> >::A == &'x usize` + --> $DIR/associated-types-eq-hr.rs:91:5 + | +LL | fn bar() + | --- required by a bound in this +LL | where +LL | T: for<'x> TheTrait<&'x isize, A = &'x usize>, + | ------------- required by this bound in `bar` +... +LL | bar::(); + | ^^^^^^^^^^^^^^^^ expected `usize`, found `isize` + | + = note: expected reference `&usize` + found reference `&isize` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/associated-types/associated-types-eq-hr.rs b/src/test/ui/associated-types/associated-types-eq-hr.rs index e6afa3f71c2f0..fb391913c321e 100644 --- a/src/test/ui/associated-types/associated-types-eq-hr.rs +++ b/src/test/ui/associated-types/associated-types-eq-hr.rs @@ -7,7 +7,7 @@ pub trait TheTrait { } struct IntStruct { - x: isize + x: isize, } impl<'a> TheTrait<&'a isize> for IntStruct { @@ -19,7 +19,7 @@ impl<'a> TheTrait<&'a isize> for IntStruct { } struct UintStruct { - x: isize + x: isize, } impl<'a> TheTrait<&'a isize> for UintStruct { @@ -30,8 +30,7 @@ impl<'a> TheTrait<&'a isize> for UintStruct { } } -struct Tuple { -} +struct Tuple {} impl<'a> TheTrait<(&'a isize, &'a isize)> for Tuple { type A = &'a isize; @@ -42,37 +41,43 @@ impl<'a> TheTrait<(&'a isize, &'a isize)> for Tuple { } fn foo() - where T : for<'x> TheTrait<&'x isize, A = &'x isize> +where + T: for<'x> TheTrait<&'x isize, A = &'x isize>, { // ok for IntStruct, but not UintStruct } fn bar() - where T : for<'x> TheTrait<&'x isize, A = &'x usize> +where + T: for<'x> TheTrait<&'x isize, A = &'x usize>, { // ok for UintStruct, but not IntStruct } fn tuple_one() - where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize> +where + T: for<'x, 'y> TheTrait<(&'x isize, &'y isize), A = &'x isize>, { // not ok for tuple, two lifetimes and we pick first } fn tuple_two() - where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize> +where + T: for<'x, 'y> TheTrait<(&'x isize, &'y isize), A = &'y isize>, { // not ok for tuple, two lifetimes and we pick second } fn tuple_three() - where T : for<'x> TheTrait<(&'x isize, &'x isize), A = &'x isize> +where + T: for<'x> TheTrait<(&'x isize, &'x isize), A = &'x isize>, { // ok for tuple } fn tuple_four() - where T : for<'x,'y> TheTrait<(&'x isize, &'y isize)> +where + T: for<'x, 'y> TheTrait<(&'x isize, &'y isize)>, { // not ok for tuple, two lifetimes, and lifetime matching is invariant } @@ -89,14 +94,14 @@ pub fn call_bar() { pub fn call_tuple_one() { tuple_one::(); - //~^ ERROR not satisfied - //~| ERROR type mismatch + //~^ ERROR implementation of `TheTrait` is not general enough + //~| ERROR implementation of `TheTrait` is not general enough } pub fn call_tuple_two() { tuple_two::(); - //~^ ERROR not satisfied - //~| ERROR type mismatch + //~^ ERROR implementation of `TheTrait` is not general enough + //~| ERROR implementation of `TheTrait` is not general enough } pub fn call_tuple_three() { @@ -105,7 +110,7 @@ pub fn call_tuple_three() { pub fn call_tuple_four() { tuple_four::(); - //~^ ERROR not satisfied + //~^ ERROR implementation of `TheTrait` is not general enough } -fn main() { } +fn main() {} 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..127ab8673556d 100644 --- a/src/test/ui/associated-types/associated-types-eq-hr.stderr +++ b/src/test/ui/associated-types/associated-types-eq-hr.stderr @@ -1,10 +1,11 @@ error[E0271]: type mismatch resolving `for<'x> >::A == &'x isize` - --> $DIR/associated-types-eq-hr.rs:82:5 + --> $DIR/associated-types-eq-hr.rs:87:5 | LL | fn foo() - | --- -LL | where T : for<'x> TheTrait<&'x isize, A = &'x isize> - | ------------- required by this bound in `foo` + | --- required by a bound in this +LL | where +LL | T: for<'x> TheTrait<&'x isize, A = &'x isize>, + | ------------- required by this bound in `foo` ... LL | foo::(); | ^^^^^^^^^^^^^^^^^ expected `isize`, found `usize` @@ -13,12 +14,13 @@ LL | foo::(); found reference `&usize` error[E0271]: type mismatch resolving `for<'x> >::A == &'x usize` - --> $DIR/associated-types-eq-hr.rs:86:5 + --> $DIR/associated-types-eq-hr.rs:91:5 | LL | fn bar() - | --- -LL | where T : for<'x> TheTrait<&'x isize, A = &'x usize> - | ------------- required by this bound in `bar` + | --- required by a bound in this +LL | where +LL | T: for<'x> TheTrait<&'x isize, A = &'x usize>, + | ------------- required by this bound in `bar` ... LL | bar::(); | ^^^^^^^^^^^^^^^^ expected `usize`, found `isize` @@ -26,71 +28,86 @@ LL | bar::(); = note: expected reference `&usize` found reference `&isize` -error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize)>` is not satisfied - --> $DIR/associated-types-eq-hr.rs:91:17 +error: implementation of `TheTrait` is not general enough + --> $DIR/associated-types-eq-hr.rs:96:5 | -LL | fn tuple_one() - | --------- -LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize> - | ---------------------------------------------------------- required by this bound in `tuple_one` +LL | / pub trait TheTrait { +LL | | type A; +LL | | +LL | | fn get(&self, t: T) -> Self::A; +LL | | } + | |_- trait `TheTrait` defined here ... -LL | tuple_one::(); - | ^^^^^ the trait `for<'x, 'y> TheTrait<(&'x isize, &'y isize)>` is not implemented for `Tuple` +LL | tuple_one::(); + | ^^^^^^^^^^^^^^^^^^ implementation of `TheTrait` is not general enough | - = help: the following implementations were found: - > + = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... + = note: ...but `Tuple` actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` -error[E0271]: type mismatch resolving `for<'x, 'y> >::A == &'x isize` - --> $DIR/associated-types-eq-hr.rs:91:5 +error: implementation of `TheTrait` is not general enough + --> $DIR/associated-types-eq-hr.rs:96:5 | -LL | fn tuple_one() - | --------- -LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'x isize> - | ------------- required by this bound in `tuple_one` +LL | / pub trait TheTrait { +LL | | type A; +LL | | +LL | | fn get(&self, t: T) -> Self::A; +LL | | } + | |_- trait `TheTrait` defined here ... -LL | tuple_one::(); - | ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'x, found concrete lifetime +LL | tuple_one::(); + | ^^^^^^^^^^^^^^^^^^ implementation of `TheTrait` is not general enough + | + = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... + = note: ...but `Tuple` actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` -error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize)>` is not satisfied - --> $DIR/associated-types-eq-hr.rs:97:17 +error: implementation of `TheTrait` is not general enough + --> $DIR/associated-types-eq-hr.rs:102:5 | -LL | fn tuple_two() - | --------- -LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize> - | ---------------------------------------------------------- required by this bound in `tuple_two` +LL | / pub trait TheTrait { +LL | | type A; +LL | | +LL | | fn get(&self, t: T) -> Self::A; +LL | | } + | |_- trait `TheTrait` defined here ... -LL | tuple_two::(); - | ^^^^^ the trait `for<'x, 'y> TheTrait<(&'x isize, &'y isize)>` is not implemented for `Tuple` +LL | tuple_two::(); + | ^^^^^^^^^^^^^^^^^^ implementation of `TheTrait` is not general enough | - = help: the following implementations were found: - > + = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... + = note: ...but `Tuple` actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` -error[E0271]: type mismatch resolving `for<'x, 'y> >::A == &'y isize` - --> $DIR/associated-types-eq-hr.rs:97:5 +error: implementation of `TheTrait` is not general enough + --> $DIR/associated-types-eq-hr.rs:102:5 | -LL | fn tuple_two() - | --------- -LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize> - | ------------- required by this bound in `tuple_two` +LL | / pub trait TheTrait { +LL | | type A; +LL | | +LL | | fn get(&self, t: T) -> Self::A; +LL | | } + | |_- trait `TheTrait` defined here ... -LL | tuple_two::(); - | ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'x, found concrete lifetime +LL | tuple_two::(); + | ^^^^^^^^^^^^^^^^^^ implementation of `TheTrait` is not general enough + | + = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... + = note: ...but `Tuple` actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` -error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize)>` is not satisfied - --> $DIR/associated-types-eq-hr.rs:107:18 +error: implementation of `TheTrait` is not general enough + --> $DIR/associated-types-eq-hr.rs:112:5 | -LL | fn tuple_four() - | ---------- -LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize)> - | ------------------------------------------- required by this bound in `tuple_four` +LL | / pub trait TheTrait { +LL | | type A; +LL | | +LL | | fn get(&self, t: T) -> Self::A; +LL | | } + | |_- trait `TheTrait` defined here ... -LL | tuple_four::(); - | ^^^^^ the trait `for<'x, 'y> TheTrait<(&'x isize, &'y isize)>` is not implemented for `Tuple` +LL | tuple_four::(); + | ^^^^^^^^^^^^^^^^^^^ implementation of `TheTrait` is not general enough | - = help: the following implementations were found: - > + = note: `Tuple` must implement `TheTrait<(&'0 isize, &'1 isize)>`, for any two lifetimes `'0` and `'1`... + = note: ...but `Tuple` actually implements `TheTrait<(&'2 isize, &'2 isize)>`, for some specific lifetime `'2` error: aborting due to 7 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0271`. 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/cache/project-fn-ret-invariant.krisskross.nll.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.nll.stderr index 71a533a36f418..4fc336122fa9d 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.nll.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.nll.stderr @@ -1,26 +1,26 @@ error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:55:4 + --> $DIR/project-fn-ret-invariant.rs:56:5 | -LL | fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - | -- -- lifetime `'b` defined here +LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here ... -LL | (a, b) - | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` +LL | (a, b) + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | = help: consider adding the following bound: `'a: 'b` error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:55:4 + --> $DIR/project-fn-ret-invariant.rs:56:5 | -LL | fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - | -- -- lifetime `'b` defined here +LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here ... -LL | (a, b) - | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` +LL | (a, b) + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` | = help: consider adding the following bound: `'b: 'a` diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.stderr index 5009e0868a7d4..9462121bdf203 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.stderr @@ -1,23 +1,23 @@ error[E0623]: lifetime mismatch - --> $DIR/project-fn-ret-invariant.rs:53:21 + --> $DIR/project-fn-ret-invariant.rs:54:22 | -LL | fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - | -------- -------------------- - | | - | this parameter and the return type are declared with different lifetimes... -LL | let a = bar(foo, y); - | ^ ...but data from `x` is returned here +LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + | -------- -------------------- + | | + | this parameter and the return type are declared with different lifetimes... +LL | let a = bar(foo, y); + | ^ ...but data from `x` is returned here error[E0623]: lifetime mismatch - --> $DIR/project-fn-ret-invariant.rs:54:21 + --> $DIR/project-fn-ret-invariant.rs:56:9 | -LL | fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - | -------- -------------------- - | | - | this parameter and the return type are declared with different lifetimes... -LL | let a = bar(foo, y); -LL | let b = bar(foo, x); - | ^ ...but data from `y` is returned here +LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + | -------- -------------------- + | | + | this parameter and the return type are declared with different lifetimes... +... +LL | (a, b) + | ^ ...but data from `x` is returned here error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.ok.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.ok.stderr index 8f445acf2b98c..2156ecb17393f 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.ok.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.ok.stderr @@ -1,8 +1,8 @@ error: fatal error triggered by #[rustc_error] - --> $DIR/project-fn-ret-invariant.rs:59:1 + --> $DIR/project-fn-ret-invariant.rs:60:1 | -LL | fn main() { } - | ^^^^^^^^^^^^^ +LL | fn main() {} + | ^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.nll.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.nll.stderr index c39030fbed1e1..44850df7b2f42 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.nll.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.nll.stderr @@ -1,26 +1,26 @@ error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:38:12 + --> $DIR/project-fn-ret-invariant.rs:39:13 | -LL | fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - | -- -- lifetime `'b` defined here +LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here -LL | let f = foo; // <-- No consistent type can be inferred for `f` here. -LL | let a = bar(f, x); - | ^^^^^^^^^ argument requires that `'a` must outlive `'b` +LL | let f = foo; // <-- No consistent type can be inferred for `f` here. +LL | let a = bar(f, x); + | ^^^^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:39:12 + --> $DIR/project-fn-ret-invariant.rs:40:13 | -LL | fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - | -- -- lifetime `'b` defined here +LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here ... -LL | let b = bar(f, y); - | ^^^^^^^^^ argument requires that `'b` must outlive `'a` +LL | let b = bar(f, y); + | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr index 65d16440ac9b0..64b5722390858 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr @@ -1,13 +1,13 @@ error[E0623]: lifetime mismatch - --> $DIR/project-fn-ret-invariant.rs:39:19 + --> $DIR/project-fn-ret-invariant.rs:40:20 | -LL | fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - | -------- -------------------- - | | - | this parameter and the return type are declared with different lifetimes... +LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + | -------- -------------------- + | | + | this parameter and the return type are declared with different lifetimes... ... -LL | let b = bar(f, y); - | ^ ...but data from `x` is returned here +LL | let b = bar(f, y); + | ^ ...but data from `x` is returned here error: aborting due to previous error diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.rs b/src/test/ui/associated-types/cache/project-fn-ret-invariant.rs index 23d873212ed1e..0034d796826de 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.rs +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.rs @@ -1,60 +1,61 @@ #![feature(unboxed_closures)] #![feature(rustc_attrs)] - // Test for projection cache. We should be able to project distinct // lifetimes from `foo` as we reinstantiate it multiple times, but not // if we do it just once. In this variant, the region `'a` is used in // an invariant position, which affects the results. // revisions: ok oneuse transmute krisskross - #![allow(dead_code, unused_variables)] use std::marker::PhantomData; struct Type<'a> { // Invariant - data: PhantomData &'a u32> + data: PhantomData &'a u32>, } -fn foo<'a>() -> Type<'a> { loop { } } +fn foo<'a>() -> Type<'a> { + loop {} +} fn bar(t: T, x: T::Output) -> T::Output - where T: FnOnce<()> +where + T: FnOnce<()>, { t() } #[cfg(ok)] // two instantiations: OK -fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { +fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { let a = bar(foo, x); let b = bar(foo, y); (a, b) } #[cfg(oneuse)] // one instantiation: BAD -fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - let f = foo; // <-- No consistent type can be inferred for `f` here. - let a = bar(f, x); - let b = bar(f, y); //[oneuse]~ ERROR lifetime mismatch [E0623] - (a, b) +fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + let f = foo; // <-- No consistent type can be inferred for `f` here. + let a = bar(f, x); + let b = bar(f, y); //[oneuse]~ ERROR lifetime mismatch [E0623] + (a, b) } #[cfg(transmute)] // one instantiations: BAD -fn baz<'a,'b>(x: Type<'a>) -> Type<'static> { - // Cannot instantiate `foo` with any lifetime other than `'a`, - // since it is provided as input. +fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> { + // Cannot instantiate `foo` with any lifetime other than `'a`, + // since it is provided as input. - bar(foo, x) //[transmute]~ ERROR E0495 + bar(foo, x) //[transmute]~ ERROR E0495 } #[cfg(krisskross)] // two instantiations, mixing and matching: BAD -fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { - let a = bar(foo, y); //[krisskross]~ ERROR E0623 - let b = bar(foo, x); //[krisskross]~ ERROR E0623 - (a, b) +fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + let a = bar(foo, y); //[krisskross]~ ERROR E0623 + let b = bar(foo, x); + (a, b) //[krisskross]~ ERROR E0623 } #[rustc_error] -fn main() { } +fn main() {} //[ok]~^ ERROR fatal error triggered by #[rustc_error] diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.nll.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.nll.stderr index f74d4ba73bf4e..db82c9fd43794 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.nll.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.nll.stderr @@ -1,11 +1,11 @@ error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:48:4 + --> $DIR/project-fn-ret-invariant.rs:49:5 | -LL | fn baz<'a,'b>(x: Type<'a>) -> Type<'static> { +LL | fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> { | -- lifetime `'a` defined here ... -LL | bar(foo, x) - | ^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` +LL | bar(foo, x) + | ^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` | = help: consider replacing `'a` with `'static` diff --git a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr index 3e39c8a792446..ef57f9e0bc480 100644 --- a/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr +++ b/src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr @@ -1,29 +1,29 @@ -error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements - --> $DIR/project-fn-ret-invariant.rs:48:8 +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements + --> $DIR/project-fn-ret-invariant.rs:49:9 | -LL | bar(foo, x) - | ^^^ +LL | bar(foo, x) + | ^^^ | -note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 44:8... - --> $DIR/project-fn-ret-invariant.rs:44:8 +note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 45:8... + --> $DIR/project-fn-ret-invariant.rs:45:8 | -LL | fn baz<'a,'b>(x: Type<'a>) -> Type<'static> { +LL | fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> { | ^^ note: ...so that the expression is assignable - --> $DIR/project-fn-ret-invariant.rs:48:13 + --> $DIR/project-fn-ret-invariant.rs:49:14 | -LL | bar(foo, x) - | ^ - = note: expected `Type<'_>` - found `Type<'a>` +LL | bar(foo, x) + | ^ + = note: expected `Type<'_>` + found `Type<'a>` = note: but, the lifetime must be valid for the static lifetime... note: ...so that the expression is assignable - --> $DIR/project-fn-ret-invariant.rs:48:4 + --> $DIR/project-fn-ret-invariant.rs:49:5 | -LL | bar(foo, x) - | ^^^^^^^^^^^ - = note: expected `Type<'static>` - found `Type<'_>` +LL | bar(foo, x) + | ^^^^^^^^^^^ + = note: expected `Type<'static>` + found `Type<'_>` 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.rs b/src/test/ui/associated-types/defaults-specialization.rs index d0ed718b83923..553705b2a4fab 100644 --- a/src/test/ui/associated-types/defaults-specialization.rs +++ b/src/test/ui/associated-types/defaults-specialization.rs @@ -1,6 +1,7 @@ //! Tests the interaction of associated type defaults and specialization. #![feature(associated_type_defaults, specialization)] +//~^ WARN the feature `specialization` is incomplete trait Tr { type Ty = u8; diff --git a/src/test/ui/associated-types/defaults-specialization.stderr b/src/test/ui/associated-types/defaults-specialization.stderr index 1dd536ec6360f..09a8c8f8a88a2 100644 --- a/src/test/ui/associated-types/defaults-specialization.stderr +++ b/src/test/ui/associated-types/defaults-specialization.stderr @@ -1,5 +1,14 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/defaults-specialization.rs:3:38 + | +LL | #![feature(associated_type_defaults, specialization)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #31844 for more information + error[E0053]: method `make` has an incompatible type for trait - --> $DIR/defaults-specialization.rs:18:18 + --> $DIR/defaults-specialization.rs:19:18 | LL | fn make() -> Self::Ty { | -------- type in trait @@ -9,26 +18,28 @@ 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 + --> $DIR/defaults-specialization.rs:35:18 | 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 + --> $DIR/defaults-specialization.rs:10: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,11 +47,9 @@ 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 + --> $DIR/defaults-specialization.rs:26:29 | LL | fn make() -> Self::Ty { 0u8 } | -------- ^^^ expected associated type, found `u8` @@ -49,12 +58,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 + --> $DIR/defaults-specialization.rs:44:29 | +LL | default type Ty = bool; + | ----------------------- expected this associated type +LL | LL | fn make() -> Self::Ty { true } | -------- ^^^^ expected associated type, found `bool` | | @@ -62,11 +74,9 @@ 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 + --> $DIR/defaults-specialization.rs:87:32 | LL | let _: as Tr>::Ty = 0u8; | ----------------- ^^^ expected associated type, found `u8` @@ -75,11 +85,14 @@ 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:9:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error[E0308]: mismatched types - --> $DIR/defaults-specialization.rs:87:32 + --> $DIR/defaults-specialization.rs:88:32 | LL | let _: as Tr>::Ty = true; | ----------------- ^^^^ expected associated type, found `bool` @@ -88,11 +101,14 @@ 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:9:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error[E0308]: mismatched types - --> $DIR/defaults-specialization.rs:88:33 + --> $DIR/defaults-specialization.rs:89:33 | LL | let _: as Tr>::Ty = 0u8; | ------------------ ^^^ expected associated type, found `u8` @@ -101,11 +117,14 @@ 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:9:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error[E0308]: mismatched types - --> $DIR/defaults-specialization.rs:89:33 + --> $DIR/defaults-specialization.rs:90:33 | LL | let _: as Tr>::Ty = true; | ------------------ ^^^^ expected associated type, found `bool` @@ -114,10 +133,13 @@ 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:9:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` -error: aborting due to 9 previous errors +error: aborting due to 9 previous errors; 1 warning emitted Some errors have detailed explanations: E0053, E0308. For more information about an error, try `rustc --explain E0053`. 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.nll.stderr b/src/test/ui/associated-types/higher-ranked-projection.bad.nll.stderr new file mode 100644 index 0000000000000..2e03986a9ed8f --- /dev/null +++ b/src/test/ui/associated-types/higher-ranked-projection.bad.nll.stderr @@ -0,0 +1,8 @@ +error: higher-ranked subtype error + --> $DIR/higher-ranked-projection.rs:25:5 + | +LL | foo(()); + | ^^^^^^^ + +error: aborting due to previous error + 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..811c9a8f5e12b 100644 --- a/src/test/ui/associated-types/higher-ranked-projection.bad.stderr +++ b/src/test/ui/associated-types/higher-ranked-projection.bad.stderr @@ -1,14 +1,12 @@ -error[E0271]: type mismatch resolving `for<'a> <&'a _ as Mirror>::Image == _` +error[E0308]: mismatched types --> $DIR/higher-ranked-projection.rs:25:5 | -LL | fn foo(_t: T) - | --- -LL | where for<'a> &'a T: Mirror - | ------- required by this bound in `foo` -... LL | foo(()); - | ^^^ expected bound lifetime parameter 'a, found concrete lifetime + | ^^^ one type is more general than the other + | + = note: expected type `&'a ()` + found type `&()` error: aborting due to previous error -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/associated-types/higher-ranked-projection.rs b/src/test/ui/associated-types/higher-ranked-projection.rs index 5315e21b0f5a9..1b5476d4c364f 100644 --- a/src/test/ui/associated-types/higher-ranked-projection.rs +++ b/src/test/ui/associated-types/higher-ranked-projection.rs @@ -23,5 +23,5 @@ fn foo(_t: T) #[rustc_error] fn main() { //[good]~ ERROR fatal error triggered by #[rustc_error] foo(()); - //[bad]~^ ERROR type mismatch + //[bad]~^ ERROR mismatched types } diff --git a/src/test/ui/associated-types/hr-associated-type-bound-1.rs b/src/test/ui/associated-types/hr-associated-type-bound-1.rs new file mode 100644 index 0000000000000..497b86eeab88d --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-1.rs @@ -0,0 +1,18 @@ +trait X<'a> +where + for<'b> >::U: Clone, +{ + type U: ?Sized; + fn f(&self, x: &Self::U) { + ::clone(x); + } +} + +impl X<'_> for i32 { + type U = str; + //~^ ERROR the trait bound `for<'b> >::U: std::clone::Clone` +} + +fn main() { + 1i32.f("abc"); +} diff --git a/src/test/ui/associated-types/hr-associated-type-bound-1.stderr b/src/test/ui/associated-types/hr-associated-type-bound-1.stderr new file mode 100644 index 0000000000000..7ef2faef9c6e7 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-1.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-1.rs:12:14 + | +LL | trait X<'a> + | - required by a bound in this +LL | where +LL | for<'b> >::U: Clone, + | ----- required by this bound in `X` +... +LL | type U = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::U` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/hr-associated-type-bound-2.rs b/src/test/ui/associated-types/hr-associated-type-bound-2.rs new file mode 100644 index 0000000000000..7ff0fede28cfe --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-2.rs @@ -0,0 +1,21 @@ +trait X<'a> +where + for<'b> >::U: Clone, +{ + type U: ?Sized; + fn f(&self, x: &Self::U) { + ::clone(x); + } +} + +impl X<'_> for u32 +where + for<'b> >::U: Clone, +{ + type U = str; +} + +fn main() { + 1u32.f("abc"); + //~^ ERROR no method named `f` found for type `u32` in the current scope +} diff --git a/src/test/ui/associated-types/hr-associated-type-bound-2.stderr b/src/test/ui/associated-types/hr-associated-type-bound-2.stderr new file mode 100644 index 0000000000000..2a364d349d77e --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-2.stderr @@ -0,0 +1,13 @@ +error[E0599]: no method named `f` found for type `u32` in the current scope + --> $DIR/hr-associated-type-bound-2.rs:19:10 + | +LL | 1u32.f("abc"); + | ^ method not found in `u32` + | + = note: the method `f` exists but the following trait bounds were not satisfied: + `>::U: std::clone::Clone` + which is required by `u32: X` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/associated-types/hr-associated-type-bound-object.rs b/src/test/ui/associated-types/hr-associated-type-bound-object.rs new file mode 100644 index 0000000000000..7c64ae38caf60 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-object.rs @@ -0,0 +1,14 @@ +trait X<'a> +where + for<'b> >::U: Clone, +{ + type U: ?Sized; +} +fn f<'a, T: X<'a> + ?Sized>(x: &>::U) { + //~^ ERROR the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied + <>::U>::clone(x); +} + +pub fn main() { + f::>("abc"); +} diff --git a/src/test/ui/associated-types/hr-associated-type-bound-object.stderr b/src/test/ui/associated-types/hr-associated-type-bound-object.stderr new file mode 100644 index 0000000000000..db966875c708f --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-object.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-object.rs:7:13 + | +LL | trait X<'a> + | - required by a bound in this +LL | where +LL | for<'b> >::U: Clone, + | ----- required by this bound in `X` +... +LL | fn f<'a, T: X<'a> + ?Sized>(x: &>::U) { + | ^^^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::U` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-1.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-1.rs new file mode 100644 index 0000000000000..a65f8a8c498b7 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-1.rs @@ -0,0 +1,20 @@ +trait Y<'a, T: ?Sized> +where + T: Y<'a, Self>, + for<'b> >::V: Clone, + for<'b> >::V: Clone, +{ + type V: ?Sized; + fn g(&self, x: &Self::V) { + ::clone(x); + } +} + +impl<'a> Y<'a, u8> for u8 { + type V = str; + //~^ ERROR the trait bound `for<'b> >::V: std::clone::Clone` is not satisfied +} + +fn main() { + 1u8.g("abc"); +} diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr new file mode 100644 index 0000000000000..347a5818dce31 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `for<'b> >::V: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-1.rs:14:14 + | +LL | trait Y<'a, T: ?Sized> + | - required by a bound in this +... +LL | for<'b> >::V: Clone, + | ----- required by this bound in `Y` +... +LL | type V = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::V` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-2.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-2.rs new file mode 100644 index 0000000000000..9f849b0327669 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-2.rs @@ -0,0 +1,21 @@ +trait Z<'a, T: ?Sized> +where + T: Z<'a, u16>, + //~^ the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied + //~| the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied + for<'b> >::W: Clone, +{ + type W: ?Sized; + fn h(&self, x: &T::W) { + ::clone(x); + } +} + +impl<'a> Z<'a, u16> for u16 { + type W = str; + //~^ ERROR the trait bound `for<'b> >::W: std::clone::Clone +} + +fn main() { + 1u16.h("abc"); +} diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr new file mode 100644 index 0000000000000..e06777e36a8c5 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr @@ -0,0 +1,51 @@ +error[E0277]: the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-2.rs:3:8 + | +LL | trait Z<'a, T: ?Sized> + | - required by a bound in this +LL | where +LL | T: Z<'a, u16>, + | ^^^^^^^^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::W` +... +LL | for<'b> >::W: Clone, + | ----- required by this bound in `Z` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error[E0277]: the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-2.rs:15:14 + | +LL | trait Z<'a, T: ?Sized> + | - required by a bound in this +... +LL | for<'b> >::W: Clone, + | ----- required by this bound in `Z` +... +LL | type W = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::W` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error[E0277]: the trait bound `for<'b> >::W: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-2.rs:3:8 + | +LL | trait Z<'a, T: ?Sized> + | - required by a bound in this +LL | where +LL | T: Z<'a, u16>, + | ^^^^^^^^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::W` +... +LL | for<'b> >::W: Clone, + | ----- required by this bound in `Z` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-3.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-3.rs new file mode 100644 index 0000000000000..9aca59f8ce6d7 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-3.rs @@ -0,0 +1,21 @@ +// ignore-tidy-linelength + +trait X<'a, T> +where + for<'b> T: X<'b, T>, + for<'b> >::U: Clone, +{ + type U: ?Sized; + fn f(x: &>::U) { + <>::U>::clone(x); + } +} + +impl X<'_, (T,)> for (S,) { + type U = str; + //~^ ERROR the trait bound `for<'b> <(T,) as X<'b, (T,)>>::U: std::clone::Clone` is not satisfied +} + +pub fn main() { + <(i32,) as X<(i32,)>>::f("abc"); +} diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr new file mode 100644 index 0000000000000..ff56f60e4c9e5 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `for<'b> <(T,) as X<'b, (T,)>>::U: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-3.rs:15:14 + | +LL | trait X<'a, T> + | - required by a bound in this +... +LL | for<'b> >::U: Clone, + | ----- required by this bound in `X` +... +LL | type U = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `<(T,) as X<'b, (T,)>>::U` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-4.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-4.rs new file mode 100644 index 0000000000000..ffe43c674c3dc --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-4.rs @@ -0,0 +1,19 @@ +trait X<'a, T> +where + for<'b> (T,): X<'b, T>, + for<'b> <(T,) as X<'b, T>>::U: Clone, +{ + type U: ?Sized; + fn f(x: &<(T,) as X<'_, T>>::U) { + <<(T,) as X<'_, T>>::U>::clone(x); + } +} + +impl X<'_, T> for (S,) { + type U = str; + //~^ ERROR the trait bound `for<'b> <(T,) as X<'b, T>>::U: std::clone::Clone` is not satisfied +} + +pub fn main() { + <(i32,) as X>::f("abc"); +} diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr new file mode 100644 index 0000000000000..c41efb8b6e1a2 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `for<'b> <(T,) as X<'b, T>>::U: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-4.rs:13:14 + | +LL | trait X<'a, T> + | - required by a bound in this +... +LL | for<'b> <(T,) as X<'b, T>>::U: Clone, + | ----- required by this bound in `X` +... +LL | type U = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `<(T,) as X<'b, T>>::U` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-5.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-5.rs new file mode 100644 index 0000000000000..dcca0b3ce92aa --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-5.rs @@ -0,0 +1,41 @@ +// ignore-tidy-linelength + +trait Cycle: Sized { + type Next: Cycle; +} + +impl Cycle for Box { + type Next = Vec; +} + +impl Cycle for Vec { + type Next = Box; +} + +trait X<'a, T: Cycle + for<'b> X<'b, T>> +where + for<'b> >::U: Clone, + for<'b> T::Next: X<'b, T::Next>, + for<'b> >::U: Clone, +{ + type U: ?Sized; + fn f(x: &>::U) { + <>::U>::clone(x); + } +} + +impl X<'_, Vec> for S { + type U = str; + //~^ ERROR the trait bound `for<'b> as X<'b, std::boxed::Box>>::U: std::clone::Clone` is not satisfied + //~| ERROR the trait bound `for<'b> as X<'b, std::vec::Vec>>::U: std::clone::Clone` is not satisfied +} + +impl X<'_, Box> for S { + type U = str; + //~^ ERROR the trait bound `for<'b> as X<'b, std::boxed::Box>>::U: std::clone::Clone` is not satisfied + //~| ERROR the trait bound `for<'b> as X<'b, std::vec::Vec>>::U: std::clone::Clone` is not satisfied +} + +pub fn main() { + >>::f("abc"); +} diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr new file mode 100644 index 0000000000000..39c191e974777 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr @@ -0,0 +1,67 @@ +error[E0277]: the trait bound `for<'b> as X<'b, std::boxed::Box>>::U: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-5.rs:28:14 + | +LL | trait X<'a, T: Cycle + for<'b> X<'b, T>> + | - required by a bound in this +... +LL | for<'b> >::U: Clone, + | ----- required by this bound in `X` +... +LL | type U = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for ` as X<'b, std::boxed::Box>>::U` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error[E0277]: the trait bound `for<'b> as X<'b, std::vec::Vec>>::U: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-5.rs:28:14 + | +LL | trait X<'a, T: Cycle + for<'b> X<'b, T>> + | - required by a bound in this +LL | where +LL | for<'b> >::U: Clone, + | ----- required by this bound in `X` +... +LL | type U = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for ` as X<'b, std::vec::Vec>>::U` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error[E0277]: the trait bound `for<'b> as X<'b, std::vec::Vec>>::U: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-5.rs:34:14 + | +LL | trait X<'a, T: Cycle + for<'b> X<'b, T>> + | - required by a bound in this +... +LL | for<'b> >::U: Clone, + | ----- required by this bound in `X` +... +LL | type U = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for ` as X<'b, std::vec::Vec>>::U` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error[E0277]: the trait bound `for<'b> as X<'b, std::boxed::Box>>::U: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-5.rs:34:14 + | +LL | trait X<'a, T: Cycle + for<'b> X<'b, T>> + | - required by a bound in this +LL | where +LL | for<'b> >::U: Clone, + | ----- required by this bound in `X` +... +LL | type U = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for ` as X<'b, std::boxed::Box>>::U` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-6.rs b/src/test/ui/associated-types/hr-associated-type-bound-param-6.rs new file mode 100644 index 0000000000000..4b8018cb43024 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-6.rs @@ -0,0 +1,20 @@ +trait X<'a, T> +where + for<'b> T: X<'b, T>, + for<'b> >::U: Clone, +{ + type U: ?Sized; + fn f(x: &>::U) { + <>::U>::clone(x); + } +} + +impl X<'_, T> for (S,) { + //~^ ERROR the trait bound `for<'b> T: X<'b, T>` is not satisfied + type U = str; + //~^ ERROR the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied +} + +pub fn main() { + <(i32,) as X>::f("abc"); +} diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-6.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-6.stderr new file mode 100644 index 0000000000000..83845d3a9410e --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-bound-param-6.stderr @@ -0,0 +1,36 @@ +error[E0277]: the trait bound `for<'b> >::U: std::clone::Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-6.rs:14:14 + | +LL | trait X<'a, T> + | - required by a bound in this +... +LL | for<'b> >::U: Clone, + | ----- required by this bound in `X` +... +LL | type U = str; + | ^^^ the trait `for<'b> std::clone::Clone` is not implemented for `>::U` + | + = help: the following implementations were found: + <&T as std::clone::Clone> + <&mut T as std::clone::Clone> + +error[E0277]: the trait bound `for<'b> T: X<'b, T>` is not satisfied + --> $DIR/hr-associated-type-bound-param-6.rs:12:12 + | +LL | trait X<'a, T> + | - required by a bound in this +LL | where +LL | for<'b> T: X<'b, T>, + | -------- required by this bound in `X` +... +LL | impl X<'_, T> for (S,) { + | ^^^^^^^^ the trait `for<'b> X<'b, T>` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | impl X<'b, T>> X<'_, T> for (S,) { + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/associated-types/hr-associated-type-projection-1.rs b/src/test/ui/associated-types/hr-associated-type-projection-1.rs new file mode 100644 index 0000000000000..0d4567a55fc99 --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-projection-1.rs @@ -0,0 +1,21 @@ +trait UnsafeCopy<'a, T: Copy> +where + for<'b> >::Item: std::ops::Deref, +{ + type Item; + + fn bug(item: &Self::Item) -> () { + let x: T = **item; + &x as *const _; + } +} + +impl UnsafeCopy<'_, T> for T { + //~^ ERROR the trait bound `>::Item: std::ops::Deref` is not satisfied + type Item = T; + //~^ ERROR the trait bound `for<'b> >::Item: std::ops::Deref +} + +pub fn main() { + <&'static str>::bug(&""); +} diff --git a/src/test/ui/associated-types/hr-associated-type-projection-1.stderr b/src/test/ui/associated-types/hr-associated-type-projection-1.stderr new file mode 100644 index 0000000000000..5ab57410c441b --- /dev/null +++ b/src/test/ui/associated-types/hr-associated-type-projection-1.stderr @@ -0,0 +1,30 @@ +error[E0277]: the trait bound `for<'b> >::Item: std::ops::Deref` is not satisfied + --> $DIR/hr-associated-type-projection-1.rs:15:17 + | +LL | trait UnsafeCopy<'a, T: Copy> + | ---------- required by a bound in this +LL | where +LL | for<'b> >::Item: std::ops::Deref, + | --------------------------- required by this bound in `UnsafeCopy` +... +LL | type Item = T; + | ^ the trait `for<'b> std::ops::Deref` is not implemented for `>::Item` + | + = help: the following implementations were found: + <&T as std::ops::Deref> + <&mut T as std::ops::Deref> + +error[E0277]: the trait bound `>::Item: std::ops::Deref` is not satisfied + --> $DIR/hr-associated-type-projection-1.rs:13:33 + | +LL | impl UnsafeCopy<'_, T> for T { + | ^^^^^^^^^^^^^^^^^ the trait `std::ops::Deref` is not implemented for `>::Item` + | +help: consider further restricting the associated type + | +LL | impl UnsafeCopy<'_, T> for T where >::Item: std::ops::Deref { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. 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/issue-72806.rs b/src/test/ui/associated-types/issue-72806.rs new file mode 100644 index 0000000000000..ae63781d568a1 --- /dev/null +++ b/src/test/ui/associated-types/issue-72806.rs @@ -0,0 +1,20 @@ +trait Bar { + type Ok; + type Sibling: Bar2; +} +trait Bar2 { + type Ok; +} + +struct Foo; +struct Foo2; + +impl Bar for Foo { //~ ERROR type mismatch resolving `::Ok == char` + type Ok = (); + type Sibling = Foo2; +} +impl Bar2 for Foo2 { + type Ok = u32; +} + +fn main() {} diff --git a/src/test/ui/associated-types/issue-72806.stderr b/src/test/ui/associated-types/issue-72806.stderr new file mode 100644 index 0000000000000..03a6565848dc3 --- /dev/null +++ b/src/test/ui/associated-types/issue-72806.stderr @@ -0,0 +1,9 @@ +error[E0271]: type mismatch resolving `::Ok == char` + --> $DIR/issue-72806.rs:12:6 + | +LL | impl Bar for Foo { + | ^^^ expected `u32`, found `char` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. 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..c7b0fbeb0e39b --- /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":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"_field0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"NonJoint"]]}]}}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} 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..59ed68c2a773f 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":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}]},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"_field0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"NonJoint"]]}]}}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} 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-block-control-flow-static-semantics.stderr b/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr index afb8f146192cc..46a132da309bb 100644 --- a/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr +++ b/src/test/ui/async-await/async-block-control-flow-static-semantics.stderr @@ -18,22 +18,6 @@ LL | | break 0u8; LL | | }; | |_________- enclosing `async` block -error[E0308]: mismatched types - --> $DIR/async-block-control-flow-static-semantics.rs:13:43 - | -LL | fn return_targets_async_block_not_fn() -> u8 { - | --------------------------------- ^^ expected `u8`, found `()` - | | - | implicitly returns `()` as its body has no tail or `return` expression - -error[E0271]: type mismatch resolving `::Output == ()` - --> $DIR/async-block-control-flow-static-semantics.rs:18:39 - | -LL | let _: &dyn Future = █ - | ^^^^^^ expected `()`, found `u8` - | - = note: required for the cast to the object type `dyn std::future::Future` - error[E0308]: mismatched types --> $DIR/async-block-control-flow-static-semantics.rs:22:58 | @@ -55,6 +39,22 @@ LL | let _: &dyn Future = █ | = note: required for the cast to the object type `dyn std::future::Future` +error[E0308]: mismatched types + --> $DIR/async-block-control-flow-static-semantics.rs:13:43 + | +LL | fn return_targets_async_block_not_fn() -> u8 { + | --------------------------------- ^^ expected `u8`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error[E0271]: type mismatch resolving `::Output == ()` + --> $DIR/async-block-control-flow-static-semantics.rs:18:39 + | +LL | let _: &dyn Future = █ + | ^^^^^^ expected `()`, found `u8` + | + = note: required for the cast to the object type `dyn std::future::Future` + error[E0308]: mismatched types --> $DIR/async-block-control-flow-static-semantics.rs:48:44 | 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-error-span.rs b/src/test/ui/async-await/async-error-span.rs index 28132c9789c67..cf10ebfeca939 100644 --- a/src/test/ui/async-await/async-error-span.rs +++ b/src/test/ui/async-await/async-error-span.rs @@ -5,6 +5,7 @@ use std::future::Future; fn get_future() -> impl Future { +//~^ ERROR the trait bound `(): std::future::Future` is not satisfied panic!() } diff --git a/src/test/ui/async-await/async-error-span.stderr b/src/test/ui/async-await/async-error-span.stderr index b551b99587dd9..4054e739c483d 100644 --- a/src/test/ui/async-await/async-error-span.stderr +++ b/src/test/ui/async-await/async-error-span.stderr @@ -1,15 +1,27 @@ +error[E0277]: the trait bound `(): std::future::Future` is not satisfied + --> $DIR/async-error-span.rs:7:20 + | +LL | fn get_future() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `()` +LL | +LL | panic!() + | -------- this returned value is of type `!` + | + = note: the return type of a function must have a statically known size + error[E0698]: type inside `async fn` body must be known in this context - --> $DIR/async-error-span.rs:12:9 + --> $DIR/async-error-span.rs:13:9 | LL | let a; | ^ cannot infer type | note: the type is part of the `async fn` body because of this `await` - --> $DIR/async-error-span.rs:13:5 + --> $DIR/async-error-span.rs:14:5 | LL | get_future().await; | ^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0698`. +Some errors have detailed explanations: E0277, E0698. +For more information about an error, try `rustc --explain E0277`. 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/async-fn-size-moved-locals.rs b/src/test/ui/async-await/async-fn-size-moved-locals.rs index 4a413381aa300..636fafc2bc44a 100644 --- a/src/test/ui/async-await/async-fn-size-moved-locals.rs +++ b/src/test/ui/async-await/async-fn-size-moved-locals.rs @@ -110,9 +110,9 @@ async fn mixed_sizes() { } fn main() { - assert_eq!(1028, std::mem::size_of_val(&single())); - assert_eq!(1032, std::mem::size_of_val(&single_with_noop())); - assert_eq!(3084, std::mem::size_of_val(&joined())); - assert_eq!(3084, std::mem::size_of_val(&joined_with_noop())); - assert_eq!(7188, std::mem::size_of_val(&mixed_sizes())); + assert_eq!(1025, std::mem::size_of_val(&single())); + assert_eq!(1026, std::mem::size_of_val(&single_with_noop())); + assert_eq!(3078, std::mem::size_of_val(&joined())); + assert_eq!(3079, std::mem::size_of_val(&joined_with_noop())); + assert_eq!(7181, std::mem::size_of_val(&mixed_sizes())); } diff --git a/src/test/ui/async-await/async-fn-size-uninit-locals.rs b/src/test/ui/async-await/async-fn-size-uninit-locals.rs index 0558084f4f8a3..d5d7b3fc3f0bd 100644 --- a/src/test/ui/async-await/async-fn-size-uninit-locals.rs +++ b/src/test/ui/async-await/async-fn-size-uninit-locals.rs @@ -95,9 +95,9 @@ async fn join_retval() -> Joiner { } fn main() { - assert_eq!(8, std::mem::size_of_val(&single())); - assert_eq!(12, std::mem::size_of_val(&single_with_noop())); - assert_eq!(3084, std::mem::size_of_val(&joined())); - assert_eq!(3084, std::mem::size_of_val(&joined_with_noop())); - assert_eq!(3080, std::mem::size_of_val(&join_retval())); + assert_eq!(2, std::mem::size_of_val(&single())); + assert_eq!(3, std::mem::size_of_val(&single_with_noop())); + assert_eq!(3078, std::mem::size_of_val(&joined())); + assert_eq!(3078, std::mem::size_of_val(&joined_with_noop())); + assert_eq!(3074, std::mem::size_of_val(&join_retval())); } diff --git a/src/test/ui/async-await/async-fn-size.rs b/src/test/ui/async-await/async-fn-size.rs index b313992db4ecb..0c1f3636446c9 100644 --- a/src/test/ui/async-await/async-fn-size.rs +++ b/src/test/ui/async-await/async-fn-size.rs @@ -86,13 +86,13 @@ async fn await3_level5() -> u8 { fn main() { assert_eq!(2, std::mem::size_of_val(&base())); - assert_eq!(8, std::mem::size_of_val(&await1_level1())); - assert_eq!(12, std::mem::size_of_val(&await2_level1())); - assert_eq!(12, std::mem::size_of_val(&await3_level1())); - assert_eq!(24, std::mem::size_of_val(&await3_level2())); - assert_eq!(36, std::mem::size_of_val(&await3_level3())); - assert_eq!(48, std::mem::size_of_val(&await3_level4())); - assert_eq!(60, std::mem::size_of_val(&await3_level5())); + assert_eq!(3, std::mem::size_of_val(&await1_level1())); + assert_eq!(4, std::mem::size_of_val(&await2_level1())); + assert_eq!(5, std::mem::size_of_val(&await3_level1())); + assert_eq!(8, std::mem::size_of_val(&await3_level2())); + assert_eq!(11, std::mem::size_of_val(&await3_level3())); + assert_eq!(14, std::mem::size_of_val(&await3_level4())); + assert_eq!(17, std::mem::size_of_val(&await3_level5())); assert_eq!(1, wait(base())); assert_eq!(1, wait(await1_level1())); diff --git a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs index 22bcbb1064dd7..cebff3be6b059 100644 --- a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs +++ b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.rs @@ -62,6 +62,7 @@ fn foo10() -> Result<(), ()> { fn foo11() -> Result<(), ()> { let _ = await bar()?; //~ ERROR `await` is only allowed inside `async` functions and blocks //~^ ERROR incorrect use of `await` + //~| ERROR the `?` operator can only be applied to values that implement `std::ops::Try` Ok(()) } fn foo12() -> Result<(), ()> { diff --git a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr index 92cef80c19360..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 @@ -71,49 +71,49 @@ LL | let _ = await bar()?; | ^^^^^^^^^^^^ help: `await` is a postfix operation: `bar()?.await` error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:68:14 + --> $DIR/incorrect-syntax-suggestions.rs:69:14 | LL | let _ = (await bar())?; | ^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await` error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:73:24 + --> $DIR/incorrect-syntax-suggestions.rs:74:24 | LL | let _ = bar().await(); | ^^ help: `await` is not a method call, remove the parentheses error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:78:24 + --> $DIR/incorrect-syntax-suggestions.rs:79:24 | LL | let _ = bar().await()?; | ^^ help: `await` is not a method call, remove the parentheses error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:106:13 + --> $DIR/incorrect-syntax-suggestions.rs:107:13 | LL | let _ = await!(bar()); | ^^^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await` error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:110:13 + --> $DIR/incorrect-syntax-suggestions.rs:111:13 | LL | let _ = await!(bar())?; | ^^^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await` error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:115:17 + --> $DIR/incorrect-syntax-suggestions.rs:116:17 | LL | let _ = await!(bar())?; | ^^^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await` error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:123:17 + --> $DIR/incorrect-syntax-suggestions.rs:124:17 | LL | let _ = await!(bar())?; | ^^^^^^^^^^^^^ help: `await` is a postfix operation: `bar().await` error: expected expression, found `=>` - --> $DIR/incorrect-syntax-suggestions.rs:131:25 + --> $DIR/incorrect-syntax-suggestions.rs:132:25 | LL | match await { await => () } | ----- ^^ expected expression @@ -121,13 +121,13 @@ LL | match await { await => () } | while parsing this incorrect await expression error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:131:11 + --> $DIR/incorrect-syntax-suggestions.rs:132:11 | LL | match await { await => () } | ^^^^^^^^^^^^^^^^^^^^^ help: `await` is a postfix operation: `{ await => () }.await` error: expected one of `.`, `?`, `{`, or an operator, found `}` - --> $DIR/incorrect-syntax-suggestions.rs:134:1 + --> $DIR/incorrect-syntax-suggestions.rs:135:1 | LL | match await { await => () } | ----- - expected one of `.`, `?`, `{`, or an operator @@ -162,7 +162,7 @@ LL | let _ = await bar()?; | ^^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/incorrect-syntax-suggestions.rs:68:14 + --> $DIR/incorrect-syntax-suggestions.rs:69:14 | LL | fn foo12() -> Result<(), ()> { | ----- this is not `async` @@ -170,7 +170,7 @@ LL | let _ = (await bar())?; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/incorrect-syntax-suggestions.rs:73:13 + --> $DIR/incorrect-syntax-suggestions.rs:74:13 | LL | fn foo13() -> Result<(), ()> { | ----- this is not `async` @@ -178,7 +178,7 @@ LL | let _ = bar().await(); | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/incorrect-syntax-suggestions.rs:78:13 + --> $DIR/incorrect-syntax-suggestions.rs:79:13 | LL | fn foo14() -> Result<(), ()> { | ----- this is not `async` @@ -186,7 +186,7 @@ LL | let _ = bar().await()?; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/incorrect-syntax-suggestions.rs:83:13 + --> $DIR/incorrect-syntax-suggestions.rs:84:13 | LL | fn foo15() -> Result<(), ()> { | ----- this is not `async` @@ -194,7 +194,7 @@ LL | let _ = bar().await; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/incorrect-syntax-suggestions.rs:87:13 + --> $DIR/incorrect-syntax-suggestions.rs:88:13 | LL | fn foo16() -> Result<(), ()> { | ----- this is not `async` @@ -202,7 +202,7 @@ LL | let _ = bar().await?; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/incorrect-syntax-suggestions.rs:92:17 + --> $DIR/incorrect-syntax-suggestions.rs:93:17 | LL | fn foo() -> Result<(), ()> { | --- this is not `async` @@ -210,7 +210,7 @@ LL | let _ = bar().await?; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/incorrect-syntax-suggestions.rs:99:17 + --> $DIR/incorrect-syntax-suggestions.rs:100:17 | LL | let foo = || { | -- this is not `async` @@ -218,7 +218,7 @@ LL | let _ = bar().await?; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/incorrect-syntax-suggestions.rs:115:17 + --> $DIR/incorrect-syntax-suggestions.rs:116:17 | LL | fn foo() -> Result<(), ()> { | --- this is not `async` @@ -226,7 +226,7 @@ LL | let _ = await!(bar())?; | ^^^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks - --> $DIR/incorrect-syntax-suggestions.rs:123:17 + --> $DIR/incorrect-syntax-suggestions.rs:124:17 | LL | let foo = || { | -- this is not `async` @@ -236,13 +236,25 @@ LL | let _ = await!(bar())?; error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` --> $DIR/incorrect-syntax-suggestions.rs:16:19 | +LL | let _ = await bar()?; + | ^^^^^^ + | | + | 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` + +error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` + --> $DIR/incorrect-syntax-suggestions.rs:63:19 + | LL | let _ = await bar()?; | ^^^^^^ the `?` operator cannot be applied to type `impl std::future::Future` | = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` = note: required by `std::ops::Try::into_result` -error: aborting due to 35 previous errors +error: aborting due to 36 previous errors Some errors have detailed explanations: E0277, E0728. For more information about an error, try `rustc --explain E0277`. 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-61949-self-return-type.stderr b/src/test/ui/async-await/issue-61949-self-return-type.stderr index 12fb77d8dd637..4eeef871c5bfc 100644 --- a/src/test/ui/async-await/issue-61949-self-return-type.stderr +++ b/src/test/ui/async-await/issue-61949-self-return-type.stderr @@ -1,8 +1,9 @@ -error: `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope +error[E0760]: `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope --> $DIR/issue-61949-self-return-type.rs:11:40 | LL | pub async fn new(_bar: &'a i32) -> Self { - | ^^^^ + | ^^^^ help: consider spelling out the type instead: `Foo<'a>` error: aborting due to previous error +For more information about this error, try `rustc --explain E0760`. 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

(&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 26132e36c9750..00529f0e2d54f 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. @@ -1480,7 +1526,11 @@ where { #[inline] fn next_back(&mut self) -> Option { - self.iter.next_back().or_else(|| self.peeked.take().and_then(|x| x)) + match self.peeked.as_mut() { + Some(v @ Some(_)) => self.iter.next_back().or_else(|| v.take()), + Some(None) => None, + None => self.iter.next_back(), + } } #[inline] @@ -1569,6 +1619,69 @@ impl Peekable { let iter = &mut self.iter; self.peeked.get_or_insert_with(|| iter.next()).as_ref() } + + /// Consume the next value of this iterator if a condition is true. + /// + /// If `func` returns `true` for the next value of this iterator, consume and return it. + /// Otherwise, return `None`. + /// + /// # Examples + /// Consume a number if it's equal to 0. + /// ``` + /// #![feature(peekable_next_if)] + /// let mut iter = (0..5).peekable(); + /// // The first item of the iterator is 0; consume it. + /// assert_eq!(iter.next_if(|&x| x == 0), Some(0)); + /// // The next item returned is now 1, so `consume` will return `false`. + /// assert_eq!(iter.next_if(|&x| x == 0), None); + /// // `next_if` saves the value of the next item if it was not equal to `expected`. + /// assert_eq!(iter.next(), Some(1)); + /// ``` + /// + /// Consume any number less than 10. + /// ``` + /// #![feature(peekable_next_if)] + /// let mut iter = (1..20).peekable(); + /// // Consume all numbers less than 10 + /// while iter.next_if(|&x| x < 10).is_some() {} + /// // The next value returned will be 10 + /// assert_eq!(iter.next(), Some(10)); + /// ``` + #[unstable(feature = "peekable_next_if", issue = "72480")] + pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option { + match self.next() { + Some(matched) if func(&matched) => Some(matched), + other => { + // Since we called `self.next()`, we consumed `self.peeked`. + assert!(self.peeked.is_none()); + self.peeked = Some(other); + None + } + } + } + + /// Consume the next item if it is equal to `expected`. + /// + /// # Example + /// Consume a number if it's equal to 0. + /// ``` + /// #![feature(peekable_next_if)] + /// let mut iter = (0..5).peekable(); + /// // The first item of the iterator is 0; consume it. + /// assert_eq!(iter.next_if_eq(&0), Some(0)); + /// // The next item returned is now 1, so `consume` will return `false`. + /// assert_eq!(iter.next_if_eq(&0), None); + /// // `next_if_eq` saves the value of the next item if it was not equal to `expected`. + /// assert_eq!(iter.next(), Some(1)); + /// ``` + #[unstable(feature = "peekable_next_if", issue = "72480")] + pub fn next_if_eq(&mut self, expected: &R) -> Option + where + R: ?Sized, + I::Item: PartialEq, + { + self.next_if(|next| next == expected) + } } /// An iterator that rejects elements while `predicate` returns `true`. @@ -1762,6 +1875,28 @@ 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")] +impl FusedIterator for TakeWhile +where + I: FusedIterator, + P: FnMut(&I::Item) -> bool, +{ } /// An iterator that only accepts elements while `predicate` returns `Some(_)`. @@ -1776,20 +1911,19 @@ where #[derive(Clone)] pub struct MapWhile { iter: I, - finished: bool, predicate: P, } impl MapWhile { pub(super) fn new(iter: I, predicate: P) -> MapWhile { - MapWhile { iter, finished: false, predicate } + MapWhile { iter, predicate } } } #[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] impl fmt::Debug for MapWhile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MapWhile").field("iter", &self.iter).field("flag", &self.finished).finish() + f.debug_struct("MapWhile").field("iter", &self.iter).finish() } } @@ -1802,63 +1936,44 @@ where #[inline] fn next(&mut self) -> Option { - if self.finished { - None - } else { - let x = self.iter.next()?; - let ret = (self.predicate)(x); - self.finished = ret.is_none(); - ret - } + let x = self.iter.next()?; + (self.predicate)(x) } #[inline] fn size_hint(&self) -> (usize, Option) { - if self.finished { - (0, Some(0)) - } else { - let (_, upper) = self.iter.size_hint(); - (0, upper) // can't know a lower bound, due to the predicate - } + let (_, upper) = self.iter.size_hint(); + (0, upper) // can't know a lower bound, due to the predicate } #[inline] - fn try_fold(&mut self, init: Acc, fold: Fold) -> R + fn try_fold(&mut self, init: Acc, mut fold: Fold) -> R where Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try, { - fn check<'a, B, T, Acc, R: Try>( - flag: &'a mut bool, - p: &'a mut impl FnMut(T) -> Option, - mut fold: impl FnMut(Acc, B) -> R + 'a, - ) -> impl FnMut(Acc, T) -> LoopState + 'a { - move |acc, x| match p(x) { - Some(item) => LoopState::from_try(fold(acc, item)), - None => { - *flag = true; - LoopState::Break(Try::from_ok(acc)) - } - } - } + let Self { iter, predicate } = self; + iter.try_fold(init, |acc, x| match predicate(x) { + Some(item) => LoopState::from_try(fold(acc, item)), + None => LoopState::Break(Try::from_ok(acc)), + }) + .into_try() + } - if self.finished { - Try::from_ok(init) - } else { - let flag = &mut self.finished; - let p = &mut self.predicate; - self.iter.try_fold(init, check(flag, p, fold)).into_try() + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + #[inline] + fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { + move |acc, x| Ok(f(acc, x)) } - } -} -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for TakeWhile -where - I: FusedIterator, - P: FnMut(&I::Item) -> bool, -{ + self.try_fold(init, ok(fold)).unwrap() + } } /// An iterator that skips over `n` elements of `iter`. @@ -2027,6 +2142,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")] @@ -2126,6 +2253,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")] @@ -2177,6 +2318,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")] @@ -2258,260 +2417,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() } } @@ -2720,4 +2638,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..bd7e6cfa5a750 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -1,51 +1,186 @@ +use crate::char; 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 + /// + /// 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))` /// - /// 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` 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. /// - /// The output of this method should always be less than the output of replace_one. - fn replace_zero(&mut self) -> Self; + /// # Invariants + /// + /// For any `a`, `n`, and `m`, where no overflow occurs: + /// + /// * `Step::forward(Step::forward(a, n), m) == Step::forward(a, n + m)` + /// + /// For any `a` and `n`, where no overflow occurs: + /// + /// * `Step::forward_checked(a, n) == Some(Step::forward(a, n))` + /// * `Step::forward(a, n) == (0..n).fold(a, |x, _| Step::forward(x, 1))` + /// * Corollary: `Step::forward(a, 0) == a` + /// * `Step::forward(a, n) >= a` + /// * `Step::backward(Step::forward(a, n), n) == a` + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] + fn forward(start: Self, count: usize) -> Self { + Step::forward_checked(start, count).expect("overflow in `Step::forward`") + } - /// 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 +188,285 @@ 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]; +} + +#[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]; } -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]); +#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +unsafe impl Step for char { + #[inline] + fn steps_between(&start: &char, &end: &char) -> Option { + let start = start as u32; + let end = end as u32; + if start <= end { + let count = end - start; + if start < 0xD800 && 0xE000 <= end { + usize::try_from(count - 0x800).ok() + } else { + usize::try_from(count).ok() + } + } else { + None + } + } + + #[inline] + fn forward_checked(start: char, count: usize) -> Option { + let start = start as u32; + let mut res = Step::forward_checked(start, count)?; + if start < 0xD800 && 0xD800 <= res { + res = Step::forward_checked(res, 0x800)?; + } + if res <= char::MAX as u32 { + // SAFETY: res is a valid unicode scalar + // (below 0x110000 and not in 0xD800..0xE000) + Some(unsafe { char::from_u32_unchecked(res) }) + } else { + None + } + } + + #[inline] + fn backward_checked(start: char, count: usize) -> Option { + let start = start as u32; + let mut res = Step::backward_checked(start, count)?; + if start >= 0xE000 && 0xE000 > res { + res = Step::backward_checked(res, 0x800)?; + } + // SAFETY: res is a valid unicode scalar + // (below 0x110000 and not in 0xD800..0xE000) + Some(unsafe { char::from_u32_unchecked(res) }) + } + + #[inline] + unsafe fn forward_unchecked(start: char, count: usize) -> char { + let start = start as u32; + let mut res = Step::forward_unchecked(start, count); + if start < 0xD800 && 0xD800 <= res { + res = Step::forward_unchecked(res, 0x800); + } + char::from_u32_unchecked(res) + } + + #[inline] + unsafe fn backward_unchecked(start: char, count: usize) -> char { + let start = start as u32; + let mut res = Step::backward_unchecked(start, count); + if start >= 0xE000 && 0xE000 > res { + res = Step::backward_unchecked(res, 0x800); + } + char::from_u32_unchecked(res) + } +} macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( @@ -189,20 +482,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 +489,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 +502,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 +540,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 +584,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 +596,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 +608,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 +619,8 @@ 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(); + let plus_n = Step::forward(self.start.clone(), n); + self.start = Step::forward(plus_n.clone(), 1); Some(plus_n) } } @@ -346,7 +642,11 @@ impl Iterator for ops::RangeInclusive { } let is_iterating = self.start < self.end; Some(if is_iterating { - let n = self.start.add_one(); + // SAFETY: just checked precondition + // We use the unchecked version here, because + // otherwise `for _ in '\0'..=char::MAX` + // does not successfully remove panicking code. + let n = unsafe { Step::forward_unchecked(self.start.clone(), 1) }; mem::replace(&mut self.start, n) } else { self.exhausted = true; @@ -372,12 +672,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 +708,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 +722,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 +761,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 +775,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 +811,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 +824,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/collect.rs b/src/libcore/iter/traits/collect.rs index f21ab8dbc3737..9d20022b6ed6d 100644 --- a/src/libcore/iter/traits/collect.rs +++ b/src/libcore/iter/traits/collect.rs @@ -322,7 +322,7 @@ impl IntoIterator for I { pub trait Extend { /// Extends a collection with the contents of an iterator. /// - /// As this is the only method for this trait, the [trait-level] docs + /// As this is the only required method for this trait, the [trait-level] docs /// contain more details. /// /// [trait-level]: trait.Extend.html @@ -341,6 +341,20 @@ pub trait Extend { /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn extend>(&mut self, iter: T); + + /// Extends a collection with exactly one element. + #[unstable(feature = "extend_one", issue = "72631")] + fn extend_one(&mut self, item: A) { + self.extend(Some(item)); + } + + /// Reserves capacity in a collection for the given number of additional elements. + /// + /// The default implementation does nothing. + #[unstable(feature = "extend_one", issue = "72631")] + fn extend_reserve(&mut self, additional: usize) { + let _ = additional; + } } #[stable(feature = "extend_for_unit", since = "1.28.0")] @@ -348,4 +362,5 @@ impl Extend<()> for () { fn extend>(&mut self, iter: T) { iter.into_iter().for_each(drop) } + fn extend_one(&mut self, _item: ()) {} } diff --git a/src/libcore/iter/traits/double_ended.rs b/src/libcore/iter/traits/double_ended.rs index 104724d9fb63a..f6329c6c593ed 100644 --- a/src/libcore/iter/traits/double_ended.rs +++ b/src/libcore/iter/traits/double_ended.rs @@ -63,6 +63,32 @@ pub trait DoubleEndedIterator: Iterator { /// assert_eq!(None, iter.next()); /// assert_eq!(None, iter.next_back()); /// ``` + /// + /// # Remarks + /// + /// The elements yielded by `DoubleEndedIterator`'s methods may differ from + /// the ones yielded by `Iterator`'s methods: + /// + /// ``` + /// let vec = vec![(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b')]; + /// let uniq_by_fst_comp = || { + /// let mut seen = std::collections::HashSet::new(); + /// vec.iter().copied().filter(move |x| seen.insert(x.0)) + /// }; + /// + /// assert_eq!(uniq_by_fst_comp().last(), Some((2, 'a'))); + /// assert_eq!(uniq_by_fst_comp().next_back(), Some((2, 'b'))); + /// + /// assert_eq!( + /// uniq_by_fst_comp().fold(vec![], |mut v, x| {v.push(x); v}), + /// vec![(1, 'a'), (2, 'a')] + /// ); + /// assert_eq!( + /// uniq_by_fst_comp().rfold(vec![], |mut v, x| {v.push(x); v}), + /// vec![(2, 'b'), (1, 'c')] + /// ); + /// ``` + /// #[stable(feature = "rust1", since = "1.0.0")] fn next_back(&mut self) -> Option; @@ -221,17 +247,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 e2ebef9c6ce2f..530cf881f29da 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); } @@ -1037,9 +1037,6 @@ pub trait Iterator { /// closure on each element of the iterator, and yield elements /// while it returns [`Some(_)`][`Some`]. /// - /// After [`None`] is returned, `map_while()`'s job is over, and the - /// rest of the elements are ignored. - /// /// # Examples /// /// Basic usage: @@ -1079,15 +1076,14 @@ pub trait Iterator { /// #![feature(iter_map_while)] /// use std::convert::TryFrom; /// - /// let a = [0, -1, 1, -2]; + /// let a = [0, 1, 2, -3, 4, 5, -6]; /// - /// let mut iter = a.iter().map_while(|x| u32::try_from(*x).ok()); + /// let iter = a.iter().map_while(|x| u32::try_from(*x).ok()); + /// let vec = iter.collect::>(); /// - /// assert_eq!(iter.next(), Some(0u32)); - /// - /// // We have more elements that are fit in u32, but since we already - /// // got a None, map_while() isn't used any more - /// assert_eq!(iter.next(), None); + /// // We have more elements which could fit in u32 (4, 5), but `map_while` returned `None` for `-3` + /// // (as the `predicate` returned `None`) and `collect` stops at the first `None` entcountered. + /// assert_eq!(vec, vec![0, 1, 2]); /// ``` /// /// Because `map_while()` needs to look at the value in order to see if it @@ -1115,8 +1111,13 @@ pub trait Iterator { /// The `-3` is no longer there, because it was consumed in order to see if /// the iteration should stop, but wasn't placed back into the iterator. /// + /// Note that unlike [`take_while`] this iterator is **not** fused. + /// It is also not specified what this iterator returns after the first` None` is returned. + /// If you need fused iterator, use [`fuse`]. + /// /// [`Some`]: ../../std/option/enum.Option.html#variant.Some /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`fuse`]: #method.fuse #[inline] #[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] fn map_while(self, predicate: P) -> MapWhile @@ -1179,6 +1180,17 @@ pub trait Iterator { /// assert_eq!(iter.next(), Some(2)); /// assert_eq!(iter.next(), None); /// ``` + /// + /// If less than `n` elements are available, + /// `take` will limit itself to the size of the underlying iterator: + /// + /// ``` + /// let v = vec![1, 2]; + /// let mut iter = v.into_iter().take(5); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), None); + /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn take(self, n: usize) -> Take @@ -1696,12 +1708,12 @@ 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)); + left.extend_one(x); } else { - right.extend(Some(x)); + right.extend_one(x); } } } @@ -1709,7 +1721,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) } @@ -1825,7 +1837,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. /// @@ -1943,6 +1955,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: @@ -1991,17 +2012,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. @@ -2208,7 +2265,7 @@ pub trait Iterator { } /// Applies function to the elements of iterator and returns - /// the first non-none result or the first error. + /// the first true result or the first error. /// /// # Examples /// @@ -2229,19 +2286,26 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "try_find", reason = "new API", issue = "63178")] - fn try_find(&mut self, mut f: F) -> Result, E> + fn try_find(&mut self, f: F) -> Result, R::Error> where Self: Sized, F: FnMut(&Self::Item) -> R, - R: Try, + R: Try, { - self.try_for_each(move |x| match f(&x).into_result() { - Ok(false) => LoopState::Continue(()), - Ok(true) => LoopState::Break(Ok(x)), - Err(x) => LoopState::Break(Err(x)), - }) - .break_value() - .transpose() + #[inline] + fn check(mut f: F) -> impl FnMut((), T) -> LoopState<(), Result> + where + F: FnMut(&T) -> R, + R: Try, + { + move |(), x| match f(&x).into_result() { + Ok(false) => LoopState::Continue(()), + Ok(true) => LoopState::Break(Ok(x)), + Err(x) => LoopState::Break(Err(x)), + } + } + + self.try_fold((), check(f)).break_value().transpose() } /// Searches for an element in an iterator, returning its index. @@ -2496,7 +2560,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 @@ -2560,7 +2624,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. @@ -2627,17 +2691,23 @@ 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)| { - ts.extend(Some(t)); - us.extend(Some(u)); + ) -> impl FnMut((), (A, B)) + 'a { + move |(), (t, u)| { + ts.extend_one(t); + us.extend_one(u); } } let mut ts: FromA = Default::default(); let mut us: FromB = Default::default(); - self.for_each(extend(&mut ts, &mut us)); + let (lower_bound, _) = self.size_hint(); + if lower_bound > 0 { + ts.extend_reserve(lower_bound); + us.extend_reserve(lower_bound); + } + + self.fold((), extend(&mut ts, &mut us)); (ts, us) } @@ -2654,12 +2724,12 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let v_cloned: Vec<_> = a.iter().copied().collect(); + /// let v_copied: Vec<_> = a.iter().copied().collect(); /// /// // copied is the same as .map(|&x| x) /// let v_map: Vec<_> = a.iter().map(|&x| x).collect(); /// - /// assert_eq!(v_cloned, vec![1, 2, 3]); + /// assert_eq!(v_copied, vec![1, 2, 3]); /// assert_eq!(v_map, vec![1, 2, 3]); /// ``` #[stable(feature = "iter_copied", since = "1.36.0")] @@ -2882,7 +2952,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 @@ -3071,7 +3141,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 @@ -3111,7 +3181,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. @@ -3132,7 +3202,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")] @@ -3159,7 +3229,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 @@ -3213,20 +3283,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 a1dde1d51ef80..4eb2fdbd07868 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -85,8 +85,12 @@ #![feature(const_panic)] #![feature(const_fn_union)] #![feature(const_generics)] +#![feature(const_ptr_offset)] #![feature(const_ptr_offset_from)] +#![cfg_attr(not(bootstrap), feature(const_raw_ptr_comparison))] #![feature(const_result)] +#![feature(const_slice_from_raw_parts)] +#![feature(const_slice_ptr_len)] #![feature(const_type_name)] #![feature(custom_inner_attributes)] #![feature(decl_macro)] @@ -98,11 +102,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 +139,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)] @@ -140,7 +146,7 @@ #![feature(associated_type_bounds)] #![feature(const_type_id)] #![feature(const_caller_location)] -#![cfg_attr(not(bootstrap), feature(no_niche))] // rust-lang/rust#68303 +#![feature(no_niche)] // rust-lang/rust#68303 #[prelude_import] #[allow(unused)] @@ -272,6 +278,9 @@ pub mod primitive; // crate uses the this crate as its libcore. #[path = "../stdarch/crates/core_arch/src/mod.rs"] #[allow(missing_docs, missing_debug_implementations, dead_code, unused_imports)] +// FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_decl is +// merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet. +#[cfg_attr(not(bootstrap), allow(clashing_extern_decl))] #[unstable(feature = "stdsimd", issue = "48556")] mod core_arch; diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs index 04af5b5f76828..3cfdde60135b7 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 = "prefer using the new asm! syntax instead" + )] + #[rustc_builtin_macro] + #[macro_export] + macro_rules! llvm_asm { ("assembly template" : $("output"(operand),)* : $("input"(operand),)* @@ -1404,6 +1411,17 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Keeps the item it's applied to if the passed path is accessible, and removes it otherwise. + #[unstable( + feature = "cfg_accessible", + issue = "64797", + reason = "`cfg_accessible` is not fully implemented" + )] + #[rustc_builtin_macro] + pub macro cfg_accessible($item:item) { + /* compiler built-in */ + } + /// Unstable implementation detail of the `rustc` compiler, do not use. #[rustc_builtin_macro] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 2800f11cc01b1..6040dd31847a9 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,9 +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 +#[rustc_specialization_trait] pub trait Sized { // Empty. } @@ -362,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. } @@ -659,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; @@ -672,6 +680,25 @@ 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" +)] +#[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; +} + /// 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 @@ -709,6 +736,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; /// @@ -759,7 +787,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; @@ -789,7 +818,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..499016545e967 100644 --- a/src/libcore/mem/maybe_uninit.rs +++ b/src/libcore/mem/maybe_uninit.rs @@ -20,9 +20,9 @@ use crate::mem::ManuallyDrop; /// # #![allow(invalid_value)] /// use std::mem::{self, MaybeUninit}; /// -/// let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior! +/// let x: &i32 = unsafe { mem::zeroed() }; // undefined behavior! ⚠️ /// // The equivalent code with `MaybeUninit<&i32>`: -/// let x: &i32 = unsafe { MaybeUninit::zeroed().assume_init() }; // undefined behavior! +/// let x: &i32 = unsafe { MaybeUninit::zeroed().assume_init() }; // undefined behavior! ⚠️ /// ``` /// /// This is exploited by the compiler for various optimizations, such as eliding @@ -35,9 +35,9 @@ use crate::mem::ManuallyDrop; /// # #![allow(invalid_value)] /// use std::mem::{self, MaybeUninit}; /// -/// let b: bool = unsafe { mem::uninitialized() }; // undefined behavior! +/// let b: bool = unsafe { mem::uninitialized() }; // undefined behavior! ⚠️ /// // The equivalent code with `MaybeUninit`: -/// let b: bool = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! +/// let b: bool = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! ⚠️ /// ``` /// /// Moreover, uninitialized memory is special in that the compiler knows that @@ -49,9 +49,9 @@ use crate::mem::ManuallyDrop; /// # #![allow(invalid_value)] /// use std::mem::{self, MaybeUninit}; /// -/// let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior! +/// let x: i32 = unsafe { mem::uninitialized() }; // undefined behavior! ⚠️ /// // The equivalent code with `MaybeUninit`: -/// let x: i32 = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! +/// let x: i32 = unsafe { MaybeUninit::uninit().assume_init() }; // undefined behavior! ⚠️ /// ``` /// (Notice that the rules around uninitialized integers are not finalized yet, but /// until they are, it is advisable to avoid them.) @@ -214,7 +214,6 @@ use crate::mem::ManuallyDrop; /// remain `#[repr(transparent)]`. That said, `MaybeUninit` will *always* guarantee that it has /// the same size, alignment, and ABI as `T`; it's just that the way `MaybeUninit` implements that /// guarantee may evolve. -#[allow(missing_debug_implementations)] #[stable(feature = "maybe_uninit", since = "1.36.0")] // Lang item so we can wrap other types in it. This is useful for generators. #[lang = "maybe_uninit"] @@ -348,7 +347,7 @@ impl MaybeUninit { /// let x = MaybeUninit::<(u8, NotZero)>::zeroed(); /// let x = unsafe { x.assume_init() }; /// // Inside a pair, we create a `NotZero` that does not have a valid discriminant. - /// // This is undefined behavior. + /// // This is undefined behavior. ⚠️ /// ``` #[stable(feature = "maybe_uninit", since = "1.36.0")] #[inline] @@ -400,7 +399,7 @@ impl MaybeUninit { /// /// let x = MaybeUninit::>::uninit(); /// let x_vec = unsafe { &*x.as_ptr() }; - /// // We have created a reference to an uninitialized vector! This is undefined behavior. + /// // We have created a reference to an uninitialized vector! This is undefined behavior. ⚠️ /// ``` /// /// (Notice that the rules around references to uninitialized data are not finalized yet, but @@ -437,7 +436,7 @@ impl MaybeUninit { /// /// let mut x = MaybeUninit::>::uninit(); /// let x_vec = unsafe { &mut *x.as_mut_ptr() }; - /// // We have created a reference to an uninitialized vector! This is undefined behavior. + /// // We have created a reference to an uninitialized vector! This is undefined behavior. ⚠️ /// ``` /// /// (Notice that the rules around references to uninitialized data are not finalized yet, but @@ -489,15 +488,12 @@ impl MaybeUninit { /// /// let x = MaybeUninit::>::uninit(); /// let x_init = unsafe { x.assume_init() }; - /// // `x` had not been initialized yet, so this last line caused undefined behavior. + /// // `x` had not been initialized yet, so this last line caused undefined behavior. ⚠️ /// ``` #[stable(feature = "maybe_uninit", since = "1.36.0")] #[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) } @@ -556,15 +552,12 @@ impl MaybeUninit { /// x.write(Some(vec![0,1,2])); /// let x1 = unsafe { x.read() }; /// let x2 = unsafe { x.read() }; - /// // We now created two copies of the same vector, leading to a double-free when + /// // We now created two copies of the same vector, leading to a double-free ⚠️ when /// // they both get dropped! /// ``` #[unstable(feature = "maybe_uninit_extra", issue = "63567")] #[inline(always)] pub unsafe fn read(&self) -> T { - #[cfg(bootstrap)] - intrinsics::panic_if_uninhabited::(); - #[cfg(not(bootstrap))] intrinsics::assert_inhabited::(); self.as_ptr().read() } @@ -609,7 +602,7 @@ impl MaybeUninit { /// /// let x = MaybeUninit::>::uninit(); /// let x_vec: &Vec = unsafe { x.get_ref() }; - /// // We have created a reference to an uninitialized vector! This is undefined behavior. + /// // We have created a reference to an uninitialized vector! This is undefined behavior. ⚠️ /// ``` /// /// ```rust,no_run @@ -627,9 +620,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 } @@ -695,7 +685,7 @@ impl MaybeUninit { /// unsafe { /// *b.get_mut() = true; /// // We have created a (mutable) reference to an uninitialized `bool`! - /// // This is undefined behavior. + /// // This is undefined behavior. ⚠️ /// } /// ``` /// @@ -748,9 +738,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 e65150af59f81..066bb8b3dc787 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; @@ -58,7 +58,9 @@ pub use crate::intrinsics::transmute; /// /// # Examples /// -/// Leak an I/O object, never closing the file: +/// The canonical safe use of `mem::forget` is to circumvent a value's destructor +/// implemented by the `Drop` trait. For example, this will leak a `File`, i.e. reclaim +/// the space taken by the variable but never close the underlying system resource: /// /// ```no_run /// use std::mem; @@ -68,9 +70,40 @@ pub use crate::intrinsics::transmute; /// mem::forget(file); /// ``` /// -/// The practical use cases for `forget` are rather specialized and mainly come -/// up in unsafe or FFI code. However, [`ManuallyDrop`] is usually preferred -/// for such cases, e.g.: +/// This is useful when the ownership of the underlying resource was previously +/// transferred to code outside of Rust, for example by transmitting the raw +/// file descriptor to C code. +/// +/// # Relationship with `ManuallyDrop` +/// +/// While `mem::forget` can also be used to transfer *memory* ownership, doing so is error-prone. +/// [`ManuallyDrop`] should be used instead. Consider, for example, this code: +/// +/// ``` +/// use std::mem; +/// +/// let mut v = vec![65, 122]; +/// // Build a `String` using the contents of `v` +/// let s = unsafe { String::from_raw_parts(v.as_mut_ptr(), v.len(), v.capacity()) }; +/// // leak `v` because its memory is now managed by `s` +/// mem::forget(v); // ERROR - v is invalid and must not be passed to a function +/// assert_eq!(s, "Az"); +/// // `s` is implicitly dropped and its memory deallocated. +/// ``` +/// +/// There are two issues with the above example: +/// +/// * If more code were added between the construction of `String` and the invocation of +/// `mem::forget()`, a panic within it would cause a double free because the same memory +/// is handled by both `v` and `s`. +/// * After calling `v.as_mut_ptr()` and transmitting the ownership of the data to `s`, +/// the `v` value is invalid. Even when a value is just moved to `mem::forget` (which won't +/// inspect it), some types have strict requirements on their values that +/// make them invalid when dangling or no longer owned. Using invalid values in any +/// way, including passing them to or returning them from functions, constitutes +/// undefined behavior and may break the assumptions made by the compiler. +/// +/// Switching to `ManuallyDrop` avoids both issues: /// /// ``` /// use std::mem::ManuallyDrop; @@ -80,24 +113,24 @@ pub use crate::intrinsics::transmute; /// // does not get dropped! /// let mut v = ManuallyDrop::new(v); /// // Now disassemble `v`. These operations cannot panic, so there cannot be a leak. -/// let ptr = v.as_mut_ptr(); -/// let cap = v.capacity(); +/// let (ptr, len, cap) = (v.as_mut_ptr(), v.len(), v.capacity()); /// // Finally, build a `String`. -/// let s = unsafe { String::from_raw_parts(ptr, 2, cap) }; +/// let s = unsafe { String::from_raw_parts(ptr, len, cap) }; /// assert_eq!(s, "Az"); /// // `s` is implicitly dropped and its memory deallocated. /// ``` /// -/// Using `ManuallyDrop` here has two advantages: +/// `ManuallyDrop` robustly prevents double-free because we disable `v`'s destructor +/// before doing anything else. `mem::forget()` doesn't allow this because it consumes its +/// argument, forcing us to call it only after extracting anything we need from `v`. Even +/// if a panic were introduced between construction of `ManuallyDrop` and building the +/// string (which cannot happen in the code as shown), it would result in a leak and not a +/// double free. In other words, `ManuallyDrop` errs on the side of leaking instead of +/// erring on the side of (double-)dropping. /// -/// * We do not "touch" `v` after disassembling it. For some types, operations -/// such as passing ownership (to a function like `mem::forget`) requires them to actually -/// be fully owned right now; that is a promise we do not want to make here as we are -/// in the process of transferring ownership to the new `String` we are building. -/// * In case of an unexpected panic, `ManuallyDrop` is not dropped, but if the panic -/// occurs before `mem::forget` was called we might end up dropping invalid data, -/// or double-dropping. In other words, `ManuallyDrop` errs on the side of leaking -/// instead of erring on the side of dropping. +/// Also, `ManuallyDrop` prevents us from having to "touch" `v` after transferring the +/// ownership to `s` — the final step of interacting with `v` to dispose of it without +/// running its destructor is entirely avoided. /// /// [drop]: fn.drop.html /// [uninit]: fn.uninitialized.html @@ -303,6 +336,53 @@ pub fn size_of_val(val: &T) -> usize { intrinsics::size_of_val(val) } +/// Returns the size of the pointed-to value in bytes. +/// +/// This is usually the same as `size_of::()`. However, when `T` *has* no +/// statically-known size, e.g., a slice [`[T]`][slice] or a [trait object], +/// then `size_of_val_raw` can be used to get the dynamically-known size. +/// +/// # Safety +/// +/// This function is only safe to call if the following conditions hold: +/// +/// - If `T` is `Sized`, this function is always safe to call. +/// - If the unsized tail of `T` is: +/// - a [slice], then the length of the slice tail must be an intialized +/// integer, and the size of the *entire value* +/// (dynamic tail length + statically sized prefix) must fit in `isize`. +/// - a [trait object], then the vtable part of the pointer must point +/// to a valid vtable acquired by an unsizing coersion, and the size +/// of the *entire value* (dynamic tail length + statically sized prefix) +/// must fit in `isize`. +/// - an (unstable) [extern type], then this function is always safe to +/// call, but may panic or otherwise return the wrong value, as the +/// extern type's layout is not known. This is the same behavior as +/// [`size_of_val`] on a reference to 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 +/// +/// # Examples +/// +/// ``` +/// #![feature(layout_for_ptr)] +/// use std::mem; +/// +/// assert_eq!(4, mem::size_of_val(&5i32)); +/// +/// let x: [u8; 13] = [0; 13]; +/// let y: &[u8] = &x; +/// assert_eq!(13, unsafe { mem::size_of_val_raw(y) }); +/// ``` +#[inline] +#[unstable(feature = "layout_for_ptr", issue = "69835")] +pub unsafe fn size_of_val_raw(val: *const T) -> usize { + intrinsics::size_of_val(val) +} + /// Returns the [ABI]-required minimum alignment of a type. /// /// Every reference to a value of the type `T` must be a multiple of this number. @@ -390,6 +470,49 @@ pub fn align_of_val(val: &T) -> usize { min_align_of_val(val) } +/// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. +/// +/// Every reference to a value of the type `T` must be a multiple of this number. +/// +/// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface +/// +/// # Safety +/// +/// This function is only safe to call if the following conditions hold: +/// +/// - If `T` is `Sized`, this function is always safe to call. +/// - If the unsized tail of `T` is: +/// - a [slice], then the length of the slice tail must be an intialized +/// integer, and the size of the *entire value* +/// (dynamic tail length + statically sized prefix) must fit in `isize`. +/// - a [trait object], then the vtable part of the pointer must point +/// to a valid vtable acquired by an unsizing coersion, and the size +/// of the *entire value* (dynamic tail length + statically sized prefix) +/// must fit in `isize`. +/// - an (unstable) [extern type], then this function is always safe to +/// call, but may panic or otherwise return the wrong value, as the +/// extern type's layout is not known. This is the same behavior as +/// [`align_of_val`] on a reference to 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 +/// +/// # Examples +/// +/// ``` +/// #![feature(layout_for_ptr)] +/// use std::mem; +/// +/// assert_eq!(4, unsafe { mem::align_of_val_raw(&5i32) }); +/// ``` +#[inline] +#[unstable(feature = "layout_for_ptr", issue = "69835")] +pub unsafe fn align_of_val_raw(val: *const T) -> usize { + intrinsics::min_align_of_val(val) +} + /// Returns `true` if dropping values of type `T` matters. /// /// This is purely an optimization hint, and may be implemented conservatively: @@ -458,11 +581,12 @@ pub const fn needs_drop() -> bool { /// This means that, for example, the padding byte in `(u8, u16)` is not /// necessarily zeroed. /// -/// There is no guarantee that an all-zero byte-pattern represents a valid value of -/// some type `T`. For example, the all-zero byte-pattern is not a valid value -/// for reference types (`&T` and `&mut T`). Using `zeroed` on such types -/// causes immediate [undefined behavior][ub] because [the Rust compiler assumes][inv] -/// that there always is a valid value in a variable it considers initialized. +/// There is no guarantee that an all-zero byte-pattern represents a valid value +/// of some type `T`. For example, the all-zero byte-pattern is not a valid value +/// for reference types (`&T`, `&mut T`) and functions pointers. Using `zeroed` +/// on such types causes immediate [undefined behavior][ub] because [the Rust +/// compiler assumes][inv] that there always is a valid value in a variable it +/// considers initialized. /// /// This has the same effect as [`MaybeUninit::zeroed().assume_init()`][zeroed]. /// It is useful for FFI sometimes, but should generally be avoided. @@ -489,18 +613,16 @@ pub const fn needs_drop() -> bool { /// use std::mem; /// /// let _x: &i32 = unsafe { mem::zeroed() }; // Undefined behavior! +/// let _y: fn() = unsafe { mem::zeroed() }; // And again! /// ``` -#[inline] +#[inline(always)] #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated_in_future)] #[allow(deprecated)] #[rustc_diagnostic_item = "mem_zeroed"] pub unsafe fn zeroed() -> T { - #[cfg(not(bootstrap))] intrinsics::assert_zero_valid::(); - #[cfg(bootstrap)] - intrinsics::panic_if_uninhabited::(); - intrinsics::init() + MaybeUninit::zeroed().assume_init() } /// Bypasses Rust's normal memory-initialization checks by pretending to @@ -525,18 +647,15 @@ pub unsafe fn zeroed() -> T { /// [uninit]: union.MaybeUninit.html#method.uninit /// [assume_init]: union.MaybeUninit.html#method.assume_init /// [inv]: union.MaybeUninit.html#initialization-invariant -#[inline] +#[inline(always)] #[rustc_deprecated(since = "1.39.0", reason = "use `mem::MaybeUninit` instead")] #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated_in_future)] #[allow(deprecated)] #[rustc_diagnostic_item = "mem_uninitialized"] pub unsafe fn uninitialized() -> T { - #[cfg(not(bootstrap))] intrinsics::assert_uninit_valid::(); - #[cfg(bootstrap)] - intrinsics::panic_if_uninhabited::(); - intrinsics::uninit() + MaybeUninit::uninit().assume_init() } /// Swaps the values at two mutable locations, without deinitializing either one. @@ -683,6 +802,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 @@ -690,7 +810,7 @@ pub fn replace(dest: &mut T, mut src: T) -> T { /// Disposes of a value. /// -/// This does call the argument's implementation of [`Drop`][drop]. +/// This does so by calling the argument's implementation of [`Drop`][drop]. /// /// This effectively does nothing for types which implement `Copy`, e.g. /// integers. Such values are copied and _then_ moved into the function, so the @@ -798,7 +918,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. @@ -807,7 +932,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. @@ -872,5 +997,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..6313de31ce4d5 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`. @@ -669,4 +810,78 @@ impl f32 { pub fn from_ne_bytes(bytes: [u8; 4]) -> Self { Self::from_bits(u32::from_ne_bytes(bytes)) } + + /// Returns an ordering between self and other values. + /// Unlike the standard partial comparison between floating point numbers, + /// this comparison always produces an ordering in accordance to + /// the totalOrder predicate as defined in IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in following order: + /// - Negative quiet NaN + /// - Negative signaling NaN + /// - Negative infinity + /// - Negative numbers + /// - Negative subnormal numbers + /// - Negative zero + /// - Positive zero + /// - Positive subnormal numbers + /// - Positive numbers + /// - Positive infinity + /// - Positive signaling NaN + /// - Positive quiet NaN + /// + /// # Example + /// ``` + /// #![feature(total_cmp)] + /// struct GoodBoy { + /// name: String, + /// weight: f32, + /// } + /// + /// let mut bois = vec![ + /// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 }, + /// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 }, + /// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 }, + /// GoodBoy { name: "Chonk".to_owned(), weight: f32::INFINITY }, + /// GoodBoy { name: "Abs. Unit".to_owned(), weight: f32::NAN }, + /// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 }, + /// ]; + /// + /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); + /// # assert!(bois.into_iter().map(|b| b.weight) + /// # .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter()) + /// # .all(|(a, b)| a.to_bits() == b.to_bits())) + /// ``` + #[unstable(feature = "total_cmp", issue = "72599")] + #[inline] + pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + let mut left = self.to_bits() as i32; + let mut right = other.to_bits() as i32; + + // In case of negatives, flip all the bits except the sign + // to achieve a similar layout as two's complement integers + // + // Why does this work? IEEE 754 floats consist of three fields: + // Sign bit, exponent and mantissa. The set of exponent and mantissa + // fields as a whole have the property that their bitwise order is + // equal to the numeric magnitude where the magnitude is defined. + // The magnitude is not normally defined on NaN values, but + // IEEE 754 totalOrder defines the NaN values also to follow the + // bitwise order. This leads to order explained in the doc comment. + // However, the representation of magnitude is the same for negative + // and positive numbers – only the sign bit is different. + // To easily compare the floats as signed integers, we need to + // flip the exponent and mantissa bits in case of negative numbers. + // We effectively convert the numbers to "two's complement" form. + // + // To do the flipping, we construct a mask and XOR against it. + // We branchlessly calculate an "all-ones except for the sign bit" + // mask from negative-signed values: right shifting sign-extends + // the integer, so we "fill" the mask with sign bits, and then + // convert to unsigned to push one more zero bit. + // On positive values, the mask is all zeros, so it's a no-op. + left ^= (((left >> 31) as u32) >> 1) as i32; + right ^= (((right >> 31) as u32) >> 1) as i32; + + left.cmp(&right) + } } diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 129df937c0bb8..d42e5392c5863 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`. @@ -683,4 +824,78 @@ impl f64 { pub fn from_ne_bytes(bytes: [u8; 8]) -> Self { Self::from_bits(u64::from_ne_bytes(bytes)) } + + /// Returns an ordering between self and other values. + /// Unlike the standard partial comparison between floating point numbers, + /// this comparison always produces an ordering in accordance to + /// the totalOrder predicate as defined in IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in following order: + /// - Negative quiet NaN + /// - Negative signaling NaN + /// - Negative infinity + /// - Negative numbers + /// - Negative subnormal numbers + /// - Negative zero + /// - Positive zero + /// - Positive subnormal numbers + /// - Positive numbers + /// - Positive infinity + /// - Positive signaling NaN + /// - Positive quiet NaN + /// + /// # Example + /// ``` + /// #![feature(total_cmp)] + /// struct GoodBoy { + /// name: String, + /// weight: f64, + /// } + /// + /// let mut bois = vec![ + /// GoodBoy { name: "Pucci".to_owned(), weight: 0.1 }, + /// GoodBoy { name: "Woofer".to_owned(), weight: 99.0 }, + /// GoodBoy { name: "Yapper".to_owned(), weight: 10.0 }, + /// GoodBoy { name: "Chonk".to_owned(), weight: f64::INFINITY }, + /// GoodBoy { name: "Abs. Unit".to_owned(), weight: f64::NAN }, + /// GoodBoy { name: "Floaty".to_owned(), weight: -5.0 }, + /// ]; + /// + /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); + /// # assert!(bois.into_iter().map(|b| b.weight) + /// # .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter()) + /// # .all(|(a, b)| a.to_bits() == b.to_bits())) + /// ``` + #[unstable(feature = "total_cmp", issue = "72599")] + #[inline] + pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + let mut left = self.to_bits() as i64; + let mut right = other.to_bits() as i64; + + // In case of negatives, flip all the bits except the sign + // to achieve a similar layout as two's complement integers + // + // Why does this work? IEEE 754 floats consist of three fields: + // Sign bit, exponent and mantissa. The set of exponent and mantissa + // fields as a whole have the property that their bitwise order is + // equal to the numeric magnitude where the magnitude is defined. + // The magnitude is not normally defined on NaN values, but + // IEEE 754 totalOrder defines the NaN values also to follow the + // bitwise order. This leads to order explained in the doc comment. + // However, the representation of magnitude is the same for negative + // and positive numbers – only the sign bit is different. + // To easily compare the floats as signed integers, we need to + // flip the exponent and mantissa bits in case of negative numbers. + // We effectively convert the numbers to "two's complement" form. + // + // To do the flipping, we construct a mask and XOR against it. + // We branchlessly calculate an "all-ones except for the sign bit" + // mask from negative-signed values: right shifting sign-extends + // the integer, so we "fill" the mask with sign bits, and then + // convert to unsigned to push one more zero bit. + // On positive values, the mask is all zeros, so it's a no-op. + left ^= (((left >> 63) as u64) >> 1) as i64; + right ^= (((right >> 63) as u64) >> 1) as i64; + + left.cmp(&right) + } } diff --git a/src/libcore/num/flt2dec/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..b1317bc2121f6 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` or `self + rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + 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` or `self - rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + 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` or `self * rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + 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. @@ -768,7 +871,7 @@ $EndFeature, " without modifying the original"] #[inline] pub const fn checked_div(self, rhs: Self) -> Option { - if rhs == 0 || (self == Self::min_value() && rhs == -1) { + if rhs == 0 || (self == Self::MIN && rhs == -1) { None } else { // SAFETY: div by zero and by INT_MIN have been checked above @@ -797,7 +900,7 @@ assert_eq!((1", stringify!($SelfT), ").checked_div_euclid(0), None); without modifying the original"] #[inline] pub const fn checked_div_euclid(self, rhs: Self) -> Option { - if rhs == 0 || (self == Self::min_value() && rhs == -1) { + if rhs == 0 || (self == Self::MIN && rhs == -1) { None } else { Some(self.div_euclid(rhs)) @@ -826,7 +929,7 @@ $EndFeature, " without modifying the original"] #[inline] pub const fn checked_rem(self, rhs: Self) -> Option { - if rhs == 0 || (self == Self::min_value() && rhs == -1) { + if rhs == 0 || (self == Self::MIN && rhs == -1) { None } else { // SAFETY: div by zero and by INT_MIN have been checked above @@ -854,7 +957,7 @@ assert_eq!(", stringify!($SelfT), "::MIN.checked_rem_euclid(-1), None); without modifying the original"] #[inline] pub const fn checked_rem_euclid(self, rhs: Self) -> Option { - if rhs == 0 || (self == Self::min_value() && rhs == -1) { + if rhs == 0 || (self == Self::MIN && rhs == -1) { None } else { Some(self.rem_euclid(rhs)) @@ -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 { @@ -1135,9 +1236,9 @@ $EndFeature, " match self.checked_mul(rhs) { Some(x) => x, None => if (self < 0) == (rhs < 0) { - Self::max_value() + Self::MAX } else { - Self::min_value() + Self::MIN } } } @@ -1166,8 +1267,8 @@ $EndFeature, " pub const fn saturating_pow(self, exp: u32) -> Self { match self.checked_pow(exp) { Some(x) => x, - None if self < 0 && exp % 2 == 1 => Self::min_value(), - None => Self::max_value(), + None if self < 0 && exp % 2 == 1 => Self::MIN, + None => Self::MAX, } } } @@ -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 @@ -1637,7 +1738,7 @@ $EndFeature, " #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { - if self == Self::min_value() && rhs == -1 { + if self == Self::MIN && rhs == -1 { (self, true) } else { (self / rhs, false) @@ -1670,7 +1771,7 @@ assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div_euclid(-1), (", stringi #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { - if self == Self::min_value() && rhs == -1 { + if self == Self::MIN && rhs == -1 { (self, true) } else { (self.div_euclid(rhs), false) @@ -1704,7 +1805,7 @@ $EndFeature, " #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { - if self == Self::min_value() && rhs == -1 { + if self == Self::MIN && rhs == -1 { (0, true) } else { (self % rhs, false) @@ -1737,7 +1838,7 @@ assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem_euclid(-1), (0, true)); without modifying the original"] #[inline] pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { - if self == Self::min_value() && rhs == -1 { + if self == Self::MIN && rhs == -1 { (0, true) } else { (self.rem_euclid(rhs), false) @@ -1768,8 +1869,8 @@ assert_eq!(", stringify!($SelfT), "::MIN.overflowing_neg(), (", stringify!($Self #[allow(unused_attributes)] #[allow_internal_unstable(const_if_match)] pub const fn overflowing_neg(self) -> (Self, bool) { - if self == Self::min_value() { - (Self::min_value(), true) + if self == Self::MIN { + (Self::MIN, true) } else { (-self, false) } @@ -1851,7 +1952,7 @@ $EndFeature, " #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[inline] pub const fn overflowing_abs(self) -> (Self, bool) { - (self.wrapping_abs(), self == Self::min_value()) + (self.wrapping_abs(), self == Self::MIN) } } @@ -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` or `self + rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + 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` or `self - rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + 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` or `self * rhs < ", stringify!($SelfT), "::MIN`."), + #[unstable( + feature = "unchecked_math", + reason = "niche optimization path", + issue = "none", + )] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + intrinsics::unchecked_mul(self, rhs) + } + } + doc_comment! { concat!("Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0`. @@ -3157,7 +3309,8 @@ Basic usage: ``` ", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); -assert_eq!(200u8.saturating_add(127), 255);", $EndFeature, " +assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(127), ", stringify!($SelfT), "::MAX);", +$EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] @@ -3214,7 +3367,7 @@ assert_eq!((", stringify!($SelfT), "::MAX).saturating_mul(10), ", stringify!($Se pub const fn saturating_mul(self, rhs: Self) -> Self { match self.checked_mul(rhs) { Some(x) => x, - None => Self::max_value(), + None => Self::MAX, } } } @@ -3241,7 +3394,7 @@ $EndFeature, " pub const fn saturating_pow(self, exp: u32) -> Self { match self.checked_pow(exp) { Some(x) => x, - None => Self::max_value(), + None => Self::MAX, } } } @@ -3456,8 +3609,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 +3643,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 @@ -3928,7 +4081,7 @@ Basic usage: } } - doc_comment! { + doc_comment! { concat!("Performs Euclidean division. Since, for the positive integers, all common @@ -4026,7 +4179,7 @@ assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " // (such as intel pre-haswell) have more efficient ctlz // intrinsics when the argument is non-zero. let z = unsafe { intrinsics::ctlz_nonzero(p) }; - <$SelfT>::max_value() >> z + <$SelfT>::MAX >> z } doc_comment! { @@ -4376,7 +4529,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 +4552,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) } @@ -5008,9 +5161,9 @@ trait FromStrRadixHelper: PartialOrd + Copy { macro_rules! doit { ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { #[inline] - fn min_value() -> Self { Self::min_value() } + fn min_value() -> Self { Self::MIN } #[inline] - fn max_value() -> Self { Self::max_value() } + fn max_value() -> Self { Self::MAX } #[inline] fn from_u32(u: u32) -> Self { u as Self } #[inline] diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs index 82fa6acfbd62a..f6acb8f8b9a92 100644 --- a/src/libcore/num/wrapping.rs +++ b/src/libcore/num/wrapping.rs @@ -337,14 +337,10 @@ Basic usage: #![feature(wrapping_int_impl)] use std::num::Wrapping; -assert_eq!(>::min_value(), ", -"Wrapping(", stringify!($t), "::min_value())); +assert_eq!(>::MIN, Wrapping(", stringify!($t), "::MIN)); ```"), #[unstable(feature = "wrapping_int_impl", issue = "32463")] - #[inline] - pub const fn min_value() -> Self { - Wrapping(<$t>::min_value()) - } + pub const MIN: Self = Self(<$t>::MIN); } doc_comment! { @@ -358,14 +354,10 @@ Basic usage: #![feature(wrapping_int_impl)] use std::num::Wrapping; -assert_eq!(>::max_value(), ", -"Wrapping(", stringify!($t), "::max_value())); +assert_eq!(>::MAX, Wrapping(", stringify!($t), "::MAX)); ```"), #[unstable(feature = "wrapping_int_impl", issue = "32463")] - #[inline] - pub const fn max_value() -> Self { - Wrapping(<$t>::max_value()) - } + pub const MAX: Self = Self(<$t>::MAX); } doc_comment! { @@ -702,7 +694,7 @@ Basic usage: #![feature(wrapping_int_impl)] use std::num::Wrapping; -let n = Wrapping(", stringify!($t), "::max_value()) >> 2; +let n = Wrapping(", stringify!($t), "::MAX) >> 2; assert_eq!(n.leading_zeros(), 3); ```"), @@ -731,8 +723,7 @@ use std::num::Wrapping; assert_eq!(Wrapping(100", stringify!($t), ").abs(), Wrapping(100)); assert_eq!(Wrapping(-100", stringify!($t), ").abs(), Wrapping(100)); -assert_eq!(Wrapping(", stringify!($t), "::min_value()).abs(), Wrapping(", stringify!($t), -"::min_value())); +assert_eq!(Wrapping(", stringify!($t), "::MIN).abs(), Wrapping(", stringify!($t), "::MIN)); assert_eq!(Wrapping(-128i8).abs().0 as u8, 128u8); ```"), #[inline] @@ -831,7 +822,7 @@ Basic usage: #![feature(wrapping_int_impl)] use std::num::Wrapping; -let n = Wrapping(", stringify!($t), "::max_value()) >> 2; +let n = Wrapping(", stringify!($t), "::MAX) >> 2; assert_eq!(n.leading_zeros(), 2); ```"), 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..3faeb170b0637 100644 --- a/src/libcore/ops/deref.rs +++ b/src/libcore/ops/deref.rs @@ -18,8 +18,8 @@ /// /// If `T` implements `Deref`, and `x` is a value of type `T`, then: /// -/// * In immutable contexts, `*x` on non-pointer types is equivalent to -/// `*Deref::deref(&x)`. +/// * In immutable contexts, `*x` (where `T` is neither a reference nor a raw pointer) +/// is equivalent to `*Deref::deref(&x)`. /// * Values of type `&T` are coerced to values of type `&U` /// * `T` implicitly implements all the (immutable) methods of the type `U`. /// @@ -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; @@ -112,8 +115,8 @@ impl Deref for &mut T { /// If `T` implements `DerefMut`, and `x` is a value of type `T`, /// then: /// -/// * In mutable contexts, `*x` on non-pointer types is equivalent to -/// `*DerefMut::deref_mut(&mut x)`. +/// * In mutable contexts, `*x` (where `T` is neither a reference nor a raw pointer) +/// is equivalent to `*DerefMut::deref_mut(&mut x)`. /// * Values of type `&mut T` are coerced to values of type `&mut U` /// * `T` implicitly implements all the (mutable) methods of the type `U`. /// 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/function.rs b/src/libcore/ops/function.rs index 04c7789fa4ff4..2cdfee87a3546 100644 --- a/src/libcore/ops/function.rs +++ b/src/libcore/ops/function.rs @@ -224,6 +224,7 @@ pub trait FnMut: FnOnce { #[must_use = "closures are lazy and do nothing unless called"] pub trait FnOnce { /// The returned type after the call operator is used. + #[cfg_attr(not(bootstrap), lang = "fn_once_output")] #[stable(feature = "fn_once_output", since = "1.12.0")] type Output; diff --git a/src/libcore/ops/generator.rs b/src/libcore/ops/generator.rs index 4e43561996c37..4f23620b92b80 100644 --- a/src/libcore/ops/generator.rs +++ b/src/libcore/ops/generator.rs @@ -67,7 +67,7 @@ pub enum GeneratorState { #[lang = "generator"] #[unstable(feature = "generator_trait", issue = "43122")] #[fundamental] -pub trait Generator<#[cfg(not(bootstrap))] R = ()> { +pub trait Generator { /// The type of value this generator yields. /// /// This associated type corresponds to the `yield` expression and the @@ -110,35 +110,9 @@ pub trait Generator<#[cfg(not(bootstrap))] R = ()> { /// been returned previously. While generator literals in the language are /// guaranteed to panic on resuming after `Complete`, this is not guaranteed /// for all implementations of the `Generator` trait. - fn resume( - self: Pin<&mut Self>, - #[cfg(not(bootstrap))] arg: R, - ) -> GeneratorState; + fn resume(self: Pin<&mut Self>, arg: R) -> GeneratorState; } -#[cfg(bootstrap)] -#[unstable(feature = "generator_trait", issue = "43122")] -impl Generator for Pin<&mut G> { - type Yield = G::Yield; - type Return = G::Return; - - fn resume(mut self: Pin<&mut Self>) -> GeneratorState { - G::resume((*self).as_mut()) - } -} - -#[cfg(bootstrap)] -#[unstable(feature = "generator_trait", issue = "43122")] -impl Generator for &mut G { - type Yield = G::Yield; - type Return = G::Return; - - fn resume(mut self: Pin<&mut Self>) -> GeneratorState { - G::resume(Pin::new(&mut *self)) - } -} - -#[cfg(not(bootstrap))] #[unstable(feature = "generator_trait", issue = "43122")] impl, R> Generator for Pin<&mut G> { type Yield = G::Yield; @@ -149,7 +123,6 @@ impl, R> Generator for Pin<&mut G> { } } -#[cfg(not(bootstrap))] #[unstable(feature = "generator_trait", issue = "43122")] impl + Unpin, R> Generator for &mut G { type Yield = G::Yield; 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 8ffad82b69d7c..d86f39c4550c8 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -1,5 +1,5 @@ use crate::fmt; -use crate::hash::{Hash, Hasher}; +use crate::hash::Hash; /// An unbounded range (`..`). /// @@ -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 { @@ -154,10 +151,16 @@ impl> Range { /// /// The `RangeFrom` `start..` contains all values with `x >= start`. /// -/// *Note*: Currently, no overflow checking is done for the [`Iterator`] -/// implementation; if you use an integer range and the integer overflows, it -/// might panic in debug mode or create an endless loop in release mode. **This -/// overflow behavior might change in the future.** +/// *Note*: Overflow in the [`Iterator`] implementation (when the contained +/// data type reaches its numerical limit) is allowed to panic, wrap, or +/// saturate. This behavior is defined by the implementation of the [`Step`] +/// trait. For primitive integers, this follows the normal rules, and respects +/// the overflow checks profile (panic in debug, wrap in release). Note also +/// that overflow happens earlier than you might assume: the overflow happens +/// in the call to `next` that yields the maximum value, as the range must be +/// set to a state to yield the next value. +/// +/// [`Step`]: crate::iter::Step /// /// # Examples /// @@ -199,8 +202,6 @@ impl> RangeFrom { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!(!(3..).contains(&2)); /// assert!( (3..).contains(&3)); /// assert!( (3..).contains(&1_000_000_000)); @@ -283,8 +284,6 @@ impl> RangeTo { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!( (..5).contains(&-1_000_000_000)); /// assert!( (..5).contains(&4)); /// assert!(!(..5).contains(&5)); @@ -330,7 +329,7 @@ impl> RangeTo { /// assert_eq!(arr[1..=3], [ 1,2,3 ]); // RangeInclusive /// ``` #[doc(alias = "..=")] -#[derive(Clone)] // not Copy -- see #27186 +#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 #[stable(feature = "inclusive_range", since = "1.26.0")] pub struct RangeInclusive { // Note that the fields here are not public to allow changing the @@ -350,26 +349,6 @@ pub struct RangeInclusive { pub(crate) exhausted: bool, } -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl PartialEq for RangeInclusive { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.start == other.start && self.end == other.end && self.exhausted == other.exhausted - } -} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl Eq for RangeInclusive {} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl Hash for RangeInclusive { - fn hash(&self, state: &mut H) { - self.start.hash(state); - self.end.hash(state); - self.exhausted.hash(state); - } -} - impl RangeInclusive { /// Creates a new inclusive range. Equivalent to writing `start..=end`. /// @@ -474,8 +453,6 @@ impl> RangeInclusive { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!(!(3..=5).contains(&2)); /// assert!( (3..=5).contains(&3)); /// assert!( (3..=5).contains(&4)); @@ -516,10 +493,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: @@ -603,8 +579,6 @@ impl> RangeToInclusive { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!( (..=5).contains(&-1_000_000_000)); /// assert!( (..=5).contains(&5)); /// assert!(!(..=5).contains(&6)); @@ -743,8 +717,6 @@ pub trait RangeBounds { /// # Examples /// /// ``` - /// use std::f32; - /// /// assert!( (3..5).contains(&4)); /// assert!(!(3..5).contains(&2)); /// diff --git a/src/libcore/ops/try.rs b/src/libcore/ops/try.rs index 996a01d413cbc..9bc35ae1f5c28 100644 --- a/src/libcore/ops/try.rs +++ b/src/libcore/ops/try.rs @@ -25,6 +25,7 @@ ) )] #[doc(alias = "?")] +#[lang = "try"] pub trait Try { /// The type of this value when viewed as successful. #[unstable(feature = "try_trait", issue = "42327")] diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 9b32442371c37..5f0a12678ff43 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() }, } } @@ -913,6 +917,65 @@ impl Option { pub fn replace(&mut self, value: T) -> Option { mem::replace(self, Some(value)) } + + /// Zips `self` with another `Option`. + /// + /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some((s, o))`. + /// Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ``` + /// let x = Some(1); + /// let y = Some("hi"); + /// let z = None::; + /// + /// assert_eq!(x.zip(y), Some((1, "hi"))); + /// assert_eq!(x.zip(z), None); + /// ``` + #[stable(feature = "option_zip_option", since = "1.46.0")] + pub fn zip(self, other: Option) -> Option<(T, U)> { + match (self, other) { + (Some(a), Some(b)) => Some((a, b)), + _ => None, + } + } + + /// Zips `self` and another `Option` with function `f`. + /// + /// If `self` is `Some(s)` and `other` is `Some(o)`, this method returns `Some(f(s, o))`. + /// Otherwise, `None` is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_zip)] + /// + /// #[derive(Debug, PartialEq)] + /// struct Point { + /// x: f64, + /// y: f64, + /// } + /// + /// impl Point { + /// fn new(x: f64, y: f64) -> Self { + /// Self { x, y } + /// } + /// } + /// + /// let x = Some(17.5); + /// let y = Some(42.7); + /// + /// assert_eq!(x.zip_with(y, Point::new), Some(Point { x: 17.5, y: 42.7 })); + /// assert_eq!(x.zip_with(None, Point::new), None); + /// ``` + #[unstable(feature = "option_zip", issue = "70086")] + pub fn zip_with(self, other: Option, f: F) -> Option + where + F: FnOnce(T, U) -> R, + { + Some(f(self?, other?)) + } } impl Option<&T> { @@ -1296,6 +1359,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) } @@ -1303,6 +1375,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() } @@ -1310,6 +1403,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..c7009b76e8148 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -39,8 +39,7 @@ pub struct PanicInfo<'a> { impl<'a> PanicInfo<'a> { #[unstable( feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] #[doc(hidden)] @@ -55,8 +54,7 @@ impl<'a> PanicInfo<'a> { #[unstable( feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] #[doc(hidden)] @@ -77,7 +75,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 +114,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 +226,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", @@ -236,8 +242,7 @@ impl<'a> Location<'a> { impl<'a> Location<'a> { #![unstable( feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] #[doc(hidden)] diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 3587f3f0ebf56..766c69a5f9420 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -19,13 +19,10 @@ //! 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", - reason = "internal details of the implementation of the `panic!` \ - and related macros", + reason = "internal details of the implementation of the `panic!` and related macros", issue = "none" )] @@ -41,7 +38,7 @@ 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() } + super::intrinsics::abort() } // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially @@ -50,50 +47,29 @@ 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() } + 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() } + super::intrinsics::abort() } // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call @@ -103,10 +79,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/pin.rs b/src/libcore/pin.rs index 774ecd997c201..6f5bf7ad9da52 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -139,10 +139,12 @@ //! otherwise invalidating the memory used to store the data is restricted, too. //! Concretely, for pinned data you have to maintain the invariant //! that *its memory will not get invalidated or repurposed from the moment it gets pinned until -//! when [`drop`] is called*. Memory can be invalidated by deallocation, but also by +//! when [`drop`] is called*. Only once [`drop`] returns or panics, the memory may be reused. +//! +//! Memory can be "invalidated" by deallocation, but also by //! replacing a [`Some(v)`] by [`None`], or calling [`Vec::set_len`] to "kill" some elements //! off of a vector. It can be repurposed by using [`ptr::write`] to overwrite it without -//! calling the destructor first. +//! calling the destructor first. None of this is allowed for pinned data without calling [`drop`]. //! //! This is exactly the kind of guarantee that the intrusive linked list from the previous //! section needs to function correctly. diff --git a/src/libcore/prelude/v1.rs b/src/libcore/prelude/v1.rs index 66b5a90b77b91..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")] @@ -67,3 +68,11 @@ pub use crate::{ pub use crate::macros::builtin::{ bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable, }; + +#[unstable( + feature = "cfg_accessible", + issue = "64797", + reason = "`cfg_accessible` is not fully implemented" +)] +#[doc(no_inline)] +pub use crate::macros::builtin::cfg_accessible; diff --git a/src/libcore/ptr/const_ptr.rs b/src/libcore/ptr/const_ptr.rs index a540016854df3..395b3879cfd0c 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,8 +150,10 @@ impl *const T { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn offset(self, count: isize) -> *const T + pub const unsafe fn offset(self, count: isize) -> *const T where T: Sized, { @@ -210,11 +210,14 @@ impl *const T { /// } /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_offset(self, count: isize) -> *const T + pub const 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) } } @@ -288,10 +291,76 @@ impl *const T { T: Sized, { let pointee_size = mem::size_of::(); - assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize); + assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); intrinsics::ptr_offset_from(self, origin) } + /// Returns whether two pointers are guaranteed to be equal. + /// + /// At runtime this function behaves like `self == other`. + /// However, in some contexts (e.g., compile-time evaluation), + /// it is not always possible to determine equality of two pointers, so this function may + /// spuriously return `false` for pointers that later actually turn out to be equal. + /// But when it returns `true`, the pointers are guaranteed to be equal. + /// + /// This function is the mirror of [`guaranteed_ne`], but not its inverse. There are pointer + /// comparisons for which both functions return `false`. + /// + /// [`guaranteed_ne`]: #method.guaranteed_ne + /// + /// The return value may change depending on the compiler version and unsafe code may not + /// rely on the result of this function for soundness. It is suggested to only use this function + /// for performance optimizations where spurious `false` return values by this function do not + /// affect the outcome, but just the performance. + /// The consequences of using this method to make runtime and compile-time code behave + /// differently have not been explored. This method should not be used to introduce such + /// differences, and it should also not be stabilized before we have a better understanding + /// of this issue. + /// ``` + #[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[inline] + #[cfg(not(bootstrap))] + pub const fn guaranteed_eq(self, other: *const T) -> bool + where + T: Sized, + { + intrinsics::ptr_guaranteed_eq(self, other) + } + + /// Returns whether two pointers are guaranteed to be inequal. + /// + /// At runtime this function behaves like `self != other`. + /// However, in some contexts (e.g., compile-time evaluation), + /// it is not always possible to determine the inequality of two pointers, so this function may + /// spuriously return `false` for pointers that later actually turn out to be inequal. + /// But when it returns `true`, the pointers are guaranteed to be inequal. + /// + /// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer + /// comparisons for which both functions return `false`. + /// + /// [`guaranteed_eq`]: #method.guaranteed_eq + /// + /// The return value may change depending on the compiler version and unsafe code may not + /// rely on the result of this function for soundness. It is suggested to only use this function + /// for performance optimizations where spurious `false` return values by this function do not + /// affect the outcome, but just the performance. + /// The consequences of using this method to make runtime and compile-time code behave + /// differently have not been explored. This method should not be used to introduce such + /// differences, and it should also not be stabilized before we have a better understanding + /// of this issue. + /// ``` + #[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[inline] + #[cfg(not(bootstrap))] + pub const fn guaranteed_ne(self, other: *const T) -> bool + where + T: Sized, + { + intrinsics::ptr_guaranteed_ne(self, other) + } + /// Calculates the distance between two pointers. The returned value is in /// units of T: the distance in bytes is divided by `mem::size_of::()`. /// @@ -327,13 +396,19 @@ impl *const T { /// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); /// ``` #[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")] + #[rustc_deprecated( + since = "1.46.0", + reason = "Pointer distances across allocation \ + boundaries are not typically meaningful. \ + Use integer subtraction if you really need this." + )] #[inline] pub fn wrapping_offset_from(self, origin: *const T) -> isize where T: Sized, { let pointee_size = mem::size_of::(); - assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize); + assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); let d = isize::wrapping_sub(self as _, origin as _); d.wrapping_div(pointee_size as _) @@ -391,8 +466,10 @@ impl *const T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn add(self, count: usize) -> Self + pub const unsafe fn add(self, count: usize) -> Self where T: Sized, { @@ -452,8 +529,10 @@ impl *const T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn sub(self, count: usize) -> Self + pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, { @@ -507,8 +586,10 @@ impl *const T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_add(self, count: usize) -> Self + pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, { @@ -562,8 +643,10 @@ impl *const T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_sub(self, count: usize) -> Self + pub const fn wrapping_sub(self, count: usize) -> Self where T: Sized, { @@ -659,8 +742,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 +785,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 72c46f58fcc7b..ca2b0c85ec121 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs @@ -65,23 +65,24 @@ //! [`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; use crate::fmt; use crate::hash; -use crate::intrinsics::{self, is_aligned_and_not_null, is_nonoverlapping}; +use crate::intrinsics::{self, abort, is_aligned_and_not_null, is_nonoverlapping}; use crate::mem::{self, MaybeUninit}; #[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] pub use crate::intrinsics::copy_nonoverlapping; #[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] pub use crate::intrinsics::copy; #[stable(feature = "rust1", since = "1.0.0")] +#[doc(inline)] pub use crate::intrinsics::write_bytes; mod non_null; @@ -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 } } @@ -392,9 +420,14 @@ pub unsafe fn swap(x: *mut T, y: *mut T) { #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] pub unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { - debug_assert!(is_aligned_and_not_null(x), "attempt to swap unaligned or null pointer"); - debug_assert!(is_aligned_and_not_null(y), "attempt to swap unaligned or null pointer"); - debug_assert!(is_nonoverlapping(x, y, count), "attempt to swap overlapping memory"); + if cfg!(debug_assertions) + && !(is_aligned_and_not_null(x) + && is_aligned_and_not_null(y) + && is_nonoverlapping(x, y, count)) + { + // Not panicking to keep codegen impact smaller. + abort(); + } let x = x as *mut u8; let y = y as *mut u8; @@ -810,9 +843,10 @@ pub unsafe fn read_unaligned(src: *const T) -> T { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn write(dst: *mut T, src: T) { - // FIXME: the debug assertion here causes codegen test failures on some architectures. - // See . - // debug_assert!(is_aligned_and_not_null(dst), "attempt to write to unaligned or null pointer"); + if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) { + // Not panicking to keep codegen impact smaller. + abort(); + } intrinsics::move_val_init(&mut *dst, src) } @@ -977,7 +1011,10 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { #[inline] #[stable(feature = "volatile", since = "1.9.0")] pub unsafe fn read_volatile(src: *const T) -> T { - debug_assert!(is_aligned_and_not_null(src), "attempt to read from unaligned or null pointer"); + if cfg!(debug_assertions) && !is_aligned_and_not_null(src) { + // Not panicking to keep codegen impact smaller. + abort(); + } intrinsics::volatile_load(src) } @@ -1046,7 +1083,10 @@ pub unsafe fn read_volatile(src: *const T) -> T { #[inline] #[stable(feature = "volatile", since = "1.9.0")] pub unsafe fn write_volatile(dst: *mut T, src: T) { - debug_assert!(is_aligned_and_not_null(dst), "attempt to write to unaligned or null pointer"); + if cfg!(debug_assertions) && !is_aligned_and_not_null(dst) { + // Not panicking to keep codegen impact smaller. + abort(); + } intrinsics::volatile_store(dst, src); } @@ -1102,7 +1142,7 @@ pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { // // Note, that we use wrapping operations here intentionally – the original formula // uses e.g., subtraction `mod n`. It is entirely fine to do them `mod - // usize::max_value()` instead, because we take the result `mod n` at the end + // usize::MAX` instead, because we take the result `mod n` at the end // anyway. inverse = inverse.wrapping_mul(2usize.wrapping_sub(x.wrapping_mul(inverse))); if going_mod >= m { @@ -1167,7 +1207,7 @@ pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { } // Cannot be aligned at all. - usize::max_value() + usize::MAX } /// Compares raw pointers for equality. @@ -1319,14 +1359,24 @@ macro_rules! fnptr_impls_safety_abi { #[stable(feature = "fnptr_impls", since = "1.4.0")] impl fmt::Pointer for $FnTy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&(*self as *const ()), f) + // HACK: The intermediate cast as usize is required for AVR + // so that the address space of the source function pointer + // is preserved in the final function pointer. + // + // https://github.com/avr-rust/rust/issues/143 + fmt::Pointer::fmt(&(*self as usize as *const ()), f) } } #[stable(feature = "fnptr_impls", since = "1.4.0")] impl fmt::Debug for $FnTy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&(*self as *const ()), f) + // HACK: The intermediate cast as usize is required for AVR + // so that the address space of the source function pointer + // is preserved in the final function pointer. + // + // https://github.com/avr-rust/rust/issues/143 + fmt::Pointer::fmt(&(*self as usize as *const ()), f) } } } @@ -1363,3 +1413,70 @@ fnptr_impls_args! { A, B, C, D, E, F, G, H, I } fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J } fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K } fnptr_impls_args! { A, B, C, D, E, F, G, H, I, J, K, L } + +/// Create a `const` raw pointer to a place, without creating an intermediate reference. +/// +/// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned +/// and points to initialized data. For cases where those requirements do not hold, +/// raw pointers should be used instead. However, `&expr as *const _` creates a reference +/// before casting it to a raw pointer, and that reference is subject to the same rules +/// as all other references. This macro can create a raw pointer *without* creating +/// a reference first. +/// +/// # Example +/// +/// ``` +/// #![feature(raw_ref_macros)] +/// use std::ptr; +/// +/// #[repr(packed)] +/// struct Packed { +/// f1: u8, +/// f2: u16, +/// } +/// +/// let packed = Packed { f1: 1, f2: 2 }; +/// // `&packed.f2` would create an unaligned reference, and thus be Undefined Behavior! +/// let raw_f2 = ptr::raw_const!(packed.f2); +/// assert_eq!(unsafe { raw_f2.read_unaligned() }, 2); +/// ``` +#[unstable(feature = "raw_ref_macros", issue = "73394")] +#[rustc_macro_transparency = "semitransparent"] +#[allow_internal_unstable(raw_ref_op)] +pub macro raw_const($e:expr) { + &raw const $e +} + +/// Create a `mut` raw pointer to a place, without creating an intermediate reference. +/// +/// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned +/// and points to initialized data. For cases where those requirements do not hold, +/// raw pointers should be used instead. However, `&mut expr as *mut _` creates a reference +/// before casting it to a raw pointer, and that reference is subject to the same rules +/// as all other references. This macro can create a raw pointer *without* creating +/// a reference first. +/// +/// # Example +/// +/// ``` +/// #![feature(raw_ref_macros)] +/// use std::ptr; +/// +/// #[repr(packed)] +/// struct Packed { +/// f1: u8, +/// f2: u16, +/// } +/// +/// let mut packed = Packed { f1: 1, f2: 2 }; +/// // `&mut packed.f2` would create an unaligned reference, and thus be Undefined Behavior! +/// let raw_f2 = ptr::raw_mut!(packed.f2); +/// unsafe { raw_f2.write_unaligned(42); } +/// assert_eq!({packed.f2}, 42); // `{...}` forces copying the field instead of creating a reference. +/// ``` +#[unstable(feature = "raw_ref_macros", issue = "73394")] +#[rustc_macro_transparency = "semitransparent"] +#[allow_internal_unstable(raw_ref_op)] +pub macro raw_mut($e:expr) { + &raw mut $e +} diff --git a/src/libcore/ptr/mut_ptr.rs b/src/libcore/ptr/mut_ptr.rs index 01d830ca18602..b86ef5b13b353 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,8 +144,10 @@ impl *mut T { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn offset(self, count: isize) -> *mut T + pub const unsafe fn offset(self, count: isize) -> *mut T where T: Sized, { @@ -203,11 +203,14 @@ 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"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_offset(self, count: isize) -> *mut T + pub const fn wrapping_offset(self, count: isize) -> *mut T where T: Sized, { + // SAFETY: the `arith_offset` intrinsic has no prerequisites to be called. unsafe { intrinsics::arith_offset(self, count) as *mut T } } @@ -270,6 +273,72 @@ impl *mut T { if self.is_null() { None } else { Some(&mut *self) } } + /// Returns whether two pointers are guaranteed to be equal. + /// + /// At runtime this function behaves like `self == other`. + /// However, in some contexts (e.g., compile-time evaluation), + /// it is not always possible to determine equality of two pointers, so this function may + /// spuriously return `false` for pointers that later actually turn out to be equal. + /// But when it returns `true`, the pointers are guaranteed to be equal. + /// + /// This function is the mirror of [`guaranteed_ne`], but not its inverse. There are pointer + /// comparisons for which both functions return `false`. + /// + /// [`guaranteed_ne`]: #method.guaranteed_ne + /// + /// The return value may change depending on the compiler version and unsafe code may not + /// rely on the result of this function for soundness. It is suggested to only use this function + /// for performance optimizations where spurious `false` return values by this function do not + /// affect the outcome, but just the performance. + /// The consequences of using this method to make runtime and compile-time code behave + /// differently have not been explored. This method should not be used to introduce such + /// differences, and it should also not be stabilized before we have a better understanding + /// of this issue. + /// ``` + #[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[inline] + #[cfg(not(bootstrap))] + pub const fn guaranteed_eq(self, other: *mut T) -> bool + where + T: Sized, + { + intrinsics::ptr_guaranteed_eq(self as *const _, other as *const _) + } + + /// Returns whether two pointers are guaranteed to be inequal. + /// + /// At runtime this function behaves like `self != other`. + /// However, in some contexts (e.g., compile-time evaluation), + /// it is not always possible to determine the inequality of two pointers, so this function may + /// spuriously return `false` for pointers that later actually turn out to be inequal. + /// But when it returns `true`, the pointers are guaranteed to be inequal. + /// + /// This function is the mirror of [`guaranteed_eq`], but not its inverse. There are pointer + /// comparisons for which both functions return `false`. + /// + /// [`guaranteed_eq`]: #method.guaranteed_eq + /// + /// The return value may change depending on the compiler version and unsafe code may not + /// rely on the result of this function for soundness. It is suggested to only use this function + /// for performance optimizations where spurious `false` return values by this function do not + /// affect the outcome, but just the performance. + /// The consequences of using this method to make runtime and compile-time code behave + /// differently have not been explored. This method should not be used to introduce such + /// differences, and it should also not be stabilized before we have a better understanding + /// of this issue. + /// ``` + #[unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")] + #[inline] + #[cfg(not(bootstrap))] + pub const unsafe fn guaranteed_ne(self, other: *mut T) -> bool + where + T: Sized, + { + intrinsics::ptr_guaranteed_ne(self as *const _, other as *const _) + } + /// Calculates the distance between two pointers. The returned value is in /// units of T: the distance in bytes is divided by `mem::size_of::()`. /// @@ -377,11 +446,18 @@ impl *mut T { /// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2); /// ``` #[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")] + #[rustc_deprecated( + since = "1.46.0", + reason = "Pointer distances across allocation \ + boundaries are not typically meaningful. \ + Use integer subtraction if you really need this." + )] #[inline] pub fn wrapping_offset_from(self, origin: *const T) -> isize where T: Sized, { + #[allow(deprecated_in_future, deprecated)] (self as *const T).wrapping_offset_from(origin) } @@ -437,8 +513,10 @@ impl *mut T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn add(self, count: usize) -> Self + pub const unsafe fn add(self, count: usize) -> Self where T: Sized, { @@ -498,8 +576,10 @@ impl *mut T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub unsafe fn sub(self, count: usize) -> Self + pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, { @@ -553,8 +633,10 @@ impl *mut T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_add(self, count: usize) -> Self + pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, { @@ -608,8 +690,10 @@ impl *mut T { /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] + #[must_use = "returns a new pointer rather than modifying its argument"] + #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] #[inline] - pub fn wrapping_sub(self, count: usize) -> Self + pub const fn wrapping_sub(self, count: usize) -> Self where T: Sized, { @@ -847,8 +931,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 +974,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..870364a61dd47 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,10 +137,70 @@ 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) } } } +impl NonNull<[T]> { + /// Creates a non-null raw slice from a thin pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// This function is safe, but dereferencing the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. + /// + /// [`slice::from_raw_parts`]: ../../std/slice/fn.from_raw_parts.html + /// + /// # Examples + /// + /// ```rust + /// #![feature(nonnull_slice_from_raw_parts)] + /// + /// use std::ptr::NonNull; + /// + /// // create a slice pointer when starting out with a pointer to the first element + /// let mut x = [5, 6, 7]; + /// let nonnull_pointer = NonNull::new(x.as_mut_ptr()).unwrap(); + /// let slice = NonNull::slice_from_raw_parts(nonnull_pointer, 3); + /// assert_eq!(unsafe { slice.as_ref()[2] }, 7); + /// ``` + /// + /// (Note that this example artifically demonstrates a use of this method, + /// but `let slice = NonNull::from(&x[..]);` would be a better way to write code like this.) + #[unstable(feature = "nonnull_slice_from_raw_parts", issue = "71941")] + #[rustc_const_unstable(feature = "const_nonnull_slice_from_raw_parts", issue = "71941")] + #[inline] + pub const fn slice_from_raw_parts(data: NonNull, len: usize) -> Self { + // SAFETY: `data` is a `NonNull` pointer which is necessarily non-null + unsafe { Self::new_unchecked(super::slice_from_raw_parts_mut(data.as_ptr(), len)) } + } + + /// Returns the length of a non-null raw slice. + /// + /// The returned value is the number of **elements**, not the number of bytes. + /// + /// This function is safe, even when the non-null raw slice cannot be dereferenced to a slice + /// because the pointer does not have a valid address. + /// + /// # Examples + /// + /// ```rust + /// #![feature(slice_ptr_len, nonnull_slice_from_raw_parts)] + /// + /// use std::ptr::NonNull; + /// + /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3); + /// assert_eq!(slice.len(), 3); + /// ``` + #[unstable(feature = "slice_ptr_len", issue = "71146")] + #[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")] + #[inline] + pub const fn len(self) -> usize { + self.as_ptr().len() + } +} + #[stable(feature = "nonnull", since = "1.25.0")] impl Clone for NonNull { #[inline] @@ -205,6 +271,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 +281,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 +290,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..741a9dc8797be 100644 --- a/src/libcore/raw.rs +++ b/src/libcore/raw.rs @@ -6,17 +6,18 @@ //! 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`. +/// The representation of a trait object like `&dyn SomeTrait`. /// -/// This struct has the same layout as types like `&SomeTrait` and +/// This struct has the same layout as types like `&dyn SomeTrait` and /// `Box`. /// /// `TraitObject` is guaranteed to match layouts, but it is not the /// type of trait objects (e.g., the fields are not directly accessible -/// on a `&SomeTrait`) nor does it control that layout (changing the -/// definition will not change the layout of a `&SomeTrait`). It is +/// on a `&dyn SomeTrait`) nor does it control that layout (changing the +/// definition will not change the layout of a `&dyn SomeTrait`). It is /// only designed to be used by unsafe code that needs to manipulate /// the low-level details. /// diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 0bc29e1bc662c..0c0e6d291bb92 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 /// @@ -1143,45 +1145,73 @@ impl> Result { } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] +#[unstable(feature = "inner_deref", issue = "50264")] impl Result { - /// Converts from `Result` (or `&Result`) to `Result<&T::Target, &E>`. + /// Converts from `Result` (or `&Result`) to `Result<&::Target, &E>`. + /// + /// Coerces the [`Ok`] variant of the original [`Result`] via [`Deref`](crate::ops::Deref) + /// and returns the new [`Result`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(inner_deref)] + /// let x: Result = Ok("hello".to_string()); + /// let y: Result<&str, &u32> = Ok("hello"); + /// assert_eq!(x.as_deref(), y); /// - /// Leaves the original `Result` in-place, creating a new one containing a reference to the - /// `Ok` type's `Deref::Target` type. + /// let x: Result = Err(42); + /// let y: Result<&str, &u32> = Err(&42); + /// assert_eq!(x.as_deref(), y); + /// ``` pub fn as_deref(&self) -> Result<&T::Target, &E> { self.as_ref().map(|t| t.deref()) } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] +#[unstable(feature = "inner_deref", issue = "50264")] impl Result { - /// Converts from `Result` (or `&Result`) to `Result<&T, &E::Target>`. + /// Converts from `Result` (or `&Result`) to `Result<&T, &::Target>`. /// - /// Leaves the original `Result` in-place, creating a new one containing a reference to the - /// `Err` type's `Deref::Target` type. + /// Coerces the [`Err`] variant of the original [`Result`] via [`Deref`](crate::ops::Deref) + /// and returns the new [`Result`]. pub fn as_deref_err(&self) -> Result<&T, &E::Target> { self.as_ref().map_err(|e| e.deref()) } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] +#[unstable(feature = "inner_deref", issue = "50264")] impl Result { - /// Converts from `Result` (or `&mut Result`) to `Result<&mut T::Target, &mut E>`. + /// Converts from `Result` (or `&mut Result`) to `Result<&mut ::Target, &mut E>`. /// - /// Leaves the original `Result` in-place, creating a new one containing a mutable reference to - /// the `Ok` type's `Deref::Target` type. + /// Coerces the [`Ok`] variant of the original [`Result`] via [`DerefMut`](crate::ops::DerefMut) + /// and returns the new [`Result`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(inner_deref)] + /// let mut s = "HELLO".to_string(); + /// let mut x: Result = Ok("hello".to_string()); + /// let y: Result<&mut str, &mut u32> = Ok(&mut s); + /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); + /// + /// let mut i = 42; + /// let mut x: Result = Err(42); + /// let y: Result<&mut str, &mut u32> = Err(&mut i); + /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); + /// ``` pub fn as_deref_mut(&mut self) -> Result<&mut T::Target, &mut E> { self.as_mut().map(|t| t.deref_mut()) } } -#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")] +#[unstable(feature = "inner_deref", issue = "50264")] impl Result { - /// Converts from `Result` (or `&mut Result`) to `Result<&mut T, &mut E::Target>`. + /// Converts from `Result` (or `&mut Result`) to `Result<&mut T, &mut ::Target>`. /// - /// Leaves the original `Result` in-place, creating a new one containing a mutable reference to - /// the `Err` type's `Deref::Target` type. + /// Coerces the [`Err`] variant of the original [`Result`] via [`DerefMut`](crate::ops::DerefMut) + /// and returns the new [`Result`]. pub fn as_deref_mut_err(&mut self) -> Result<&mut T, &mut E::Target> { self.as_mut().map_err(|e| e.deref_mut()) } @@ -1214,6 +1244,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..c69aafe687cf8 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; @@ -410,7 +409,7 @@ impl [T] { /// The returned range is half-open, which means that the end pointer /// points *one past* the last element of the slice. This way, an empty /// slice is represented by two equal pointers, and the difference between - /// the two pointers represents the size of the size. + /// the two pointers represents the size of the slice. /// /// See [`as_ptr`] for warnings on using these pointers. The end pointer /// requires extra caution, as it does not point to a valid element in the @@ -465,7 +464,7 @@ impl [T] { /// The returned range is half-open, which means that the end pointer /// points *one past* the last element of the slice. This way, an empty /// slice is represented by two equal pointers, and the difference between - /// the two pointers represents the size of the size. + /// the two pointers represents the size of the slice. /// /// See [`as_mut_ptr`] for warnings on using these pointers. The end /// pointer requires extra caution, as it does not point to a valid element @@ -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 @@ -1655,7 +1654,7 @@ impl [T] { /// /// ``` /// let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0]; - /// floats.sort_by(|a, b| a.partial_cmp(b).unwrap()); + /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); /// ``` /// @@ -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,11 +2144,36 @@ 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`. /// - /// If `src` implements `Copy`, it can be more performant to use + /// If `T` implements `Copy`, it can be more performant to use /// [`copy_from_slice`]. /// /// # Panics @@ -2220,7 +2244,7 @@ impl [T] { /// /// The length of `src` must be the same as `self`. /// - /// If `src` does not implement `Copy`, use [`clone_from_slice`]. + /// If `T` does not implement `Copy`, use [`clone_from_slice`]. /// /// # Panics /// @@ -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; } @@ -3013,16 +3043,12 @@ impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn get(self, slice: &[T]) -> Option<&[T]> { - if *self.end() == usize::max_value() { - None - } else { - (*self.start()..self.end() + 1).get(slice) - } + if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get_mut(slice) @@ -3041,7 +3067,7 @@ impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn index(self, slice: &[T]) -> &[T] { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { slice_index_overflow_fail(); } (*self.start()..self.end() + 1).index(slice) @@ -3049,7 +3075,7 @@ impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { slice_index_overflow_fail(); } (*self.start()..self.end() + 1).index_mut(slice) @@ -3149,6 +3175,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 +3344,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