--- /dev/null
+#
+# Azure Pipelines "auto" branch build for Rust on Linux, macOS, and Windows.
+#
+
+pr: none
+trigger:
+ - auto
+
+variables:
+- group: prod-credentials
+
+jobs:
+- job: Linux
+ pool:
+ vmImage: ubuntu-16.04
+ steps:
+ - template: steps/run.yml
+ strategy:
+ matrix:
+ x86_64-gnu-llvm-6.0:
+ IMAGE: x86_64-gnu-llvm-6.0
+ RUST_BACKTRACE: 1
+
+ dist-x86_64-linux:
+ IMAGE: dist-x86_64-linux
+ DEPLOY: 1
+
+ # "alternate" deployments, these are "nightlies" but have LLVM assertions
+ # turned on, they're deployed to a different location primarily for
+ # additional testing.
+ dist-x86_64-linux-alt:
+ IMAGE: dist-x86_64-linux
+ DEPLOY_ALT: 1
+
+ # Linux builders, remaining docker images
+ arm-android:
+ IMAGE: arm-android
+
+ armhf-gnu:
+ IMAGE: armhf-gnu
+
+ dist-various-1:
+ IMAGE: dist-various-1
+ DEPLOY: 1
+
+ dist-various-2:
+ IMAGE: dist-various-2
+ DEPLOY: 1
+
+ dist-aarch64-linux:
+ IMAGE: dist-aarch64-linux
+ DEPLOY: 1
+
+ dist-android:
+ IMAGE: dist-android
+ DEPLOY: 1
+
+ dist-arm-linux:
+ IMAGE: dist-arm-linux
+ DEPLOY: 1
+
+ dist-armhf-linux:
+ IMAGE: dist-armhf-linux
+ DEPLOY: 1
+
+ dist-armv7-linux:
+ IMAGE: dist-armv7-linux
+ DEPLOY: 1
+
+ dist-i586-gnu-i586-i686-musl:
+ IMAGE: dist-i586-gnu-i586-i686-musl
+ DEPLOY: 1
+
+ dist-i686-freebsd:
+ IMAGE: dist-i686-freebsd
+ DEPLOY: 1
+
+ dist-i686-linux:
+ IMAGE: dist-i686-linux
+ DEPLOY: 1
+
+ dist-mips-linux:
+ IMAGE: dist-mips-linux
+ DEPLOY: 1
+
+ dist-mips64-linux:
+ IMAGE: dist-mips64-linux
+ DEPLOY: 1
+
+ dist-mips64el-linux:
+ IMAGE: dist-mips64el-linux
+ DEPLOY: 1
+
+ dist-mipsel-linux:
+ IMAGE: dist-mipsel-linux
+ DEPLOY: 1
+
+ dist-powerpc-linux:
+ IMAGE: dist-powerpc-linux
+ DEPLOY: 1
+
+ dist-powerpc64-linux:
+ IMAGE: dist-powerpc64-linux
+ DEPLOY: 1
+
+ dist-powerpc64le-linux:
+ IMAGE: dist-powerpc64le-linux
+ DEPLOY: 1
+
+ dist-s390x-linux:
+ IMAGE: dist-s390x-linux
+ DEPLOY: 1
+
+ dist-x86_64-freebsd:
+ IMAGE: dist-x86_64-freebsd
+ DEPLOY: 1
+
+ dist-x86_64-musl:
+ IMAGE: dist-x86_64-musl
+ DEPLOY: 1
+
+ dist-x86_64-netbsd:
+ IMAGE: dist-x86_64-netbsd
+ DEPLOY: 1
+
+ asmjs:
+ IMAGE: asmjs
+ i686-gnu:
+ IMAGE: i686-gnu
+ i686-gnu-nopt:
+ IMAGE: i686-gnu-nopt
+ test-various:
+ IMAGE: test-various
+ x86_64-gnu:
+ IMAGE: x86_64-gnu
+ x86_64-gnu-full-bootstrap:
+ IMAGE: x86_64-gnu-full-bootstrap
+ x86_64-gnu-aux:
+ IMAGE: x86_64-gnu-aux
+ x86_64-gnu-tools:
+ IMAGE: x86_64-gnu-tools
+ # FIXME if: branch = auto OR (type = pull_request AND commit_message =~ /(?i:^update.*\b(rls|rustfmt|clippy|miri|cargo)\b)/)
+ x86_64-gnu-debug:
+ IMAGE: x86_64-gnu-debug
+ x86_64-gnu-nopt:
+ IMAGE: x86_64-gnu-nopt
+ x86_64-gnu-distcheck:
+ IMAGE: x86_64-gnu-distcheck
+ mingw-check:
+ IMAGE: mingw-check
+
+- job: macOS
+ pool:
+ vmImage: macos-10.13
+ steps:
+ - checkout: self
+ fetchDepth: 2
+ - template: steps/run.yml
+ strategy:
+ matrix:
+ # OSX builders running tests, these run the full test suite.
+ # NO_DEBUG_ASSERTIONS=1 to make them go faster, but also do have some
+ # runners that run `//ignore-debug` tests.
+ #
+ # Note that the compiler is compiled to target 10.8 here because the Xcode
+ # version that we're using, 8.2, cannot compile LLVM for OSX 10.7.
+ x86_64-apple:
+ RUST_CHECK_TARGET: check
+ 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
+
+ dist-x86_64-apple:
+ RUST_CHECK_TARGET: 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 --enable-lldb --set rust.jemalloc
+ DEPLOY: 1
+ RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
+ MACOSX_DEPLOYMENT_TARGET: 10.7
+ NO_LLVM_ASSERTIONS: 1
+ NO_DEBUG_ASSERTIONS: 1
+ DIST_REQUIRE_ALL_TOOLS: 1
+
+ dist-x86_64-apple-alt:
+ RUST_CHECK_TARGET: dist
+ RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --enable-lldb --set rust.jemalloc
+ DEPLOY_ALT: 1
+ RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
+ MACOSX_DEPLOYMENT_TARGET: 10.7
+ NO_LLVM_ASSERTIONS: 1
+ NO_DEBUG_ASSERTIONS: 1
+
+ i686-apple:
+ RUST_CHECK_TARGET: check
+ RUST_CONFIGURE_ARGS: --build=i686-apple-darwin --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
+
+ dist-i686-apple:
+ RUST_CHECK_TARGET: dist
+ RUST_CONFIGURE_ARGS: --build=i686-apple-darwin --enable-full-tools --enable-profiler --enable-lldb --set rust.jemalloc
+ DEPLOY: 1
+ 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: Windows
+ pool:
+ vmImage: 'vs2017-win2016'
+ steps:
+ - template: steps/run.yml
+ strategy:
+ matrix:
+ # 32/64 bit MSVC tests
+ x86_64-msvc-1:
+ MSYS_BITS: 64
+ 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:
+ MSYS_BITS: 64
+ RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler
+ SCRIPT: make ci-subset-2
+ i686-msvc-1:
+ MSYS_BITS: 32
+ RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc
+ SCRIPT: make ci-subset-1
+ i686-msvc-2:
+ MSYS_BITS: 32
+ RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc
+ SCRIPT: make ci-subset-2
+ # MSVC aux tests
+ x86_64-msvc-aux:
+ MSYS_BITS: 64
+ RUST_CHECK_TARGET: check-aux EXCLUDE_CARGO=1
+ RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc
+ x86_64-msvc-cargo:
+ MSYS_BITS: 64
+ SCRIPT: python x.py test src/tools/cargotest src/tools/cargo
+ RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc
+ VCVARS_BAT: vcvars64.bat
+ # MSVC tools tests
+ x86_64-msvc-tools:
+ MSYS_BITS: 64
+ SCRIPT: src/ci/docker/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstates.json windows
+ RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --save-toolstates=/tmp/toolstates.json --enable-test-miri
+
+ # 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.
+ i686-mingw-1:
+ MSYS_BITS: 32
+ RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
+ SCRIPT: make ci-subset-1
+ MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
+ MINGW_ARCHIVE: i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z
+ MINGW_DIR: mingw32
+ # FIXME(#59637)
+ NO_DEBUG_ASSERTIONS: 1
+ NO_LLVM_ASSERTIONS: 1
+ i686-mingw-2:
+ MSYS_BITS: 32
+ RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
+ SCRIPT: make ci-subset-2
+ MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
+ MINGW_ARCHIVE: i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z
+ MINGW_DIR: mingw32
+ x86_64-mingw-1:
+ MSYS_BITS: 64
+ SCRIPT: make ci-subset-1
+ RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu
+ MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
+ MINGW_ARCHIVE: x86_64-6.3.0-release-posix-seh-rt_v5-rev2.7z
+ MINGW_DIR: mingw64
+ # FIXME(#59637)
+ NO_DEBUG_ASSERTIONS: 1
+ NO_LLVM_ASSERTIONS: 1
+ x86_64-mingw-2:
+ MSYS_BITS: 64
+ SCRIPT: make ci-subset-2
+ RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu
+ MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
+ MINGW_ARCHIVE: x86_64-6.3.0-release-posix-seh-rt_v5-rev2.7z
+ MINGW_DIR: mingw64
+
+ # 32/64 bit MSVC and GNU deployment
+ dist-x86_64-msvc:
+ 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
+ DEPLOY: 1
+ dist-i686-msvc:
+ 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
+ DEPLOY: 1
+ dist-i686-mingw:
+ MSYS_BITS: 32
+ RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-full-tools
+ SCRIPT: python x.py dist
+ MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
+ MINGW_ARCHIVE: i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z
+ MINGW_DIR: mingw32
+ DIST_REQUIRE_ALL_TOOLS: 1
+ DEPLOY: 1
+ dist-x86_64-mingw:
+ MSYS_BITS: 64
+ SCRIPT: python x.py dist
+ RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-full-tools
+ MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
+ MINGW_ARCHIVE: x86_64-6.3.0-release-posix-seh-rt_v5-rev2.7z
+ MINGW_DIR: mingw64
+ DIST_REQUIRE_ALL_TOOLS: 1
+ DEPLOY: 1
+
+ # "alternate" deployment, see .travis.yml for more info
+ dist-x86_64-msvc-alt:
+ MSYS_BITS: 64
+ RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler
+ SCRIPT: python x.py dist
+ DEPLOY_ALT: 1
--- /dev/null
+#
+# Azure Pipelines job to publish toolstate. Only triggers on pushes to master.
+#
+
+pr: none
+trigger:
+ - master
+
+pool:
+ vmImage: ubuntu-16.04
+
+steps:
+- checkout: self
+ fetchDepth: 2
+
+- script: |
+ export MESSAGE_FILE=$(mktemp -t msg.XXXXXX)
+ . src/ci/docker/x86_64-gnu-tools/repo.sh
+ # FIXME(pietro): committing is disabled until we switch to Azure Pipelines
+ # as the source of truth, or until we setup a separate test repo.
+ #commit_toolstate_change "$MESSAGE_FILE" "$BUILD_SOURCESDIRECTORY/src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" "$(git log --format=%s -n1 HEAD)" "$MESSAGE_FILE" "$TOOLSTATE_REPO_ACCESS_TOKEN"
+ displayName: Publish toolstate
+ env:
+ TOOLSTATE_REPO_ACCESS_TOKEN: $(TOOLSTATE_REPO_ACCESS_TOKEN_SECRET)
--- /dev/null
+#
+# Azure Pipelines pull request build for Rust
+#
+
+trigger: none
+pr:
+- master # FIXME: really just want any branch, but want an explicit "pr" property set so it's clear
+
+jobs:
+- job: Linux
+ pool:
+ vmImage: ubuntu-16.04
+ steps:
+ - template: steps/run.yml
+ strategy:
+ matrix:
+ x86_64-gnu-llvm-6.0:
+ RUST_BACKTRACE: 1
+
+# x86_64-gnu-tools: {}
+# # if: branch = auto OR (type = pull_request AND commit_message =~ /(?i:^update.*\b(rls|rustfmt|clippy|miri|cargo)\b)/)
+# mingw-check: {}
--- /dev/null
+steps:
+
+- bash: |
+ set -e
+ curl -f http://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz | tar xJf -
+
+ export CC=`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/bin/clang
+ echo "##vso[task.setvariable variable=CC]$CC"
+
+ export CXX=`pwd`/clang+llvm-7.0.0-x86_64-apple-darwin/bin/clang++
+ echo "##vso[task.setvariable variable=CXX]$CXX"
+
+ # Configure `AR` specifically so rustbuild doesn't try to infer it as
+ # `clang-ar` by accident.
+ echo "##vso[task.setvariable variable=AR]ar"
+ displayName: Install clang (OSX)
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin'))
+
+# If we're compiling for MSVC then we, like most other distribution builders,
+# switch to clang as the compiler. This'll allow us eventually to enable LTO
+# amongst LLVM and rustc. Note that we only do this on MSVC as I don't think
+# clang has an output mode compatible with MinGW that we need. If it does we
+# should switch to clang for MinGW as well!
+#
+# Note that the LLVM installer is an NSIS installer
+#
+# Original downloaded here came from
+# http://releases.llvm.org/7.0.0/LLVM-7.0.0-win64.exe
+- script: |
+ powershell -Command "iwr -outf %TEMP%\LLVM-7.0.0-win64.exe https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/LLVM-7.0.0-win64.exe"
+ set CLANG_DIR=%CD%\citools\clang-rust
+ %TEMP%\LLVM-7.0.0-win64.exe /S /NCRC /D=%CLANG_DIR%
+ set RUST_CONFIGURE_ARGS=%RUST_CONFIGURE_ARGS% --set llvm.clang-cl=%CLANG_DIR%\bin\clang-cl.exe
+ echo ##vso[task.setvariable variable=RUST_CONFIGURE_ARGS]%RUST_CONFIGURE_ARGS%
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['MINGW_URL'],''))
+ displayName: Install clang (Windows)
+
+# Note that we don't install clang on Linux since its compiler story is just so
+# different. Each container has its own toolchain configured appropriately
+# already.
--- /dev/null
+steps:
+
+- bash: |
+ set -e
+ curl -fo /usr/local/bin/sccache https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2018-04-02-sccache-x86_64-apple-darwin
+ chmod +x /usr/local/bin/sccache
+ displayName: Install sccache (OSX)
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin'))
+
+- script: |
+ md sccache
+ powershell -Command "iwr -outf sccache\sccache.exe https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2018-04-26-sccache-x86_64-pc-windows-msvc"
+ echo ##vso[task.prependpath]%CD%\sccache
+ displayName: Install sccache (Windows)
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
+
+# Note that we don't install sccache on Linux since it's installed elsewhere
+# through all the containers.
+#
+# FIXME: we should probably install sccache outside the containers and then
+# mount it inside the containers so we can centralize all installation here.
--- /dev/null
+steps:
+# 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.
+- script: |
+ mkdir c:\MORE_SPACE
+ mklink /J build c:\MORE_SPACE
+ displayName: "Ensure build happens on C:/ instead of D:/"
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
+
+# 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.
+- script: |
+ set MSYS_PATH=%CD%\citools\msys64
+ choco install msys2 --params="/InstallDir:%MSYS_PATH% /NoPath" -y
+ set PATH=%MSYS_PATH%\usr\bin;%PATH%
+ pacman -S --noconfirm --needed base-devel ca-certificates make diffutils tar
+ IF "%MINGW_URL%"=="" (
+ IF "%MSYS_BITS%"=="32" pacman -S --noconfirm --needed mingw-w64-i686-toolchain mingw-w64-i686-cmake mingw-w64-i686-gcc mingw-w64-i686-python2
+ IF "%MSYS_BITS%"=="64" pacman -S --noconfirm --needed mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc mingw-w64-x86_64-python2
+ )
+ where rev
+ rev --help
+ where make
+
+ echo ##vso[task.setvariable variable=MSYS_PATH]%MSYS_PATH%
+ echo ##vso[task.prependpath]%MSYS_PATH%\usr\bin
+ displayName: Install msys2
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
+
+# If we need to download a custom MinGW, do so here and set the path
+# appropriately.
+#
+# Here we also do a pretty heinous thing which is to mangle the MinGW
+# installation we just downloaded. Currently, as of this writing, we're using
+# MinGW-w64 builds of gcc, and that's currently at 6.3.0. We use 6.3.0 as it
+# appears to be the first version which contains a fix for #40546, builds
+# randomly failing during LLVM due to ar.exe/ranlib.exe failures.
+#
+# Unfortunately, though, 6.3.0 *also* is the first version of MinGW-w64 builds
+# to contain a regression in gdb (#40184). As a result if we were to use the
+# gdb provided (7.11.1) then we would fail all debuginfo tests.
+#
+# In order to fix spurious failures (pretty high priority) we use 6.3.0. To
+# avoid disabling gdb tests we download an *old* version of gdb, specifically
+# that found inside the 6.2.0 distribution. We then overwrite the 6.3.0 gdb
+# with the 6.2.0 gdb to get tests passing.
+#
+# Note that we don't literally overwrite the gdb.exe binary because it appears
+# to just use gdborig.exe, so that's the binary we deal with instead.
+- script: |
+ powershell -Command "iwr -outf %MINGW_ARCHIVE% %MINGW_URL%/%MINGW_ARCHIVE%"
+ 7z x -y %MINGW_ARCHIVE% > nul
+ powershell -Command "iwr -outf 2017-04-20-%MSYS_BITS%bit-gdborig.exe %MINGW_URL%/2017-04-20-%MSYS_BITS%bit-gdborig.exe"
+ mv 2017-04-20-%MSYS_BITS%bit-gdborig.exe %MINGW_DIR%\bin\gdborig.exe
+ echo ##vso[task.prependpath]%CD%\%MINGW_DIR%\bin
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), ne(variables['MINGW_URL'],''))
+ displayName: Download custom MinGW
+
+# Otherwise pull in the MinGW installed on appveyor
+- script: |
+ echo ##vso[task.prependpath]%MSYS_PATH%\mingw%MSYS_BITS%\bin
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['MINGW_URL'],''))
+ displayName: Add MinGW to path
+
+# 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.
+- script: |
+ copy C:\Python27amd64\python.exe C:\Python27amd64\python2.7.exe
+ echo ##vso[task.prependpath]C:\Python27amd64
+ displayName: Prefer the "native" Python as LLVM has trouble building with MSYS sometimes
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
+
+# Note that this is originally from the github releases patch of Ninja
+- script: |
+ md ninja
+ powershell -Command "iwr -outf 2017-03-15-ninja-win.zip https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror/2017-03-15-ninja-win.zip"
+ 7z x -oninja 2017-03-15-ninja-win.zip
+ del 2017-03-15-ninja-win.zip
+ set RUST_CONFIGURE_ARGS=%RUST_CONFIGURE_ARGS% --enable-ninja
+ echo ##vso[task.setvariable variable=RUST_CONFIGURE_ARGS]%RUST_CONFIGURE_ARGS%
+ echo ##vso[task.prependpath]%CD%\ninja
+ displayName: Download and install ninja
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
--- /dev/null
+# FIXME(linux): need to configure core dumps, enable them, and then dump
+# backtraces on failure from all core dumps:
+#
+# - bash: sudo apt install gdb
+# - bash: sudo sh -c 'echo "/checkout/obj/cores/core.%p.%E" > /proc/sys/kernel/core_pattern'
+#
+# Check travis config for `gdb --batch` command to print all crash logs
+
+steps:
+
+- checkout: self
+ fetchDepth: 2
+
+- bash: printenv | sort
+ displayName: Show environment variables
+
+- bash: |
+ set -e
+ df -h
+ du . | sort -nr | head -n100
+ displayName: Show disk usage
+ # FIXME: this hasn't been tested, but maybe it works on Windows? Should test!
+ condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT'))
+
+- template: install-sccache.yml
+- template: install-clang.yml
+
+# Install some dependencies needed to build LLDB/Clang, currently only needed
+# during the `dist` target
+- bash: |
+ set -e
+ brew update
+ brew install xz
+ brew install swig
+ displayName: Install build dependencies (OSX)
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin'), eq(variables['RUST_CHECK_TARGET'],'dist'))
+
+# Switch to XCode 9.3 on OSX since it seems to be the last version that supports
+# i686-apple-darwin. We'll eventually want to upgrade this and it will probably
+# force us to drop i686-apple-darwin, but let's keep the wheels turning for now.
+- bash: |
+ set -e
+ sudo xcode-select --switch /Applications/Xcode_9.3.app
+ displayName: Switch to Xcode 9.3 (OSX)
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin'))
+
+- template: install-windows-build-deps.yml
+
+# Looks like docker containers have IPv6 disabled by default, so let's turn it
+# on since libstd tests require it
+- bash: |
+ set -e
+ echo '{"ipv6":true,"fixed-cidr-v6":"fd9a:8454:6789:13f7::/64"}' | sudo tee /etc/docker/daemon.json
+ sudo service docker restart
+ displayName: Enable IPv6
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
+
+# Check out all our submodules, but more quickly than using git by using one of
+# our custom scripts
+- bash: |
+ set -e
+ mkdir -p $HOME/rustsrc
+ $BUILD_SOURCESDIRECTORY/src/ci/init_repo.sh . $HOME/rustsrc
+ condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT'))
+ displayName: Check out submodules (Unix)
+- script: |
+ if not exist D:\cache\rustsrc\NUL mkdir D:\cache\rustsrc
+ sh src/ci/init_repo.sh . /d/cache/rustsrc
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
+ displayName: Check out submodules (Windows)
+
+# Ensure the `aws` CLI is installed so we can deploy later on, cache docker
+# images, etc.
+- bash: |
+ set -e
+ sudo apt-get install -y python3-setuptools
+ pip3 install awscli --upgrade --user
+ echo "##vso[task.prependpath]$HOME/.local/bin"
+ displayName: Install awscli (Linux)
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
+- script: pip install awscli
+ displayName: Install awscli (non-Linux)
+ condition: and(succeeded(), ne(variables['Agent.OS'], 'Linux'))
+
+# Configure our CI_JOB_NAME variable which log analyzers can use for the main
+# step to see what's going on.
+- bash: echo "##vso[task.setvariable variable=CI_JOB_NAME]$SYSTEM_JOBNAME"
+ displayName: Configure Job Name
+
+# As a quick smoke check on the otherwise very fast mingw-check linux builder
+# check our own internal scripts.
+- bash: |
+ set -e
+ git clone --depth=1 https://github.com/rust-lang-nursery/rust-toolstate.git
+ cd rust-toolstate
+ python2.7 "$BUILD_SOURCESDIRECTORY/src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" "$(git log --format=%s -n1 HEAD)" "" ""
+ cd ..
+ rm -rf rust-toolstate
+ condition: and(succeeded(), eq(variables['IMAGE'], 'mingw-check'))
+ displayName: Verify the publish_toolstate script works
+
+- bash: |
+ set -e
+ if [ "$IMAGE" = "" ]; then
+ src/ci/run.sh
+ else
+ src/ci/docker/run.sh $IMAGE
+ fi
+ #timeoutInMinutes: 180
+ timeoutInMinutes: 600
+ env:
+ CI: true
+ SRC: .
+ AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
+ displayName: Run build
+
+# If we're a deploy builder, use the `aws` command to publish everything to our
+# bucket.
+- bash: |
+ set -e
+ if [ "$AGENT_OS" = "Linux" ]; then
+ rm -rf obj/build/dist/doc
+ upload_dir=obj/build/dist
+ else
+ rm -rf build/dist/doc
+ upload_dir=build/dist
+ fi
+ ls -la $upload_dir
+ deploy_dir=rustc-builds
+ if [ "$DEPLOY_ALT" == "1" ]; then
+ deploy_dir=rustc-builds-alt
+ fi
+ aws s3 cp --no-progress --recursive --acl public-read ./$upload_dir s3://$DEPLOY_BUCKET/$deploy_dir/$BUILD_SOURCEVERSION
+ env:
+ AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)
+ condition: and(succeeded(), or(eq(variables.DEPLOY, '1'), eq(variables.DEPLOY_ALT, '1')))
+ displayName: Upload artifacts
--- /dev/null
+#
+# Azure Pipelines "auto" branch build for Rust on Linux, macOS, and Windows.
+#
+
+pr: none
+trigger:
+- try
+
+variables:
+- group: prod-credentials
+
+jobs:
+- job: Linux
+ timeoutInMinutes: 600
+ pool:
+ vmImage: ubuntu-16.04
+ steps:
+ - template: steps/run.yml
+ strategy:
+ matrix:
+ dist-x86_64-linux:
+ IMAGE: dist-x86_64-linux
+ DEPLOY: 1
+
+ dist-x86_64-linux-alt:
+ IMAGE: dist-x86_64-linux
+ DEPLOY_ALT: 1
+
+- job: macOS
+ timeoutInMinutes: 600
+ pool:
+ vmImage: macos-10.13
+ steps:
+ - template: steps/run.yml
+ strategy:
+ matrix:
+ dist-x86_64-apple:
+ RUST_CHECK_TARGET: 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 --enable-lldb --set rust.jemalloc
+ DEPLOY: 1
+ RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
+ MACOSX_DEPLOYMENT_TARGET: 10.7
+ NO_LLVM_ASSERTIONS: 1
+ NO_DEBUG_ASSERTIONS: 1
+ DIST_REQUIRE_ALL_TOOLS: 1
+
+ dist-x86_64-apple-alt:
+ RUST_CHECK_TARGET: dist
+ RUST_CONFIGURE_ARGS: --enable-extended --enable-profiler --enable-lldb --set rust.jemalloc
+ DEPLOY_ALT: 1
+ RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
+ MACOSX_DEPLOYMENT_TARGET: 10.7
+ NO_LLVM_ASSERTIONS: 1
+ NO_DEBUG_ASSERTIONS: 1
+
+- job: Windows
+ timeoutInMinutes: 600
+ pool:
+ vmImage: 'vs2017-win2016'
+ steps:
+ - template: steps/run.yml
+ strategy:
+ matrix:
+ dist-x86_64-msvc:
+ 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
+ DEPLOY: 1
+
+ dist-x86_64-msvc-alt:
+ MSYS_BITS: 64
+ RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler
+ SCRIPT: python x.py dist
+ DEPLOY_ALT: 1
"rustc_errors 0.0.0",
"rustc_target 0.0.0",
"serialize 0.0.0",
+ "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"stable_deref_trait 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntax 0.0.0",
"syntax_ext 0.0.0",
- CI_JOB_NAME: i686-msvc-1
MSYS_BITS: 32
RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc
- SCRIPT: make appveyor-subset-1
+ SCRIPT: make ci-subset-1
- CI_JOB_NAME: i686-msvc-2
MSYS_BITS: 32
RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc
- SCRIPT: make appveyor-subset-2
+ SCRIPT: make ci-subset-2
# MSVC aux tests
- CI_JOB_NAME: x86_64-msvc-aux
- CI_JOB_NAME: i686-mingw-1
MSYS_BITS: 32
RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
- SCRIPT: make appveyor-subset-1
+ SCRIPT: make ci-subset-1
MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
MINGW_ARCHIVE: i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z
MINGW_DIR: mingw32
- CI_JOB_NAME: i686-mingw-2
MSYS_BITS: 32
RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
- SCRIPT: make appveyor-subset-2
+ SCRIPT: make ci-subset-2
MINGW_URL: https://s3-us-west-1.amazonaws.com/rust-lang-ci2/rust-ci-mirror
MINGW_ARCHIVE: i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z
MINGW_DIR: mingw32
# library.
#debug-assertions = false
-# Whether or not debuginfo is emitted
-#debuginfo = false
+# Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`.
+# `0` - no debug info
+# `1` - line tables only
+# `2` - full debug info with variable and type information
+# Can be overriden for specific subsets of Rust code (rustc, std or tools).
+# Debuginfo for tests run with compiletest is not controlled by this option
+# and needs to be enabled separately with `debuginfo-level-tests`.
+#debuginfo-level = if debug { 2 } else { 0 }
-# Whether or not line number debug information is emitted
-#debuginfo-lines = false
+# Debuginfo level for the compiler.
+#debuginfo-level-rustc = debuginfo-level
-# Whether or not to only build debuginfo for the standard library if enabled.
-# If enabled, this will not compile the compiler with debuginfo, just the
-# standard library.
-#debuginfo-only-std = false
+# Debuginfo level for the standard library.
+#debuginfo-level-std = debuginfo-level
-# Enable debuginfo for the extended tools: cargo, rls, rustfmt
-# Adding debuginfo makes them several times larger.
-#debuginfo-tools = false
+# Debuginfo level for the tools.
+#debuginfo-level-tools = debuginfo-level
+
+# Debuginfo level for the test suites run with compiletest.
+# FIXME(#61117): Some tests fail when this option is enabled.
+#debuginfo-level-tests = 0
# Whether or not `panic!`s generate backtraces (RUST_BACKTRACE)
#backtrace = true
# harness are debuggable just from logfiles.
#verbose-tests = false
-# Flag indicating whether tests are compiled with optimizations (the -O flag) or
-# with debuginfo (the -g flag)
+# Flag indicating whether tests are compiled with optimizations (the -O flag).
#optimize-tests = true
-#debuginfo-tests = true
# Flag indicating whether codegen tests will be run or not. If you get an error
# saying that the FileCheck executable is missing, you may want to disable this.
cmd.env("RUSTC_BREAK_ON_ICE", "1");
+ if let Ok(debuginfo_level) = env::var("RUSTC_DEBUGINFO_LEVEL") {
+ cmd.arg(format!("-Cdebuginfo={}", debuginfo_level));
+ }
+
if let Some(target) = target {
// The stage0 compiler has a special sysroot distinct from what we
// actually downloaded, so we just always pass the `--sysroot` option.
// Set various options from config.toml to configure how we're building
// code.
- if env::var("RUSTC_DEBUGINFO") == Ok("true".to_string()) {
- cmd.arg("-g");
- } else if env::var("RUSTC_DEBUGINFO_LINES") == Ok("true".to_string()) {
- cmd.arg("-Cdebuginfo=1");
- }
let debug_assertions = match env::var("RUSTC_DEBUG_ASSERTIONS") {
Ok(s) => if s == "true" { "y" } else { "n" },
Err(..) => "n",
cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
}
- if mode.is_tool() {
- // Tools like cargo and rls don't get debuginfo by default right now, but this can be
- // enabled in the config. Adding debuginfo makes them several times larger.
- if self.config.rust_debuginfo_tools {
- cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string());
- cargo.env(
- "RUSTC_DEBUGINFO_LINES",
- self.config.rust_debuginfo_lines.to_string(),
- );
- }
- } else {
- cargo.env("RUSTC_DEBUGINFO", self.config.rust_debuginfo.to_string());
- cargo.env(
- "RUSTC_DEBUGINFO_LINES",
- self.config.rust_debuginfo_lines.to_string(),
- );
+ let debuginfo_level = match mode {
+ Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
+ Mode::Std | Mode::Test => self.config.rust_debuginfo_level_std,
+ Mode::ToolBootstrap | Mode::ToolStd |
+ Mode::ToolTest | Mode::ToolRustc => self.config.rust_debuginfo_level_tools,
+ };
+ cargo.env("RUSTC_DEBUGINFO_LEVEL", debuginfo_level.to_string());
+
+ if !mode.is_tool() {
cargo.env("RUSTC_FORCE_UNSTABLE", "1");
// Currently the compiler depends on crates from crates.io, and
let libdir_relative = builder.config.libdir_relative().unwrap_or(Path::new("lib"));
cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative);
- // If we're not building a compiler with debugging information then remove
- // these two env vars which would be set otherwise.
- if builder.config.rust_debuginfo_only_std {
- cargo.env_remove("RUSTC_DEBUGINFO");
- cargo.env_remove("RUSTC_DEBUGINFO_LINES");
- }
-
if let Some(ref ver_date) = builder.rust_info.commit_date() {
cargo.env("CFG_VER_DATE", ver_date);
}
pub rust_codegen_units: Option<u32>,
pub rust_codegen_units_std: Option<u32>,
pub rust_debug_assertions: bool,
- pub rust_debuginfo: bool,
- pub rust_debuginfo_lines: bool,
- pub rust_debuginfo_only_std: bool,
- pub rust_debuginfo_tools: bool,
+ pub rust_debuginfo_level_rustc: u32,
+ pub rust_debuginfo_level_std: u32,
+ pub rust_debuginfo_level_tools: u32,
+ pub rust_debuginfo_level_tests: u32,
pub rust_rpath: bool,
pub rustc_parallel: bool,
pub rustc_default_linker: Option<String>,
pub rust_optimize_tests: bool,
- pub rust_debuginfo_tests: bool,
pub rust_dist_src: bool,
pub rust_codegen_backends: Vec<Interned<String>>,
pub rust_codegen_backends_dir: String,
codegen_units: Option<u32>,
codegen_units_std: Option<u32>,
debug_assertions: Option<bool>,
- debuginfo: Option<bool>,
- debuginfo_lines: Option<bool>,
- debuginfo_only_std: Option<bool>,
- debuginfo_tools: Option<bool>,
+ debuginfo_level: Option<u32>,
+ debuginfo_level_rustc: Option<u32>,
+ debuginfo_level_std: Option<u32>,
+ debuginfo_level_tools: Option<u32>,
+ debuginfo_level_tests: Option<u32>,
parallel_compiler: Option<bool>,
backtrace: Option<bool>,
default_linker: Option<String>,
musl_root: Option<String>,
rpath: Option<bool>,
optimize_tests: Option<bool>,
- debuginfo_tests: Option<bool>,
codegen_tests: Option<bool>,
ignore_git: Option<bool>,
debug: Option<bool>,
// Store off these values as options because if they're not provided
// we'll infer default values for them later
let mut llvm_assertions = None;
- let mut debuginfo_lines = None;
- let mut debuginfo_only_std = None;
- let mut debuginfo_tools = None;
let mut debug = None;
- let mut debuginfo = None;
let mut debug_assertions = None;
+ let mut debuginfo_level = None;
+ let mut debuginfo_level_rustc = None;
+ let mut debuginfo_level_std = None;
+ let mut debuginfo_level_tools = None;
+ let mut debuginfo_level_tests = None;
let mut optimize = None;
let mut ignore_git = None;
if let Some(ref rust) = toml.rust {
debug = rust.debug;
debug_assertions = rust.debug_assertions;
- debuginfo = rust.debuginfo;
- debuginfo_lines = rust.debuginfo_lines;
- debuginfo_only_std = rust.debuginfo_only_std;
- debuginfo_tools = rust.debuginfo_tools;
+ debuginfo_level = rust.debuginfo_level;
+ debuginfo_level_rustc = rust.debuginfo_level_rustc;
+ debuginfo_level_std = rust.debuginfo_level_std;
+ debuginfo_level_tools = rust.debuginfo_level_tools;
+ debuginfo_level_tests = rust.debuginfo_level_tests;
optimize = rust.optimize;
ignore_git = rust.ignore_git;
set(&mut config.rust_optimize_tests, rust.optimize_tests);
- set(&mut config.rust_debuginfo_tests, rust.debuginfo_tests);
set(&mut config.codegen_tests, rust.codegen_tests);
set(&mut config.rust_rpath, rust.rpath);
set(&mut config.jemalloc, rust.jemalloc);
let default = true;
config.rust_optimize = optimize.unwrap_or(default);
- let default = match &config.channel[..] {
- "stable" | "beta" | "nightly" => true,
- _ => false,
- };
- config.rust_debuginfo_lines = debuginfo_lines.unwrap_or(default);
- config.rust_debuginfo_only_std = debuginfo_only_std.unwrap_or(default);
- config.rust_debuginfo_tools = debuginfo_tools.unwrap_or(false);
-
let default = debug == Some(true);
- config.rust_debuginfo = debuginfo.unwrap_or(default);
config.rust_debug_assertions = debug_assertions.unwrap_or(default);
+ let with_defaults = |debuginfo_level_specific: Option<u32>| {
+ debuginfo_level_specific
+ .or(debuginfo_level)
+ .unwrap_or(if debug == Some(true) { 2 } else { 0 })
+ };
+ config.rust_debuginfo_level_rustc = with_defaults(debuginfo_level_rustc);
+ config.rust_debuginfo_level_std = with_defaults(debuginfo_level_std);
+ config.rust_debuginfo_level_tools = with_defaults(debuginfo_level_tools);
+ config.rust_debuginfo_level_tests = debuginfo_level_tests.unwrap_or(0);
+
let default = config.channel == "dev";
config.ignore_git = ignore_git.unwrap_or(default);
o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")
o("parallel-compiler", "rust.parallel-compiler", "build a multi-threaded rustc")
o("test-miri", "rust.test-miri", "run miri's test suite")
-o("debuginfo-tests", "rust.debuginfo-tests", "build tests with debugger metadata")
o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests")
o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds")
o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds")
o("llvm-assertions", "llvm.assertions", "build LLVM with assertions")
o("debug-assertions", "rust.debug-assertions", "build with debugging assertions")
o("llvm-release-debuginfo", "llvm.release-debuginfo", "build LLVM with debugger metadata")
-o("debuginfo", "rust.debuginfo", "build with debugger metadata")
-o("debuginfo-lines", "rust.debuginfo-lines", "build with line number debugger metadata")
-o("debuginfo-only-std", "rust.debuginfo-only-std", "build only libstd with debugging information")
-o("debuginfo-tools", "rust.debuginfo-tools", "build extended tools with debugging information")
+o("debuginfo-level", "rust.debuginfo-level", "debuginfo level for Rust code")
+o("debuginfo-level-rustc", "rust.debuginfo-level-rustc", "debuginfo level for the compiler")
+o("debuginfo-level-std", "rust.debuginfo-level-std", "debuginfo level for the standard library")
+o("debuginfo-level-tools", "rust.debuginfo-level-tools", "debuginfo level for the tools")
+o("debuginfo-level-tests", "rust.debuginfo-level-tests", "debuginfo level for the test suites run with compiletest")
v("save-toolstates", "rust.save-toolstates", "save build and test status of external tools into this file")
v("prefix", "install.prefix", "set installation prefix")
src/test/run-pass-fulldeps \
src/tools/linkchecker
-appveyor-subset-1:
+ci-subset-1:
$(Q)$(BOOTSTRAP) test $(TESTS_IN_2:%=--exclude %)
-appveyor-subset-2:
+ci-subset-2:
$(Q)$(BOOTSTRAP) test $(TESTS_IN_2)
}
if suite == "debuginfo" {
- // Skip debuginfo tests on MSVC
- if builder.config.build.contains("msvc") {
- return;
- }
-
+ let msvc = builder.config.build.contains("msvc");
if mode == "debuginfo" {
return builder.ensure(Compiletest {
- mode: "debuginfo-both",
+ mode: if msvc { "debuginfo-cdb" } else { "debuginfo-gdb+lldb" },
..self
});
}
if builder.config.rust_optimize_tests {
flags.push("-O".to_string());
}
- if builder.config.rust_debuginfo_tests {
- flags.push("-g".to_string());
- }
}
+ flags.push(format!("-Cdebuginfo={}", builder.config.rust_debuginfo_level_tests));
flags.push("-Zunstable-options".to_string());
flags.push(builder.config.cmd.rustc_args().join(" "));
&[],
);
- // Most tools don't get debuginfo, but rustdoc should.
- cargo.env("RUSTC_DEBUGINFO", builder.config.rust_debuginfo.to_string())
- .env("RUSTC_DEBUGINFO_LINES", builder.config.rust_debuginfo_lines.to_string());
-
let _folder = builder.fold_output(|| format!("stage{}-rustdoc", target_compiler.stage));
builder.info(&format!("Building rustdoc for stage{} ({})",
target_compiler.stage, target_compiler.host));
Travis,
/// The AppVeyor environment, for Windows builds.
AppVeyor,
+ /// The Azure Pipelines environment, for Linux (including Docker), Windows, and macOS builds.
+ AzurePipelines,
}
impl CiEnv {
CiEnv::Travis
} else if env::var("APPVEYOR").ok().map_or(false, |e| &*e == "True") {
CiEnv::AppVeyor
+ } else if env::var("TF_BUILD").ok().map_or(false, |e| &*e == "True") {
+ CiEnv::AzurePipelines
} else {
CiEnv::None
}
RUN /tmp/build-cloudabi-toolchain.sh x86_64-unknown-cloudabi
COPY dist-various-2/build-fuchsia-toolchain.sh /tmp/
RUN /tmp/build-fuchsia-toolchain.sh
-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
+# FIXME(#61022) - reenable solaris
+# 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/
# 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.
ENV TARGETS=x86_64-fuchsia
ENV TARGETS=$TARGETS,aarch64-fuchsia
-ENV TARGETS=$TARGETS,sparcv9-sun-solaris
ENV TARGETS=$TARGETS,wasm32-unknown-unknown
ENV TARGETS=$TARGETS,wasm32-wasi
-ENV TARGETS=$TARGETS,x86_64-sun-solaris
+# FIXME(#61022) - reenable solaris
+# ENV TARGETS=$TARGETS,sparcv9-sun-solaris
+# ENV TARGETS=$TARGETS,x86_64-sun-solaris
ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32
ENV TARGETS=$TARGETS,x86_64-unknown-cloudabi
ENV TARGETS=$TARGETS,x86_64-fortanix-unknown-sgx
docker --version >> $hash_key
cksum=$(sha512sum $hash_key | \
awk '{print $1}')
+
s3url="s3://$SCCACHE_BUCKET/docker/$cksum"
- url="https://s3-us-west-1.amazonaws.com/$SCCACHE_BUCKET/docker/$cksum"
- echo "Attempting to download $s3url"
+ url="https://$SCCACHE_BUCKET.s3.amazonaws.com/docker/$cksum"
+ upload="aws s3 cp - $s3url"
+
+ echo "Attempting to download $url"
rm -f /tmp/rustci_docker_cache
set +e
retry curl -y 30 -Y 10 --connect-timeout 30 -f -L -C - -o /tmp/rustci_docker_cache "$url"
-f "$dockerfile" \
"$context"
- if [ "$s3url" != "" ]; then
+ if [ "$upload" != "" ]; then
digest=$(docker inspect rust-ci --format '{{.Id}}')
echo "Built container $digest"
if ! grep -q "$digest" <(echo "$loaded_images"); then
- echo "Uploading finished image to $s3url"
+ echo "Uploading finished image to $url"
set +e
docker history -q rust-ci | \
grep -v missing | \
xargs docker save | \
gzip | \
- aws s3 cp - $s3url
+ $upload
set -e
else
echo "Looks like docker image is the same as before, not uploading"
echo "$digest" >>"$info"
fi
elif [ -f "$docker_dir/disabled/$image/Dockerfile" ]; then
- if [ -n "$TRAVIS_OS_NAME" ]; then
- echo Cannot run disabled images on travis!
+ if isCI; then
+ echo Cannot run disabled images on CI!
exit 1
fi
# retry messes with the pipe from tar to docker. Not needed on non-travis
--env DEPLOY \
--env DEPLOY_ALT \
--env LOCAL_USER_ID=`id -u` \
+ --env CI \
--env TRAVIS \
--env TRAVIS_BRANCH \
+ --env TF_BUILD \
+ --env BUILD_SOURCEBRANCHNAME \
--env TOOLSTATE_REPO_ACCESS_TOKEN \
--env CI_JOB_NAME="${CI_JOB_NAME-$IMAGE}" \
--volume "$HOME/.cargo:/cargo" \
ci_dir=`cd $(dirname $0) && pwd`
source "$ci_dir/shared.sh"
-if [ "$TRAVIS" != "true" ] || [ "$TRAVIS_BRANCH" == "auto" ]; then
+branch_name=$(getCIBranch)
+
+if [ ! isCI ] || [ "$branch_name" = "auto" ]; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests"
fi
# FIXME: need a scheme for changing this `nightly` value to `beta` and `stable`
# either automatically or manually.
export RUST_RELEASE_CHANNEL=nightly
-if [ "$DEPLOY$DEPLOY_ALT" != "" ]; then
+if [ "$DEPLOY$DEPLOY_ALT" = "1" ]; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --release-channel=$RUST_RELEASE_CHANNEL"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-llvm-static-stdcpp"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.remap-debuginfo"
+ RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.debuginfo-level-std=1"
if [ "$NO_LLVM_ASSERTIONS" = "1" ]; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-llvm-assertions"
# Display the CPU and memory information. This helps us know why the CI timing
# is fluctuating.
travis_fold start log-system-info
-if [ "$TRAVIS_OS_NAME" = "osx" ]; then
+if isOSX; then
system_profiler SPHardwareDataType || true
sysctl hw || true
ncpus=$(sysctl -n hw.ncpu)
do_make all
do_make "$RUST_CHECK_TARGET"
fi
+
+sccache --show-stats || true
done
}
+function isCI {
+ [ "$CI" = "true" ] || [ "$TRAVIS" = "true" ] || [ "$TF_BUILD" = "True" ]
+}
+
+function isOSX {
+ [ "$TRAVIS_OS_NAME" = "osx" ] || [ "$AGENT_OS" = "Darwin" ]
+}
+
+function getCIBranch {
+ if [ "$TRAVIS" = "true" ]; then
+ echo "$TRAVIS_BRANCH"
+ else
+ echo "$BUILD_SOURCEBRANCHNAME"
+ fi;
+}
+
if ! declare -F travis_fold; then
if [ "${TRAVIS-false}" = 'true' ]; then
# This is a trimmed down copy of
#[unstable(feature = "ptr_internals", issue = "0", reason = "use into_raw_non_null instead")]
#[inline]
#[doc(hidden)]
- pub fn into_unique(mut b: Box<T>) -> Unique<T> {
+ pub fn into_unique(b: Box<T>) -> Unique<T> {
+ 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 `Uniq` tag. We round-trip through a mutable reference to avoid that.
- let unique = unsafe { b.0.as_mut() as *mut T };
- mem::forget(b);
- unsafe { Unique::new_unchecked(unique) }
+ // 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) }
}
/// Consumes and leaks the `Box`, returning a mutable reference,
-#![cfg(not(miri))]
-
use std::borrow::Cow;
use std::mem::size_of;
use std::{usize, isize};
it.next().unwrap();
let vec = it.collect::<Vec<_>>();
assert_eq!(vec, [2, 3]);
+ #[cfg(not(miri))] // Miri does not support comparing dangling pointers
assert!(ptr != vec.as_ptr());
}
}
#[test]
+#[cfg(not(miri))] // Miri does not support signalling OOM
fn test_try_reserve() {
// These are the interesting cases:
}
#[test]
+#[cfg(not(miri))] // Miri does not support signalling OOM
fn test_try_reserve_exact() {
// This is exactly the same as test_try_reserve with the method changed.
let count = (*other).len();
self.reserve(count);
let len = self.len();
- ptr::copy_nonoverlapping(other as *const T, self.get_unchecked_mut(len), count);
+ ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count);
self.len += count;
}
}
}
+ #[inline]
+ unsafe fn write_from_iter<T, I: Iterator<Item = T>>(
+ &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.offset(i as isize), value.unwrap());
+ i += 1;
+ }
+ }
+
#[inline]
pub fn alloc_from_iter<T, I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
- let mut iter = iter.into_iter();
+ let iter = iter.into_iter();
assert!(mem::size_of::<T>() != 0);
assert!(!mem::needs_drop::<T>());
let size = len.checked_mul(mem::size_of::<T>()).unwrap();
let mem = self.alloc_raw(size, mem::align_of::<T>()) as *mut _ as *mut T;
unsafe {
- for i in 0..len {
- ptr::write(mem.offset(i as isize), iter.next().unwrap())
- }
- slice::from_raw_parts_mut(mem, len)
+ self.write_from_iter(iter, len, mem)
}
}
(_, _) => {
use std::marker::PhantomData;
use smallvec::SmallVec;
+/// 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) => (
rustc::hir::def_id::DefId,
rustc::ty::subst::SubstsRef<$tcx>
)>,
- [few] mir_keys: rustc::util::nodemap::DefIdSet,
+ [few, decode] mir_keys: rustc::util::nodemap::DefIdSet,
[decode] specialization_graph: rustc::traits::specialization_graph::Graph,
[] region_scope_tree: rustc::middle::region::ScopeTree,
[] item_local_set: rustc::util::nodemap::ItemLocalSet,
rustc::infer::canonical::Canonical<'tcx,
rustc::infer::canonical::QueryResponse<'tcx, rustc::ty::Ty<'tcx>>
>,
+ [few] crate_inherent_impls: rustc::ty::CrateInherentImpls,
+ [decode] borrowck: rustc::middle::borrowck::BorrowCheckResult,
+ [few] upstream_monomorphizations:
+ rustc::util::nodemap::DefIdMap<
+ rustc_data_structures::fx::FxHashMap<
+ rustc::ty::subst::SubstsRef<'tcx>,
+ rustc::hir::def_id::CrateNum
+ >
+ >,
+ [few] resolve_lifetimes: rustc::middle::resolve_lifetime::ResolveLifetimes,
+ [decode] generic_predicates: rustc::ty::GenericPredicates<'tcx>,
+ [few] lint_levels: rustc::lint::LintLevelMap,
+ [few] stability_index: rustc::middle::stability::Index<'tcx>,
+ [few] features: syntax::feature_gate::Features,
+ [few] all_traits: Vec<rustc::hir::def_id::DefId>,
+ [few] privacy_access_levels: rustc::middle::privacy::AccessLevels,
+ [few] target_features_whitelist: rustc_data_structures::fx::FxHashMap<
+ String,
+ Option<syntax::symbol::Symbol>
+ >,
+ [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::util::nodemap::DefIdMap<rustc::hir::def_id::DefId>,
+ [few] foreign_module: rustc::middle::cstore::ForeignModule,
+ [few] foreign_modules: Vec<rustc::middle::cstore::ForeignModule>,
+ [few] reachable_non_generics: rustc::util::nodemap::DefIdMap<
+ rustc::middle::exported_symbols::SymbolExportLevel
+ >,
+ [few] crate_variances: rustc::ty::CrateVariancesMap<'tcx>,
+ [few] inferred_outlives_crate: rustc::ty::CratePredicatesMap<'tcx>,
], $tcx);
)
}
impl<T: Copy> ArenaAllocatable for T {}
-pub unsafe trait ArenaField<'tcx>: Sized {
+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<Self>>;
fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
let kind = match e.node {
ExprKind::Box(ref inner) => hir::ExprKind::Box(P(self.lower_expr(inner))),
- ExprKind::ObsoleteInPlace(..) => {
- self.sess.abort_if_errors();
- span_bug!(e.span, "encountered ObsoleteInPlace expr during lowering");
- }
ExprKind::Array(ref exprs) => {
hir::ExprKind::Array(exprs.iter().map(|x| self.lower_expr(x)).collect())
}
pub use self::Level::*;
pub use self::LintSource::*;
-use rustc_data_structures::sync::{self, Lrc};
+use rustc_data_structures::sync;
use crate::hir::def_id::{CrateNum, LOCAL_CRATE};
use crate::hir::intravisit;
use errors::{DiagnosticBuilder, DiagnosticId};
use std::{hash, ptr};
use syntax::ast;
-use syntax::source_map::{MultiSpan, ExpnFormat};
+use syntax::source_map::{MultiSpan, ExpnFormat, CompilerDesugaringKind};
use syntax::early_buffered_lints::BufferedEarlyLintId;
use syntax::edition::Edition;
use syntax::symbol::{Symbol, sym};
}
fn lint_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, cnum: CrateNum)
- -> Lrc<LintLevelMap>
+ -> &'tcx LintLevelMap
{
assert_eq!(cnum, LOCAL_CRATE);
let mut builder = LintLevelMapBuilder {
intravisit::walk_crate(&mut builder, krate);
builder.levels.pop(push);
- Lrc::new(builder.levels.build_map())
+ tcx.arena.alloc(builder.levels.build_map())
}
struct LintLevelMapBuilder<'a, 'tcx: 'a> {
};
match info.format {
- ExpnFormat::MacroAttribute(..) => return true, // definitely a plugin
- ExpnFormat::CompilerDesugaring(_) => return true, // well, it's "external"
- ExpnFormat::MacroBang(..) => {} // check below
- }
-
- let def_site = match info.def_site {
- Some(span) => span,
- // no span for the def_site means it's an external macro
- None => return true,
- };
+ ExpnFormat::MacroAttribute(..) => true, // definitely a plugin
+ ExpnFormat::CompilerDesugaring(CompilerDesugaringKind::ForLoop) => false,
+ ExpnFormat::CompilerDesugaring(_) => true, // well, it's "external"
+ ExpnFormat::MacroBang(..) => {
+ let def_site = match info.def_site {
+ Some(span) => span,
+ // no span for the def_site means it's an external macro
+ None => return true,
+ };
- match sess.source_map().span_to_snippet(def_site) {
- Ok(code) => !code.starts_with("macro_rules"),
- // no snippet = external macro or compiler-builtin expansion
- Err(_) => true,
+ match sess.source_map().span_to_snippet(def_site) {
+ Ok(code) => !code.starts_with("macro_rules"),
+ // no snippet = external macro or compiler-builtin expansion
+ Err(_) => true,
+ }
+ }
}
}
Some((cnum, path))
})
.collect::<Vec<_>>();
- let mut ordering = tcx.postorder_cnums(LOCAL_CRATE);
- Lrc::make_mut(&mut ordering).reverse();
+ let mut ordering = tcx.postorder_cnums(LOCAL_CRATE).to_owned();
+ ordering.reverse();
libs.sort_by_cached_key(|&(a, _)| {
ordering.iter().position(|x| *x == a)
});
use crate::session::Session;
use crate::util::nodemap::{DefIdMap, FxHashMap, FxHashSet, HirIdMap, HirIdSet};
use errors::{Applicability, DiagnosticBuilder};
-use rustc_data_structures::sync::Lrc;
use rustc_macros::HashStable;
use std::borrow::Cow;
use std::cell::Cell;
/// See [`NamedRegionMap`].
#[derive(Default)]
pub struct ResolveLifetimes {
- defs: FxHashMap<LocalDefId, Lrc<FxHashMap<ItemLocalId, Region>>>,
- late_bound: FxHashMap<LocalDefId, Lrc<FxHashSet<ItemLocalId>>>,
+ defs: FxHashMap<LocalDefId, FxHashMap<ItemLocalId, Region>>,
+ late_bound: FxHashMap<LocalDefId, FxHashSet<ItemLocalId>>,
object_lifetime_defaults:
- FxHashMap<LocalDefId, Lrc<FxHashMap<ItemLocalId, Lrc<Vec<ObjectLifetimeDefault>>>>>,
+ FxHashMap<LocalDefId, FxHashMap<ItemLocalId, Vec<ObjectLifetimeDefault>>>,
}
impl_stable_hash_for!(struct crate::middle::resolve_lifetime::ResolveLifetimes {
named_region_map: |tcx, id| {
let id = LocalDefId::from_def_id(DefId::local(id)); // (*)
- tcx.resolve_lifetimes(LOCAL_CRATE).defs.get(&id).cloned()
+ tcx.resolve_lifetimes(LOCAL_CRATE).defs.get(&id)
},
is_late_bound_map: |tcx, id| {
tcx.resolve_lifetimes(LOCAL_CRATE)
.late_bound
.get(&id)
- .cloned()
},
object_lifetime_defaults_map: |tcx, id| {
tcx.resolve_lifetimes(LOCAL_CRATE)
.object_lifetime_defaults
.get(&id)
- .cloned()
},
..*providers
fn resolve_lifetimes<'tcx>(
tcx: TyCtxt<'_, 'tcx, 'tcx>,
for_krate: CrateNum,
-) -> Lrc<ResolveLifetimes> {
+) -> &'tcx ResolveLifetimes {
assert_eq!(for_krate, LOCAL_CRATE);
let named_region_map = krate(tcx);
for (hir_id, v) in named_region_map.defs {
let map = rl.defs.entry(hir_id.owner_local_def_id()).or_default();
- Lrc::get_mut(map).unwrap().insert(hir_id.local_id, v);
+ map.insert(hir_id.local_id, v);
}
for hir_id in named_region_map.late_bound {
let map = rl.late_bound
.entry(hir_id.owner_local_def_id())
.or_default();
- Lrc::get_mut(map).unwrap().insert(hir_id.local_id);
+ map.insert(hir_id.local_id);
}
for (hir_id, v) in named_region_map.object_lifetime_defaults {
let map = rl.object_lifetime_defaults
.entry(hir_id.owner_local_def_id())
.or_default();
- Lrc::get_mut(map)
- .unwrap()
- .insert(hir_id.local_id, Lrc::new(v));
+ map.insert(hir_id.local_id, v);
}
- Lrc::new(rl)
+ tcx.arena.alloc(rl)
}
fn krate<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) -> NamedRegionMap {
remaining_lib_features.remove(&Symbol::intern("test"));
let check_features =
- |remaining_lib_features: &mut FxHashMap<_, _>, defined_features: &Vec<_>| {
+ |remaining_lib_features: &mut FxHashMap<_, _>, defined_features: &[_]| {
for &(feature, since) in defined_features {
if let Some(since) = since {
if let Some(span) = remaining_lib_features.get(&feature) {
if remaining_lib_features.is_empty() {
break;
}
- check_features(&mut remaining_lib_features, &tcx.defined_lib_features(cnum));
+ check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum));
}
}
}
}
+ /// 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.compiler_desugaring_kind().is_some()
+ }
+
/// Creates a new `LocalDecl` for a temporary.
#[inline]
pub fn new_temp(ty: Ty<'tcx>, span: Span) -> Self {
/// 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(_: DefId) -> Lrc<ty::GenericPredicates<'tcx>> {}
+ query predicates_of(_: DefId) -> &'tcx ty::GenericPredicates<'tcx> {}
query native_libraries(_: CrateNum) -> Lrc<Vec<NativeLibrary>> {
desc { "looking up the native libraries of a linked crate" }
}
- query lint_levels(_: CrateNum) -> Lrc<lint::LintLevelMap> {
+ query lint_levels(_: CrateNum) -> &'tcx lint::LintLevelMap {
eval_always
desc { "computing the lint levels for items in this crate" }
}
}
Linking {
- query wasm_import_module_map(_: CrateNum) -> Lrc<FxHashMap<DefId, String>> {
+ query wasm_import_module_map(_: CrateNum) -> &'tcx FxHashMap<DefId, String> {
desc { "wasm import module map" }
}
}
/// equal to the `explicit_predicates_of` predicates plus the
/// `inferred_outlives_of` predicates.
query predicates_defined_on(_: DefId)
- -> Lrc<ty::GenericPredicates<'tcx>> {}
+ -> &'tcx ty::GenericPredicates<'tcx> {}
/// Returns the predicates written explicitly by the user.
query explicit_predicates_of(_: DefId)
- -> Lrc<ty::GenericPredicates<'tcx>> {}
+ -> &'tcx ty::GenericPredicates<'tcx> {}
/// Returns the inferred outlives predicates (e.g., for `struct
/// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`).
/// 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) -> Lrc<ty::GenericPredicates<'tcx>> {
+ query super_predicates_of(key: DefId) -> &'tcx 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))
- -> Lrc<ty::GenericPredicates<'tcx>> {
+ -> &'tcx ty::GenericPredicates<'tcx> {
no_force
desc { |tcx| "computing the bounds for type parameter `{}`", {
let id = tcx.hir().as_local_hir_id(key.1).unwrap();
query static_mutability(_: DefId) -> Option<hir::Mutability> {}
/// Gets a map with the variance of every item; use `item_variance` instead.
- query crate_variances(_: CrateNum) -> Lrc<ty::CrateVariancesMap<'tcx>> {
+ query crate_variances(_: CrateNum) -> &'tcx ty::CrateVariancesMap<'tcx> {
desc { "computing the variances for items in this crate" }
}
TypeChecking {
/// Maps from thee `DefId` of a type to its (inferred) outlives.
query inferred_outlives_crate(_: CrateNum)
- -> Lrc<ty::CratePredicatesMap<'tcx>> {
+ -> &'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) -> Lrc<Vec<DefId>> {}
+ query associated_item_def_ids(_: DefId) -> &'tcx [DefId] {}
/// Maps from a trait item to the trait item "descriptor".
query associated_item(_: DefId) -> ty::AssociatedItem {}
/// 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) -> Lrc<Vec<DefId>> {
+ query inherent_impls(_: DefId) -> &'tcx [DefId] {
eval_always
}
}
}
Other {
- query used_trait_imports(_: DefId) -> Lrc<DefIdSet> {}
+ query used_trait_imports(_: DefId) -> &'tcx DefIdSet {}
}
TypeChecking {
}
BorrowChecking {
- query borrowck(_: DefId) -> Lrc<BorrowCheckResult> {}
+ query borrowck(_: DefId) -> &'tcx BorrowCheckResult {}
/// Borrow-checks the function body. If this is a closure, returns
/// additional requirements that the closure's creator must verify.
/// Not meant to be used directly outside of coherence.
/// (Defined only for `LOCAL_CRATE`.)
query crate_inherent_impls(k: CrateNum)
- -> Lrc<CrateInherentImpls> {
+ -> &'tcx CrateInherentImpls {
eval_always
desc { "all inherent impls defined in crate `{:?}`", k }
}
query check_match(_: DefId) -> () {}
/// Performs part of the privacy check and computes "access levels".
- query privacy_access_levels(_: CrateNum) -> Lrc<AccessLevels> {
+ query privacy_access_levels(_: CrateNum) -> &'tcx AccessLevels {
eval_always
desc { "privacy access levels" }
}
Other {
query dylib_dependency_formats(_: CrateNum)
- -> Lrc<Vec<(CrateNum, LinkagePreference)>> {
+ -> &'tcx [(CrateNum, LinkagePreference)] {
desc { "dylib dependency formats of crate" }
}
}
desc { "test whether a crate has #![no_builtins]" }
}
- query extern_crate(_: DefId) -> Lrc<Option<ExternCrate>> {
+ query extern_crate(_: DefId) -> Option<&'tcx ExternCrate> {
eval_always
desc { "getting crate's ExternCrateData" }
}
desc { "computing whether impls specialize one another" }
}
query in_scope_traits_map(_: DefIndex)
- -> Option<Lrc<FxHashMap<ItemLocalId, Lrc<StableVec<TraitCandidate>>>>> {
+ -> Option<&'tcx FxHashMap<ItemLocalId, StableVec<TraitCandidate>>> {
eval_always
desc { "traits in scope at a block" }
}
}
Other {
- query module_exports(_: DefId) -> Option<Lrc<Vec<Export<hir::HirId>>>> {
+ query module_exports(_: DefId) -> Option<&'tcx [Export<hir::HirId>]> {
eval_always
}
}
// 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)
- -> Lrc<DefIdMap<SymbolExportLevel>> {
+ -> &'tcx DefIdMap<SymbolExportLevel> {
desc { "looking up the exported symbols of a crate" }
}
query is_reachable_non_generic(_: DefId) -> bool {}
Codegen {
query upstream_monomorphizations(
k: CrateNum
- ) -> Lrc<DefIdMap<Lrc<FxHashMap<SubstsRef<'tcx>, CrateNum>>>> {
+ ) -> &'tcx DefIdMap<FxHashMap<SubstsRef<'tcx>, CrateNum>> {
desc { "collecting available upstream monomorphizations `{:?}`", k }
}
query upstream_monomorphizations_for(_: DefId)
- -> Option<Lrc<FxHashMap<SubstsRef<'tcx>, CrateNum>>> {}
+ -> Option<&'tcx FxHashMap<SubstsRef<'tcx>, CrateNum>> {}
}
Other {
- query foreign_modules(_: CrateNum) -> Lrc<Vec<ForeignModule>> {
+ query foreign_modules(_: CrateNum) -> &'tcx [ForeignModule] {
desc { "looking up the foreign modules of a linked crate" }
}
TypeChecking {
query implementations_of_trait(_: (CrateNum, DefId))
- -> Lrc<Vec<DefId>> {
+ -> &'tcx [DefId] {
no_force
desc { "looking up implementations of a trait in a crate" }
}
query all_trait_implementations(_: CrateNum)
- -> Lrc<Vec<DefId>> {
+ -> &'tcx [DefId] {
desc { "looking up all (?) trait implementations" }
}
}
Other {
query dllimport_foreign_items(_: CrateNum)
- -> Lrc<FxHashSet<DefId>> {
+ -> &'tcx FxHashSet<DefId> {
desc { "dllimport_foreign_items" }
}
query is_dllimport_foreign_item(_: DefId) -> bool {}
BorrowChecking {
// Lifetime resolution. See `middle::resolve_lifetimes`.
- query resolve_lifetimes(_: CrateNum) -> Lrc<ResolveLifetimes> {
+ query resolve_lifetimes(_: CrateNum) -> &'tcx ResolveLifetimes {
desc { "resolving lifetimes" }
}
query named_region_map(_: DefIndex) ->
- Option<Lrc<FxHashMap<ItemLocalId, Region>>> {
+ Option<&'tcx FxHashMap<ItemLocalId, Region>> {
desc { "looking up a named region" }
}
query is_late_bound_map(_: DefIndex) ->
- Option<Lrc<FxHashSet<ItemLocalId>>> {
+ Option<&'tcx FxHashSet<ItemLocalId>> {
desc { "testing if a region is late bound" }
}
query object_lifetime_defaults_map(_: DefIndex)
- -> Option<Lrc<FxHashMap<ItemLocalId, Lrc<Vec<ObjectLifetimeDefault>>>>> {
+ -> Option<&'tcx FxHashMap<ItemLocalId, Vec<ObjectLifetimeDefault>>> {
desc { "looking up lifetime defaults for a region" }
}
}
eval_always
desc { "fetching what a crate is named" }
}
- query item_children(_: DefId) -> Lrc<Vec<Export<hir::HirId>>> {}
+ query item_children(_: DefId) -> &'tcx [Export<hir::HirId>] {}
query extern_mod_stmt_cnum(_: DefId) -> Option<CrateNum> {}
- query get_lib_features(_: CrateNum) -> Lrc<LibFeatures> {
+ query get_lib_features(_: CrateNum) -> &'tcx LibFeatures {
eval_always
desc { "calculating the lib features map" }
}
query defined_lib_features(_: CrateNum)
- -> Lrc<Vec<(Symbol, Option<Symbol>)>> {
+ -> &'tcx [(Symbol, Option<Symbol>)] {
desc { "calculating the lib features defined in a crate" }
}
- query get_lang_items(_: CrateNum) -> Lrc<LanguageItems> {
+ query get_lang_items(_: CrateNum) -> &'tcx LanguageItems {
eval_always
desc { "calculating the lang items map" }
}
- query defined_lang_items(_: CrateNum) -> Lrc<Vec<(DefId, usize)>> {
+ query defined_lang_items(_: CrateNum) -> &'tcx [(DefId, usize)] {
desc { "calculating the lang items defined in a crate" }
}
- query missing_lang_items(_: CrateNum) -> Lrc<Vec<LangItem>> {
+ query missing_lang_items(_: CrateNum) -> &'tcx [LangItem] {
desc { "calculating the missing lang items in a crate" }
}
query visible_parent_map(_: CrateNum)
- -> Lrc<DefIdMap<DefId>> {
+ -> &'tcx DefIdMap<DefId> {
desc { "calculating the visible parent map" }
}
query missing_extern_crate_item(_: CrateNum) -> bool {
eval_always
desc { "looking at the source for a crate" }
}
- query postorder_cnums(_: CrateNum) -> Lrc<Vec<CrateNum>> {
+ query postorder_cnums(_: CrateNum) -> &'tcx [CrateNum] {
eval_always
desc { "generating a postorder list of CrateNums" }
}
- query upvars(_: DefId) -> Option<Lrc<Vec<hir::Upvar>>> {
+ query upvars(_: DefId) -> Option<&'tcx [hir::Upvar]> {
eval_always
}
query maybe_unused_trait_import(_: DefId) -> bool {
eval_always
}
query maybe_unused_extern_crates(_: CrateNum)
- -> Lrc<Vec<(DefId, Span)>> {
+ -> &'tcx [(DefId, Span)] {
eval_always
desc { "looking up all possibly unused extern crates" }
}
eval_always
}
- query stability_index(_: CrateNum) -> Lrc<stability::Index<'tcx>> {
+ query stability_index(_: CrateNum) -> &'tcx stability::Index<'tcx> {
eval_always
desc { "calculating the stability index for the local crate" }
}
- query all_crate_nums(_: CrateNum) -> Lrc<Vec<CrateNum>> {
+ 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) -> Lrc<Vec<DefId>> {
+ query all_traits(_: CrateNum) -> &'tcx [DefId] {
desc { "fetching all foreign and local traits" }
}
}
}
Other {
- query target_features_whitelist(_: CrateNum) -> Lrc<FxHashMap<String, Option<Symbol>>> {
+ query target_features_whitelist(_: CrateNum) -> &'tcx FxHashMap<String, Option<Symbol>> {
eval_always
desc { "looking up the whitelist of target features" }
}
desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) }
}
- query features_query(_: CrateNum) -> Lrc<feature_gate::Features> {
+ query features_query(_: CrateNum) -> &'tcx feature_gate::Features {
eval_always
desc { "looking up enabled feature gates" }
}
Ok(result)
}
- fn verify(&self,
- tcx: TyCtxt<'a, 'gcx, 'tcx>,
- trait_def_id: DefId,
- span: Span)
- -> Result<(), ErrorReported>
- {
+ fn verify(
+ &self,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ trait_def_id: DefId,
+ span: Span,
+ ) -> Result<(), ErrorReported> {
let name = tcx.item_name(trait_def_id);
let generics = tcx.generics_of(trait_def_id);
let parser = Parser::new(&self.0, None, vec![], false);
result
}
- pub fn format(&self,
- tcx: TyCtxt<'a, 'gcx, 'tcx>,
- trait_ref: ty::TraitRef<'tcx>,
- options: &FxHashMap<String, String>)
- -> String
- {
+ pub fn format(
+ &self,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ trait_ref: ty::TraitRef<'tcx>,
+ options: &FxHashMap<String, String>,
+ ) -> String {
let name = tcx.item_name(trait_ref.def_id);
let trait_str = tcx.def_path_str(trait_ref.def_id);
let generics = tcx.generics_of(trait_ref.def_id);
}
}
+pub struct Common<'tcx> {
+ pub empty_predicates: ty::GenericPredicates<'tcx>,
+}
+
pub struct CommonTypes<'tcx> {
pub unit: Ty<'tcx>,
pub bool: Ty<'tcx>,
pub dep_graph: DepGraph,
+ /// Common objects.
+ pub common: Common<'tcx>,
+
/// Common types, pre-interned for your convenience.
pub types: CommonTypes<'tcx>,
/// Map indicating what traits are in scope for places where this
/// is relevant; generated by resolve.
trait_map: FxHashMap<DefIndex,
- Lrc<FxHashMap<ItemLocalId,
- Lrc<StableVec<TraitCandidate>>>>>,
+ FxHashMap<ItemLocalId,
+ StableVec<TraitCandidate>>>,
/// Export map produced by name resolution.
- export_map: FxHashMap<DefId, Lrc<Vec<Export<hir::HirId>>>>,
+ export_map: FxHashMap<DefId, Vec<Export<hir::HirId>>>,
hir_map: hir_map::Map<'tcx>,
// Records the captured variables referenced by every closure
// expression. Do not track deps for this, just recompute it from
// scratch every time.
- upvars: FxHashMap<DefId, Lrc<Vec<hir::Upvar>>>,
+ upvars: FxHashMap<DefId, Vec<hir::Upvar>>,
maybe_unused_trait_imports: FxHashSet<DefId>,
maybe_unused_extern_crates: Vec<(DefId, Span)>,
s.fatal(&err);
});
let interners = CtxtInterners::new(&arenas.interner);
+ let common = Common {
+ empty_predicates: ty::GenericPredicates {
+ parent: None,
+ predicates: vec![],
+ },
+ };
let common_types = CommonTypes::new(&interners);
let common_lifetimes = CommonLifetimes::new(&interners);
let common_consts = CommonConsts::new(&interners, &common_types);
None
};
- let mut trait_map: FxHashMap<_, Lrc<FxHashMap<_, _>>> = FxHashMap::default();
+ let mut trait_map: FxHashMap<_, FxHashMap<_, _>> = FxHashMap::default();
for (k, v) in resolutions.trait_map {
let hir_id = hir.node_to_hir_id(k);
let map = trait_map.entry(hir_id.owner).or_default();
- Lrc::get_mut(map).unwrap()
- .insert(hir_id.local_id,
- Lrc::new(StableVec::new(v)));
+ map.insert(hir_id.local_id, StableVec::new(v));
}
GlobalCtxt {
global_arenas: &arenas.global,
global_interners: interners,
dep_graph,
+ common,
types: common_types,
lifetimes: common_lifetimes,
consts: common_consts,
let exports: Vec<_> = v.into_iter().map(|e| {
e.map_id(|id| hir.node_to_hir_id(id))
}).collect();
- (k, Lrc::new(exports))
+ (k, exports)
}).collect(),
upvars: resolutions.upvars.into_iter().map(|(k, v)| {
let vars: Vec<_> = v.into_iter().map(|e| {
e.map_id(|id| hir.node_to_hir_id(id))
}).collect();
- (hir.local_def_id(k), Lrc::new(vars))
+ (hir.local_def_id(k), vars)
}).collect(),
maybe_unused_trait_imports:
resolutions.maybe_unused_trait_imports
self.sess.consider_optimizing(&cname, msg)
}
- pub fn lib_features(self) -> Lrc<middle::lib_features::LibFeatures> {
+ pub fn lib_features(self) -> &'gcx middle::lib_features::LibFeatures {
self.get_lib_features(LOCAL_CRATE)
}
- pub fn lang_items(self) -> Lrc<middle::lang_items::LanguageItems> {
+ pub fn lang_items(self) -> &'gcx middle::lang_items::LanguageItems {
self.get_lang_items(LOCAL_CRATE)
}
else { None }
}
- pub fn stability(self) -> Lrc<stability::Index<'tcx>> {
+ pub fn stability(self) -> &'gcx stability::Index<'gcx> {
self.stability_index(LOCAL_CRATE)
}
- pub fn crates(self) -> Lrc<Vec<CrateNum>> {
+ pub fn crates(self) -> &'gcx [CrateNum] {
self.all_crate_nums(LOCAL_CRATE)
}
- pub fn features(self) -> Lrc<feature_gate::Features> {
+ pub fn features(self) -> &'gcx feature_gate::Features {
self.features_query(LOCAL_CRATE)
}
lint::struct_lint_level(self.sess, lint, level, src, None, msg)
}
- pub fn in_scope_traits(self, id: HirId) -> Option<Lrc<StableVec<TraitCandidate>>> {
+ pub fn in_scope_traits(self, id: HirId) -> Option<&'gcx StableVec<TraitCandidate>> {
self.in_scope_traits_map(id.owner)
- .and_then(|map| map.get(&id.local_id).cloned())
+ .and_then(|map| map.get(&id.local_id))
}
pub fn named_region(self, id: HirId) -> Option<resolve_lifetime::Region> {
}
pub fn object_lifetime_defaults(self, id: HirId)
- -> Option<Lrc<Vec<ObjectLifetimeDefault>>>
+ -> Option<&'gcx [ObjectLifetimeDefault]>
{
self.object_lifetime_defaults_map(id.owner)
- .and_then(|map| map.get(&id.local_id).cloned())
+ .and_then(|map| map.get(&id.local_id).map(|v| &**v))
}
}
}
pub fn provide(providers: &mut ty::query::Providers<'_>) {
- providers.in_scope_traits_map = |tcx, id| tcx.gcx.trait_map.get(&id).cloned();
- providers.module_exports = |tcx, id| tcx.gcx.export_map.get(&id).cloned();
+ providers.in_scope_traits_map = |tcx, id| tcx.gcx.trait_map.get(&id);
+ providers.module_exports = |tcx, id| tcx.gcx.export_map.get(&id).map(|v| &v[..]);
providers.crate_name = |tcx, id| {
assert_eq!(id, LOCAL_CRATE);
tcx.crate_name
};
providers.get_lib_features = |tcx, id| {
assert_eq!(id, LOCAL_CRATE);
- Lrc::new(middle::lib_features::collect(tcx))
+ tcx.arena.alloc(middle::lib_features::collect(tcx))
};
providers.get_lang_items = |tcx, id| {
assert_eq!(id, LOCAL_CRATE);
- Lrc::new(middle::lang_items::collect(tcx))
+ tcx.arena.alloc(middle::lang_items::collect(tcx))
};
- providers.upvars = |tcx, id| tcx.gcx.upvars.get(&id).cloned();
+ providers.upvars = |tcx, id| tcx.gcx.upvars.get(&id).map(|v| &v[..]);
providers.maybe_unused_trait_import = |tcx, id| {
tcx.maybe_unused_trait_imports.contains(&id)
};
providers.maybe_unused_extern_crates = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- Lrc::new(tcx.maybe_unused_extern_crates.clone())
+ &tcx.maybe_unused_extern_crates[..]
};
providers.names_imported_by_glob_use = |tcx, id| {
assert_eq!(id.krate, LOCAL_CRATE);
providers.stability_index = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- Lrc::new(stability::Index::new(tcx))
+ tcx.arena.alloc(stability::Index::new(tcx))
};
providers.lookup_stability = |tcx, id| {
assert_eq!(id.krate, LOCAL_CRATE);
};
providers.all_crate_nums = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- Lrc::new(tcx.cstore.crates_untracked())
+ tcx.arena.alloc_slice(&tcx.cstore.crates_untracked())
};
providers.postorder_cnums = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- Lrc::new(tcx.cstore.postorder_cnums_untracked())
+ tcx.arena.alloc_slice(&tcx.cstore.postorder_cnums_untracked())
};
providers.output_filenames = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
};
providers.features_query = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- Lrc::new(tcx.sess.features_untracked().clone())
+ tcx.arena.alloc(tcx.sess.features_untracked().clone())
};
providers.is_panic_runtime = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
}
#[inline]
- pub fn predicates(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Lrc<GenericPredicates<'gcx>> {
+ pub fn predicates(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> &'tcx GenericPredicates<'gcx> {
tcx.predicates_of(self.did)
}
pub struct AssociatedItemsIterator<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
- def_ids: Lrc<Vec<DefId>>,
+ def_ids: &'gcx [DefId],
next_index: usize,
}
fn associated_item_def_ids<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
- -> Lrc<Vec<DefId>> {
+ -> &'tcx [DefId] {
let id = tcx.hir().as_local_hir_id(def_id).unwrap();
let item = tcx.hir().expect_item_by_hir_id(id);
- let vec: Vec<_> = match item.node {
+ match item.node {
hir::ItemKind::Trait(.., ref trait_item_refs) => {
- trait_item_refs.iter()
- .map(|trait_item_ref| trait_item_ref.id)
- .map(|id| tcx.hir().local_def_id_from_hir_id(id.hir_id))
- .collect()
+ tcx.arena.alloc_from_iter(
+ trait_item_refs.iter()
+ .map(|trait_item_ref| trait_item_ref.id)
+ .map(|id| tcx.hir().local_def_id_from_hir_id(id.hir_id))
+ )
}
hir::ItemKind::Impl(.., ref impl_item_refs) => {
- impl_item_refs.iter()
- .map(|impl_item_ref| impl_item_ref.id)
- .map(|id| tcx.hir().local_def_id_from_hir_id(id.hir_id))
- .collect()
+ tcx.arena.alloc_from_iter(
+ impl_item_refs.iter()
+ .map(|impl_item_ref| impl_item_ref.id)
+ .map(|id| tcx.hir().local_def_id_from_hir_id(id.hir_id))
+ )
}
- hir::ItemKind::TraitAlias(..) => vec![],
+ hir::ItemKind::TraitAlias(..) => &[],
_ => span_bug!(item.span, "associated_item_def_ids: not impl or trait")
- };
- Lrc::new(vec)
+ }
}
fn def_span<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Span {
/// (constructing this map requires touching the entire crate).
#[derive(Clone, Debug, Default, HashStable)]
pub struct CrateInherentImpls {
- pub inherent_impls: DefIdMap<Lrc<Vec<DefId>>>,
+ pub inherent_impls: DefIdMap<Vec<DefId>>,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)]
// 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 {
+ match self.tcx().extern_crate(def_id) {
+ Some(&ExternCrate {
src: ExternCrateSource::Extern(def_id),
direct: true,
span,
}
}
-impl<'tcx, T: Default> Value<'tcx> for T {
- default fn from_cycle_error<'a>(_: TyCtxt<'a, 'tcx, 'tcx>) -> T {
- T::default()
- }
-}
-
impl<'tcx> Value<'tcx> for Ty<'tcx> {
fn from_cycle_error<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> {
tcx.types.err
use std::cell::{Cell, RefCell};
use std::fmt;
use std::rc::Rc;
-use rustc_data_structures::sync::Lrc;
use std::hash::{Hash, Hasher};
use syntax::source_map::CompilerDesugaringKind;
use syntax_pos::{MultiSpan, Span};
}
fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId)
- -> Lrc<BorrowCheckResult>
+ -> &'tcx BorrowCheckResult
{
assert!(tcx.use_ast_borrowck() || tcx.migrate_borrowck());
// those things (notably the synthesized constructors from
// tuple structs/variants) do not have an associated body
// and do not need borrowchecking.
- return Lrc::new(BorrowCheckResult {
+ return tcx.arena.alloc(BorrowCheckResult {
used_mut_nodes: Default::default(),
signalled_any_error: SignalledError::NoErrorsSeen,
})
check_loans::check_loans(&mut bccx, &loan_dfcx, &flowed_moves, &all_loans, body);
}
- Lrc::new(BorrowCheckResult {
+ tcx.arena.alloc(BorrowCheckResult {
used_mut_nodes: bccx.used_mut_nodes.into_inner(),
signalled_any_error: bccx.signalled_any_error.into_inner(),
})
use rustc::ty::layout::HasTyCtxt;
use rustc::ty::query::Providers;
use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_data_structures::sync::Lrc;
use rustc_data_structures::fx::FxHashMap;
use rustc_target::spec::PanicStrategy;
use rustc_codegen_ssa::traits::*;
if tcx.sess.opts.actually_rustdoc {
// rustdoc needs to be able to document functions that use all the features, so
// whitelist them all
- Lrc::new(llvm_util::all_known_features()
+ tcx.arena.alloc(llvm_util::all_known_features()
.map(|(a, b)| (a.to_string(), b))
.collect())
} else {
- Lrc::new(llvm_util::target_feature_whitelist(tcx.sess)
+ tcx.arena.alloc(llvm_util::target_feature_whitelist(tcx.sess)
.iter()
.map(|&(a, b)| (a.to_string(), b))
.collect())
}));
}
- Lrc::new(ret)
+ tcx.arena.alloc(ret)
};
}
-use rustc_data_structures::sync::Lrc;
use std::sync::Arc;
use rustc::ty::Instance;
fn reachable_non_generics_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
cnum: CrateNum)
- -> Lrc<DefIdMap<SymbolExportLevel>>
+ -> &'tcx DefIdMap<SymbolExportLevel>
{
assert_eq!(cnum, LOCAL_CRATE);
if !tcx.sess.opts.output_types.should_codegen() {
- return Default::default();
+ return tcx.arena.alloc(Default::default());
}
// Check to see if this crate is a "special runtime crate". These
reachable_non_generics.insert(id, SymbolExportLevel::C);
}
- Lrc::new(reachable_non_generics)
+ tcx.arena.alloc(reachable_non_generics)
}
fn is_reachable_non_generic_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
fn upstream_monomorphizations_provider<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
cnum: CrateNum)
- -> Lrc<DefIdMap<Lrc<FxHashMap<SubstsRef<'tcx>, CrateNum>>>>
+ -> &'tcx DefIdMap<FxHashMap<SubstsRef<'tcx>, CrateNum>>
{
debug_assert!(cnum == LOCAL_CRATE);
}
}
- Lrc::new(instances.into_iter()
- .map(|(key, value)| (key, Lrc::new(value)))
- .collect())
+ tcx.arena.alloc(instances)
}
fn upstream_monomorphizations_for_provider<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
- -> Option<Lrc<FxHashMap<SubstsRef<'tcx>, CrateNum>>>
+ -> Option<&'tcx FxHashMap<SubstsRef<'tcx>, CrateNum>>
{
debug_assert!(!def_id.is_local());
- tcx.upstream_monomorphizations(LOCAL_CRATE)
- .get(&def_id)
- .cloned()
+ tcx.upstream_monomorphizations(LOCAL_CRATE).get(&def_id)
}
fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_, '_, '_>, def_id: DefId) -> bool {
use rustc_mir::monomorphize::partitioning::{CodegenUnit, CodegenUnitExt};
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
-use rustc_data_structures::sync::Lrc;
use rustc_codegen_utils::{symbol_names_test, check_for_rustc_errors_attr};
use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
use crate::mir::place::PlaceRef;
.map(|id| &module_map[&id])
.flat_map(|module| module.foreign_items.iter().cloned())
.collect();
- Lrc::new(dllimports)
+ tcx.arena.alloc(dllimports)
};
providers.is_dllimport_foreign_item = |tcx, def_id| {
flate2 = "1.0"
log = "0.4"
memmap = "0.6"
+smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
rustc = { path = "../librustc" }
rustc_data_structures = { path = "../librustc_data_structures" }
errors = { path = "../librustc_errors", package = "rustc_errors" }
use rustc::util::nodemap::DefIdMap;
use rustc_data_structures::svh::Svh;
+use smallvec::SmallVec;
use std::any::Any;
use rustc_data_structures::sync::Lrc;
use std::sync::Arc;
generics_of => {
tcx.alloc_generics(cdata.get_generics(def_id.index, tcx.sess))
}
- predicates_of => { Lrc::new(cdata.get_predicates(def_id.index, tcx)) }
- predicates_defined_on => { Lrc::new(cdata.get_predicates_defined_on(def_id.index, tcx)) }
- super_predicates_of => { Lrc::new(cdata.get_super_predicates(def_id.index, tcx)) }
+ predicates_of => { tcx.arena.alloc(cdata.get_predicates(def_id.index, tcx)) }
+ predicates_defined_on => {
+ tcx.arena.alloc(cdata.get_predicates_defined_on(def_id.index, tcx))
+ }
+ super_predicates_of => { tcx.arena.alloc(cdata.get_super_predicates(def_id.index, tcx)) }
trait_def => {
tcx.alloc_trait_def(cdata.get_trait_def(def_id.index, tcx.sess))
}
}
variances_of => { tcx.arena.alloc_from_iter(cdata.get_item_variances(def_id.index)) }
associated_item_def_ids => {
- let mut result = vec![];
+ let mut result = SmallVec::<[_; 8]>::new();
cdata.each_child_of_item(def_id.index,
|child| result.push(child.res.def_id()), tcx.sess);
- Lrc::new(result)
+ tcx.arena.alloc_slice(&result)
}
associated_item => { cdata.get_associated_item(def_id.index) }
impl_trait_ref => { cdata.get_impl_trait(def_id.index, tcx) }
(cdata.mir_const_qualif(def_id.index), tcx.arena.alloc(BitSet::new_empty(0)))
}
fn_sig => { cdata.fn_sig(def_id.index, tcx) }
- inherent_impls => { Lrc::new(cdata.get_inherent_implementations_for_type(def_id.index)) }
+ inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) }
is_foreign_item => { cdata.is_foreign_item(def_id.index) }
static_mutability => { cdata.static_mutability(def_id.index) }
}
is_mir_available => { cdata.is_item_mir_available(def_id.index) }
- dylib_dependency_formats => { Lrc::new(cdata.get_dylib_dependency_formats()) }
+ dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) }
is_panic_runtime => { cdata.root.panic_runtime }
is_compiler_builtins => { cdata.root.compiler_builtins }
has_global_allocator => { cdata.root.has_global_allocator }
is_profiler_runtime => { cdata.root.profiler_runtime }
panic_strategy => { cdata.root.panic_strategy }
extern_crate => {
- let r = Lrc::new(*cdata.extern_crate.lock());
- r
+ let r = *cdata.extern_crate.lock();
+ r.map(|c| &*tcx.arena.alloc(c))
}
is_no_builtins => { cdata.root.no_builtins }
impl_defaultness => { cdata.get_impl_defaultness(def_id.index) }
})
.collect();
- Lrc::new(reachable_non_generics)
+ tcx.arena.alloc(reachable_non_generics)
}
native_libraries => { Lrc::new(cdata.get_native_libraries(tcx.sess)) }
- foreign_modules => { Lrc::new(cdata.get_foreign_modules(tcx.sess)) }
+ foreign_modules => { cdata.get_foreign_modules(tcx) }
plugin_registrar_fn => {
cdata.root.plugin_registrar_fn.map(|index| {
DefId { krate: def_id.krate, index }
extra_filename => { cdata.root.extra_filename.clone() }
-
implementations_of_trait => {
- let mut result = vec![];
- let filter = Some(other);
- cdata.get_implementations_for_trait(filter, &mut result);
- Lrc::new(result)
+ cdata.get_implementations_for_trait(tcx, Some(other))
}
all_trait_implementations => {
- let mut result = vec![];
- cdata.get_implementations_for_trait(None, &mut result);
- Lrc::new(result)
+ cdata.get_implementations_for_trait(tcx, None)
}
visibility => { cdata.get_visibility(def_id.index) }
}
crate_name => { cdata.name }
item_children => {
- let mut result = vec![];
+ let mut result = SmallVec::<[_; 8]>::new();
cdata.each_child_of_item(def_id.index, |child| result.push(child), tcx.sess);
- Lrc::new(result)
+ tcx.arena.alloc_slice(&result)
}
- defined_lib_features => { Lrc::new(cdata.get_lib_features()) }
- defined_lang_items => { Lrc::new(cdata.get_lang_items()) }
- missing_lang_items => { Lrc::new(cdata.get_missing_lang_items()) }
+ defined_lib_features => { cdata.get_lib_features(tcx) }
+ defined_lang_items => { cdata.get_lang_items(tcx) }
+ missing_lang_items => { cdata.get_missing_lang_items(tcx) }
missing_extern_crate_item => {
let r = match *cdata.extern_crate.borrow() {
},
foreign_modules: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- Lrc::new(foreign_modules::collect(tcx))
+ &tcx.arena.alloc(foreign_modules::collect(tcx))[..]
},
link_args: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
// which is to say, its not deterministic in general. But
// we believe that libstd is consistently assigned crate
// num 1, so it should be enough to resolve #46112.
- let mut crates: Vec<CrateNum> = (*tcx.crates()).clone();
+ let mut crates: Vec<CrateNum> = (*tcx.crates()).to_owned();
crates.sort();
for &cnum in crates.iter() {
}
}
- Lrc::new(visible_parent_map)
+ tcx.arena.alloc(visible_parent_map)
},
..*providers
}
/// Iterates over all the stability attributes in the given crate.
- pub fn get_lib_features(&self) -> Vec<(ast::Name, Option<ast::Name>)> {
+ pub fn get_lib_features(
+ &self,
+ tcx: TyCtxt<'_, 'tcx, '_>,
+ ) -> &'tcx [(ast::Name, Option<ast::Name>)] {
// FIXME: For a proc macro crate, not sure whether we should return the "host"
// features or an empty Vec. Both don't cause ICEs.
- self.root
+ tcx.arena.alloc_from_iter(self.root
.lib_features
- .decode(self)
- .collect()
+ .decode(self))
}
/// Iterates over the language items in the given crate.
- pub fn get_lang_items(&self) -> Vec<(DefId, usize)> {
+ pub fn get_lang_items(
+ &self,
+ tcx: TyCtxt<'_, 'tcx, '_>,
+ ) -> &'tcx [(DefId, usize)] {
if self.proc_macros.is_some() {
// Proc macro crates do not export any lang-items to the target.
- vec![]
+ &[]
} else {
- self.root
+ tcx.arena.alloc_from_iter(self.root
.lang_items
.decode(self)
- .map(|(def_index, index)| (self.local_def_id(def_index), index))
- .collect()
+ .map(|(def_index, index)| (self.local_def_id(def_index), index)))
}
}
None
}
- pub fn get_inherent_implementations_for_type(&self, id: DefIndex) -> Vec<DefId> {
- self.entry(id)
- .inherent_impls
- .decode(self)
- .map(|index| self.local_def_id(index))
- .collect()
+ pub fn get_inherent_implementations_for_type(
+ &self,
+ tcx: TyCtxt<'_, 'tcx, '_>,
+ id: DefIndex
+ ) -> &'tcx [DefId] {
+ tcx.arena.alloc_from_iter(self.entry(id)
+ .inherent_impls
+ .decode(self)
+ .map(|index| self.local_def_id(index)))
}
- pub fn get_implementations_for_trait(&self,
- filter: Option<DefId>,
- result: &mut Vec<DefId>) {
+ pub fn get_implementations_for_trait(
+ &self,
+ tcx: TyCtxt<'_, 'tcx, '_>,
+ filter: Option<DefId>,
+ ) -> &'tcx [DefId] {
if self.proc_macros.is_some() {
// proc-macro crates export no trait impls.
- return
+ return &[]
}
// Do a reverse lookup beforehand to avoid touching the crate_num
// hash map in the loop below.
let filter = match filter.map(|def_id| self.reverse_translate_def_id(def_id)) {
Some(Some(def_id)) => Some((def_id.krate.as_u32(), def_id.index)),
- Some(None) => return,
+ Some(None) => return &[],
None => None,
};
if let Some(filter) = filter {
- if let Some(impls) = self.trait_impls
- .get(&filter) {
- result.extend(impls.decode(self).map(|idx| self.local_def_id(idx)));
+ if let Some(impls) = self.trait_impls.get(&filter) {
+ tcx.arena.alloc_from_iter(impls.decode(self).map(|idx| self.local_def_id(idx)))
+ } else {
+ &[]
}
} else {
- for impls in self.trait_impls.values() {
- result.extend(impls.decode(self).map(|idx| self.local_def_id(idx)));
- }
+ tcx.arena.alloc_from_iter(self.trait_impls.values().flat_map(|impls| {
+ impls.decode(self).map(|idx| self.local_def_id(idx))
+ }))
}
}
}
}
- pub fn get_foreign_modules(&self, sess: &Session) -> Vec<ForeignModule> {
+ pub fn get_foreign_modules(
+ &self,
+ tcx: TyCtxt<'_, 'tcx, '_>,
+ ) -> &'tcx [ForeignModule] {
if self.proc_macros.is_some() {
// Proc macro crates do not have any *target* foreign modules.
- vec![]
+ &[]
} else {
- self.root.foreign_modules.decode((self, sess)).collect()
+ tcx.arena.alloc_from_iter(self.root.foreign_modules.decode((self, tcx.sess)))
}
}
- pub fn get_dylib_dependency_formats(&self) -> Vec<(CrateNum, LinkagePreference)> {
- self.root
+ pub fn get_dylib_dependency_formats(
+ &self,
+ tcx: TyCtxt<'_, 'tcx, '_>,
+ ) -> &'tcx [(CrateNum, LinkagePreference)] {
+ tcx.arena.alloc_from_iter(self.root
.dylib_dependency_formats
.decode(self)
.enumerate()
.flat_map(|(i, link)| {
let cnum = CrateNum::new(i + 1);
link.map(|link| (self.cnum_map[cnum], link))
- })
- .collect()
+ }))
}
- pub fn get_missing_lang_items(&self) -> Vec<lang_items::LangItem> {
+ pub fn get_missing_lang_items(
+ &self,
+ tcx: TyCtxt<'_, 'tcx, '_>,
+ ) -> &'tcx [lang_items::LangItem] {
if self.proc_macros.is_some() {
// Proc macro crates do not depend on any target weak lang-items.
- vec![]
+ &[]
} else {
- self.root
+ tcx.arena.alloc_from_iter(self.root
.lang_items_missing
- .decode(self)
- .collect()
+ .decode(self))
}
}
let data = ModData {
reexports: match tcx.module_exports(def_id) {
- Some(ref exports) => self.lazy_seq_ref(&exports[..]),
+ Some(exports) => self.lazy_seq_ref(exports),
_ => LazySeq::empty(),
},
};
--- /dev/null
+use rustc::hir;
+use rustc::hir::def_id::DefId;
+use rustc::mir::{
+ self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, Local,
+ LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, PlaceProjection,
+ ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
+};
+use rustc::ty::{self, Ty};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::indexed_vec::Idx;
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use syntax_pos::Span;
+use syntax::source_map::CompilerDesugaringKind;
+
+use super::nll::explain_borrow::BorrowExplanation;
+use super::nll::region_infer::{RegionName, RegionNameSource};
+use super::prefixes::IsPrefixOf;
+use super::WriteKind;
+use super::borrow_set::BorrowData;
+use super::MirBorrowckCtxt;
+use super::{InitializationRequiringAction, PrefixSet};
+use super::error_reporting::{IncludingDowncast, UseSpans};
+use crate::dataflow::drop_flag_effects;
+use crate::dataflow::indexes::{MovePathIndex, MoveOutIndex};
+use crate::util::borrowck_errors::{BorrowckErrors, Origin};
+
+#[derive(Debug)]
+struct MoveSite {
+ /// Index of the "move out" that we found. The `MoveData` can
+ /// then tell us where the move occurred.
+ moi: MoveOutIndex,
+
+ /// `true` if we traversed a back edge while walking from the point
+ /// of error to the move site.
+ traversed_back_edge: bool
+}
+
+/// Which case a StorageDeadOrDrop is for.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum StorageDeadOrDrop<'tcx> {
+ LocalStorageDead,
+ BoxedStorageDead,
+ Destructor(Ty<'tcx>),
+}
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+ pub(super) fn report_use_of_moved_or_uninitialized(
+ &mut self,
+ location: Location,
+ desired_action: InitializationRequiringAction,
+ (moved_place, used_place, span): (&Place<'tcx>, &Place<'tcx>, Span),
+ mpi: MovePathIndex,
+ ) {
+ debug!(
+ "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
+ moved_place={:?} used_place={:?} span={:?} mpi={:?}",
+ location, desired_action, moved_place, used_place, span, mpi
+ );
+
+ let use_spans = self.move_spans(moved_place, location)
+ .or_else(|| self.borrow_spans(span, location));
+ let span = use_spans.args_or_use();
+
+ let move_site_vec = self.get_moved_indexes(location, mpi);
+ debug!(
+ "report_use_of_moved_or_uninitialized: move_site_vec={:?}",
+ move_site_vec
+ );
+ let move_out_indices: Vec<_> = move_site_vec
+ .iter()
+ .map(|move_site| move_site.moi)
+ .collect();
+
+ if move_out_indices.is_empty() {
+ let root_place = self.prefixes(&used_place, PrefixSet::All).last().unwrap();
+
+ if self.uninitialized_error_reported.contains(root_place) {
+ debug!(
+ "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
+ root_place
+ );
+ return;
+ }
+
+ self.uninitialized_error_reported.insert(root_place.clone());
+
+ let item_msg = match self.describe_place_with_options(used_place,
+ IncludingDowncast(true)) {
+ Some(name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+ let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable(
+ span,
+ desired_action.as_noun(),
+ &self.describe_place_with_options(moved_place, IncludingDowncast(true))
+ .unwrap_or_else(|| "_".to_owned()),
+ Origin::Mir,
+ );
+ err.span_label(span, format!("use of possibly uninitialized {}", item_msg));
+
+ use_spans.var_span_label(
+ &mut err,
+ format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
+ );
+
+ err.buffer(&mut self.errors_buffer);
+ } else {
+ if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) {
+ if self.prefixes(&reported_place, PrefixSet::All)
+ .any(|p| p == used_place)
+ {
+ debug!(
+ "report_use_of_moved_or_uninitialized place: error suppressed \
+ mois={:?}",
+ move_out_indices
+ );
+ return;
+ }
+ }
+
+ let msg = ""; //FIXME: add "partially " or "collaterally "
+
+ let mut err = self.infcx.tcx.cannot_act_on_moved_value(
+ span,
+ desired_action.as_noun(),
+ msg,
+ self.describe_place_with_options(&moved_place, IncludingDowncast(true)),
+ Origin::Mir,
+ );
+
+ self.add_moved_or_invoked_closure_note(
+ location,
+ used_place,
+ &mut err,
+ );
+
+ let mut is_loop_move = false;
+ let is_partial_move = move_site_vec.iter().any(|move_site| {
+ let move_out = self.move_data.moves[(*move_site).moi];
+ let moved_place = &self.move_data.move_paths[move_out.path].place;
+ used_place != moved_place && used_place.is_prefix_of(moved_place)
+ });
+ for move_site in &move_site_vec {
+ let move_out = self.move_data.moves[(*move_site).moi];
+ let moved_place = &self.move_data.move_paths[move_out.path].place;
+
+ let move_spans = self.move_spans(moved_place, move_out.source);
+ let move_span = move_spans.args_or_use();
+
+ let move_msg = if move_spans.for_closure() {
+ " into closure"
+ } else {
+ ""
+ };
+
+ if span == move_span {
+ err.span_label(
+ span,
+ format!("value moved{} here, in previous iteration of loop", move_msg),
+ );
+ if Some(CompilerDesugaringKind::ForLoop) == span.compiler_desugaring_kind() {
+ if let Ok(snippet) = self.infcx.tcx.sess.source_map()
+ .span_to_snippet(span)
+ {
+ err.span_suggestion(
+ move_span,
+ "consider borrowing this to avoid moving it into the for loop",
+ format!("&{}", snippet),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ is_loop_move = true;
+ } else if move_site.traversed_back_edge {
+ err.span_label(
+ move_span,
+ format!(
+ "value moved{} here, in previous iteration of loop",
+ move_msg
+ ),
+ );
+ } else {
+ err.span_label(move_span, format!("value moved{} here", move_msg));
+ move_spans.var_span_label(
+ &mut err,
+ format!("variable moved due to use{}", move_spans.describe()),
+ );
+ };
+ }
+
+ use_spans.var_span_label(
+ &mut err,
+ format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
+ );
+
+ if !is_loop_move {
+ err.span_label(
+ span,
+ format!(
+ "value {} here {}",
+ desired_action.as_verb_in_past_tense(),
+ if is_partial_move { "after partial move" } else { "after move" },
+ ),
+ );
+ }
+
+ let ty = used_place.ty(self.mir, self.infcx.tcx).ty;
+ let needs_note = match ty.sty {
+ 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();
+
+ tables.closure_kind_origins().get(hir_id).is_none()
+ }
+ _ => true,
+ };
+
+ if needs_note {
+ 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.mir, self.infcx.tcx).ty;
+ let opt_name = self.describe_place_with_options(place, IncludingDowncast(true));
+ let note_msg = match opt_name {
+ Some(ref name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+ if let ty::Param(param_ty) = ty.sty {
+ let tcx = self.infcx.tcx;
+ let generics = tcx.generics_of(self.mir_def_id);
+ let def_id = generics.type_param(¶m_ty, tcx).def_id;
+ if let Some(sp) = tcx.hir().span_if_local(def_id) {
+ err.span_label(
+ sp,
+ "consider adding a `Copy` constraint to this type argument",
+ );
+ }
+ }
+ if let Place::Base(PlaceBase::Local(local)) = place {
+ let decl = &self.mir.local_decls[*local];
+ err.span_label(
+ decl.source_info.span,
+ format!(
+ "move occurs because {} has type `{}`, \
+ which does not implement the `Copy` trait",
+ note_msg, ty,
+ ));
+ } else {
+ err.note(&format!(
+ "move occurs because {} has type `{}`, \
+ which does not implement the `Copy` trait",
+ note_msg, ty
+ ));
+ }
+ }
+
+ if let Some((_, mut old_err)) = self.move_error_reported
+ .insert(move_out_indices, (used_place.clone(), err))
+ {
+ // Cancel the old error so it doesn't ICE.
+ old_err.cancel();
+ }
+ }
+ }
+
+ pub(super) fn report_move_out_while_borrowed(
+ &mut self,
+ location: Location,
+ (place, span): (&Place<'tcx>, Span),
+ borrow: &BorrowData<'tcx>,
+ ) {
+ debug!(
+ "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
+ location, place, span, borrow
+ );
+ let tcx = self.infcx.tcx;
+ let value_msg = match self.describe_place(place) {
+ Some(name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+ let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
+ Some(name) => format!("`{}`", name),
+ None => "value".to_owned(),
+ };
+
+ let borrow_spans = self.retrieve_borrow_spans(borrow);
+ let borrow_span = borrow_spans.args_or_use();
+
+ let move_spans = self.move_spans(place, location);
+ let span = move_spans.args_or_use();
+
+ let mut err = tcx.cannot_move_when_borrowed(
+ span,
+ &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
+ Origin::Mir,
+ );
+ err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
+ err.span_label(span, format!("move out of {} occurs here", value_msg));
+
+ borrow_spans.var_span_label(
+ &mut err,
+ format!("borrow occurs due to use{}", borrow_spans.describe())
+ );
+
+ move_spans.var_span_label(
+ &mut err,
+ format!("move occurs due to use{}", move_spans.describe())
+ );
+
+ self.explain_why_borrow_contains_point(
+ location,
+ borrow,
+ None,
+ ).add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", Some(borrow_span));
+ err.buffer(&mut self.errors_buffer);
+ }
+
+ pub(super) fn report_use_while_mutably_borrowed(
+ &mut self,
+ location: Location,
+ (place, _span): (&Place<'tcx>, Span),
+ borrow: &BorrowData<'tcx>,
+ ) -> DiagnosticBuilder<'cx> {
+ let tcx = self.infcx.tcx;
+
+ let borrow_spans = self.retrieve_borrow_spans(borrow);
+ let borrow_span = borrow_spans.args_or_use();
+
+ // Conflicting borrows are reported separately, so only check for move
+ // captures.
+ let use_spans = self.move_spans(place, location);
+ let span = use_spans.var_or_use();
+
+ let mut err = tcx.cannot_use_when_mutably_borrowed(
+ span,
+ &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
+ borrow_span,
+ &self.describe_place(&borrow.borrowed_place)
+ .unwrap_or_else(|| "_".to_owned()),
+ Origin::Mir,
+ );
+
+ borrow_spans.var_span_label(&mut err, {
+ let place = &borrow.borrowed_place;
+ let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned());
+
+ format!("borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe())
+ });
+
+ self.explain_why_borrow_contains_point(location, borrow, None)
+ .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
+ err
+ }
+
+ pub(super) fn report_conflicting_borrow(
+ &mut self,
+ location: Location,
+ (place, span): (&Place<'tcx>, Span),
+ gen_borrow_kind: BorrowKind,
+ issued_borrow: &BorrowData<'tcx>,
+ ) -> DiagnosticBuilder<'cx> {
+ let issued_spans = self.retrieve_borrow_spans(issued_borrow);
+ let issued_span = issued_spans.args_or_use();
+
+ let borrow_spans = self.borrow_spans(span, location);
+ let span = borrow_spans.args_or_use();
+
+ let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
+ "generator"
+ } else {
+ "closure"
+ };
+
+ let (desc_place, msg_place, msg_borrow, union_type_name) =
+ 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 {
+ ""
+ };
+
+ // FIXME: supply non-"" `opt_via` when appropriate
+ let tcx = self.infcx.tcx;
+ let first_borrow_desc;
+ let mut err = match (
+ gen_borrow_kind,
+ "immutable",
+ "mutable",
+ issued_borrow.kind,
+ "immutable",
+ "mutable",
+ ) {
+ (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt) => {
+ first_borrow_desc = "mutable ";
+ tcx.cannot_reborrow_already_borrowed(
+ span,
+ &desc_place,
+ &msg_place,
+ lft,
+ issued_span,
+ "it",
+ rgt,
+ &msg_borrow,
+ None,
+ Origin::Mir,
+ )
+ }
+ (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => {
+ first_borrow_desc = "immutable ";
+ tcx.cannot_reborrow_already_borrowed(
+ span,
+ &desc_place,
+ &msg_place,
+ lft,
+ issued_span,
+ "it",
+ rgt,
+ &msg_borrow,
+ None,
+ Origin::Mir,
+ )
+ }
+
+ (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => {
+ first_borrow_desc = "first ";
+ tcx.cannot_mutably_borrow_multiply(
+ span,
+ &desc_place,
+ &msg_place,
+ issued_span,
+ &msg_borrow,
+ None,
+ Origin::Mir,
+ )
+ }
+
+ (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => {
+ first_borrow_desc = "first ";
+ tcx.cannot_uniquely_borrow_by_two_closures(
+ span,
+ &desc_place,
+ issued_span,
+ None,
+ Origin::Mir,
+ )
+ }
+
+ (BorrowKind::Mut { .. }, _, _, BorrowKind::Shallow, _, _)
+ | (BorrowKind::Unique, _, _, BorrowKind::Shallow, _, _) => {
+ let mut err = tcx.cannot_mutate_in_match_guard(
+ span,
+ issued_span,
+ &desc_place,
+ "mutably borrow",
+ Origin::Mir,
+ );
+ borrow_spans.var_span_label(
+ &mut err,
+ format!(
+ "borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe()
+ ),
+ );
+
+ return err;
+ }
+
+ (BorrowKind::Unique, _, _, _, _, _) => {
+ first_borrow_desc = "first ";
+ tcx.cannot_uniquely_borrow_by_one_closure(
+ span,
+ container_name,
+ &desc_place,
+ "",
+ issued_span,
+ "it",
+ "",
+ None,
+ Origin::Mir,
+ )
+ },
+
+ (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => {
+ first_borrow_desc = "first ";
+ tcx.cannot_reborrow_already_uniquely_borrowed(
+ span,
+ container_name,
+ &desc_place,
+ "",
+ lft,
+ issued_span,
+ "",
+ None,
+ second_borrow_desc,
+ Origin::Mir,
+ )
+ }
+
+ (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => {
+ first_borrow_desc = "first ";
+ tcx.cannot_reborrow_already_uniquely_borrowed(
+ span,
+ container_name,
+ &desc_place,
+ "",
+ lft,
+ issued_span,
+ "",
+ None,
+ second_borrow_desc,
+ Origin::Mir,
+ )
+ }
+
+ (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!(),
+ };
+
+ if issued_spans == borrow_spans {
+ borrow_spans.var_span_label(
+ &mut err,
+ 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)
+ .unwrap_or_else(|| "_".to_owned());
+ issued_spans.var_span_label(
+ &mut err,
+ format!(
+ "first borrow occurs due to use of `{}`{}",
+ borrow_place_desc,
+ issued_spans.describe(),
+ ),
+ );
+
+ borrow_spans.var_span_label(
+ &mut err,
+ format!(
+ "second borrow occurs due to use of `{}`{}",
+ desc_place,
+ borrow_spans.describe(),
+ ),
+ );
+ }
+
+ if union_type_name != "" {
+ err.note(&format!(
+ "`{}` is a field of the union `{}`, so it overlaps the field `{}`",
+ msg_place, union_type_name, msg_borrow,
+ ));
+ }
+
+ explanation.add_explanation_to_diagnostic(
+ self.infcx.tcx,
+ self.mir,
+ &mut err,
+ first_borrow_desc,
+ None,
+ );
+
+ err
+ }
+
+ /// Returns the description of the root place for a conflicting borrow and the full
+ /// descriptions of the places that caused the conflict.
+ ///
+ /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
+ /// attempted while a shared borrow is live, then this function will return:
+ ///
+ /// ("x", "", "")
+ ///
+ /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
+ /// a shared borrow of another field `x.y`, then this function will return:
+ ///
+ /// ("x", "x.z", "x.y")
+ ///
+ /// In the more complex union case, where the union is a field of a struct, then if a mutable
+ /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
+ /// another field `x.u.y`, then this function will return:
+ ///
+ /// ("x.u", "x.u.z", "x.u.y")
+ ///
+ /// 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]
+ pub(super) fn describe_place_for_conflicting_borrow(
+ &self,
+ 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 is_union = |place: &Place<'tcx>| -> bool {
+ place.ty(self.mir, self.infcx.tcx).ty
+ .ty_adt_def()
+ .map(|adt| adt.is_union())
+ .unwrap_or(false)
+ };
+
+ // 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
+ // case).
+ Some(())
+ .filter(|_| {
+ // If we have a conflicting borrow of the same place, then we don't want to add
+ // an extraneous "via x.y" to our diagnostics, so filter out this case.
+ first_borrowed_place != second_borrowed_place
+ })
+ .and_then(|_| {
+ // We're going to want to traverse the first borrowed place to see if we can find
+ // field access to a union. If we find that, then we will keep the place of the
+ // union being accessed and the field that was being accessed so we can check the
+ // second borrowed place for the same union and a access to a different field.
+ let mut current = first_borrowed_place;
+ while let Place::Projection(box PlaceProjection { base, elem }) = current {
+ match elem {
+ ProjectionElem::Field(field, _) if is_union(base) => {
+ return Some((base, field));
+ },
+ _ => current = base,
+ }
+ }
+ None
+ })
+ .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 mut current = second_borrowed_place;
+ while let Place::Projection(box PlaceProjection { base, elem }) = current {
+ match elem {
+ ProjectionElem::Field(field, _) if {
+ is_union(base) && field != target_field && base == target_base
+ } => {
+ let desc_base = self.describe_place(base)
+ .unwrap_or_else(|| "_".to_owned());
+ let desc_first = self.describe_place(first_borrowed_place)
+ .unwrap_or_else(|| "_".to_owned());
+ let desc_second = self.describe_place(second_borrowed_place)
+ .unwrap_or_else(|| "_".to_owned());
+
+ // Also compute the name of the union type, eg. `Foo` so we
+ // can add a helpful note with it.
+ let ty = base.ty(self.mir, self.infcx.tcx).ty;
+
+ return Some((desc_base, desc_first, desc_second, ty.to_string()));
+ },
+ _ => current = base,
+ }
+ }
+ None
+ })
+ .unwrap_or_else(|| {
+ // If we didn't find a field access into a union, or both places match, then
+ // only return the description of the first place.
+ let desc_place = self.describe_place(first_borrowed_place)
+ .unwrap_or_else(|| "_".to_owned());
+ (desc_place, "".to_string(), "".to_string(), "".to_string())
+ })
+ }
+
+ /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
+ ///
+ /// This means that some data referenced by `borrow` needs to live
+ /// past the point where the StorageDeadOrDrop of `place` occurs.
+ /// This is usually interpreted as meaning that `place` has too
+ /// short a lifetime. (But sometimes it is more useful to report
+ /// it as a more direct conflict between the execution of a
+ /// `Drop::drop` with an aliasing borrow.)
+ pub(super) fn report_borrowed_value_does_not_live_long_enough(
+ &mut self,
+ location: Location,
+ borrow: &BorrowData<'tcx>,
+ place_span: (&Place<'tcx>, Span),
+ kind: Option<WriteKind>,
+ ) {
+ debug!(
+ "report_borrowed_value_does_not_live_long_enough(\
+ {:?}, {:?}, {:?}, {:?}\
+ )",
+ location, borrow, place_span, kind
+ );
+
+ let drop_span = place_span.1;
+ let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All)
+ .last()
+ .unwrap();
+
+ let borrow_spans = self.retrieve_borrow_spans(borrow);
+ let borrow_span = borrow_spans.var_or_use();
+
+ let proper_span = match *root_place {
+ Place::Base(PlaceBase::Local(local)) => self.mir.local_decls[local].source_info.span,
+ _ => drop_span,
+ };
+
+ if self.access_place_error_reported
+ .contains(&(root_place.clone(), borrow_span))
+ {
+ debug!(
+ "suppressing access_place error when borrow doesn't live long enough for {:?}",
+ borrow_span
+ );
+ return;
+ }
+
+ self.access_place_error_reported
+ .insert((root_place.clone(), borrow_span));
+
+ if let StorageDeadOrDrop::Destructor(dropped_ty) =
+ self.classify_drop_access_kind(&borrow.borrowed_place)
+ {
+ // If a borrow of path `B` conflicts with drop of `D` (and
+ // we're not in the uninteresting case where `B` is a
+ // prefix of `D`), then report this as a more interesting
+ // destructor conflict.
+ if !borrow.borrowed_place.is_prefix_of(place_span.0) {
+ self.report_borrow_conflicts_with_destructor(
+ location, borrow, place_span, kind, dropped_ty,
+ );
+ return;
+ }
+ }
+
+ let place_desc = self.describe_place(&borrow.borrowed_place);
+
+ let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
+ let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
+
+ let err = match (place_desc, explanation) {
+ (Some(_), _) if self.is_place_thread_local(root_place) => {
+ self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span)
+ }
+ // If the outlives constraint comes from inside the closure,
+ // for example:
+ //
+ // let x = 0;
+ // let y = &x;
+ // Box::new(|| y) as Box<Fn() -> &'static i32>
+ //
+ // then just use the normal error. The closure isn't escaping
+ // and `move` will not help here.
+ (
+ 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,
+ from_closure: false,
+ ref region_name,
+ span,
+ ..
+ },
+ ) if borrow_spans.for_closure() => self.report_escaping_closure_capture(
+ borrow_spans.args_or_use(),
+ borrow_span,
+ region_name,
+ category,
+ span,
+ &format!("`{}`", name),
+ ),
+ (
+ ref name,
+ BorrowExplanation::MustBeValidFor {
+ category: ConstraintCategory::Assignment,
+ from_closure: false,
+ region_name: RegionName {
+ source: RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
+ ..
+ },
+ span,
+ ..
+ },
+ ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
+ (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
+ location,
+ &name,
+ &borrow,
+ drop_span,
+ borrow_spans,
+ explanation,
+ ),
+ (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
+ location,
+ &borrow,
+ drop_span,
+ borrow_spans,
+ proper_span,
+ explanation,
+ ),
+ };
+
+ err.buffer(&mut self.errors_buffer);
+ }
+
+ fn report_local_value_does_not_live_long_enough(
+ &mut self,
+ location: Location,
+ name: &str,
+ borrow: &BorrowData<'tcx>,
+ drop_span: Span,
+ borrow_spans: UseSpans,
+ explanation: BorrowExplanation,
+ ) -> DiagnosticBuilder<'cx> {
+ debug!(
+ "report_local_value_does_not_live_long_enough(\
+ {:?}, {:?}, {:?}, {:?}, {:?}\
+ )",
+ location, name, borrow, drop_span, borrow_spans
+ );
+
+ let borrow_span = borrow_spans.var_or_use();
+ if let BorrowExplanation::MustBeValidFor {
+ category,
+ span,
+ ref opt_place_desc,
+ from_closure: false,
+ ..
+ } = explanation {
+ if let Some(diag) = self.try_report_cannot_return_reference_to_local(
+ borrow,
+ borrow_span,
+ span,
+ category,
+ opt_place_desc.as_ref(),
+ ) {
+ return diag;
+ }
+ }
+
+ let mut err = self.infcx.tcx.path_does_not_live_long_enough(
+ borrow_span,
+ &format!("`{}`", name),
+ Origin::Mir,
+ );
+
+ if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
+ let region_name = annotation.emit(self, &mut err);
+
+ err.span_label(
+ borrow_span,
+ format!("`{}` 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 function `{}` returns",
+ name,
+ self.infcx.tcx.hir().name_by_hir_id(fn_hir_id),
+ ),
+ );
+
+ 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 <https://doc.rust-lang.org/book/ch04-02-\
+ references-and-borrowing.html#dangling-references>",
+ );
+ } else {
+ err.span_label(
+ drop_span,
+ format!("...but `{}` dropped here while still borrowed", name),
+ );
+ }
+
+ if let BorrowExplanation::MustBeValidFor { .. } = explanation {
+ } else {
+ explanation.add_explanation_to_diagnostic(
+ self.infcx.tcx,
+ self.mir,
+ &mut err,
+ "",
+ None,
+ );
+ }
+ } else {
+ err.span_label(borrow_span, "borrowed value does not live long enough");
+ err.span_label(
+ drop_span,
+ format!("`{}` dropped here while still borrowed", name),
+ );
+
+ let within = if borrow_spans.for_generator() {
+ " by generator"
+ } else {
+ ""
+ };
+
+ borrow_spans.args_span_label(
+ &mut err,
+ format!("value captured here{}", within),
+ );
+
+ explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
+ }
+
+ err
+ }
+
+ fn report_borrow_conflicts_with_destructor(
+ &mut self,
+ location: Location,
+ borrow: &BorrowData<'tcx>,
+ (place, drop_span): (&Place<'tcx>, Span),
+ kind: Option<WriteKind>,
+ dropped_ty: Ty<'tcx>,
+ ) {
+ debug!(
+ "report_borrow_conflicts_with_destructor(\
+ {:?}, {:?}, ({:?}, {:?}), {:?}\
+ )",
+ location, borrow, place, drop_span, kind,
+ );
+
+ let borrow_spans = self.retrieve_borrow_spans(borrow);
+ let borrow_span = borrow_spans.var_or_use();
+
+ let mut err = self.infcx
+ .tcx
+ .cannot_borrow_across_destructor(borrow_span, Origin::Mir);
+
+ let what_was_dropped = match self.describe_place(place) {
+ Some(name) => format!("`{}`", name.as_str()),
+ None => String::from("temporary value"),
+ };
+
+ let label = match self.describe_place(&borrow.borrowed_place) {
+ Some(borrowed) => format!(
+ "here, drop of {D} needs exclusive access to `{B}`, \
+ because the type `{T}` implements the `Drop` trait",
+ D = what_was_dropped,
+ T = dropped_ty,
+ B = borrowed
+ ),
+ None => format!(
+ "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
+ D = what_was_dropped,
+ T = dropped_ty
+ ),
+ };
+ err.span_label(drop_span, label);
+
+ // Only give this note and suggestion if they could be relevant.
+ let explanation =
+ self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
+ match explanation {
+ BorrowExplanation::UsedLater { .. }
+ | BorrowExplanation::UsedLaterWhenDropped { .. } => {
+ err.note("consider using a `let` binding to create a longer lived value");
+ }
+ _ => {}
+ }
+
+ explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
+
+ err.buffer(&mut self.errors_buffer);
+ }
+
+ fn report_thread_local_value_does_not_live_long_enough(
+ &mut self,
+ drop_span: Span,
+ borrow_span: Span,
+ ) -> DiagnosticBuilder<'cx> {
+ debug!(
+ "report_thread_local_value_does_not_live_long_enough(\
+ {:?}, {:?}\
+ )",
+ drop_span, borrow_span
+ );
+
+ let mut err = self.infcx
+ .tcx
+ .thread_local_value_does_not_live_long_enough(borrow_span, Origin::Mir);
+
+ err.span_label(
+ borrow_span,
+ "thread-local variables cannot be borrowed beyond the end of the function",
+ );
+ err.span_label(drop_span, "end of enclosing function is here");
+
+ err
+ }
+
+ fn report_temporary_value_does_not_live_long_enough(
+ &mut self,
+ location: Location,
+ borrow: &BorrowData<'tcx>,
+ drop_span: Span,
+ borrow_spans: UseSpans,
+ proper_span: Span,
+ explanation: BorrowExplanation,
+ ) -> DiagnosticBuilder<'cx> {
+ debug!(
+ "report_temporary_value_does_not_live_long_enough(\
+ {:?}, {:?}, {:?}, {:?}\
+ )",
+ location, borrow, drop_span, proper_span
+ );
+
+ if let BorrowExplanation::MustBeValidFor {
+ category,
+ span,
+ from_closure: false,
+ ..
+ } = explanation {
+ if let Some(diag) = self.try_report_cannot_return_reference_to_local(
+ borrow,
+ proper_span,
+ span,
+ category,
+ None,
+ ) {
+ return diag;
+ }
+ }
+
+ let tcx = self.infcx.tcx;
+ let mut err = tcx.temporary_value_borrowed_for_too_long(proper_span, Origin::Mir);
+ err.span_label(
+ proper_span,
+ "creates a temporary which is freed while still in use",
+ );
+ err.span_label(
+ drop_span,
+ "temporary value is freed at the end of this statement",
+ );
+
+ match explanation {
+ BorrowExplanation::UsedLater(..)
+ | BorrowExplanation::UsedLaterInLoop(..)
+ | BorrowExplanation::UsedLaterWhenDropped { .. } => {
+ // Only give this note and suggestion if it could be relevant.
+ err.note("consider using a `let` binding to create a longer lived value");
+ }
+ _ => {}
+ }
+ explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
+
+ let within = if borrow_spans.for_generator() {
+ " by generator"
+ } else {
+ ""
+ };
+
+ borrow_spans.args_span_label(
+ &mut err,
+ format!("value captured here{}", within),
+ );
+
+ err
+ }
+
+ fn try_report_cannot_return_reference_to_local(
+ &self,
+ borrow: &BorrowData<'tcx>,
+ borrow_span: Span,
+ return_span: Span,
+ category: ConstraintCategory,
+ opt_place_desc: Option<&String>,
+ ) -> Option<DiagnosticBuilder<'cx>> {
+ let tcx = self.infcx.tcx;
+
+ let return_kind = match category {
+ ConstraintCategory::Return => "return",
+ ConstraintCategory::Yield => "yield",
+ _ => return None,
+ };
+
+ // FIXME use a better heuristic than Spans
+ let reference_desc = if return_span == self.mir.source_info(borrow.reserve_location).span {
+ "reference to"
+ } else {
+ "value referencing"
+ };
+
+ let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
+ let local_kind = match borrow.borrowed_place {
+ Place::Base(PlaceBase::Local(local)) => {
+ match self.mir.local_kind(local) {
+ LocalKind::ReturnPointer
+ | LocalKind::Temp => bug!("temporary or return pointer with a name"),
+ LocalKind::Var => "local variable ",
+ LocalKind::Arg
+ if !self.upvars.is_empty()
+ && local == Local::new(1) => {
+ "variable captured by `move` "
+ }
+ LocalKind::Arg => {
+ "function parameter "
+ }
+ }
+ }
+ _ => "local data ",
+ };
+ (
+ format!("{}`{}`", local_kind, place_desc),
+ format!("`{}` is borrowed here", place_desc),
+ )
+ } else {
+ let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All)
+ .last()
+ .unwrap();
+ let local = if let Place::Base(PlaceBase::Local(local)) = *root_place {
+ local
+ } else {
+ bug!("try_report_cannot_return_reference_to_local: not a local")
+ };
+ match self.mir.local_kind(local) {
+ LocalKind::ReturnPointer | LocalKind::Temp => {
+ (
+ "temporary value".to_string(),
+ "temporary value created here".to_string(),
+ )
+ }
+ LocalKind::Arg => {
+ (
+ "function parameter".to_string(),
+ "function parameter borrowed here".to_string(),
+ )
+ },
+ LocalKind::Var => bug!("local variable without a name"),
+ }
+ };
+
+ let mut err = tcx.cannot_return_reference_to_local(
+ return_span,
+ return_kind,
+ reference_desc,
+ &place_desc,
+ Origin::Mir,
+ );
+
+ if return_span != borrow_span {
+ err.span_label(borrow_span, note);
+ }
+
+ Some(err)
+ }
+
+ fn report_escaping_closure_capture(
+ &mut self,
+ args_span: Span,
+ var_span: Span,
+ fr_name: &RegionName,
+ category: ConstraintCategory,
+ constraint_span: Span,
+ captured_var: &str,
+ ) -> DiagnosticBuilder<'cx> {
+ let tcx = self.infcx.tcx;
+
+ let mut err = tcx.cannot_capture_in_long_lived_closure(
+ args_span,
+ captured_var,
+ var_span,
+ Origin::Mir,
+ );
+
+ let suggestion = match tcx.sess.source_map().span_to_snippet(args_span) {
+ Ok(string) => format!("move {}", string),
+ Err(_) => "move |<args>| <body>".to_string()
+ };
+
+ err.span_suggestion(
+ args_span,
+ &format!("to force the closure to take ownership of {} (and any \
+ other referenced variables), use the `move` keyword",
+ captured_var),
+ suggestion,
+ Applicability::MachineApplicable,
+ );
+
+ match category {
+ ConstraintCategory::Return => {
+ err.span_note(constraint_span, "closure is returned here");
+ }
+ ConstraintCategory::CallArgument => {
+ fr_name.highlight_region_name(&mut err);
+ err.span_note(
+ constraint_span,
+ &format!("function requires argument type to outlive `{}`", fr_name),
+ );
+ }
+ _ => bug!("report_escaping_closure_capture called with unexpected constraint \
+ category: `{:?}`", category),
+ }
+ err
+ }
+
+ fn report_escaping_data(
+ &mut self,
+ borrow_span: Span,
+ name: &Option<String>,
+ upvar_span: Span,
+ upvar_name: &str,
+ escape_span: Span,
+ ) -> DiagnosticBuilder<'cx> {
+ let tcx = self.infcx.tcx;
+
+ let escapes_from = if tcx.is_closure(self.mir_def_id) {
+ let tables = tcx.typeck_tables_of(self.mir_def_id);
+ let mir_hir_id = tcx.hir().def_index_to_hir_id(self.mir_def_id.index);
+ match tables.node_type(mir_hir_id).sty {
+ ty::Closure(..) => "closure",
+ ty::Generator(..) => "generator",
+ _ => bug!("Closure body doesn't have a closure or generator type"),
+ }
+ } else {
+ "function"
+ };
+
+ let mut err = tcx.borrowed_data_escapes_closure(escape_span, escapes_from, Origin::Mir);
+
+ err.span_label(
+ upvar_span,
+ format!(
+ "`{}` is declared here, outside of the {} body",
+ upvar_name, escapes_from
+ ),
+ );
+
+ err.span_label(
+ borrow_span,
+ format!(
+ "borrow is only valid in the {} body",
+ escapes_from
+ ),
+ );
+
+ if let Some(name) = name {
+ err.span_label(
+ escape_span,
+ format!("reference to `{}` escapes the {} body here", name, escapes_from),
+ );
+ } else {
+ err.span_label(
+ escape_span,
+ format!("reference escapes the {} body here", escapes_from),
+ );
+ }
+
+ err
+ }
+
+ fn get_moved_indexes(&mut self, location: Location, mpi: MovePathIndex) -> Vec<MoveSite> {
+ let mir = self.mir;
+
+ let mut stack = Vec::new();
+ stack.extend(mir.predecessor_locations(location).map(|predecessor| {
+ let is_back_edge = location.dominates(predecessor, &self.dominators);
+ (predecessor, is_back_edge)
+ }));
+
+ let mut visited = FxHashSet::default();
+ let mut result = vec![];
+
+ 'dfs: while let Some((location, is_back_edge)) = stack.pop() {
+ debug!(
+ "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
+ location, is_back_edge
+ );
+
+ if !visited.insert(location) {
+ continue;
+ }
+
+ // check for moves
+ let stmt_kind = mir[location.block]
+ .statements
+ .get(location.statement_index)
+ .map(|s| &s.kind);
+ if let Some(StatementKind::StorageDead(..)) = stmt_kind {
+ // this analysis only tries to find moves explicitly
+ // written by the user, so we ignore the move-outs
+ // created by `StorageDead` and at the beginning
+ // of a function.
+ } else {
+ // If we are found a use of a.b.c which was in error, then we want to look for
+ // moves not only of a.b.c but also a.b and a.
+ //
+ // Note that the moves data already includes "parent" paths, so we don't have to
+ // worry about the other case: that is, if there is a move of a.b.c, it is already
+ // marked as a move of a.b and a as well, so we will generate the correct errors
+ // there.
+ let mut mpis = vec![mpi];
+ let move_paths = &self.move_data.move_paths;
+ mpis.extend(move_paths[mpi].parents(move_paths));
+
+ for moi in &self.move_data.loc_map[location] {
+ debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
+ if mpis.contains(&self.move_data.moves[*moi].path) {
+ debug!("report_use_of_moved_or_uninitialized: found");
+ result.push(MoveSite {
+ moi: *moi,
+ traversed_back_edge: is_back_edge,
+ });
+
+ // Strictly speaking, we could continue our DFS here. There may be
+ // other moves that can reach the point of error. But it is kind of
+ // confusing to highlight them.
+ //
+ // Example:
+ //
+ // ```
+ // let a = vec![];
+ // let b = a;
+ // let c = a;
+ // drop(a); // <-- current point of error
+ // ```
+ //
+ // Because we stop the DFS here, we only highlight `let c = a`,
+ // and not `let b = a`. We will of course also report an error at
+ // `let c = a` which highlights `let b = a` as the move.
+ continue 'dfs;
+ }
+ }
+ }
+
+ // check for inits
+ let mut any_match = false;
+ drop_flag_effects::for_location_inits(
+ self.infcx.tcx,
+ self.mir,
+ self.move_data,
+ location,
+ |m| {
+ if m == mpi {
+ any_match = true;
+ }
+ },
+ );
+ if any_match {
+ continue 'dfs;
+ }
+
+ stack.extend(mir.predecessor_locations(location).map(|predecessor| {
+ let back_edge = location.dominates(predecessor, &self.dominators);
+ (predecessor, is_back_edge || back_edge)
+ }));
+ }
+
+ result
+ }
+
+ pub(super) fn report_illegal_mutation_of_borrowed(
+ &mut self,
+ location: Location,
+ (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 tcx = self.infcx.tcx;
+ if loan.kind == BorrowKind::Shallow {
+ let mut err = tcx.cannot_mutate_in_match_guard(
+ span,
+ loan_span,
+ &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
+ "assign",
+ Origin::Mir,
+ );
+ loan_spans.var_span_label(
+ &mut err,
+ format!("borrow occurs due to use{}", loan_spans.describe()),
+ );
+
+ err.buffer(&mut self.errors_buffer);
+
+ return;
+ }
+
+ let mut err = tcx.cannot_assign_to_borrowed(
+ span,
+ loan_span,
+ &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
+ Origin::Mir,
+ );
+
+ loan_spans.var_span_label(
+ &mut err,
+ format!("borrow occurs due to use{}", loan_spans.describe()),
+ );
+
+ self.explain_why_borrow_contains_point(location, loan, None)
+ .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
+
+ err.buffer(&mut self.errors_buffer);
+ }
+
+ /// Reports an illegal reassignment; for example, an assignment to
+ /// (part of) a non-`mut` local that occurs potentially after that
+ /// local has already been initialized. `place` is the path being
+ /// assigned; `err_place` is a place providing a reason why
+ /// `place` is not mutable (e.g., the non-`mut` local `x` in an
+ /// assignment to `x.f`).
+ pub(super) fn report_illegal_reassignment(
+ &mut self,
+ _location: Location,
+ (place, span): (&Place<'tcx>, Span),
+ assigned_span: Span,
+ err_place: &Place<'tcx>,
+ ) {
+ let (from_arg, local_decl) = if let Place::Base(PlaceBase::Local(local)) = *err_place {
+ if let LocalKind::Arg = self.mir.local_kind(local) {
+ (true, Some(&self.mir.local_decls[local]))
+ } else {
+ (false, Some(&self.mir.local_decls[local]))
+ }
+ } else {
+ (false, None)
+ };
+
+ // If root local is initialized immediately (everything apart from let
+ // 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 {
+ is_user_variable: Some(ClearCrossCrate::Clear),
+ ..
+ })
+ | Some(LocalDecl {
+ is_user_variable:
+ Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
+ opt_match_place: None,
+ ..
+ }))),
+ ..
+ })
+ | Some(LocalDecl {
+ is_user_variable: None,
+ ..
+ })
+ | None => (self.describe_place(place), assigned_span),
+ Some(decl) => (self.describe_place(err_place), decl.source_info.span),
+ };
+
+ let mut err = self.infcx.tcx.cannot_reassign_immutable(
+ span,
+ place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
+ from_arg,
+ Origin::Mir,
+ );
+ let msg = if from_arg {
+ "cannot assign to immutable argument"
+ } else {
+ "cannot assign twice to immutable variable"
+ };
+ 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));
+ }
+ }
+ if let Some(decl) = local_decl {
+ if let Some(name) = decl.name {
+ if decl.can_be_made_mutable() {
+ err.span_suggestion(
+ decl.source_info.span,
+ "make this binding mutable",
+ format!("mut {}", name),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+ err.span_label(span, msg);
+ err.buffer(&mut self.errors_buffer);
+ }
+
+ fn classify_drop_access_kind(&self, place: &Place<'tcx>) -> StorageDeadOrDrop<'tcx> {
+ let tcx = self.infcx.tcx;
+ match place {
+ Place::Base(PlaceBase::Local(_)) |
+ Place::Base(PlaceBase::Static(_)) => {
+ StorageDeadOrDrop::LocalStorageDead
+ }
+ Place::Projection(box PlaceProjection { base, elem }) => {
+ let base_access = self.classify_drop_access_kind(base);
+ match elem {
+ ProjectionElem::Deref => match base_access {
+ StorageDeadOrDrop::LocalStorageDead
+ | StorageDeadOrDrop::BoxedStorageDead => {
+ assert!(
+ base.ty(self.mir, tcx).ty.is_box(),
+ "Drop of value behind a reference or raw pointer"
+ );
+ StorageDeadOrDrop::BoxedStorageDead
+ }
+ StorageDeadOrDrop::Destructor(_) => base_access,
+ },
+ ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
+ let base_ty = base.ty(self.mir, tcx).ty;
+ match base_ty.sty {
+ ty::Adt(def, _) if def.has_dtor(tcx) => {
+ // Report the outermost adt with a destructor
+ match base_access {
+ StorageDeadOrDrop::Destructor(_) => base_access,
+ StorageDeadOrDrop::LocalStorageDead
+ | StorageDeadOrDrop::BoxedStorageDead => {
+ StorageDeadOrDrop::Destructor(base_ty)
+ }
+ }
+ }
+ _ => base_access,
+ }
+ }
+
+ ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. }
+ | ProjectionElem::Index(_) => base_access,
+ }
+ }
+ }
+ }
+
+ /// Annotate argument and return type of function and closure with (synthesized) lifetime for
+ /// borrow of local value that does not live long enough.
+ fn annotate_argument_and_return_for_borrow(
+ &self,
+ borrow: &BorrowData<'tcx>,
+ ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
+ // 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);
+ if is_closure {
+ None
+ } else {
+ let ty = self.infcx.tcx.type_of(self.mir_def_id);
+ match ty.sty {
+ ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
+ self.mir_def_id,
+ self.infcx.tcx.fn_sig(self.mir_def_id),
+ ),
+ _ => None,
+ }
+ }
+ };
+
+ // In order to determine whether we need to annotate, we need to check whether the reserve
+ // place was an assignment into a temporary.
+ //
+ // If it was, we check whether or not that temporary is eventually assigned into the return
+ // place. If it was, we can add annotations about the function's return type and arguments
+ // and it'll make sense.
+ let location = borrow.reserve_location;
+ debug!(
+ "annotate_argument_and_return_for_borrow: location={:?}",
+ location
+ );
+ if let Some(&Statement { kind: StatementKind::Assign(ref reservation, _), ..})
+ = &self.mir[location.block].statements.get(location.statement_index)
+ {
+ debug!(
+ "annotate_argument_and_return_for_borrow: reservation={:?}",
+ reservation
+ );
+ // Check that the initial assignment of the reserve location is into a temporary.
+ let mut target = *match reservation {
+ Place::Base(PlaceBase::Local(local))
+ if self.mir.local_kind(*local) == LocalKind::Temp => local,
+ _ => return None,
+ };
+
+ // Next, look through the rest of the block, checking if we are assigning the
+ // `target` (that is, the place that contains our borrow) to anything.
+ let mut annotated_closure = None;
+ for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
+ debug!(
+ "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
+ target, stmt
+ );
+ if let StatementKind::Assign(
+ Place::Base(PlaceBase::Local(assigned_to)),
+ box rvalue
+ ) = &stmt.kind {
+ debug!(
+ "annotate_argument_and_return_for_borrow: assigned_to={:?} \
+ rvalue={:?}",
+ assigned_to, rvalue
+ );
+ // Check if our `target` was captured by a closure.
+ if let Rvalue::Aggregate(
+ box AggregateKind::Closure(def_id, substs),
+ operands,
+ ) = rvalue
+ {
+ for operand in operands {
+ let assigned_from = match operand {
+ Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
+ assigned_from
+ }
+ _ => continue,
+ };
+ debug!(
+ "annotate_argument_and_return_for_borrow: assigned_from={:?}",
+ assigned_from
+ );
+
+ // Find the local from the operand.
+ let assigned_from_local = match assigned_from.local() {
+ Some(local) => local,
+ None => continue,
+ };
+
+ if assigned_from_local != target {
+ continue;
+ }
+
+ // 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),
+ );
+ debug!(
+ "annotate_argument_and_return_for_borrow: \
+ annotated_closure={:?} assigned_from_local={:?} \
+ assigned_to={:?}",
+ annotated_closure, assigned_from_local, assigned_to
+ );
+
+ if *assigned_to == mir::RETURN_PLACE {
+ // If it was assigned directly into the return place, then
+ // return now.
+ return annotated_closure;
+ } else {
+ // Otherwise, update the target.
+ target = *assigned_to;
+ }
+ }
+
+ // If none of our closure's operands matched, then skip to the next
+ // statement.
+ continue;
+ }
+
+ // Otherwise, look at other types of assignment.
+ let assigned_from = match rvalue {
+ Rvalue::Ref(_, _, assigned_from) => assigned_from,
+ Rvalue::Use(operand) => match operand {
+ Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
+ assigned_from
+ }
+ _ => continue,
+ },
+ _ => continue,
+ };
+ debug!(
+ "annotate_argument_and_return_for_borrow: \
+ assigned_from={:?}",
+ assigned_from,
+ );
+
+ // Find the local from the rvalue.
+ let assigned_from_local = match assigned_from.local() {
+ Some(local) => local,
+ None => continue,
+ };
+ debug!(
+ "annotate_argument_and_return_for_borrow: \
+ assigned_from_local={:?}",
+ assigned_from_local,
+ );
+
+ // Check if our local matches the target - if so, we've assigned our
+ // borrow to a new place.
+ if assigned_from_local != target {
+ continue;
+ }
+
+ // If we assigned our `target` into a new place, then we should
+ // check if it was the return place.
+ debug!(
+ "annotate_argument_and_return_for_borrow: \
+ assigned_from_local={:?} assigned_to={:?}",
+ assigned_from_local, assigned_to
+ );
+ if *assigned_to == mir::RETURN_PLACE {
+ // If it was then return the annotated closure if there was one,
+ // else, annotate this function.
+ return annotated_closure.or_else(fallback);
+ }
+
+ // If we didn't assign into the return place, then we just update
+ // the target.
+ target = *assigned_to;
+ }
+ }
+
+ // Check the terminator if we didn't find anything in the statements.
+ let terminator = &self.mir[location.block].terminator();
+ debug!(
+ "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
+ target, terminator
+ );
+ if let TerminatorKind::Call {
+ destination: Some((Place::Base(PlaceBase::Local(assigned_to)), _)),
+ args,
+ ..
+ } = &terminator.kind
+ {
+ debug!(
+ "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
+ assigned_to, args
+ );
+ for operand in args {
+ let assigned_from = match operand {
+ Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
+ assigned_from
+ }
+ _ => continue,
+ };
+ debug!(
+ "annotate_argument_and_return_for_borrow: assigned_from={:?}",
+ assigned_from,
+ );
+
+ if let Some(assigned_from_local) = assigned_from.local() {
+ debug!(
+ "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
+ assigned_from_local,
+ );
+
+ if *assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
+ return annotated_closure.or_else(fallback);
+ }
+ }
+ }
+ }
+ }
+
+ // If we haven't found an assignment into the return place, then we need not add
+ // any annotations.
+ debug!("annotate_argument_and_return_for_borrow: none found");
+ None
+ }
+
+ /// Annotate the first argument and return type of a function signature if they are
+ /// references.
+ fn annotate_fn_sig(
+ &self,
+ did: DefId,
+ sig: ty::PolyFnSig<'tcx>,
+ ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
+ 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_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
+ // at the return type, where there are three cases:
+ //
+ // 1. If there are named arguments, then we should highlight the return type and
+ // highlight any of the arguments that are also references with that lifetime.
+ // If there are no arguments that have the same lifetime as the return type,
+ // then don't highlight anything.
+ // 2. The return type is a reference with an anonymous lifetime. If this is
+ // the case, then we can take advantage of (and teach) the lifetime elision
+ // rules.
+ //
+ // We know that an error is being reported. So the arguments and return type
+ // must satisfy the elision rules. Therefore, if there is a single argument
+ // then that means the return type and first (and only) argument have the same
+ // lifetime and the borrow isn't meeting that, we can highlight the argument
+ // and return type.
+ //
+ // If there are multiple arguments then the first argument must be self (else
+ // it would not satisfy the elision rules), so we can highlight self and the
+ // return type.
+ // 3. The return type is not a reference. In this case, we don't highlight
+ // anything.
+ let return_ty = sig.output();
+ match return_ty.skip_binder().sty {
+ ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
+ // This is case 1 from above, return type is a named reference so we need to
+ // search for relevant arguments.
+ let mut arguments = Vec::new();
+ for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
+ if let ty::Ref(argument_region, _, _) = argument.sty {
+ if argument_region == return_region {
+ // Need to use the `rustc::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].node {
+ // With access to the lifetime, we can get
+ // the span of it.
+ arguments.push((*argument, lifetime.span));
+ } else {
+ bug!("ty type is a ref but hir type is not");
+ }
+ }
+ }
+ }
+
+ // We need to have arguments. This shouldn't happen, but it's worth checking.
+ if arguments.is_empty() {
+ return None;
+ }
+
+ // We use a mix of the HIR and the Ty types to get information
+ // as the HIR doesn't have full types for closure arguments.
+ let return_ty = *sig.output().skip_binder();
+ let mut return_span = fn_decl.output.span();
+ if let hir::FunctionRetTy::Return(ty) = fn_decl.output {
+ if let hir::TyKind::Rptr(lifetime, _) = ty.into_inner().node {
+ return_span = lifetime.span;
+ }
+ }
+
+ Some(AnnotatedBorrowFnSignature::NamedFunction {
+ arguments,
+ return_ty,
+ return_span,
+ })
+ }
+ ty::Ref(_, _, _) if is_closure => {
+ // This is case 2 from above but only for closures, return type is anonymous
+ // reference so we select
+ // the first argument.
+ let argument_span = fn_decl.inputs.first()?.span;
+ let argument_ty = sig.inputs().skip_binder().first()?;
+
+ // Closure arguments are wrapped in a tuple, so we need to get the first
+ // from that.
+ if let ty::Tuple(elems) = argument_ty.sty {
+ let argument_ty = elems.first()?.expect_ty();
+ if let ty::Ref(_, _, _) = argument_ty.sty {
+ return Some(AnnotatedBorrowFnSignature::Closure {
+ argument_ty,
+ argument_span,
+ });
+ }
+ }
+
+ None
+ }
+ ty::Ref(_, _, _) => {
+ // This is also case 2 from above but for functions, return type is still an
+ // anonymous reference so we select the first argument.
+ let argument_span = fn_decl.inputs.first()?.span;
+ let argument_ty = sig.inputs().skip_binder().first()?;
+
+ let return_span = fn_decl.output.span();
+ let return_ty = *sig.output().skip_binder();
+
+ // We expect the first argument to be a reference.
+ match argument_ty.sty {
+ ty::Ref(_, _, _) => {}
+ _ => return None,
+ }
+
+ Some(AnnotatedBorrowFnSignature::AnonymousFunction {
+ argument_ty,
+ argument_span,
+ return_ty,
+ return_span,
+ })
+ }
+ _ => {
+ // This is case 3 from above, return type is not a reference so don't highlight
+ // anything.
+ None
+ }
+ }
+ }
+}
+
+#[derive(Debug)]
+enum AnnotatedBorrowFnSignature<'tcx> {
+ NamedFunction {
+ arguments: Vec<(Ty<'tcx>, Span)>,
+ return_ty: Ty<'tcx>,
+ return_span: Span,
+ },
+ AnonymousFunction {
+ argument_ty: Ty<'tcx>,
+ argument_span: Span,
+ return_ty: Ty<'tcx>,
+ return_span: Span,
+ },
+ Closure {
+ argument_ty: Ty<'tcx>,
+ argument_span: Span,
+ },
+}
+
+impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
+ /// Annotate the provided diagnostic with information about borrow from the fn signature that
+ /// helps explain.
+ pub(super) fn emit(
+ &self,
+ cx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
+ diag: &mut DiagnosticBuilder<'_>,
+ ) -> String {
+ match self {
+ AnnotatedBorrowFnSignature::Closure {
+ argument_ty,
+ argument_span,
+ } => {
+ diag.span_label(
+ *argument_span,
+ format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
+ );
+
+ cx.get_region_name_for_ty(argument_ty, 0)
+ }
+ AnnotatedBorrowFnSignature::AnonymousFunction {
+ argument_ty,
+ argument_span,
+ return_ty,
+ return_span,
+ } => {
+ let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
+ diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name));
+
+ let return_ty_name = cx.get_name_for_ty(return_ty, 0);
+ let types_equal = return_ty_name == argument_ty_name;
+ diag.span_label(
+ *return_span,
+ format!(
+ "{}has type `{}`",
+ if types_equal { "also " } else { "" },
+ return_ty_name,
+ ),
+ );
+
+ diag.note(
+ "argument and return type have the same lifetime due to lifetime elision rules",
+ );
+ diag.note(
+ "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
+ lifetime-syntax.html#lifetime-elision>",
+ );
+
+ cx.get_region_name_for_ty(return_ty, 0)
+ }
+ AnnotatedBorrowFnSignature::NamedFunction {
+ arguments,
+ return_ty,
+ return_span,
+ } => {
+ // Region of return type and arguments checked to be the same earlier.
+ let region_name = cx.get_region_name_for_ty(return_ty, 0);
+ for (_, argument_span) in arguments {
+ diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
+ }
+
+ diag.span_label(
+ *return_span,
+ format!("also has lifetime `{}`", region_name,),
+ );
+
+ diag.help(&format!(
+ "use data from the highlighted arguments which match the `{}` lifetime of \
+ the return type",
+ region_name,
+ ));
+
+ region_name
+ }
+ }
+ }
+}
-use crate::borrow_check::nll::explain_borrow::BorrowExplanation;
-use crate::borrow_check::nll::region_infer::{RegionName, RegionNameSource};
-use crate::borrow_check::prefixes::IsPrefixOf;
-use crate::borrow_check::WriteKind;
use rustc::hir;
use rustc::hir::def::Namespace;
use rustc::hir::def_id::DefId;
-use rustc::middle::region::ScopeTree;
use rustc::mir::{
- self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, Constant,
- ConstraintCategory, Field, Local, LocalDecl, LocalKind, Location, Operand,
- Place, PlaceBase, PlaceProjection, ProjectionElem, Rvalue, Statement, StatementKind,
- Static, StaticKind, TerminatorKind, VarBindingForm,
+ AggregateKind, BindingForm, ClearCrossCrate, Constant, Field, Local,
+ LocalKind, Location, Operand, Place, PlaceBase, ProjectionElem, Rvalue,
+ Statement, StatementKind, Static, StaticKind, TerminatorKind,
};
use rustc::ty::{self, DefIdTree, Ty};
use rustc::ty::layout::VariantIdx;
use rustc::ty::print::Print;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::indexed_vec::Idx;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::DiagnosticBuilder;
use syntax_pos::Span;
-use syntax::source_map::CompilerDesugaringKind;
use syntax::symbol::sym;
use super::borrow_set::BorrowData;
use super::{MirBorrowckCtxt};
-use super::{InitializationRequiringAction, PrefixSet};
-use crate::dataflow::drop_flag_effects;
-use crate::dataflow::indexes::{MovePathIndex, MoveOutIndex};
-use crate::util::borrowck_errors::{BorrowckErrors, Origin};
-#[derive(Debug)]
-struct MoveSite {
- /// Index of the "move out" that we found. The `MoveData` can
- /// then tell us where the move occurred.
- moi: MoveOutIndex,
-
- /// `true` if we traversed a back edge while walking from the point
- /// of error to the move site.
- traversed_back_edge: bool
-}
-
-impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
- pub(super) fn report_use_of_moved_or_uninitialized(
- &mut self,
- location: Location,
- desired_action: InitializationRequiringAction,
- (moved_place, used_place, span): (&Place<'tcx>, &Place<'tcx>, Span),
- mpi: MovePathIndex,
- ) {
- debug!(
- "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \
- moved_place={:?} used_place={:?} span={:?} mpi={:?}",
- location, desired_action, moved_place, used_place, span, mpi
- );
-
- let use_spans = self.move_spans(moved_place, location)
- .or_else(|| self.borrow_spans(span, location));
- let span = use_spans.args_or_use();
-
- let move_site_vec = self.get_moved_indexes(location, mpi);
- debug!(
- "report_use_of_moved_or_uninitialized: move_site_vec={:?}",
- move_site_vec
- );
- let move_out_indices: Vec<_> = move_site_vec
- .iter()
- .map(|move_site| move_site.moi)
- .collect();
-
- if move_out_indices.is_empty() {
- let root_place = self.prefixes(&used_place, PrefixSet::All).last().unwrap();
-
- if self.uninitialized_error_reported.contains(root_place) {
- debug!(
- "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
- root_place
- );
- return;
- }
-
- self.uninitialized_error_reported.insert(root_place.clone());
-
- let item_msg = match self.describe_place_with_options(used_place,
- IncludingDowncast(true)) {
- Some(name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
- let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable(
- span,
- desired_action.as_noun(),
- &self.describe_place_with_options(moved_place, IncludingDowncast(true))
- .unwrap_or_else(|| "_".to_owned()),
- Origin::Mir,
- );
- err.span_label(span, format!("use of possibly uninitialized {}", item_msg));
-
- use_spans.var_span_label(
- &mut err,
- format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
- );
-
- err.buffer(&mut self.errors_buffer);
- } else {
- if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) {
- if self.prefixes(&reported_place, PrefixSet::All)
- .any(|p| p == used_place)
- {
- debug!(
- "report_use_of_moved_or_uninitialized place: error suppressed \
- mois={:?}",
- move_out_indices
- );
- return;
- }
- }
-
- let msg = ""; //FIXME: add "partially " or "collaterally "
-
- let mut err = self.infcx.tcx.cannot_act_on_moved_value(
- span,
- desired_action.as_noun(),
- msg,
- self.describe_place_with_options(&moved_place, IncludingDowncast(true)),
- Origin::Mir,
- );
-
- self.add_moved_or_invoked_closure_note(
- location,
- used_place,
- &mut err,
- );
-
- let mut is_loop_move = false;
- let is_partial_move = move_site_vec.iter().any(|move_site| {
- let move_out = self.move_data.moves[(*move_site).moi];
- let moved_place = &self.move_data.move_paths[move_out.path].place;
- used_place != moved_place && used_place.is_prefix_of(moved_place)
- });
- for move_site in &move_site_vec {
- let move_out = self.move_data.moves[(*move_site).moi];
- let moved_place = &self.move_data.move_paths[move_out.path].place;
-
- let move_spans = self.move_spans(moved_place, move_out.source);
- let move_span = move_spans.args_or_use();
-
- let move_msg = if move_spans.for_closure() {
- " into closure"
- } else {
- ""
- };
-
- if span == move_span {
- err.span_label(
- span,
- format!("value moved{} here, in previous iteration of loop", move_msg),
- );
- if Some(CompilerDesugaringKind::ForLoop) == span.compiler_desugaring_kind() {
- if let Ok(snippet) = self.infcx.tcx.sess.source_map()
- .span_to_snippet(span)
- {
- err.span_suggestion(
- move_span,
- "consider borrowing this to avoid moving it into the for loop",
- format!("&{}", snippet),
- Applicability::MaybeIncorrect,
- );
- }
- }
- is_loop_move = true;
- } else if move_site.traversed_back_edge {
- err.span_label(
- move_span,
- format!(
- "value moved{} here, in previous iteration of loop",
- move_msg
- ),
- );
- } else {
- err.span_label(move_span, format!("value moved{} here", move_msg));
- move_spans.var_span_label(
- &mut err,
- format!("variable moved due to use{}", move_spans.describe()),
- );
- };
- }
-
- use_spans.var_span_label(
- &mut err,
- format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
- );
-
- if !is_loop_move {
- err.span_label(
- span,
- format!(
- "value {} here {}",
- desired_action.as_verb_in_past_tense(),
- if is_partial_move { "after partial move" } else { "after move" },
- ),
- );
- }
-
- let ty = used_place.ty(self.mir, self.infcx.tcx).ty;
- let needs_note = match ty.sty {
- 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();
-
- tables.closure_kind_origins().get(hir_id).is_none()
- }
- _ => true,
- };
-
- if needs_note {
- 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.mir, self.infcx.tcx).ty;
- let opt_name = self.describe_place_with_options(place, IncludingDowncast(true));
- let note_msg = match opt_name {
- Some(ref name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
- if let ty::Param(param_ty) = ty.sty {
- let tcx = self.infcx.tcx;
- let generics = tcx.generics_of(self.mir_def_id);
- let def_id = generics.type_param(¶m_ty, tcx).def_id;
- if let Some(sp) = tcx.hir().span_if_local(def_id) {
- err.span_label(
- sp,
- "consider adding a `Copy` constraint to this type argument",
- );
- }
- }
- if let Place::Base(PlaceBase::Local(local)) = place {
- let decl = &self.mir.local_decls[*local];
- err.span_label(
- decl.source_info.span,
- format!(
- "move occurs because {} has type `{}`, \
- which does not implement the `Copy` trait",
- note_msg, ty,
- ));
- } else {
- err.note(&format!(
- "move occurs because {} has type `{}`, \
- which does not implement the `Copy` trait",
- note_msg, ty
- ));
- }
- }
-
- if let Some((_, mut old_err)) = self.move_error_reported
- .insert(move_out_indices, (used_place.clone(), err))
- {
- // Cancel the old error so it doesn't ICE.
- old_err.cancel();
- }
- }
- }
-
- pub(super) fn report_move_out_while_borrowed(
- &mut self,
- location: Location,
- (place, span): (&Place<'tcx>, Span),
- borrow: &BorrowData<'tcx>,
- ) {
- debug!(
- "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",
- location, place, span, borrow
- );
- let tcx = self.infcx.tcx;
- let value_msg = match self.describe_place(place) {
- Some(name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
- let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
- Some(name) => format!("`{}`", name),
- None => "value".to_owned(),
- };
-
- let borrow_spans = self.retrieve_borrow_spans(borrow);
- let borrow_span = borrow_spans.args_or_use();
-
- let move_spans = self.move_spans(place, location);
- let span = move_spans.args_or_use();
-
- let mut err = tcx.cannot_move_when_borrowed(
- span,
- &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
- Origin::Mir,
- );
- err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
- err.span_label(span, format!("move out of {} occurs here", value_msg));
-
- borrow_spans.var_span_label(
- &mut err,
- format!("borrow occurs due to use{}", borrow_spans.describe())
- );
-
- move_spans.var_span_label(
- &mut err,
- format!("move occurs due to use{}", move_spans.describe())
- );
-
- self.explain_why_borrow_contains_point(
- location,
- borrow,
- None,
- ).add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", Some(borrow_span));
- err.buffer(&mut self.errors_buffer);
- }
-
- pub(super) fn report_use_while_mutably_borrowed(
- &mut self,
- location: Location,
- (place, _span): (&Place<'tcx>, Span),
- borrow: &BorrowData<'tcx>,
- ) -> DiagnosticBuilder<'cx> {
- let tcx = self.infcx.tcx;
-
- let borrow_spans = self.retrieve_borrow_spans(borrow);
- let borrow_span = borrow_spans.args_or_use();
-
- // Conflicting borrows are reported separately, so only check for move
- // captures.
- let use_spans = self.move_spans(place, location);
- let span = use_spans.var_or_use();
-
- let mut err = tcx.cannot_use_when_mutably_borrowed(
- span,
- &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
- borrow_span,
- &self.describe_place(&borrow.borrowed_place)
- .unwrap_or_else(|| "_".to_owned()),
- Origin::Mir,
- );
-
- borrow_spans.var_span_label(&mut err, {
- let place = &borrow.borrowed_place;
- let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned());
-
- format!("borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe())
- });
-
- self.explain_why_borrow_contains_point(location, borrow, None)
- .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
- err
- }
-
- pub(super) fn report_conflicting_borrow(
- &mut self,
- location: Location,
- (place, span): (&Place<'tcx>, Span),
- gen_borrow_kind: BorrowKind,
- issued_borrow: &BorrowData<'tcx>,
- ) -> DiagnosticBuilder<'cx> {
- let issued_spans = self.retrieve_borrow_spans(issued_borrow);
- let issued_span = issued_spans.args_or_use();
-
- let borrow_spans = self.borrow_spans(span, location);
- let span = borrow_spans.args_or_use();
-
- let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
- "generator"
- } else {
- "closure"
- };
-
- let (desc_place, msg_place, msg_borrow, union_type_name) =
- 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 {
- ""
- };
-
- // FIXME: supply non-"" `opt_via` when appropriate
- let tcx = self.infcx.tcx;
- let first_borrow_desc;
- let mut err = match (
- gen_borrow_kind,
- "immutable",
- "mutable",
- issued_borrow.kind,
- "immutable",
- "mutable",
- ) {
- (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt) => {
- first_borrow_desc = "mutable ";
- tcx.cannot_reborrow_already_borrowed(
- span,
- &desc_place,
- &msg_place,
- lft,
- issued_span,
- "it",
- rgt,
- &msg_borrow,
- None,
- Origin::Mir,
- )
- }
- (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => {
- first_borrow_desc = "immutable ";
- tcx.cannot_reborrow_already_borrowed(
- span,
- &desc_place,
- &msg_place,
- lft,
- issued_span,
- "it",
- rgt,
- &msg_borrow,
- None,
- Origin::Mir,
- )
- }
-
- (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => {
- first_borrow_desc = "first ";
- tcx.cannot_mutably_borrow_multiply(
- span,
- &desc_place,
- &msg_place,
- issued_span,
- &msg_borrow,
- None,
- Origin::Mir,
- )
- }
-
- (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => {
- first_borrow_desc = "first ";
- tcx.cannot_uniquely_borrow_by_two_closures(
- span,
- &desc_place,
- issued_span,
- None,
- Origin::Mir,
- )
- }
-
- (BorrowKind::Mut { .. }, _, _, BorrowKind::Shallow, _, _)
- | (BorrowKind::Unique, _, _, BorrowKind::Shallow, _, _) => {
- let mut err = tcx.cannot_mutate_in_match_guard(
- span,
- issued_span,
- &desc_place,
- "mutably borrow",
- Origin::Mir,
- );
- borrow_spans.var_span_label(
- &mut err,
- format!(
- "borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe()
- ),
- );
-
- return err;
- }
-
- (BorrowKind::Unique, _, _, _, _, _) => {
- first_borrow_desc = "first ";
- tcx.cannot_uniquely_borrow_by_one_closure(
- span,
- container_name,
- &desc_place,
- "",
- issued_span,
- "it",
- "",
- None,
- Origin::Mir,
- )
- },
-
- (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => {
- first_borrow_desc = "first ";
- tcx.cannot_reborrow_already_uniquely_borrowed(
- span,
- container_name,
- &desc_place,
- "",
- lft,
- issued_span,
- "",
- None,
- second_borrow_desc,
- Origin::Mir,
- )
- }
-
- (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => {
- first_borrow_desc = "first ";
- tcx.cannot_reborrow_already_uniquely_borrowed(
- span,
- container_name,
- &desc_place,
- "",
- lft,
- issued_span,
- "",
- None,
- second_borrow_desc,
- Origin::Mir,
- )
- }
-
- (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!(),
- };
-
- if issued_spans == borrow_spans {
- borrow_spans.var_span_label(
- &mut err,
- 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)
- .unwrap_or_else(|| "_".to_owned());
- issued_spans.var_span_label(
- &mut err,
- format!(
- "first borrow occurs due to use of `{}`{}",
- borrow_place_desc,
- issued_spans.describe(),
- ),
- );
-
- borrow_spans.var_span_label(
- &mut err,
- format!(
- "second borrow occurs due to use of `{}`{}",
- desc_place,
- borrow_spans.describe(),
- ),
- );
- }
-
- if union_type_name != "" {
- err.note(&format!(
- "`{}` is a field of the union `{}`, so it overlaps the field `{}`",
- msg_place, union_type_name, msg_borrow,
- ));
- }
-
- explanation.add_explanation_to_diagnostic(
- self.infcx.tcx,
- self.mir,
- &mut err,
- first_borrow_desc,
- None,
- );
-
- err
- }
-
- /// Returns the description of the root place for a conflicting borrow and the full
- /// descriptions of the places that caused the conflict.
- ///
- /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
- /// attempted while a shared borrow is live, then this function will return:
- ///
- /// ("x", "", "")
- ///
- /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
- /// a shared borrow of another field `x.y`, then this function will return:
- ///
- /// ("x", "x.z", "x.y")
- ///
- /// In the more complex union case, where the union is a field of a struct, then if a mutable
- /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
- /// another field `x.u.y`, then this function will return:
- ///
- /// ("x.u", "x.u.z", "x.u.y")
- ///
- /// 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]
- pub(super) fn describe_place_for_conflicting_borrow(
- &self,
- 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 is_union = |place: &Place<'tcx>| -> bool {
- place.ty(self.mir, self.infcx.tcx).ty
- .ty_adt_def()
- .map(|adt| adt.is_union())
- .unwrap_or(false)
- };
-
- // 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
- // case).
- Some(())
- .filter(|_| {
- // If we have a conflicting borrow of the same place, then we don't want to add
- // an extraneous "via x.y" to our diagnostics, so filter out this case.
- first_borrowed_place != second_borrowed_place
- })
- .and_then(|_| {
- // We're going to want to traverse the first borrowed place to see if we can find
- // field access to a union. If we find that, then we will keep the place of the
- // union being accessed and the field that was being accessed so we can check the
- // second borrowed place for the same union and a access to a different field.
- let mut current = first_borrowed_place;
- while let Place::Projection(box PlaceProjection { base, elem }) = current {
- match elem {
- ProjectionElem::Field(field, _) if is_union(base) => {
- return Some((base, field));
- },
- _ => current = base,
- }
- }
- None
- })
- .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 mut current = second_borrowed_place;
- while let Place::Projection(box PlaceProjection { base, elem }) = current {
- match elem {
- ProjectionElem::Field(field, _) if {
- is_union(base) && field != target_field && base == target_base
- } => {
- let desc_base = self.describe_place(base)
- .unwrap_or_else(|| "_".to_owned());
- let desc_first = self.describe_place(first_borrowed_place)
- .unwrap_or_else(|| "_".to_owned());
- let desc_second = self.describe_place(second_borrowed_place)
- .unwrap_or_else(|| "_".to_owned());
-
- // Also compute the name of the union type, eg. `Foo` so we
- // can add a helpful note with it.
- let ty = base.ty(self.mir, self.infcx.tcx).ty;
-
- return Some((desc_base, desc_first, desc_second, ty.to_string()));
- },
- _ => current = base,
- }
- }
- None
- })
- .unwrap_or_else(|| {
- // If we didn't find a field access into a union, or both places match, then
- // only return the description of the first place.
- let desc_place = self.describe_place(first_borrowed_place)
- .unwrap_or_else(|| "_".to_owned());
- (desc_place, "".to_string(), "".to_string(), "".to_string())
- })
- }
-
- /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
- ///
- /// This means that some data referenced by `borrow` needs to live
- /// past the point where the StorageDeadOrDrop of `place` occurs.
- /// This is usually interpreted as meaning that `place` has too
- /// short a lifetime. (But sometimes it is more useful to report
- /// it as a more direct conflict between the execution of a
- /// `Drop::drop` with an aliasing borrow.)
- pub(super) fn report_borrowed_value_does_not_live_long_enough(
- &mut self,
- location: Location,
- borrow: &BorrowData<'tcx>,
- place_span: (&Place<'tcx>, Span),
- kind: Option<WriteKind>,
- ) {
- debug!(
- "report_borrowed_value_does_not_live_long_enough(\
- {:?}, {:?}, {:?}, {:?}\
- )",
- location, borrow, place_span, kind
- );
-
- let drop_span = place_span.1;
- let scope_tree = self.infcx.tcx.region_scope_tree(self.mir_def_id);
- let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All)
- .last()
- .unwrap();
-
- let borrow_spans = self.retrieve_borrow_spans(borrow);
- let borrow_span = borrow_spans.var_or_use();
-
- let proper_span = match *root_place {
- Place::Base(PlaceBase::Local(local)) => self.mir.local_decls[local].source_info.span,
- _ => drop_span,
- };
-
- if self.access_place_error_reported
- .contains(&(root_place.clone(), borrow_span))
- {
- debug!(
- "suppressing access_place error when borrow doesn't live long enough for {:?}",
- borrow_span
- );
- return;
- }
-
- self.access_place_error_reported
- .insert((root_place.clone(), borrow_span));
-
- if let StorageDeadOrDrop::Destructor(dropped_ty) =
- self.classify_drop_access_kind(&borrow.borrowed_place)
- {
- // If a borrow of path `B` conflicts with drop of `D` (and
- // we're not in the uninteresting case where `B` is a
- // prefix of `D`), then report this as a more interesting
- // destructor conflict.
- if !borrow.borrowed_place.is_prefix_of(place_span.0) {
- self.report_borrow_conflicts_with_destructor(
- location, borrow, place_span, kind, dropped_ty,
- );
- return;
- }
- }
-
- let place_desc = self.describe_place(&borrow.borrowed_place);
-
- let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
- let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
-
- let err = match (place_desc, explanation) {
- (Some(_), _) if self.is_place_thread_local(root_place) => {
- self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span)
- }
- // If the outlives constraint comes from inside the closure,
- // for example:
- //
- // let x = 0;
- // let y = &x;
- // Box::new(|| y) as Box<Fn() -> &'static i32>
- //
- // then just use the normal error. The closure isn't escaping
- // and `move` will not help here.
- (
- 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,
- from_closure: false,
- ref region_name,
- span,
- ..
- },
- ) if borrow_spans.for_closure() => self.report_escaping_closure_capture(
- borrow_spans.args_or_use(),
- borrow_span,
- region_name,
- category,
- span,
- &format!("`{}`", name),
- ),
- (
- ref name,
- BorrowExplanation::MustBeValidFor {
- category: ConstraintCategory::Assignment,
- from_closure: false,
- region_name: RegionName {
- source: RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name),
- ..
- },
- span,
- ..
- },
- ) => self.report_escaping_data(borrow_span, name, upvar_span, upvar_name, span),
- (Some(name), explanation) => self.report_local_value_does_not_live_long_enough(
- location,
- &name,
- &scope_tree,
- &borrow,
- drop_span,
- borrow_spans,
- explanation,
- ),
- (None, explanation) => self.report_temporary_value_does_not_live_long_enough(
- location,
- &scope_tree,
- &borrow,
- drop_span,
- borrow_spans,
- proper_span,
- explanation,
- ),
- };
-
- err.buffer(&mut self.errors_buffer);
- }
-
- fn report_local_value_does_not_live_long_enough(
- &mut self,
- location: Location,
- name: &str,
- scope_tree: &'tcx ScopeTree,
- borrow: &BorrowData<'tcx>,
- drop_span: Span,
- borrow_spans: UseSpans,
- explanation: BorrowExplanation,
- ) -> DiagnosticBuilder<'cx> {
- debug!(
- "report_local_value_does_not_live_long_enough(\
- {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
- )",
- location, name, scope_tree, borrow, drop_span, borrow_spans
- );
-
- let borrow_span = borrow_spans.var_or_use();
- if let BorrowExplanation::MustBeValidFor {
- category,
- span,
- ref opt_place_desc,
- from_closure: false,
- ..
- } = explanation {
- if let Some(diag) = self.try_report_cannot_return_reference_to_local(
- borrow,
- borrow_span,
- span,
- category,
- opt_place_desc.as_ref(),
- ) {
- return diag;
- }
- }
-
- let mut err = self.infcx.tcx.path_does_not_live_long_enough(
- borrow_span,
- &format!("`{}`", name),
- Origin::Mir,
- );
-
- if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
- let region_name = annotation.emit(self, &mut err);
-
- err.span_label(
- borrow_span,
- format!("`{}` 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 function `{}` returns",
- name,
- self.infcx.tcx.hir().name_by_hir_id(fn_hir_id),
- ),
- );
-
- 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 <https://doc.rust-lang.org/book/ch04-02-\
- references-and-borrowing.html#dangling-references>",
- );
- } else {
- err.span_label(
- drop_span,
- format!("...but `{}` dropped here while still borrowed", name),
- );
- }
-
- if let BorrowExplanation::MustBeValidFor { .. } = explanation {
- } else {
- explanation.add_explanation_to_diagnostic(
- self.infcx.tcx,
- self.mir,
- &mut err,
- "",
- None,
- );
- }
- } else {
- err.span_label(borrow_span, "borrowed value does not live long enough");
- err.span_label(
- drop_span,
- format!("`{}` dropped here while still borrowed", name),
- );
-
- let within = if borrow_spans.for_generator() {
- " by generator"
- } else {
- ""
- };
-
- borrow_spans.args_span_label(
- &mut err,
- format!("value captured here{}", within),
- );
-
- explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
- }
-
- err
- }
-
- fn report_borrow_conflicts_with_destructor(
- &mut self,
- location: Location,
- borrow: &BorrowData<'tcx>,
- (place, drop_span): (&Place<'tcx>, Span),
- kind: Option<WriteKind>,
- dropped_ty: Ty<'tcx>,
- ) {
- debug!(
- "report_borrow_conflicts_with_destructor(\
- {:?}, {:?}, ({:?}, {:?}), {:?}\
- )",
- location, borrow, place, drop_span, kind,
- );
-
- let borrow_spans = self.retrieve_borrow_spans(borrow);
- let borrow_span = borrow_spans.var_or_use();
-
- let mut err = self.infcx
- .tcx
- .cannot_borrow_across_destructor(borrow_span, Origin::Mir);
-
- let what_was_dropped = match self.describe_place(place) {
- Some(name) => format!("`{}`", name.as_str()),
- None => String::from("temporary value"),
- };
-
- let label = match self.describe_place(&borrow.borrowed_place) {
- Some(borrowed) => format!(
- "here, drop of {D} needs exclusive access to `{B}`, \
- because the type `{T}` implements the `Drop` trait",
- D = what_was_dropped,
- T = dropped_ty,
- B = borrowed
- ),
- None => format!(
- "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
- D = what_was_dropped,
- T = dropped_ty
- ),
- };
- err.span_label(drop_span, label);
-
- // Only give this note and suggestion if they could be relevant.
- let explanation =
- self.explain_why_borrow_contains_point(location, borrow, kind.map(|k| (k, place)));
- match explanation {
- BorrowExplanation::UsedLater { .. }
- | BorrowExplanation::UsedLaterWhenDropped { .. } => {
- err.note("consider using a `let` binding to create a longer lived value");
- }
- _ => {}
- }
-
- explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
-
- err.buffer(&mut self.errors_buffer);
- }
-
- fn report_thread_local_value_does_not_live_long_enough(
- &mut self,
- drop_span: Span,
- borrow_span: Span,
- ) -> DiagnosticBuilder<'cx> {
- debug!(
- "report_thread_local_value_does_not_live_long_enough(\
- {:?}, {:?}\
- )",
- drop_span, borrow_span
- );
-
- let mut err = self.infcx
- .tcx
- .thread_local_value_does_not_live_long_enough(borrow_span, Origin::Mir);
-
- err.span_label(
- borrow_span,
- "thread-local variables cannot be borrowed beyond the end of the function",
- );
- err.span_label(drop_span, "end of enclosing function is here");
-
- err
- }
-
- fn report_temporary_value_does_not_live_long_enough(
- &mut self,
- location: Location,
- scope_tree: &'tcx ScopeTree,
- borrow: &BorrowData<'tcx>,
- drop_span: Span,
- borrow_spans: UseSpans,
- proper_span: Span,
- explanation: BorrowExplanation,
- ) -> DiagnosticBuilder<'cx> {
- debug!(
- "report_temporary_value_does_not_live_long_enough(\
- {:?}, {:?}, {:?}, {:?}, {:?}\
- )",
- location, scope_tree, borrow, drop_span, proper_span
- );
-
- if let BorrowExplanation::MustBeValidFor {
- category,
- span,
- from_closure: false,
- ..
- } = explanation {
- if let Some(diag) = self.try_report_cannot_return_reference_to_local(
- borrow,
- proper_span,
- span,
- category,
- None,
- ) {
- return diag;
- }
- }
-
- let tcx = self.infcx.tcx;
- let mut err = tcx.temporary_value_borrowed_for_too_long(proper_span, Origin::Mir);
- err.span_label(
- proper_span,
- "creates a temporary which is freed while still in use",
- );
- err.span_label(
- drop_span,
- "temporary value is freed at the end of this statement",
- );
-
- match explanation {
- BorrowExplanation::UsedLater(..)
- | BorrowExplanation::UsedLaterInLoop(..)
- | BorrowExplanation::UsedLaterWhenDropped { .. } => {
- // Only give this note and suggestion if it could be relevant.
- err.note("consider using a `let` binding to create a longer lived value");
- }
- _ => {}
- }
- explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
-
- let within = if borrow_spans.for_generator() {
- " by generator"
- } else {
- ""
- };
-
- borrow_spans.args_span_label(
- &mut err,
- format!("value captured here{}", within),
- );
-
- err
- }
-
- fn try_report_cannot_return_reference_to_local(
- &self,
- borrow: &BorrowData<'tcx>,
- borrow_span: Span,
- return_span: Span,
- category: ConstraintCategory,
- opt_place_desc: Option<&String>,
- ) -> Option<DiagnosticBuilder<'cx>> {
- let tcx = self.infcx.tcx;
-
- let return_kind = match category {
- ConstraintCategory::Return => "return",
- ConstraintCategory::Yield => "yield",
- _ => return None,
- };
-
- // FIXME use a better heuristic than Spans
- let reference_desc = if return_span == self.mir.source_info(borrow.reserve_location).span {
- "reference to"
- } else {
- "value referencing"
- };
-
- let (place_desc, note) = if let Some(place_desc) = opt_place_desc {
- let local_kind = match borrow.borrowed_place {
- Place::Base(PlaceBase::Local(local)) => {
- match self.mir.local_kind(local) {
- LocalKind::ReturnPointer
- | LocalKind::Temp => bug!("temporary or return pointer with a name"),
- LocalKind::Var => "local variable ",
- LocalKind::Arg
- if !self.upvars.is_empty()
- && local == Local::new(1) => {
- "variable captured by `move` "
- }
- LocalKind::Arg => {
- "function parameter "
- }
- }
- }
- _ => "local data ",
- };
- (
- format!("{}`{}`", local_kind, place_desc),
- format!("`{}` is borrowed here", place_desc),
- )
- } else {
- let root_place = self.prefixes(&borrow.borrowed_place, PrefixSet::All)
- .last()
- .unwrap();
- let local = if let Place::Base(PlaceBase::Local(local)) = *root_place {
- local
- } else {
- bug!("try_report_cannot_return_reference_to_local: not a local")
- };
- match self.mir.local_kind(local) {
- LocalKind::ReturnPointer | LocalKind::Temp => {
- (
- "temporary value".to_string(),
- "temporary value created here".to_string(),
- )
- }
- LocalKind::Arg => {
- (
- "function parameter".to_string(),
- "function parameter borrowed here".to_string(),
- )
- },
- LocalKind::Var => bug!("local variable without a name"),
- }
- };
-
- let mut err = tcx.cannot_return_reference_to_local(
- return_span,
- return_kind,
- reference_desc,
- &place_desc,
- Origin::Mir,
- );
-
- if return_span != borrow_span {
- err.span_label(borrow_span, note);
- }
-
- Some(err)
- }
-
- fn report_escaping_closure_capture(
- &mut self,
- args_span: Span,
- var_span: Span,
- fr_name: &RegionName,
- category: ConstraintCategory,
- constraint_span: Span,
- captured_var: &str,
- ) -> DiagnosticBuilder<'cx> {
- let tcx = self.infcx.tcx;
-
- let mut err = tcx.cannot_capture_in_long_lived_closure(
- args_span,
- captured_var,
- var_span,
- Origin::Mir,
- );
-
- let suggestion = match tcx.sess.source_map().span_to_snippet(args_span) {
- Ok(string) => format!("move {}", string),
- Err(_) => "move |<args>| <body>".to_string()
- };
-
- err.span_suggestion(
- args_span,
- &format!("to force the closure to take ownership of {} (and any \
- other referenced variables), use the `move` keyword",
- captured_var),
- suggestion,
- Applicability::MachineApplicable,
- );
-
- match category {
- ConstraintCategory::Return => {
- err.span_note(constraint_span, "closure is returned here");
- }
- ConstraintCategory::CallArgument => {
- fr_name.highlight_region_name(&mut err);
- err.span_note(
- constraint_span,
- &format!("function requires argument type to outlive `{}`", fr_name),
- );
- }
- _ => bug!("report_escaping_closure_capture called with unexpected constraint \
- category: `{:?}`", category),
- }
- err
- }
-
- fn report_escaping_data(
- &mut self,
- borrow_span: Span,
- name: &Option<String>,
- upvar_span: Span,
- upvar_name: &str,
- escape_span: Span,
- ) -> DiagnosticBuilder<'cx> {
- let tcx = self.infcx.tcx;
-
- let escapes_from = if tcx.is_closure(self.mir_def_id) {
- let tables = tcx.typeck_tables_of(self.mir_def_id);
- let mir_hir_id = tcx.hir().def_index_to_hir_id(self.mir_def_id.index);
- match tables.node_type(mir_hir_id).sty {
- ty::Closure(..) => "closure",
- ty::Generator(..) => "generator",
- _ => bug!("Closure body doesn't have a closure or generator type"),
- }
- } else {
- "function"
- };
-
- let mut err = tcx.borrowed_data_escapes_closure(escape_span, escapes_from, Origin::Mir);
-
- err.span_label(
- upvar_span,
- format!(
- "`{}` is declared here, outside of the {} body",
- upvar_name, escapes_from
- ),
- );
-
- err.span_label(
- borrow_span,
- format!(
- "borrow is only valid in the {} body",
- escapes_from
- ),
- );
-
- if let Some(name) = name {
- err.span_label(
- escape_span,
- format!("reference to `{}` escapes the {} body here", name, escapes_from),
- );
- } else {
- err.span_label(
- escape_span,
- format!("reference escapes the {} body here", escapes_from),
- );
- }
-
- err
- }
-
- fn get_moved_indexes(&mut self, location: Location, mpi: MovePathIndex) -> Vec<MoveSite> {
- let mir = self.mir;
-
- let mut stack = Vec::new();
- stack.extend(mir.predecessor_locations(location).map(|predecessor| {
- let is_back_edge = location.dominates(predecessor, &self.dominators);
- (predecessor, is_back_edge)
- }));
-
- let mut visited = FxHashSet::default();
- let mut result = vec![];
-
- 'dfs: while let Some((location, is_back_edge)) = stack.pop() {
- debug!(
- "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
- location, is_back_edge
- );
-
- if !visited.insert(location) {
- continue;
- }
-
- // check for moves
- let stmt_kind = mir[location.block]
- .statements
- .get(location.statement_index)
- .map(|s| &s.kind);
- if let Some(StatementKind::StorageDead(..)) = stmt_kind {
- // this analysis only tries to find moves explicitly
- // written by the user, so we ignore the move-outs
- // created by `StorageDead` and at the beginning
- // of a function.
- } else {
- // If we are found a use of a.b.c which was in error, then we want to look for
- // moves not only of a.b.c but also a.b and a.
- //
- // Note that the moves data already includes "parent" paths, so we don't have to
- // worry about the other case: that is, if there is a move of a.b.c, it is already
- // marked as a move of a.b and a as well, so we will generate the correct errors
- // there.
- let mut mpis = vec![mpi];
- let move_paths = &self.move_data.move_paths;
- mpis.extend(move_paths[mpi].parents(move_paths));
-
- for moi in &self.move_data.loc_map[location] {
- debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
- if mpis.contains(&self.move_data.moves[*moi].path) {
- debug!("report_use_of_moved_or_uninitialized: found");
- result.push(MoveSite {
- moi: *moi,
- traversed_back_edge: is_back_edge,
- });
-
- // Strictly speaking, we could continue our DFS here. There may be
- // other moves that can reach the point of error. But it is kind of
- // confusing to highlight them.
- //
- // Example:
- //
- // ```
- // let a = vec![];
- // let b = a;
- // let c = a;
- // drop(a); // <-- current point of error
- // ```
- //
- // Because we stop the DFS here, we only highlight `let c = a`,
- // and not `let b = a`. We will of course also report an error at
- // `let c = a` which highlights `let b = a` as the move.
- continue 'dfs;
- }
- }
- }
-
- // check for inits
- let mut any_match = false;
- drop_flag_effects::for_location_inits(
- self.infcx.tcx,
- self.mir,
- self.move_data,
- location,
- |m| {
- if m == mpi {
- any_match = true;
- }
- },
- );
- if any_match {
- continue 'dfs;
- }
-
- stack.extend(mir.predecessor_locations(location).map(|predecessor| {
- let back_edge = location.dominates(predecessor, &self.dominators);
- (predecessor, is_back_edge || back_edge)
- }));
- }
-
- result
- }
-
- pub(super) fn report_illegal_mutation_of_borrowed(
- &mut self,
- location: Location,
- (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 tcx = self.infcx.tcx;
- if loan.kind == BorrowKind::Shallow {
- let mut err = tcx.cannot_mutate_in_match_guard(
- span,
- loan_span,
- &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
- "assign",
- Origin::Mir,
- );
- loan_spans.var_span_label(
- &mut err,
- format!("borrow occurs due to use{}", loan_spans.describe()),
- );
-
- err.buffer(&mut self.errors_buffer);
-
- return;
- }
-
- let mut err = tcx.cannot_assign_to_borrowed(
- span,
- loan_span,
- &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
- Origin::Mir,
- );
-
- loan_spans.var_span_label(
- &mut err,
- format!("borrow occurs due to use{}", loan_spans.describe()),
- );
-
- self.explain_why_borrow_contains_point(location, loan, None)
- .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "", None);
-
- err.buffer(&mut self.errors_buffer);
- }
-
- /// Reports an illegal reassignment; for example, an assignment to
- /// (part of) a non-`mut` local that occurs potentially after that
- /// local has already been initialized. `place` is the path being
- /// assigned; `err_place` is a place providing a reason why
- /// `place` is not mutable (e.g., the non-`mut` local `x` in an
- /// assignment to `x.f`).
- pub(super) fn report_illegal_reassignment(
- &mut self,
- _location: Location,
- (place, span): (&Place<'tcx>, Span),
- assigned_span: Span,
- err_place: &Place<'tcx>,
- ) {
- let (from_arg, local_decl) = if let Place::Base(PlaceBase::Local(local)) = *err_place {
- if let LocalKind::Arg = self.mir.local_kind(local) {
- (true, Some(&self.mir.local_decls[local]))
- } else {
- (false, Some(&self.mir.local_decls[local]))
- }
- } else {
- (false, None)
- };
-
- // If root local is initialized immediately (everything apart from let
- // 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 {
- is_user_variable: Some(ClearCrossCrate::Clear),
- ..
- })
- | Some(LocalDecl {
- is_user_variable:
- Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
- opt_match_place: None,
- ..
- }))),
- ..
- })
- | Some(LocalDecl {
- is_user_variable: None,
- ..
- })
- | None => (self.describe_place(place), assigned_span),
- Some(decl) => (self.describe_place(err_place), decl.source_info.span),
- };
-
- let mut err = self.infcx.tcx.cannot_reassign_immutable(
- span,
- place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
- from_arg,
- Origin::Mir,
- );
- let msg = if from_arg {
- "cannot assign to immutable argument"
- } else {
- "cannot assign twice to immutable variable"
- };
- 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));
- }
- }
- if let Some(decl) = local_decl {
- if let Some(name) = decl.name {
- if decl.can_be_made_mutable() {
- err.span_suggestion(
- decl.source_info.span,
- "make this binding mutable",
- format!("mut {}", name),
- Applicability::MachineApplicable,
- );
- }
- }
- }
- err.span_label(span, msg);
- err.buffer(&mut self.errors_buffer);
- }
-}
-
-pub(super) struct IncludingDowncast(bool);
-
-/// Which case a StorageDeadOrDrop is for.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum StorageDeadOrDrop<'tcx> {
- LocalStorageDead,
- BoxedStorageDead,
- Destructor(Ty<'tcx>),
-}
+pub(super) struct IncludingDowncast(pub(super) bool);
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
/// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
}
/// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
- /// a name, then `Err` is returned
+ /// a name, or its name was generated by the compiler, then `Err` is returned
fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
let local = &self.mir.local_decls[local_index];
match local.name {
- Some(name) => {
- buf.push_str(&name.to_string());
+ Some(name) if !local.from_compiler_desugaring() => {
+ buf.push_str(name.as_str().get());
Ok(())
}
- None => Err(()),
+ _ => Err(()),
}
}
false
}
}
-
- fn classify_drop_access_kind(&self, place: &Place<'tcx>) -> StorageDeadOrDrop<'tcx> {
- let tcx = self.infcx.tcx;
- match place {
- Place::Base(PlaceBase::Local(_)) |
- Place::Base(PlaceBase::Static(_)) => {
- StorageDeadOrDrop::LocalStorageDead
- }
- Place::Projection(box PlaceProjection { base, elem }) => {
- let base_access = self.classify_drop_access_kind(base);
- match elem {
- ProjectionElem::Deref => match base_access {
- StorageDeadOrDrop::LocalStorageDead
- | StorageDeadOrDrop::BoxedStorageDead => {
- assert!(
- base.ty(self.mir, tcx).ty.is_box(),
- "Drop of value behind a reference or raw pointer"
- );
- StorageDeadOrDrop::BoxedStorageDead
- }
- StorageDeadOrDrop::Destructor(_) => base_access,
- },
- ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
- let base_ty = base.ty(self.mir, tcx).ty;
- match base_ty.sty {
- ty::Adt(def, _) if def.has_dtor(tcx) => {
- // Report the outermost adt with a destructor
- match base_access {
- StorageDeadOrDrop::Destructor(_) => base_access,
- StorageDeadOrDrop::LocalStorageDead
- | StorageDeadOrDrop::BoxedStorageDead => {
- StorageDeadOrDrop::Destructor(base_ty)
- }
- }
- }
- _ => base_access,
- }
- }
-
- ProjectionElem::ConstantIndex { .. }
- | ProjectionElem::Subslice { .. }
- | ProjectionElem::Index(_) => base_access,
- }
- }
- }
- }
-
- /// Annotate argument and return type of function and closure with (synthesized) lifetime for
- /// borrow of local value that does not live long enough.
- fn annotate_argument_and_return_for_borrow(
- &self,
- borrow: &BorrowData<'tcx>,
- ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
- // 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);
- if is_closure {
- None
- } else {
- let ty = self.infcx.tcx.type_of(self.mir_def_id);
- match ty.sty {
- ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
- self.mir_def_id,
- self.infcx.tcx.fn_sig(self.mir_def_id),
- ),
- _ => None,
- }
- }
- };
-
- // In order to determine whether we need to annotate, we need to check whether the reserve
- // place was an assignment into a temporary.
- //
- // If it was, we check whether or not that temporary is eventually assigned into the return
- // place. If it was, we can add annotations about the function's return type and arguments
- // and it'll make sense.
- let location = borrow.reserve_location;
- debug!(
- "annotate_argument_and_return_for_borrow: location={:?}",
- location
- );
- if let Some(&Statement { kind: StatementKind::Assign(ref reservation, _), ..})
- = &self.mir[location.block].statements.get(location.statement_index)
- {
- debug!(
- "annotate_argument_and_return_for_borrow: reservation={:?}",
- reservation
- );
- // Check that the initial assignment of the reserve location is into a temporary.
- let mut target = *match reservation {
- Place::Base(PlaceBase::Local(local))
- if self.mir.local_kind(*local) == LocalKind::Temp => local,
- _ => return None,
- };
-
- // Next, look through the rest of the block, checking if we are assigning the
- // `target` (that is, the place that contains our borrow) to anything.
- let mut annotated_closure = None;
- for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
- debug!(
- "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
- target, stmt
- );
- if let StatementKind::Assign(
- Place::Base(PlaceBase::Local(assigned_to)),
- box rvalue
- ) = &stmt.kind {
- debug!(
- "annotate_argument_and_return_for_borrow: assigned_to={:?} \
- rvalue={:?}",
- assigned_to, rvalue
- );
- // Check if our `target` was captured by a closure.
- if let Rvalue::Aggregate(
- box AggregateKind::Closure(def_id, substs),
- operands,
- ) = rvalue
- {
- for operand in operands {
- let assigned_from = match operand {
- Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
- assigned_from
- }
- _ => continue,
- };
- debug!(
- "annotate_argument_and_return_for_borrow: assigned_from={:?}",
- assigned_from
- );
-
- // Find the local from the operand.
- let assigned_from_local = match assigned_from.local() {
- Some(local) => local,
- None => continue,
- };
-
- if assigned_from_local != target {
- continue;
- }
-
- // 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),
- );
- debug!(
- "annotate_argument_and_return_for_borrow: \
- annotated_closure={:?} assigned_from_local={:?} \
- assigned_to={:?}",
- annotated_closure, assigned_from_local, assigned_to
- );
-
- if *assigned_to == mir::RETURN_PLACE {
- // If it was assigned directly into the return place, then
- // return now.
- return annotated_closure;
- } else {
- // Otherwise, update the target.
- target = *assigned_to;
- }
- }
-
- // If none of our closure's operands matched, then skip to the next
- // statement.
- continue;
- }
-
- // Otherwise, look at other types of assignment.
- let assigned_from = match rvalue {
- Rvalue::Ref(_, _, assigned_from) => assigned_from,
- Rvalue::Use(operand) => match operand {
- Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
- assigned_from
- }
- _ => continue,
- },
- _ => continue,
- };
- debug!(
- "annotate_argument_and_return_for_borrow: \
- assigned_from={:?}",
- assigned_from,
- );
-
- // Find the local from the rvalue.
- let assigned_from_local = match assigned_from.local() {
- Some(local) => local,
- None => continue,
- };
- debug!(
- "annotate_argument_and_return_for_borrow: \
- assigned_from_local={:?}",
- assigned_from_local,
- );
-
- // Check if our local matches the target - if so, we've assigned our
- // borrow to a new place.
- if assigned_from_local != target {
- continue;
- }
-
- // If we assigned our `target` into a new place, then we should
- // check if it was the return place.
- debug!(
- "annotate_argument_and_return_for_borrow: \
- assigned_from_local={:?} assigned_to={:?}",
- assigned_from_local, assigned_to
- );
- if *assigned_to == mir::RETURN_PLACE {
- // If it was then return the annotated closure if there was one,
- // else, annotate this function.
- return annotated_closure.or_else(fallback);
- }
-
- // If we didn't assign into the return place, then we just update
- // the target.
- target = *assigned_to;
- }
- }
-
- // Check the terminator if we didn't find anything in the statements.
- let terminator = &self.mir[location.block].terminator();
- debug!(
- "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
- target, terminator
- );
- if let TerminatorKind::Call {
- destination: Some((Place::Base(PlaceBase::Local(assigned_to)), _)),
- args,
- ..
- } = &terminator.kind
- {
- debug!(
- "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
- assigned_to, args
- );
- for operand in args {
- let assigned_from = match operand {
- Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
- assigned_from
- }
- _ => continue,
- };
- debug!(
- "annotate_argument_and_return_for_borrow: assigned_from={:?}",
- assigned_from,
- );
-
- if let Some(assigned_from_local) = assigned_from.local() {
- debug!(
- "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
- assigned_from_local,
- );
-
- if *assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
- return annotated_closure.or_else(fallback);
- }
- }
- }
- }
- }
-
- // If we haven't found an assignment into the return place, then we need not add
- // any annotations.
- debug!("annotate_argument_and_return_for_borrow: none found");
- None
- }
-
- /// Annotate the first argument and return type of a function signature if they are
- /// references.
- fn annotate_fn_sig(
- &self,
- did: DefId,
- sig: ty::PolyFnSig<'tcx>,
- ) -> Option<AnnotatedBorrowFnSignature<'tcx>> {
- 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_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
- // at the return type, where there are three cases:
- //
- // 1. If there are named arguments, then we should highlight the return type and
- // highlight any of the arguments that are also references with that lifetime.
- // If there are no arguments that have the same lifetime as the return type,
- // then don't highlight anything.
- // 2. The return type is a reference with an anonymous lifetime. If this is
- // the case, then we can take advantage of (and teach) the lifetime elision
- // rules.
- //
- // We know that an error is being reported. So the arguments and return type
- // must satisfy the elision rules. Therefore, if there is a single argument
- // then that means the return type and first (and only) argument have the same
- // lifetime and the borrow isn't meeting that, we can highlight the argument
- // and return type.
- //
- // If there are multiple arguments then the first argument must be self (else
- // it would not satisfy the elision rules), so we can highlight self and the
- // return type.
- // 3. The return type is not a reference. In this case, we don't highlight
- // anything.
- let return_ty = sig.output();
- match return_ty.skip_binder().sty {
- ty::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
- // This is case 1 from above, return type is a named reference so we need to
- // search for relevant arguments.
- let mut arguments = Vec::new();
- for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
- if let ty::Ref(argument_region, _, _) = argument.sty {
- if argument_region == return_region {
- // Need to use the `rustc::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].node {
- // With access to the lifetime, we can get
- // the span of it.
- arguments.push((*argument, lifetime.span));
- } else {
- bug!("ty type is a ref but hir type is not");
- }
- }
- }
- }
-
- // We need to have arguments. This shouldn't happen, but it's worth checking.
- if arguments.is_empty() {
- return None;
- }
-
- // We use a mix of the HIR and the Ty types to get information
- // as the HIR doesn't have full types for closure arguments.
- let return_ty = *sig.output().skip_binder();
- let mut return_span = fn_decl.output.span();
- if let hir::FunctionRetTy::Return(ty) = fn_decl.output {
- if let hir::TyKind::Rptr(lifetime, _) = ty.into_inner().node {
- return_span = lifetime.span;
- }
- }
-
- Some(AnnotatedBorrowFnSignature::NamedFunction {
- arguments,
- return_ty,
- return_span,
- })
- }
- ty::Ref(_, _, _) if is_closure => {
- // This is case 2 from above but only for closures, return type is anonymous
- // reference so we select
- // the first argument.
- let argument_span = fn_decl.inputs.first()?.span;
- let argument_ty = sig.inputs().skip_binder().first()?;
-
- // Closure arguments are wrapped in a tuple, so we need to get the first
- // from that.
- if let ty::Tuple(elems) = argument_ty.sty {
- let argument_ty = elems.first()?.expect_ty();
- if let ty::Ref(_, _, _) = argument_ty.sty {
- return Some(AnnotatedBorrowFnSignature::Closure {
- argument_ty,
- argument_span,
- });
- }
- }
-
- None
- }
- ty::Ref(_, _, _) => {
- // This is also case 2 from above but for functions, return type is still an
- // anonymous reference so we select the first argument.
- let argument_span = fn_decl.inputs.first()?.span;
- let argument_ty = sig.inputs().skip_binder().first()?;
-
- let return_span = fn_decl.output.span();
- let return_ty = *sig.output().skip_binder();
-
- // We expect the first argument to be a reference.
- match argument_ty.sty {
- ty::Ref(_, _, _) => {}
- _ => return None,
- }
-
- Some(AnnotatedBorrowFnSignature::AnonymousFunction {
- argument_ty,
- argument_span,
- return_ty,
- return_span,
- })
- }
- _ => {
- // This is case 3 from above, return type is not a reference so don't highlight
- // anything.
- None
- }
- }
- }
-}
-
-#[derive(Debug)]
-enum AnnotatedBorrowFnSignature<'tcx> {
- NamedFunction {
- arguments: Vec<(Ty<'tcx>, Span)>,
- return_ty: Ty<'tcx>,
- return_span: Span,
- },
- AnonymousFunction {
- argument_ty: Ty<'tcx>,
- argument_span: Span,
- return_ty: Ty<'tcx>,
- return_span: Span,
- },
- Closure {
- argument_ty: Ty<'tcx>,
- argument_span: Span,
- },
-}
-
-impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
- /// Annotate the provided diagnostic with information about borrow from the fn signature that
- /// helps explain.
- fn emit(
- &self,
- cx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
- diag: &mut DiagnosticBuilder<'_>,
- ) -> String {
- match self {
- AnnotatedBorrowFnSignature::Closure {
- argument_ty,
- argument_span,
- } => {
- diag.span_label(
- *argument_span,
- format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
- );
-
- cx.get_region_name_for_ty(argument_ty, 0)
- }
- AnnotatedBorrowFnSignature::AnonymousFunction {
- argument_ty,
- argument_span,
- return_ty,
- return_span,
- } => {
- let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
- diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name));
-
- let return_ty_name = cx.get_name_for_ty(return_ty, 0);
- let types_equal = return_ty_name == argument_ty_name;
- diag.span_label(
- *return_span,
- format!(
- "{}has type `{}`",
- if types_equal { "also " } else { "" },
- return_ty_name,
- ),
- );
-
- diag.note(
- "argument and return type have the same lifetime due to lifetime elision rules",
- );
- diag.note(
- "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
- lifetime-syntax.html#lifetime-elision>",
- );
-
- cx.get_region_name_for_ty(return_ty, 0)
- }
- AnnotatedBorrowFnSignature::NamedFunction {
- arguments,
- return_ty,
- return_span,
- } => {
- // Region of return type and arguments checked to be the same earlier.
- let region_name = cx.get_region_name_for_ty(return_ty, 0);
- for (_, argument_span) in arguments {
- diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
- }
-
- diag.span_label(
- *return_span,
- format!("also has lifetime `{}`", region_name,),
- );
-
- diag.help(&format!(
- "use data from the highlighted arguments which match the `{}` lifetime of \
- the return type",
- region_name,
- ));
-
- region_name
- }
- }
- }
}
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
/// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
/// name where required.
- fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
+ pub(super) fn get_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
let mut s = String::new();
let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
/// Returns the name of the provided `Ty` (that must be a reference)'s region with a
/// synthesized lifetime name where required.
- fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
+ pub(super) fn get_region_name_for_ty(&self, ty: Ty<'tcx>, counter: usize) -> String {
let mut s = String::new();
let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
}
/// Returns `false` if this place is not used in a closure.
- fn for_closure(&self) -> bool {
+ pub(super) fn for_closure(&self) -> bool {
match *self {
UseSpans::ClosureUse { is_generator, .. } => !is_generator,
_ => false,
}
/// Returns `false` if this place is not used in a generator.
- fn for_generator(&self) -> bool {
+ pub(super) fn for_generator(&self) -> bool {
match *self {
UseSpans::ClosureUse { is_generator, .. } => is_generator,
_ => false,
}
/// Describe the span associated with a use of a place.
- fn describe(&self) -> String {
+ pub(super) fn describe(&self) -> String {
match *self {
UseSpans::ClosureUse { is_generator, .. } => if is_generator {
" in generator".to_string()
mod error_reporting;
mod flows;
mod location;
+mod conflict_errors;
mod move_errors;
mod mutability_errors;
mod path_utils;
);
}
- if let Some(name) = local_decl.name {
- err.span_label(
- span,
- format!(
- "`{NAME}` is a `{SIGIL}` {DESC}, \
- so the data it refers to cannot be {ACTED_ON}",
- NAME = name,
- SIGIL = pointer_sigil,
- DESC = pointer_desc,
- ACTED_ON = acted_on
- ),
- );
- } else {
- err.span_label(
- span,
- format!(
- "cannot {ACT} through `{SIGIL}` {DESC}",
- ACT = act,
- SIGIL = pointer_sigil,
- DESC = pointer_desc
- ),
- );
+ match local_decl.name {
+ Some(name) if !local_decl.from_compiler_desugaring() => {
+ err.span_label(
+ span,
+ format!(
+ "`{NAME}` is a `{SIGIL}` {DESC}, \
+ so the data it refers to cannot be {ACTED_ON}",
+ NAME = name,
+ SIGIL = pointer_sigil,
+ DESC = pointer_desc,
+ ACTED_ON = acted_on
+ ),
+ );
+ }
+ _ => {
+ err.span_label(
+ span,
+ format!(
+ "cannot {ACT} through `{SIGIL}` {DESC}",
+ ACT = act,
+ SIGIL = pointer_sigil,
+ DESC = pointer_desc
+ ),
+ );
+ }
}
}
};
match local_decl.name {
- Some(local_name) => {
+ Some(local_name) if !local_decl.from_compiler_desugaring() => {
let message = format!(
"{B}borrow might be used here, when `{LOC}` is dropped \
and runs the {DTOR} for {TYPE}",
);
}
}
- None => {
+ _ => {
err.span_label(
local_decl.source_info.span,
format!(
ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => {
span_err!(self.session, expr.span, E0472, "asm! is unsupported on this target");
}
- ExprKind::ObsoleteInPlace(ref place, ref val) => {
- let mut err = self.err_handler().struct_span_err(
- expr.span,
- "emplacement syntax is obsolete (for now, anyway)",
- );
- err.note(
- "for more information, see \
- <https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>"
- );
- match val.node {
- ExprKind::Lit(ref v) if v.node.is_numeric() => {
- err.span_suggestion(
- place.span.between(val.span),
- "if you meant to write a comparison against a negative value, add a \
- space in between `<` and `-`",
- "< -".to_string(),
- Applicability::MaybeIncorrect
- );
- }
- _ => {}
- }
- err.emit();
- }
_ => {}
}
use rustc::ty::subst::InternalSubsts;
use rustc::util::nodemap::HirIdSet;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::sync::Lrc;
use syntax::ast::Ident;
use syntax::attr;
use syntax::symbol::{kw, sym};
fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> bool {
self.skeleton().visit_trait(trait_ref)
}
- fn visit_predicates(&mut self, predicates: Lrc<ty::GenericPredicates<'tcx>>) -> bool {
+ fn visit_predicates(&mut self, predicates: &ty::GenericPredicates<'tcx>) -> bool {
self.skeleton().visit_predicates(predicates)
}
}
(!self.def_id_visitor.shallow() && substs.visit_with(self))
}
- fn visit_predicates(&mut self, predicates: Lrc<ty::GenericPredicates<'tcx>>) -> bool {
- let ty::GenericPredicates { parent: _, predicates } = &*predicates;
+ 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) => {
fn privacy_access_levels<'tcx>(
tcx: TyCtxt<'_, 'tcx, 'tcx>,
krate: CrateNum,
-) -> Lrc<AccessLevels> {
+) -> &'tcx AccessLevels {
assert_eq!(krate, LOCAL_CRATE);
// Build up a set of all exported items in the AST. This is a set of all
}
visitor.update(hir::CRATE_HIR_ID, Some(AccessLevel::Public));
- Lrc::new(visitor.access_levels)
+ tcx.arena.alloc(visitor.access_levels)
}
fn check_private_in_public<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, krate: CrateNum) {
use rustc::ty::{self, DefIdTree, TyCtxt};
use rustc::{bug, span_bug};
use rustc_codegen_utils::link::{filename_for_metadata, out_filename};
-use rustc_data_structures::sync::Lrc;
use std::cell::Cell;
use std::default::Default;
let mut result = Vec::with_capacity(self.tcx.crates().len());
for &n in self.tcx.crates().iter() {
- let span = match *self.tcx.extern_crate(n.as_def_id()) {
- Some(ExternCrate { span, .. }) => span,
+ let span = match self.tcx.extern_crate(n.as_def_id()) {
+ Some(&ExternCrate { span, .. }) => span,
None => {
debug!("Skipping crate {}, no data", n);
continue;
// fallback in case the access levels couldn't have been correctly computed.
let access_levels = match tcx.sess.compile_status() {
Ok(..) => tcx.privacy_access_levels(LOCAL_CRATE),
- Err(..) => Lrc::new(AccessLevels::default()),
+ Err(..) => tcx.arena.alloc(AccessLevels::default()),
};
let save_ctxt = SaveContext {
use rustc::ty::subst::{Kind, Subst, InternalSubsts, SubstsRef};
use rustc::ty::wf::object_region_bounds;
use rustc::mir::interpret::ConstValue;
-use rustc_data_structures::sync::Lrc;
use rustc_target::spec::abi;
use crate::require_c_abi_if_c_variadic;
use smallvec::SmallVec;
/// Returns the set of bounds in scope for the type parameter with
/// the given id.
fn get_type_parameter_bounds(&self, span: Span, def_id: DefId)
- -> Lrc<ty::GenericPredicates<'tcx>>;
+ -> &'tcx ty::GenericPredicates<'tcx>;
/// What lifetime should we use when a lifetime is omitted (and not elided)?
fn re_infer(&self, span: Span, _def: Option<&ty::GenericParamDef>)
/// In addition of this check, it also checks between references mutability state. If the
/// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
/// `&mut`!".
- pub fn check_ref(&self,
- expr: &hir::Expr,
- checked_ty: Ty<'tcx>,
- expected: Ty<'tcx>)
- -> Option<(Span, &'static str, String)> {
+ pub fn check_ref(
+ &self,
+ expr: &hir::Expr,
+ checked_ty: Ty<'tcx>,
+ expected: Ty<'tcx>,
+ ) -> Option<(Span, &'static str, String)> {
let cm = self.sess().source_map();
let sp = expr.span;
if !cm.span_to_filename(sp).is_real() {
} else {
String::new()
};
+ if let Some(hir::Node::Expr(hir::Expr {
+ node: hir::ExprKind::Assign(left_expr, _),
+ ..
+ })) = self.tcx.hir().find_by_hir_id(
+ self.tcx.hir().get_parent_node_by_hir_id(expr.hir_id),
+ ) {
+ if mutability == hir::Mutability::MutMutable {
+ // Found the following case:
+ // fn foo(opt: &mut Option<String>){ opt = None }
+ // --- ^^^^
+ // | |
+ // consider dereferencing here: `*opt` |
+ // expected mutable reference, found enum `Option`
+ if let Ok(src) = cm.span_to_snippet(left_expr.span) {
+ return Some((
+ left_expr.span,
+ "consider dereferencing here to assign to the mutable \
+ borrowed piece of memory",
+ format!("*{}", src),
+ ));
+ }
+ }
+ }
return Some(match mutability {
hir::Mutability::MutMutable => (
sp,
use crate::namespace::Namespace;
use crate::util::nodemap::FxHashSet;
use errors::{Applicability, DiagnosticBuilder};
-use rustc_data_structures::sync::Lrc;
use rustc::hir::{self, ExprKind, Node, QPath};
use rustc::hir::def::{Res, DefKind};
use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, DefId};
pub fn provide(providers: &mut ty::query::Providers<'_>) {
providers.all_traits = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- Lrc::new(compute_all_traits(tcx))
+ &tcx.arena.alloc(compute_all_traits(tcx))[..]
}
}
use rustc::infer::{self, InferCtxt, InferOk, InferResult};
use rustc::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_data_structures::indexed_vec::Idx;
-use rustc_data_structures::sync::Lrc;
use rustc_target::spec::abi::Abi;
use rustc::infer::opaque_types::OpaqueTypeDecl;
use rustc::infer::type_variable::{TypeVariableOrigin};
fn used_trait_imports<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
- -> Lrc<DefIdSet> {
- tcx.typeck_tables_of(def_id).used_trait_imports.clone()
+ -> &'tcx DefIdSet {
+ &*tcx.typeck_tables_of(def_id).used_trait_imports
}
fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.tcx }
fn get_type_parameter_bounds(&self, _: Span, def_id: DefId)
- -> Lrc<ty::GenericPredicates<'tcx>>
+ -> &'tcx ty::GenericPredicates<'tcx>
{
let tcx = self.tcx;
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
let item_def_id = tcx.hir().local_def_id_from_hir_id(item_id);
let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&def_id];
- Lrc::new(ty::GenericPredicates {
+ tcx.arena.alloc(ty::GenericPredicates {
parent: None,
predicates: self.param_env.caller_bounds.iter().filter_map(|&predicate| {
match predicate {
use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc::ty::{self, CrateInherentImpls, TyCtxt};
-use rustc_data_structures::sync::Lrc;
use syntax::ast;
use syntax_pos::Span;
/// On-demand query: yields a map containing all types mapped to their inherent impls.
pub fn crate_inherent_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
crate_num: CrateNum)
- -> Lrc<CrateInherentImpls> {
+ -> &'tcx CrateInherentImpls {
assert_eq!(crate_num, LOCAL_CRATE);
let krate = tcx.hir().krate();
impls_map: Default::default(),
};
krate.visit_all_item_likes(&mut collect);
- Lrc::new(collect.impls_map)
+ tcx.arena.alloc(collect.impls_map)
}
/// On-demand query: yields a vector of the inherent impls for a specific type.
pub fn inherent_impls<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty_def_id: DefId)
- -> Lrc<Vec<DefId>> {
+ -> &'tcx [DefId] {
assert!(ty_def_id.is_local());
// NB. Until we adopt the red-green dep-tracking algorithm (see
//
// [the plan]: https://github.com/rust-lang/rust-roadmap/issues/4
- thread_local! {
- static EMPTY_DEF_ID_VEC: Lrc<Vec<DefId>> = Lrc::new(vec![])
- }
-
let result = tcx.dep_graph.with_ignore(|| {
let crate_map = tcx.crate_inherent_impls(ty_def_id.krate);
match crate_map.inherent_impls.get(&ty_def_id) {
- Some(v) => v.clone(),
- None => EMPTY_DEF_ID_VEC.with(|v| v.clone())
+ Some(v) => &v[..],
+ None => &[],
}
});
// type def ID, if there is a base type for this implementation and
// the implementation does not have any associated traits.
let impl_def_id = self.tcx.hir().local_def_id_from_hir_id(item.hir_id);
- let mut rc_vec = self.impls_map.inherent_impls
- .entry(def_id)
- .or_default();
-
- // At this point, there should not be any clones of the
- // `Lrc`, so we can still safely push into it in place:
- Lrc::get_mut(&mut rc_vec).unwrap().push(impl_def_id);
+ let vec = self.impls_map.inherent_impls.entry(def_id).or_default();
+ vec.push(impl_def_id);
} else {
struct_span_err!(self.tcx.sess,
item.span,
use rustc::ty::{ReprOptions, ToPredicate};
use rustc::util::captures::Captures;
use rustc::util::nodemap::FxHashMap;
-use rustc_data_structures::sync::Lrc;
use rustc_target::spec::abi;
use syntax::ast;
}
fn get_type_parameter_bounds(&self, span: Span, def_id: DefId)
- -> Lrc<ty::GenericPredicates<'tcx>> {
+ -> &'tcx ty::GenericPredicates<'tcx> {
self.tcx
.at(span)
.type_param_predicates((self.item_def_id, def_id))
fn type_param_predicates<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
(item_def_id, def_id): (DefId, DefId),
-) -> Lrc<ty::GenericPredicates<'tcx>> {
+) -> &'tcx ty::GenericPredicates<'tcx> {
use rustc::hir::*;
// In the AST, bounds can derive from two places. Either
tcx.generics_of(item_def_id).parent
};
- let mut result = parent.map_or_else(
- || Lrc::new(ty::GenericPredicates {
- parent: None,
- predicates: vec![],
- }),
- |parent| {
- let icx = ItemCtxt::new(tcx, parent);
- icx.get_type_parameter_bounds(DUMMY_SP, def_id)
- },
- );
+ let result = parent.map_or(&tcx.common.empty_predicates, |parent| {
+ let icx = ItemCtxt::new(tcx, parent);
+ icx.get_type_parameter_bounds(DUMMY_SP, def_id)
+ });
+ let mut extend = None;
let item_hir_id = tcx.hir().as_local_hir_id(item_def_id).unwrap();
let ast_generics = match tcx.hir().get_by_hir_id(item_hir_id) {
// Implied `Self: Trait` and supertrait bounds.
if param_id == item_hir_id {
let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id);
- Lrc::make_mut(&mut result)
- .predicates
- .push((identity_trait_ref.to_predicate(), item.span));
+ extend = Some((identity_trait_ref.to_predicate(), item.span));
}
generics
}
};
let icx = ItemCtxt::new(tcx, item_def_id);
- Lrc::make_mut(&mut result)
- .predicates
- .extend(icx.type_parameter_bounds_in_generics(ast_generics, param_id, ty,
- OnlySelfBounds(true)));
- result
+ let mut result = (*result).clone();
+ result.predicates.extend(extend.into_iter());
+ result.predicates
+ .extend(icx.type_parameter_bounds_in_generics(ast_generics, param_id, ty,
+ OnlySelfBounds(true)));
+ tcx.arena.alloc(result)
}
impl<'a, 'tcx> ItemCtxt<'a, 'tcx> {
fn super_predicates_of<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
trait_def_id: DefId,
-) -> Lrc<ty::GenericPredicates<'tcx>> {
+) -> &'tcx ty::GenericPredicates<'tcx> {
debug!("super_predicates(trait_def_id={:?})", trait_def_id);
let trait_hir_id = tcx.hir().as_local_hir_id(trait_def_id).unwrap();
}
}
- Lrc::new(ty::GenericPredicates {
+ tcx.arena.alloc(ty::GenericPredicates {
parent: None,
predicates: superbounds,
})
fn predicates_defined_on<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
-) -> Lrc<ty::GenericPredicates<'tcx>> {
+) -> &'tcx ty::GenericPredicates<'tcx> {
debug!("predicates_defined_on({:?})", def_id);
let mut result = tcx.explicit_predicates_of(def_id);
debug!(
def_id,
inferred_outlives,
);
- Lrc::make_mut(&mut result)
- .predicates
- .extend(inferred_outlives.iter().map(|&p| (p, span)));
+ let mut predicates = (*result).clone();
+ predicates.predicates.extend(inferred_outlives.iter().map(|&p| (p, span)));
+ result = tcx.arena.alloc(predicates);
}
debug!("predicates_defined_on({:?}) = {:?}", def_id, result);
result
fn predicates_of<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
-) -> Lrc<ty::GenericPredicates<'tcx>> {
+) -> &'tcx ty::GenericPredicates<'tcx> {
let mut result = tcx.predicates_defined_on(def_id);
if tcx.is_trait(def_id) {
// used, and adding the predicate into this list ensures
// that this is done.
let span = tcx.def_span(def_id);
- Lrc::make_mut(&mut result)
- .predicates
- .push((ty::TraitRef::identity(tcx, def_id).to_predicate(), span));
+ let mut predicates = (*result).clone();
+ predicates.predicates.push((ty::TraitRef::identity(tcx, def_id).to_predicate(), span));
+ result = tcx.arena.alloc(predicates);
}
debug!("predicates_of(def_id={:?}) = {:?}", def_id, result);
result
fn explicit_predicates_of<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
-) -> Lrc<ty::GenericPredicates<'tcx>> {
+) -> &'tcx ty::GenericPredicates<'tcx> {
use rustc::hir::*;
use rustc_data_structures::fx::FxHashSet;
if impl_trait_fn.is_some() {
// impl Trait
- return Lrc::new(ty::GenericPredicates {
+ return tcx.arena.alloc(ty::GenericPredicates {
parent: None,
predicates: bounds.predicates(tcx, opaque_ty),
});
);
}
- let result = Lrc::new(ty::GenericPredicates {
+ let result = tcx.arena.alloc(ty::GenericPredicates {
parent: generics.parent,
predicates,
});
use rustc::ty::query::Providers;
use rustc::ty::subst::UnpackedKind;
use rustc::ty::{self, CratePredicatesMap, TyCtxt};
-use rustc_data_structures::sync::Lrc;
use syntax::symbol::sym;
mod explicit;
fn inferred_outlives_crate<'tcx>(
tcx: TyCtxt<'_, 'tcx, 'tcx>,
crate_num: CrateNum,
-) -> Lrc<CratePredicatesMap<'tcx>> {
+) -> &'tcx CratePredicatesMap<'tcx> {
assert_eq!(crate_num, LOCAL_CRATE);
// Compute a map from each struct/enum/union S to the **explicit**
(def_id, &*predicates)
}).collect();
- Lrc::new(ty::CratePredicatesMap {
+ tcx.arena.alloc(ty::CratePredicatesMap {
predicates,
})
}
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc::ty::{self, CrateVariancesMap, TyCtxt};
use rustc::ty::query::Providers;
-use rustc_data_structures::sync::Lrc;
/// Defines the `TermsContext` basically houses an arena where we can
/// allocate terms.
}
fn crate_variances<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum)
- -> Lrc<CrateVariancesMap<'tcx>> {
+ -> &'tcx CrateVariancesMap<'tcx> {
assert_eq!(crate_num, LOCAL_CRATE);
let mut arena = arena::TypedArena::default();
let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena);
let constraints_cx = constraints::add_constraints_from_crate(terms_cx);
- Lrc::new(solve::solve_constraints(constraints_cx))
+ tcx.arena.alloc(solve::solve_constraints(constraints_cx))
}
fn variances_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_def_id: DefId)
// Instead, we generate `impl !Send for Foo<T>`, which better
// expresses the fact that `Foo<T>` never implements `Send`,
// regardless of the choice of `T`.
- let params = (self.cx.tcx.generics_of(param_env_def_id), &Default::default())
- .clean(self.cx).params;
+ let params = (
+ self.cx.tcx.generics_of(param_env_def_id),
+ &&self.cx.tcx.common.empty_predicates,
+ ).clean(self.cx).params;
Generics {
params,
mod blanket_impl;
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
-use rustc_data_structures::sync::Lrc;
use rustc_target::spec::abi::Abi;
use rustc_typeck::hir_ty_to_ty;
use rustc::infer::region_constraints::{RegionConstraintData, Constraint};
}
impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics,
- &'a Lrc<ty::GenericPredicates<'tcx>>) {
+ &'a &'tcx ty::GenericPredicates<'tcx>) {
fn clean(&self, cx: &DocContext<'_>) -> Generics {
use self::WherePredicate as WP;
loop {
let segment = path_it.next()?;
- for item in mem::replace(&mut items, Lrc::new(vec![])).iter() {
+ for item in mem::replace(&mut items, &[]).iter() {
if item.ident.name == *segment {
if path_it.peek().is_none() {
return match item.res {
pub fn precedence(&self) -> ExprPrecedence {
match self.node {
ExprKind::Box(_) => ExprPrecedence::Box,
- ExprKind::ObsoleteInPlace(..) => ExprPrecedence::ObsoleteInPlace,
ExprKind::Array(_) => ExprPrecedence::Array,
ExprKind::Call(..) => ExprPrecedence::Call,
ExprKind::MethodCall(..) => ExprPrecedence::MethodCall,
pub enum ExprKind {
/// A `box x` expression.
Box(P<Expr>),
- /// First expr is the place; second expr is the value.
- ObsoleteInPlace(P<Expr>, P<Expr>),
/// An array (`[a, b, c, d]`)
Array(Vec<P<Expr>>),
/// A function call
pub fn parse<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, T>
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
{
- let mut parser = Parser::new(sess, self.tokens.clone(), None, false, false);
+ let mut parser = Parser::new(
+ sess,
+ self.tokens.clone(),
+ None,
+ false,
+ false,
+ Some("attribute"),
+ );
let result = f(&mut parser)?;
if parser.token != token::Eof {
parser.unexpected()?;
use crate::parse::token;
use crate::ptr::P;
use crate::symbol::{kw, sym, Ident, Symbol};
-use crate::ThinVec;
+use crate::{ThinVec, MACRO_ARGUMENTS};
use crate::tokenstream::{self, TokenStream};
use errors::{DiagnosticBuilder, DiagnosticId};
}
pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree]) -> parser::Parser<'a> {
- parse::stream_to_parser(self.parse_sess, tts.iter().cloned().collect())
+ parse::stream_to_parser(self.parse_sess, tts.iter().cloned().collect(), MACRO_ARGUMENTS)
}
pub fn source_map(&self) -> &'a SourceMap { self.parse_sess.source_map() }
pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess }
recurse_into_modules: bool,
) -> NamedParseResult {
// Create a parser that can be used for the "black box" parts.
- let mut parser = Parser::new(sess, tts, directory, recurse_into_modules, true);
+ let mut parser = Parser::new(
+ sess,
+ tts,
+ directory,
+ recurse_into_modules,
+ true,
+ crate::MACRO_ARGUMENTS,
+ );
// A queue of possible matcher positions. We initialize it with the matcher position in which
// the "dot" is before the first token of the first token tree in `ms`. `inner_parse_loop` then
path: Cow::from(cx.current_expansion.module.directory.as_path()),
ownership: cx.current_expansion.directory_ownership,
};
- let mut p = Parser::new(cx.parse_sess(), tts, Some(directory), true, false);
+ 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.as_str().to_string());
"type ascription is experimental");
}
}
- ast::ExprKind::ObsoleteInPlace(..) => {
- // these get a hard error in ast-validation
- }
ast::ExprKind::Yield(..) => {
gate_feature_post!(&self, generators,
e.span,
use ast::AttrId;
use syntax_pos::edition::Edition;
+const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments");
+
// 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
pub fn noop_visit_expr<T: MutVisitor>(Expr { node, id, span, attrs }: &mut Expr, vis: &mut T) {
match node {
ExprKind::Box(expr) => vis.visit_expr(expr),
- ExprKind::ObsoleteInPlace(a, b) => {
- vis.visit_expr(a);
- vis.visit_expr(b);
- }
ExprKind::Array(exprs) => visit_exprs(exprs, vis),
ExprKind::Repeat(expr, count) => {
vis.visit_expr(expr);
use crate::ast;
-use crate::ast::{BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind};
-use crate::parse::parser::{BlockMode, PathStyle, TokenType, SemiColonMode};
+use crate::ast::{
+ BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind, VariantData,
+};
+use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType};
use crate::parse::token;
use crate::parse::PResult;
use crate::parse::Parser;
use crate::print::pprust;
use crate::ptr::P;
+use crate::source_map::Spanned;
use crate::symbol::kw;
use crate::ThinVec;
use errors::{Applicability, DiagnosticBuilder};
-use syntax_pos::Span;
use log::debug;
+use syntax_pos::{Span, DUMMY_SP};
pub trait RecoverQPath: Sized + 'static {
const PATH_STYLE: PathStyle = PathStyle::Expr;
}
}
+ crate fn maybe_report_invalid_custom_discriminants(
+ &mut self,
+ discriminant_spans: Vec<Span>,
+ variants: &[Spanned<ast::Variant_>],
+ ) {
+ let has_fields = variants.iter().any(|variant| match variant.node.data {
+ VariantData::Tuple(..) | VariantData::Struct(..) => true,
+ VariantData::Unit(..) => false,
+ });
+
+ if !discriminant_spans.is_empty() && has_fields {
+ let mut err = self.struct_span_err(
+ discriminant_spans.clone(),
+ "custom discriminant values are not allowed in enums with fields",
+ );
+ for sp in discriminant_spans {
+ err.span_label(sp, "invalid custom discriminant");
+ }
+ for variant in variants.iter() {
+ if let VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) =
+ &variant.node.data
+ {
+ let fields = if fields.len() > 1 {
+ "fields"
+ } else {
+ "a field"
+ };
+ err.span_label(
+ variant.span,
+ &format!("variant with {fields} defined here", fields = fields),
+ );
+
+ }
+ }
+ err.emit();
+ }
+ }
+
crate fn maybe_recover_from_bad_type_plus(
&mut self,
allow_plus: bool,
let mut path = ast::Path {
segments: Vec::new(),
- span: syntax_pos::DUMMY_SP,
+ span: DUMMY_SP,
};
self.parse_path_segments(&mut path.segments, T::PATH_STYLE)?;
path.span = ty_span.to(self.prev_span);
}
}
+ /// Create a `DiagnosticBuilder` for an unexpected token `t` and try to recover if it is a
+ /// closing delimiter.
+ pub fn unexpected_try_recover(
+ &mut self,
+ t: &token::Token,
+ ) -> PResult<'a, bool /* recovered */> {
+ let token_str = pprust::token_to_string(t);
+ let this_token_str = self.this_token_descr();
+ let (prev_sp, sp) = match (&self.token, self.subparser_name) {
+ // Point at the end of the macro call when reaching end of macro arguments.
+ (token::Token::Eof, Some(_)) => {
+ let sp = self.sess.source_map().next_point(self.span);
+ (sp, sp)
+ }
+ // We don't want to point at the following span after DUMMY_SP.
+ // This happens when the parser finds an empty TokenStream.
+ _ if self.prev_span == DUMMY_SP => (self.span, self.span),
+ // EOF, don't want to point at the following char, but rather the last token.
+ (token::Token::Eof, None) => (self.prev_span, self.span),
+ _ => (self.sess.source_map().next_point(self.prev_span), self.span),
+ };
+ let msg = format!(
+ "expected `{}`, found {}",
+ token_str,
+ match (&self.token, self.subparser_name) {
+ (token::Token::Eof, Some(origin)) => format!("end of {}", origin),
+ _ => this_token_str,
+ },
+ );
+ let mut err = self.struct_span_err(sp, &msg);
+ let label_exp = format!("expected `{}`", token_str);
+ match self.recover_closing_delimiter(&[t.clone()], err) {
+ Err(e) => err = e,
+ Ok(recovered) => {
+ return Ok(recovered);
+ }
+ }
+ let cm = self.sess.source_map();
+ match (cm.lookup_line(prev_sp.lo()), cm.lookup_line(sp.lo())) {
+ (Ok(ref a), Ok(ref b)) if a.line == b.line => {
+ // When the spans are in the same line, it means that the only content
+ // between them is whitespace, point only at the found token.
+ err.span_label(sp, label_exp);
+ }
+ _ => {
+ err.span_label(prev_sp, label_exp);
+ err.span_label(sp, "unexpected token");
+ }
+ }
+ Err(err)
+ }
+
/// Consume alternative await syntaxes like `await <expr>`, `await? <expr>`, `await(<expr>)`
/// and `await { <expr> }`.
crate fn parse_incorrect_await_syntax(
}
}
+ crate fn expected_expression_found(&self) -> DiagnosticBuilder<'a> {
+ let (span, msg) = match (&self.token, self.subparser_name) {
+ (&token::Token::Eof, Some(origin)) => {
+ let sp = self.sess.source_map().next_point(self.span);
+ (sp, format!("expected expression, found end of {}", origin))
+ }
+ _ => (self.span, format!(
+ "expected expression, found {}",
+ self.this_token_descr(),
+ )),
+ };
+ let mut err = self.struct_span_err(span, &msg);
+ let sp = self.sess.source_map().start_point(self.span);
+ if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) {
+ self.sess.expr_parentheses_needed(&mut err, *sp, None);
+ }
+ err.span_label(span, "expected expression");
+ err
+ }
}
) -> Result<Parser<'_>, Vec<Diagnostic>> {
let end_pos = source_file.end_pos;
let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?;
- let mut parser = stream_to_parser(sess, stream);
+ let mut parser = stream_to_parser(sess, stream, None);
parser.unclosed_delims = unclosed_delims;
if parser.token == token::Eof && parser.span.is_dummy() {
parser.span = Span::new(end_pos, end_pos, parser.span.ctxt());
// must preserve old name for now, because quote! from the *existing*
// compiler expands into it
pub fn new_parser_from_tts(sess: &ParseSess, tts: Vec<TokenTree>) -> Parser<'_> {
- stream_to_parser(sess, tts.into_iter().collect())
+ stream_to_parser(sess, tts.into_iter().collect(), crate::MACRO_ARGUMENTS)
}
}
/// Given stream and the `ParseSess`, produces a parser.
-pub fn stream_to_parser(sess: &ParseSess, stream: TokenStream) -> Parser<'_> {
- Parser::new(sess, stream, None, true, false)
+pub fn stream_to_parser<'a>(
+ sess: &'a ParseSess,
+ stream: TokenStream,
+ subparser_name: Option<&'static str>,
+) -> Parser<'a> {
+ Parser::new(sess, stream, None, true, false, subparser_name)
}
/// Given stream, the `ParseSess` and the base directory, produces a parser.
/// The main usage of this function is outside of rustc, for those who uses
/// libsyntax 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<'a>(sess: &'a ParseSess,
- stream: TokenStream,
- base_dir: Directory<'a>) -> Parser<'a> {
- Parser::new(sess, stream, Some(base_dir), true, false)
+pub fn stream_to_parser_with_base_dir<'a>(
+ sess: &'a ParseSess,
+ stream: TokenStream,
+ base_dir: Directory<'a>,
+) -> Parser<'a> {
+ Parser::new(sess, stream, Some(base_dir), true, false, None)
}
/// A sequence separator.
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError};
use rustc_target::spec::abi::{self, Abi};
use syntax_pos::{
- Span, MultiSpan, BytePos, FileName,
+ BytePos, DUMMY_SP, FileName, MultiSpan, Span,
hygiene::CompilerDesugaringKind,
};
use log::{debug, trace};
/// error.
crate unclosed_delims: Vec<UnmatchedBrace>,
last_unexpected_token_span: Option<Span>,
+ /// If present, this `Parser` is not parsing Rust code but rather a macro call.
+ crate subparser_name: Option<&'static str>,
}
impl<'a> Drop for Parser<'a> {
self.frame = frame;
continue
} else {
- return TokenAndSpan { tok: token::Eof, sp: syntax_pos::DUMMY_SP }
+ return TokenAndSpan { tok: token::Eof, sp: DUMMY_SP }
};
match self.frame.last_token {
}
impl<'a> Parser<'a> {
- pub fn new(sess: &'a ParseSess,
- tokens: TokenStream,
- directory: Option<Directory<'a>>,
- recurse_into_file_modules: bool,
- desugar_doc_comments: bool)
- -> Self {
+ pub fn new(
+ sess: &'a ParseSess,
+ tokens: TokenStream,
+ directory: Option<Directory<'a>>,
+ recurse_into_file_modules: bool,
+ desugar_doc_comments: bool,
+ subparser_name: Option<&'static str>,
+ ) -> Self {
let mut parser = Parser {
sess,
token: token::Whitespace,
- span: syntax_pos::DUMMY_SP,
- prev_span: syntax_pos::DUMMY_SP,
+ span: DUMMY_SP,
+ prev_span: DUMMY_SP,
meta_var_span: None,
prev_token_kind: PrevTokenKind::Other,
restrictions: Restrictions::empty(),
max_angle_bracket_count: 0,
unclosed_delims: Vec::new(),
last_unexpected_token_span: None,
+ subparser_name,
};
let tok = parser.next_tok();
}
/// Expects and consumes the token `t`. Signals an error if the next token is not `t`.
- pub fn expect(&mut self, t: &token::Token) -> PResult<'a, bool /* recovered */> {
+ pub fn expect(&mut self, t: &token::Token) -> PResult<'a, bool /* recovered */> {
if self.expected_tokens.is_empty() {
if self.token == *t {
self.bump();
Ok(false)
} else {
- let token_str = pprust::token_to_string(t);
- let this_token_str = self.this_token_descr();
- let mut err = self.fatal(&format!("expected `{}`, found {}",
- token_str,
- this_token_str));
-
- let sp = if self.token == token::Token::Eof {
- // EOF, don't want to point at the following char, but rather the last token
- self.prev_span
- } else {
- self.sess.source_map().next_point(self.prev_span)
- };
- let label_exp = format!("expected `{}`", token_str);
- match self.recover_closing_delimiter(&[t.clone()], err) {
- Err(e) => err = e,
- Ok(recovered) => {
- return Ok(recovered);
- }
- }
- let cm = self.sess.source_map();
- match (cm.lookup_line(self.span.lo()), cm.lookup_line(sp.lo())) {
- (Ok(ref a), Ok(ref b)) if a.line == b.line => {
- // When the spans are in the same line, it means that the only content
- // between them is whitespace, point only at the found token.
- err.span_label(self.span, label_exp);
- }
- _ => {
- err.span_label(sp, label_exp);
- err.span_label(self.span, "unexpected token");
- }
- }
- Err(err)
+ self.unexpected_try_recover(t)
}
} else {
self.expect_one_of(slice::from_ref(t), &[])
// | expected one of 8 possible tokens here
err.span_label(self.span, label_exp);
}
- _ if self.prev_span == syntax_pos::DUMMY_SP => {
+ _ if self.prev_span == DUMMY_SP => {
// Account for macro context where the previous span might not be
// available to avoid incorrect output (#54841).
err.span_label(self.span, "unexpected token");
path = self.parse_path(PathStyle::Type)?;
path_span = path_lo.to(self.prev_span);
} else {
- path = ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP };
+ path = ast::Path { segments: Vec::new(), span: DUMMY_SP };
path_span = self.span.to(self.span);
}
}
Err(mut err) => {
self.cancel(&mut err);
- let msg = format!("expected expression, found {}",
- self.this_token_descr());
- let mut err = self.fatal(&msg);
- let sp = self.sess.source_map().start_point(self.span);
- if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow()
- .get(&sp)
- {
- self.sess.expr_parentheses_needed(&mut err, *sp, None);
- }
- err.span_label(self.span, "expected expression");
- return Err(err);
+ return Err(self.expected_expression_found());
}
}
}
let (span, e) = self.interpolated_or_expr_span(e)?;
(lo.to(span), ExprKind::AddrOf(m, e))
}
- token::Ident(..) if self.token.is_keyword(kw::In) => {
- self.bump();
- let place = self.parse_expr_res(
- Restrictions::NO_STRUCT_LITERAL,
- None,
- )?;
- let blk = self.parse_block()?;
- let span = blk.span;
- let blk_expr = self.mk_expr(span, ExprKind::Block(blk, None), ThinVec::new());
- (lo.to(span), ExprKind::ObsoleteInPlace(place, blk_expr))
- }
token::Ident(..) if self.token.is_keyword(kw::Box) => {
self.bump();
let e = self.parse_prefix_expr(None);
self.mk_expr(span, binary, ThinVec::new())
}
AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs), ThinVec::new()),
- AssocOp::ObsoleteInPlace =>
- self.mk_expr(span, ExprKind::ObsoleteInPlace(lhs, rhs), ThinVec::new()),
AssocOp::AssignOp(k) => {
let aop = match k {
token::Plus => BinOpKind::Add,
String::new(),
Applicability::MachineApplicable,
);
- err.note("if you meant to use emplacement syntax, it is obsolete (for now, anyway)");
- err.note("for more information on the status of emplacement syntax, see <\
- https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>");
err.emit();
}
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
where_clause: WhereClause {
id: ast::DUMMY_NODE_ID,
predicates: Vec::new(),
- span: syntax_pos::DUMMY_SP,
+ span: DUMMY_SP,
},
span: span_lo.to(self.prev_span),
})
let mut where_clause = WhereClause {
id: ast::DUMMY_NODE_ID,
predicates: Vec::new(),
- span: syntax_pos::DUMMY_SP,
+ span: DUMMY_SP,
};
if !self.eat_keyword(kw::Where) {
Ident::with_empty_ctxt(sym::warn_directory_ownership)),
tokens: TokenStream::empty(),
is_sugared_doc: false,
- span: syntax_pos::DUMMY_SP,
+ span: DUMMY_SP,
};
attr::mark_known(&attr);
attrs.push(attr);
Ok((id, ItemKind::Mod(module), Some(attrs)))
} else {
let placeholder = ast::Mod {
- inner: syntax_pos::DUMMY_SP,
+ inner: DUMMY_SP,
items: Vec::new(),
inline: false
};
/// Parses the part of an enum declaration following the `{`.
fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult<'a, EnumDef> {
let mut variants = Vec::new();
- let mut all_nullary = true;
let mut any_disr = vec![];
while self.token != token::CloseDelim(token::Brace) {
let variant_attrs = self.parse_outer_attributes()?;
let ident = self.parse_ident()?;
if self.check(&token::OpenDelim(token::Brace)) {
// Parse a struct variant.
- all_nullary = false;
let (fields, recovered) = self.parse_record_struct_body()?;
struct_def = VariantData::Struct(fields, recovered);
} else if self.check(&token::OpenDelim(token::Paren)) {
- all_nullary = false;
struct_def = VariantData::Tuple(
self.parse_tuple_struct_body()?,
ast::DUMMY_NODE_ID,
}
}
self.expect(&token::CloseDelim(token::Brace))?;
- if !any_disr.is_empty() && !all_nullary {
- let mut err = self.struct_span_err(
- any_disr.clone(),
- "discriminator values can only be used with a field-less enum",
- );
- for sp in any_disr {
- err.span_label(sp, "only valid in field-less enums");
- }
- err.emit();
- }
+ self.maybe_report_invalid_custom_discriminants(any_disr, &variants);
Ok(ast::EnumDef { variants })
}
for (index, input) in decl.inputs.iter_mut().enumerate() {
let id = ast::DUMMY_NODE_ID;
let span = input.pat.span;
+ let desugared_span = self.sess.source_map()
+ .mark_span_with_reason(CompilerDesugaringKind::Async, span, None);
// Construct a name for our temporary argument.
let name = format!("__arg{}", index);
// this would affect the input to procedural macros, but they can have
// their span marked as being the result of a compiler desugaring so
// that they aren't linted against.
- input.pat.span = self.sess.source_map().mark_span_with_reason(
- CompilerDesugaringKind::Async, span, None);
+ input.pat.span = desugared_span;
(binding_mode, ident, true)
}
node: PatKind::Ident(
BindingMode::ByValue(Mutability::Immutable), ident, None,
),
- span,
+ span: desugared_span,
}),
source: ArgSource::AsyncFn(input.pat.clone()),
})
pat: P(Pat {
id,
node: PatKind::Ident(binding_mode, ident, None),
- span,
+ span: desugared_span,
}),
// We explicitly do not specify the type for this statement. When the user's
// argument type is `impl Trait` then this would require the
self.word_space("box")?;
self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)?;
}
- ast::ExprKind::ObsoleteInPlace(ref place, ref expr) => {
- let prec = AssocOp::ObsoleteInPlace.precedence() as i8;
- self.print_expr_maybe_paren(place, prec + 1)?;
- self.s.space()?;
- self.word_space("<-")?;
- self.print_expr_maybe_paren(expr, prec)?;
- }
ast::ExprKind::Array(ref exprs) => {
self.print_expr_vec(&exprs[..], attrs)?;
}
GreaterEqual,
/// `=`
Assign,
- /// `<-`
- ObsoleteInPlace,
/// `?=` where ? is one of the BinOpToken
AssignOp(BinOpToken),
/// `as`
use AssocOp::*;
match *t {
Token::BinOpEq(k) => Some(AssignOp(k)),
- Token::LArrow => Some(ObsoleteInPlace),
Token::Eq => Some(Assign),
Token::BinOp(BinOpToken::Star) => Some(Multiply),
Token::BinOp(BinOpToken::Slash) => Some(Divide),
LAnd => 6,
LOr => 5,
DotDot | DotDotEq => 4,
- ObsoleteInPlace => 3,
Assign | AssignOp(_) => 2,
}
}
use AssocOp::*;
// NOTE: it is a bug to have an operators that has same precedence but different fixities!
match *self {
- ObsoleteInPlace | Assign | AssignOp(_) => Fixity::Right,
+ Assign | AssignOp(_) => Fixity::Right,
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd |
BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual |
LAnd | LOr | Colon => Fixity::Left,
use AssocOp::*;
match *self {
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
- ObsoleteInPlace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add |
+ Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add |
Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr |
DotDot | DotDotEq | Colon => false
}
pub fn is_assign_like(&self) -> bool {
use AssocOp::*;
match *self {
- Assign | AssignOp(_) | ObsoleteInPlace => true,
+ Assign | AssignOp(_) => true,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide |
Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd |
LOr | DotDot | DotDotEq | Colon => false
BitOr => Some(BinOpKind::BitOr),
LAnd => Some(BinOpKind::And),
LOr => Some(BinOpKind::Or),
- ObsoleteInPlace | Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None
+ Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None
}
}
Binary(BinOpKind),
- ObsoleteInPlace,
Cast,
Type,
// Binop-like expr kinds, handled by `AssocOp`.
ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8,
- ExprPrecedence::ObsoleteInPlace => AssocOp::ObsoleteInPlace.precedence() as i8,
ExprPrecedence::Cast => AssocOp::As.precedence() as i8,
ExprPrecedence::Type => AssocOp::Colon.precedence() as i8,
ExprKind::Box(ref subexpression) => {
visitor.visit_expr(subexpression)
}
- ExprKind::ObsoleteInPlace(ref place, ref subexpression) => {
- visitor.visit_expr(place);
- visitor.visit_expr(subexpression)
- }
ExprKind::Array(ref subexpressions) => {
walk_list!(visitor, visit_expr, subexpressions);
}
if p2.token != token::Eof {
let mut extra_tts = p2.parse_all_token_trees()?;
extra_tts.extend(tts[first_colon..].iter().cloned());
- p = parse::stream_to_parser(cx.parse_sess, extra_tts.into_iter().collect());
+ p = parse::stream_to_parser(
+ cx.parse_sess,
+ extra_tts.into_iter().collect(),
+ Some("inline assembly"),
+ );
}
asm = s;
let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
let msg = "proc-macro derive produced unparseable tokens";
- let mut parser = parse::stream_to_parser(ecx.parse_sess, stream);
+ let mut parser = parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
let mut items = vec![];
loop {
// min-lldb-version: 310
+// ignore-cdb: Fails with exit code 0xc0000135 ("the application failed to initialize properly")
// aux-build:issue-13213-aux.rs
-// ignore-windows failing on win32 bot
// ignore-freebsd: gdb package too new
-// ignore-test // Test temporarily ignored due to debuginfo tests being disabled, see PR 47155
+// only-cdb // "Temporarily" ignored on GDB/LLDB due to debuginfo tests being disabled, see PR 47155
// ignore-android: FIXME(#10381)
// compile-flags:-g
// min-gdb-version 7.7
// lldb-check:[...]$5 = None
+// === CDB TESTS ==================================================================================
+
+// cdb-command: g
+
+// cdb-command: dx slice,d
+// cdb-check:slice,d [...]
+// NOTE: While slices have a .natvis entry that works in VS & VS Code, it fails in CDB 10.0.18362.1
+
+// cdb-command: dx vec,d
+// cdb-check:vec,d [...] : { size=4 } [Type: [...]::Vec<u64>]
+// cdb-check: [size] : 4 [Type: [...]]
+// cdb-check: [capacity] : [...] [Type: [...]]
+// cdb-check: [0] : 4 [Type: unsigned __int64]
+// cdb-check: [1] : 5 [Type: unsigned __int64]
+// cdb-check: [2] : 6 [Type: unsigned __int64]
+// cdb-check: [3] : 7 [Type: unsigned __int64]
+
+// cdb-command: dx str_slice
+// cdb-check:str_slice [...]
+// NOTE: While string slices have a .natvis entry that works in VS & VS Code, it fails in CDB
+
+// cdb-command: dx string
+// cdb-check:string : "IAMA string!" [Type: [...]::String]
+// cdb-check: [<Raw View>] [Type: [...]::String]
+// cdb-check: [size] : 0xc [Type: [...]]
+// cdb-check: [capacity] : 0xc [Type: [...]]
+// cdb-check: [0] : 73 'I' [Type: char]
+// cdb-check: [1] : 65 'A' [Type: char]
+// cdb-check: [2] : 77 'M' [Type: char]
+// cdb-check: [3] : 65 'A' [Type: char]
+// cdb-check: [4] : 32 ' ' [Type: char]
+// cdb-check: [5] : 115 's' [Type: char]
+// cdb-check: [6] : 116 't' [Type: char]
+// cdb-check: [7] : 114 'r' [Type: char]
+// cdb-check: [8] : 105 'i' [Type: char]
+// cdb-check: [9] : 110 'n' [Type: char]
+// cdb-check: [10] : 103 'g' [Type: char]
+// cdb-check: [11] : 33 '!' [Type: char]
+
+// cdb-command: dx os_string
+// cdb-check:os_string [Type: [...]::OsString]
+// NOTE: OsString doesn't have a .natvis entry yet.
+
+// cdb-command: dx some
+// cdb-check:some : { Some 8 } [Type: [...]::Option<i16>]
+// cdb-command: dx none
+// cdb-check:none : { None } [Type: [...]::Option<i64>]
+// cdb-command: dx some_string
+// cdb-check:some_string : { Some "IAMA optional string!" } [[...]::Option<[...]::String>]
+
#![allow(unused_variables)]
use std::ffi::OsString;
// lldb-command:print x
// lldb-check:[...]$0 = 5
+// === CDB TESTS ==================================================================================
+
+// cdb-command:g
+
+// cdb-command:dx x
+// cdb-check:string [...] : 5 [Type: [...]]
+
fn main() {
let x = 1;
#[cfg(cfail2)]
pub trait T2: T1 { }
//[cfail2]~^ ERROR cycle detected when computing the supertraits of `T2`
-//[cfail2]~| ERROR cycle detected when computing the supertraits of `T2`
pub trait T1: T2 { }
fn provide(&self, providers: &mut Providers) {
rustc_codegen_utils::symbol_names::provide(providers);
- providers.target_features_whitelist = |_tcx, _cnum| {
- Default::default() // Just a dummy
+ providers.target_features_whitelist = |tcx, _cnum| {
+ tcx.arena.alloc(Default::default()) // Just a dummy
};
providers.is_reachable_non_generic = |_tcx, _defid| true;
providers.exported_symbols = |_tcx, _crate| Arc::new(Vec::new());
where T : Trait,
T : Add<T::Item>
//~^ ERROR cycle detected
- //~| ERROR associated type `Item` not found for `T`
{
data: T
}
LL | T : Add<T::Item>
| ^^^^^^^
-error[E0220]: associated type `Item` not found for `T`
- --> $DIR/cycle-projection-based-on-where-clause.rs:17:19
- |
-LL | T : Add<T::Item>
- | ^^^^^^^ associated type `Item` not found
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-Some errors have detailed explanations: E0220, E0391.
-For more information about an error, try `rustc --explain E0220`.
+For more information about this error, try `rustc --explain E0391`.
trait T : Iterator<Item=Self::Item>
//~^ ERROR cycle detected
-//~| ERROR associated type `Item` not found for `Self`
{}
fn main() {}
|
LL | / trait T : Iterator<Item=Self::Item>
LL | |
-LL | |
LL | | {}
| |__^
|
|
LL | / trait T : Iterator<Item=Self::Item>
LL | |
-LL | |
LL | | {}
| |__^
-error[E0220]: associated type `Item` not found for `Self`
- --> $DIR/issue-20772.rs:1:25
- |
-LL | trait T : Iterator<Item=Self::Item>
- | ^^^^^^^^^^ associated type `Item` not found
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-Some errors have detailed explanations: E0220, E0391.
-For more information about an error, try `rustc --explain E0220`.
+For more information about this error, try `rustc --explain E0391`.
fn foo<T: Trait<A = T::B>>() { }
//~^ ERROR cycle detected
-//~| ERROR associated type `B` not found for `T`
fn main() { }
LL | fn foo<T: Trait<A = T::B>>() { }
| ^^^^
-error[E0220]: associated type `B` not found for `T`
- --> $DIR/issue-21177.rs:6:21
- |
-LL | fn foo<T: Trait<A = T::B>>() { }
- | ^^^^ associated type `B` not found
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-Some errors have detailed explanations: E0220, E0391.
-For more information about an error, try `rustc --explain E0220`.
+For more information about this error, try `rustc --explain E0391`.
const A: i32 = B; //~ ERROR cycle detected
-//~^ ERROR cycle detected
const B: i32 = A;
LL | const A: i32 = B;
| ^
note: ...which requires const checking if rvalue is promotable to static `B`...
- --> $DIR/issue-23302-3.rs:4:1
+ --> $DIR/issue-23302-3.rs:3:1
|
LL | const B: i32 = A;
| ^^^^^^^^^^^^^^^^^
note: ...which requires checking which parts of `B` are promotable to static...
- --> $DIR/issue-23302-3.rs:4:16
+ --> $DIR/issue-23302-3.rs:3:16
|
LL | const B: i32 = A;
| ^
= note: ...which again requires const checking if rvalue is promotable to static `A`, completing the cycle
= note: cycle used when running analysis passes on this crate
-error[E0391]: cycle detected when processing `A`
- --> $DIR/issue-23302-3.rs:1:16
- |
-LL | const A: i32 = B;
- | ^
- |
-note: ...which requires processing `B`...
- --> $DIR/issue-23302-3.rs:4:16
- |
-LL | const B: i32 = A;
- | ^
- = note: ...which again requires processing `A`, completing the cycle
-note: cycle used when processing `A`
- --> $DIR/issue-23302-3.rs:1:1
- |
-LL | const A: i32 = B;
- | ^^^^^^^^^^^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0391`.
fn main() {
let x: u8 = 256;
//~^ error: literal out of range for `u8`
+
+ for _ in 0..256u8 {}
+ //~^ error: range endpoint is out of range for `u8`
}
|
= note: #[deny(overflowing_literals)] on by default
-error: aborting due to previous error
+error: range endpoint is out of range for `u8`
+ --> $DIR/deny-overflowing-literals.rs:5:14
+ |
+LL | for _ in 0..256u8 {}
+ | ^^^^^^^^ help: use an inclusive range instead: `0..=255u8`
+
+error: aborting due to 2 previous errors
LL | format!(struct);
| ^^^^^^ expected expression
-error: expected expression, found `<eof>`
- --> $DIR/format-parse-errors.rs:4:23
+error: expected expression, found end of macro arguments
+ --> $DIR/format-parse-errors.rs:4:24
|
LL | format!("s", name =);
- | ^ expected expression
+ | ^ expected expression
-error: expected `=`, found `<eof>`
- --> $DIR/format-parse-errors.rs:5:29
+error: expected `=`, found end of macro arguments
+ --> $DIR/format-parse-errors.rs:5:32
|
LL | format!("s", foo = foo, bar);
- | ^^^ expected `=`
+ | ^ expected `=`
error: expected expression, found keyword `struct`
--> $DIR/format-parse-errors.rs:6:24
-#[derive(Copy(Bad))]
-//~^ ERROR expected one of `)`, `,`, or `::`, found `(`
+#[derive(Copy(Bad))] //~ ERROR expected one of `)`, `,`, or `::`, found `(`
struct Test1;
-#[derive(Copy="bad")]
-//~^ ERROR expected one of `)`, `,`, or `::`, found `=`
+#[derive(Copy="bad")] //~ ERROR expected one of `)`, `,`, or `::`, found `=`
struct Test2;
-#[derive()]
-//~^ WARNING empty trait list
+#[derive()] //~ WARNING empty trait list
struct Test3;
-#[derive]
-//~^ ERROR attribute must be of the form
+#[derive] //~ ERROR attribute must be of the form `#[derive(Trait1, Trait2, ...)]`
struct Test4;
fn main() {}
| ^ expected one of `)`, `,`, or `::` here
error: expected one of `)`, `,`, or `::`, found `=`
- --> $DIR/malformed-derive-entry.rs:5:14
+ --> $DIR/malformed-derive-entry.rs:4:14
|
LL | #[derive(Copy="bad")]
| ^ expected one of `)`, `,`, or `::` here
warning: empty trait list in `derive`
- --> $DIR/malformed-derive-entry.rs:9:1
+ --> $DIR/malformed-derive-entry.rs:7:1
|
LL | #[derive()]
| ^^^^^^^^^^^
error: attribute must be of the form `#[derive(Trait1, Trait2, ...)]`
- --> $DIR/malformed-derive-entry.rs:13:1
+ --> $DIR/malformed-derive-entry.rs:10:1
|
LL | #[derive]
| ^^^^^^^^^
-#[cfg_attr] //~ ERROR expected `(`, found `<eof>`
+#[cfg_attr] //~ ERROR expected `(`, found end of attribute
struct S1;
#[cfg_attr = ""] //~ ERROR expected `(`, found `=`
-error: expected `(`, found `<eof>`
+error: expected `(`, found end of attribute
+ --> $DIR/malformed-special-attrs.rs:1:1
+ |
+LL | #[cfg_attr]
+ | ^ expected `(`
error: expected `(`, found `=`
--> $DIR/malformed-special-attrs.rs:4:12
|
-LL | #[cfg_attr]
- | - expected `(`
-...
LL | #[cfg_attr = ""]
- | ^ unexpected token
+ | ^ expected `(`
error: attribute must be of the form `#[derive(Trait1, Trait2, ...)]`
--> $DIR/malformed-special-attrs.rs:7:1
--- /dev/null
+// Test that we don't show variables with from async fn desugaring
+
+// edition:2018
+#![feature(async_await)]
+
+async fn async_fn(&ref mut s: &[i32]) {}
+//~^ ERROR cannot borrow data in a `&` reference as mutable [E0596]
+
+fn main() {}
--- /dev/null
+error[E0596]: cannot borrow data in a `&` reference as mutable
+ --> $DIR/dont-print-desugared-async.rs:6:20
+ |
+LL | async fn async_fn(&ref mut s: &[i32]) {}
+ | -^^^^^^^^^
+ | ||
+ | |cannot borrow as mutable through `&` reference
+ | help: consider changing this to be a mutable reference: `&mut ref mut s`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
--- /dev/null
+// Test that we don't show variables with from for loop desugaring
+
+fn for_loop(s: &[i32]) {
+ for &ref mut x in s {}
+ //~^ ERROR cannot borrow data in a `&` reference as mutable [E0596]
+}
+
+struct D<'a>(&'a ());
+
+impl Drop for D<'_> {
+ fn drop(&mut self) {}
+}
+
+fn for_loop_dropck(v: Vec<D<'static>>) {
+ for ref mut d in v {
+ let y = ();
+ *d = D(&y); //~ ERROR `y` does not live long enough
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0596]: cannot borrow data in a `&` reference as mutable
+ --> $DIR/dont-print-desugared.rs:4:10
+ |
+LL | for &ref mut x in s {}
+ | -^^^^^^^^^
+ | ||
+ | |cannot borrow as mutable through `&` reference
+ | help: consider changing this to be a mutable reference: `&mut ref mut x`
+
+error[E0597]: `y` does not live long enough
+ --> $DIR/dont-print-desugared.rs:17:16
+ |
+LL | for ref mut d in v {
+ | - a temporary with access to the borrow is created here ...
+LL | let y = ();
+LL | *d = D(&y);
+ | ^^ borrowed value does not live long enough
+LL | }
+ | -
+ | |
+ | `y` dropped here while still borrowed
+ | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0596, E0597.
+For more information about an error, try `rustc --explain E0596`.
-error: emplacement syntax is obsolete (for now, anyway)
- --> $DIR/bad.rs:9:5
- |
-LL | x <- y;
- | ^^^^^^
- |
- = note: for more information, see <https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>
-
-error: emplacement syntax is obsolete (for now, anyway)
+error: expected expression, found keyword `in`
--> $DIR/bad.rs:10:5
|
LL | in(foo) { bar };
- | ^^^^^^^^^^^^^^^
+ | ^^ expected expression
+
+error[E0282]: type annotations needed
+ --> $DIR/bad.rs:9:8
+ |
+LL | let (x, y, foo, bar);
+ | ---------------- consider giving the pattern a type
+LL | x <- y;
+ | ^^^ cannot infer type
|
- = note: for more information, see <https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>
+ = note: type must be known at this point
error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0282`.
// Check that `<-` and `in` syntax gets a hard error.
-// revisions: good bad
-//[good] run-pass
-
-#[cfg(bad)]
-fn main() {
- let (x, y, foo, bar);
- x <- y; //[bad]~ ERROR emplacement syntax is obsolete
- in(foo) { bar }; //[bad]~ ERROR emplacement syntax is obsolete
+fn foo() {
+ let (x, y) = (0, 0);
+ x <- y; //~ ERROR expected one of
+ //~^ ERROR mismatched types
}
-#[cfg(good)]
fn main() {
+ let (foo, bar) = (0, 0);
+ in(foo) { bar }; //~ ERROR expected expression, found keyword `in`
}
--- /dev/null
+error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `<-`
+ --> $DIR/bad.rs:5:7
+ |
+LL | x <- y;
+ | ^^ expected one of 8 possible tokens here
+
+error: expected expression, found keyword `in`
+ --> $DIR/bad.rs:11:5
+ |
+LL | in(foo) { bar };
+ | ^^ expected expression
+
+error[E0308]: mismatched types
+ --> $DIR/bad.rs:5:5
+ |
+LL | fn foo() {
+ | - possibly return type missing here?
+LL | let (x, y) = (0, 0);
+LL | x <- y;
+ | ^ expected (), found integer
+ |
+ = note: expected type `()`
+ found type `{integer}`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
| ---^^
| |
| help: remove the duplicated `in`
- |
- = note: if you meant to use emplacement syntax, it is obsolete (for now, anyway)
- = note: for more information on the status of emplacement syntax, see <https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>
error: aborting due to previous error
enum X {
A = 3,
- //~^ ERROR discriminator values can only be used with a field-less enum
+ //~^ ERROR custom discriminant values are not allowed in enums with fields
B(usize)
}
-error: discriminator values can only be used with a field-less enum
+error: custom discriminant values are not allowed in enums with fields
--> $DIR/issue-17383.rs:2:9
|
LL | A = 3,
- | ^ only valid in field-less enums
+ | ^ invalid custom discriminant
+LL |
+LL | B(usize)
+ | -------- variant with a field defined here
error: aborting due to previous error
--- /dev/null
+fn main() {
+ let message = "world";
+ println!("Hello, {}", message/); //~ ERROR expected expression
+}
--- /dev/null
+error: expected expression, found end of macro arguments
+ --> $DIR/bad-macro-argument.rs:3:35
+ |
+LL | println!("Hello, {}", message/);
+ | ^ expected expression
+
+error: aborting due to previous error
+
enum Color {
Red = 0xff0000,
- //~^ ERROR discriminator values can only be used with a field-less enum
+ //~^ ERROR custom discriminant values are not allowed in enums with fields
Green = 0x00ff00,
Blue = 0x0000ff,
Black = 0x000000,
White = 0xffffff,
Other(usize),
+ Other2(usize, usize),
}
fn main() {}
-error: discriminator values can only be used with a field-less enum
+error: custom discriminant values are not allowed in enums with fields
--> $DIR/tag-variant-disr-non-nullary.rs:2:11
|
LL | Red = 0xff0000,
- | ^^^^^^^^ only valid in field-less enums
+ | ^^^^^^^^ invalid custom discriminant
LL |
LL | Green = 0x00ff00,
- | ^^^^^^^^ only valid in field-less enums
+ | ^^^^^^^^ invalid custom discriminant
LL | Blue = 0x0000ff,
- | ^^^^^^^^ only valid in field-less enums
+ | ^^^^^^^^ invalid custom discriminant
LL | Black = 0x000000,
- | ^^^^^^^^ only valid in field-less enums
+ | ^^^^^^^^ invalid custom discriminant
LL | White = 0xffffff,
- | ^^^^^^^^ only valid in field-less enums
+ | ^^^^^^^^ invalid custom discriminant
+LL | Other(usize),
+ | ------------ variant with a field defined here
+LL | Other2(usize, usize),
+ | -------------------- variant with fields defined here
error: aborting due to previous error
fn main() {
let x = -5;
- if x<-1 {
- //~^ ERROR emplacement syntax is obsolete
+ if x<-1 { //~ ERROR expected `{`, found `<-`
println!("ok");
}
}
-error: emplacement syntax is obsolete (for now, anyway)
- --> $DIR/placement-syntax.rs:3:8
+error: expected `{`, found `<-`
+ --> $DIR/placement-syntax.rs:3:9
|
LL | if x<-1 {
- | ^^^^
- |
- = note: for more information, see <https://github.com/rust-lang/rust/issues/27779#issuecomment-378416911>
-help: if you meant to write a comparison against a negative value, add a space in between `<` and `-`
- |
-LL | if x< -1 {
- | ^^^
+ | -- ^^ expected `{`
+ | |
+ | this `if` statement has a condition, but no block
error: aborting due to previous error
fn main() {
let _ = #[no_output] "Hello, world!";
- //~^ ERROR expected expression, found `<eof>`
+ //~^ ERROR expected expression, found end of macro arguments
let _ = #[duplicate] "Hello, world!";
//~^ ERROR macro expansion ignores token `,` and any following
-error: expected expression, found `<eof>`
+error: expected expression, found end of macro arguments
--> $DIR/attr-invalid-exprs.rs:11:13
|
LL | let _ = #[no_output] "Hello, world!";
--- /dev/null
+fn suggestion(opt: &mut Option<String>) {
+ opt = None; //~ ERROR mismatched types
+}
+
+fn no_suggestion(opt: &mut Result<String, ()>) {
+ opt = None //~ ERROR mismatched types
+}
+
+fn suggestion2(opt: &mut Option<String>) {
+ opt = Some(String::new())//~ ERROR mismatched types
+}
+
+fn no_suggestion2(opt: &mut Option<String>) {
+ opt = Some(42)//~ ERROR mismatched types
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/mut-ref-reassignment.rs:2:11
+ |
+LL | opt = None;
+ | ^^^^ expected mutable reference, found enum `std::option::Option`
+ |
+ = note: expected type `&mut std::option::Option<std::string::String>`
+ found type `std::option::Option<_>`
+help: consider dereferencing here to assign to the mutable borrowed piece of memory
+ |
+LL | *opt = None;
+ | ^^^^
+
+error[E0308]: mismatched types
+ --> $DIR/mut-ref-reassignment.rs:6:11
+ |
+LL | opt = None
+ | ^^^^ expected mutable reference, found enum `std::option::Option`
+ |
+ = note: expected type `&mut std::result::Result<std::string::String, ()>`
+ found type `std::option::Option<_>`
+
+error[E0308]: mismatched types
+ --> $DIR/mut-ref-reassignment.rs:10:11
+ |
+LL | opt = Some(String::new())
+ | ^^^^^^^^^^^^^^^^^^^ expected mutable reference, found enum `std::option::Option`
+ |
+ = note: expected type `&mut std::option::Option<std::string::String>`
+ found type `std::option::Option<std::string::String>`
+help: consider dereferencing here to assign to the mutable borrowed piece of memory
+ |
+LL | *opt = Some(String::new())
+ | ^^^^
+
+error[E0308]: mismatched types
+ --> $DIR/mut-ref-reassignment.rs:14:11
+ |
+LL | opt = Some(42)
+ | ^^^^^^^^ expected mutable reference, found enum `std::option::Option`
+ |
+ = note: expected type `&mut std::option::Option<std::string::String>`
+ found type `std::option::Option<{integer}>`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
fn main() {
for _ in unimplemented!() as Void {}
//~^ ERROR unreachable pattern
+ //~^^ ERROR unreachable pattern
}
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
-error: aborting due to previous error
+error: unreachable pattern
+ --> $DIR/unreachable-loop-patterns.rs:20:14
+ |
+LL | for _ in unimplemented!() as Void {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
Test {
name: "webrender",
repo: "https://github.com/servo/webrender",
- sha: "57250b2b8fa63934f80e5376a29f7dcb3f759ad6",
+ sha: "cdadd068f4c7218bd983d856981d561e605270ab",
lock: None,
packages: &[],
},
pub use self::Mode::*;
+use std::ffi::OsString;
use std::fmt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
RunPass,
RunPassValgrind,
Pretty,
- DebugInfoBoth,
+ DebugInfoCdb,
+ DebugInfoGdbLldb,
DebugInfoGdb,
DebugInfoLldb,
Codegen,
pub fn disambiguator(self) -> &'static str {
// Run-pass and pretty run-pass tests could run concurrently, and if they do,
// they need to keep their output segregated. Same is true for debuginfo tests that
- // can be run both on gdb and lldb.
+ // can be run on cdb, gdb, and lldb.
match self {
Pretty => ".pretty",
+ DebugInfoCdb => ".cdb",
DebugInfoGdb => ".gdb",
DebugInfoLldb => ".lldb",
_ => "",
"run-pass" => Ok(RunPass),
"run-pass-valgrind" => Ok(RunPassValgrind),
"pretty" => Ok(Pretty),
- "debuginfo-both" => Ok(DebugInfoBoth),
+ "debuginfo-cdb" => Ok(DebugInfoCdb),
+ "debuginfo-gdb+lldb" => Ok(DebugInfoGdbLldb),
"debuginfo-lldb" => Ok(DebugInfoLldb),
"debuginfo-gdb" => Ok(DebugInfoGdb),
"codegen" => Ok(Codegen),
RunPass => "run-pass",
RunPassValgrind => "run-pass-valgrind",
Pretty => "pretty",
- DebugInfoBoth => "debuginfo-both",
+ DebugInfoCdb => "debuginfo-cdb",
+ DebugInfoGdbLldb => "debuginfo-gdb+lldb",
DebugInfoGdb => "debuginfo-gdb",
DebugInfoLldb => "debuginfo-lldb",
Codegen => "codegen",
/// Host triple for the compiler being invoked
pub host: String,
+ /// Path to / name of the Microsoft Console Debugger (CDB) executable
+ pub cdb: Option<OsString>,
+
/// Path to / name of the GDB executable
pub gdb: Option<String>,
NoMatch,
/// Match.
Match,
- /// Mode was DebugInfoBoth and this matched gdb.
+ /// Mode was DebugInfoGdbLldb and this matched gdb.
MatchGdb,
- /// Mode was DebugInfoBoth and this matched lldb.
+ /// Mode was DebugInfoGdbLldb and this matched lldb.
MatchLldb,
}
revisions: vec![],
};
- if config.mode == common::DebugInfoBoth {
+ if config.mode == common::DebugInfoGdbLldb {
if config.lldb_python_dir.is_none() {
props.ignore = props.ignore.no_lldb();
}
if config.gdb_version.is_none() {
props.ignore = props.ignore.no_gdb();
}
+ } else if config.mode == common::DebugInfoCdb {
+ if config.cdb.is_none() {
+ props.ignore = Ignore::Ignore;
+ }
}
let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
}
}
- if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoBoth) &&
+ if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoGdbLldb) &&
props.ignore.can_run_gdb() && ignore_gdb(config, ln) {
props.ignore = props.ignore.no_gdb();
}
- if (config.mode == common::DebugInfoLldb || config.mode == common::DebugInfoBoth) &&
+ if (config.mode == common::DebugInfoLldb || config.mode == common::DebugInfoGdbLldb) &&
props.ignore.can_run_lldb() && ignore_lldb(config, ln) {
props.ignore = props.ignore.no_lldb();
}
ParsedNameDirective::Match
} else {
match self.mode {
- common::DebugInfoBoth => {
+ common::DebugInfoGdbLldb => {
if name == "gdb" {
ParsedNameDirective::MatchGdb
} else if name == "lldb" {
ParsedNameDirective::NoMatch
}
},
+ common::DebugInfoCdb => if name == "cdb" {
+ ParsedNameDirective::Match
+ } else {
+ ParsedNameDirective::NoMatch
+ },
common::DebugInfoGdb => if name == "gdb" {
ParsedNameDirective::Match
} else {
use crate::common::CompareMode;
use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
use crate::common::{Config, TestPaths};
-use crate::common::{DebugInfoBoth, DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
+use crate::common::{DebugInfoCdb, DebugInfoGdbLldb, DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
use getopts::Options;
use std::env;
use std::ffi::OsString;
.optopt("", "logfile", "file to log test execution to", "FILE")
.optopt("", "target", "the target to build for", "TARGET")
.optopt("", "host", "the host to build for", "HOST")
+ .optopt(
+ "",
+ "cdb",
+ "path to CDB to use for CDB debuginfo tests",
+ "PATH",
+ )
.optopt(
"",
"gdb",
let target = opt_str2(matches.opt_str("target"));
let android_cross_path = opt_path(matches, "android-cross-path");
+ let cdb = analyze_cdb(matches.opt_str("cdb"), &target);
let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"), &target,
&android_cross_path);
let (lldb_version, lldb_native_rust) = extract_lldb_version(matches.opt_str("lldb-version"));
target_rustcflags: matches.opt_str("target-rustcflags"),
target: target,
host: opt_str2(matches.opt_str("host")),
+ cdb,
gdb,
gdb_version,
gdb_native_rust,
pub fn run_tests(config: &Config) {
if config.target.contains("android") {
- if config.mode == DebugInfoGdb || config.mode == DebugInfoBoth {
+ if config.mode == DebugInfoGdb || config.mode == DebugInfoGdbLldb {
println!(
"{} debug-info test uses tcp 5039 port.\
please reserve it",
match config.mode {
// Note that we don't need to emit the gdb warning when
- // DebugInfoBoth, so it is ok to list that here.
- DebugInfoBoth | DebugInfoLldb => {
+ // DebugInfoGdbLldb, so it is ok to list that here.
+ DebugInfoGdbLldb | DebugInfoLldb => {
if let Some(lldb_version) = config.lldb_version.as_ref() {
if is_blacklisted_lldb_version(&lldb_version[..]) {
println!(
return;
}
}
- _ => { /* proceed */ }
+
+ DebugInfoCdb | _ => { /* proceed */ }
}
// FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
&early_props,
revision.map(|s| s.as_str()),
)
- || ((config.mode == DebugInfoBoth ||
+ || ((config.mode == DebugInfoGdbLldb || config.mode == DebugInfoCdb ||
config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
&& config.target.contains("emscripten"))
|| (config.mode == DebugInfoGdb && !early_props.ignore.can_run_gdb())
revision: Option<&String>,
) -> test::TestFn {
let mut config = config.clone();
- if config.mode == DebugInfoBoth {
+ if config.mode == DebugInfoGdbLldb {
// If both gdb and lldb were ignored, then the test as a whole
// would be ignored.
if !ignore.can_run_gdb() {
}
}
+/// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
+fn is_pc_windows_msvc_target(target: &String) -> bool {
+ target.ends_with("-pc-windows-msvc")
+}
+
+fn find_cdb(target: &String) -> Option<OsString> {
+ if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
+ return None;
+ }
+
+ let pf86 = env::var_os("ProgramFiles(x86)").or(env::var_os("ProgramFiles"))?;
+ let cdb_arch = if cfg!(target_arch="x86") {
+ "x86"
+ } else if cfg!(target_arch="x86_64") {
+ "x64"
+ } else if cfg!(target_arch="aarch64") {
+ "arm64"
+ } else if cfg!(target_arch="arm") {
+ "arm"
+ } else {
+ return None; // No compatible CDB.exe in the Windows 10 SDK
+ };
+
+ let mut path = PathBuf::new();
+ path.push(pf86);
+ path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
+ path.push(cdb_arch);
+ path.push(r"cdb.exe");
+
+ if !path.exists() {
+ return None;
+ }
+
+ Some(path.into_os_string())
+}
+
+/// Returns Path to CDB
+fn analyze_cdb(cdb: Option<String>, target: &String) -> Option<OsString> {
+ cdb.map(|s| OsString::from(s)).or(find_cdb(target))
+}
+
/// Returns (Path to GDB, GDB Version, GDB has Rust Support)
fn analyze_gdb(gdb: Option<String>, target: &String, android_cross_path: &PathBuf)
-> (Option<String>, Option<u32>, bool) {
use crate::common::CompareMode;
use crate::common::{expected_output_path, UI_EXTENSIONS, UI_FIXED, UI_STDERR, UI_STDOUT};
use crate::common::{output_base_dir, output_base_name, output_testname_unique};
-use crate::common::{Codegen, CodegenUnits, DebugInfoBoth, DebugInfoGdb, DebugInfoLldb, Rustdoc};
+use crate::common::{Codegen, CodegenUnits, Rustdoc};
+use crate::common::{DebugInfoCdb, DebugInfoGdbLldb, DebugInfoGdb, DebugInfoLldb};
use crate::common::{CompileFail, Pretty, RunFail, RunPass, RunPassValgrind};
use crate::common::{Config, TestPaths};
use crate::common::{Incremental, MirOpt, RunMake, Ui, JsDocTest, Assembly};
let mut hash = DefaultHasher::new();
config.stage_id.hash(&mut hash);
- if config.mode == DebugInfoGdb || config.mode == DebugInfoBoth {
+ if config.mode == DebugInfoCdb {
+ config.cdb.hash(&mut hash);
+ }
+
+ if config.mode == DebugInfoGdb || config.mode == DebugInfoGdbLldb {
match config.gdb {
None => env::var_os("PATH").hash(&mut hash),
Some(ref s) if s.is_empty() => env::var_os("PATH").hash(&mut hash),
};
}
- if config.mode == DebugInfoLldb || config.mode == DebugInfoBoth {
+ if config.mode == DebugInfoLldb || config.mode == DebugInfoGdbLldb {
env::var_os("PATH").hash(&mut hash);
env::var_os("PYTHONPATH").hash(&mut hash);
}
RunFail => self.run_rfail_test(),
RunPassValgrind => self.run_valgrind_test(),
Pretty => self.run_pretty_test(),
- DebugInfoBoth => {
+ DebugInfoGdbLldb => {
self.run_debuginfo_gdb_test();
self.run_debuginfo_lldb_test();
},
+ DebugInfoCdb => self.run_debuginfo_cdb_test(),
DebugInfoGdb => self.run_debuginfo_gdb_test(),
DebugInfoLldb => self.run_debuginfo_lldb_test(),
Codegen => self.run_codegen_test(),
self.compose_and_run_compiler(rustc, Some(src))
}
+ fn run_debuginfo_cdb_test(&self) {
+ assert!(self.revision.is_none(), "revisions not relevant here");
+
+ let config = Config {
+ target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
+ host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
+ mode: DebugInfoCdb,
+ ..self.config.clone()
+ };
+
+ let test_cx = TestCx {
+ config: &config,
+ ..*self
+ };
+
+ test_cx.run_debuginfo_cdb_test_no_opt();
+ }
+
+ fn run_debuginfo_cdb_test_no_opt(&self) {
+ // compile test file (it should have 'compile-flags:-g' in the header)
+ let compile_result = self.compile_test();
+ if !compile_result.status.success() {
+ self.fatal_proc_rec("compilation failed!", &compile_result);
+ }
+
+ let exe_file = self.make_exe_name();
+
+ let prefixes = {
+ static PREFIXES: &'static [&'static str] = &["cdb", "cdbg"];
+ // No "native rust support" variation for CDB yet.
+ PREFIXES
+ };
+
+ // Parse debugger commands etc from test files
+ let DebuggerCommands {
+ commands,
+ check_lines,
+ breakpoint_lines,
+ ..
+ } = self.parse_debugger_commands(prefixes);
+
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
+ let mut script_str = String::with_capacity(2048);
+ script_str.push_str("version\n"); // List CDB (and more) version info in test output
+ script_str.push_str(".nvlist\n"); // List loaded `*.natvis` files, bulk of custom MSVC debug
+
+ // Set breakpoints on every line that contains the string "#break"
+ let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
+ for line in &breakpoint_lines {
+ script_str.push_str(&format!(
+ "bp `{}:{}`\n",
+ source_file_name, line
+ ));
+ }
+
+ // Append the other `cdb-command:`s
+ for line in &commands {
+ script_str.push_str(line);
+ script_str.push_str("\n");
+ }
+
+ script_str.push_str("\nqq\n"); // Quit the debugger (including remote debugger, if any)
+
+ // Write the script into a file
+ debug!("script_str = {}", script_str);
+ self.dump_output_file(&script_str, "debugger.script");
+ let debugger_script = self.make_out_name("debugger.script");
+
+ let cdb_path = &self.config.cdb.as_ref().unwrap();
+ let mut cdb = Command::new(cdb_path);
+ cdb
+ .arg("-lines") // Enable source line debugging.
+ .arg("-cf").arg(&debugger_script)
+ .arg(&exe_file);
+
+ let debugger_run_result = self.compose_and_run(
+ cdb,
+ self.config.run_lib_path.to_str().unwrap(),
+ None, // aux_path
+ None // input
+ );
+
+ if !debugger_run_result.status.success() {
+ self.fatal_proc_rec("Error while running CDB", &debugger_run_result);
+ }
+
+ self.check_debugger_output(&debugger_run_result, &check_lines);
+ }
+
fn run_debuginfo_gdb_test(&self) {
assert!(self.revision.is_none(), "revisions not relevant here");
RunPass | Ui => self.should_run_successfully(),
Incremental => self.revision.unwrap().starts_with("r"),
RunFail | RunPassValgrind | MirOpt |
- DebugInfoBoth | DebugInfoGdb | DebugInfoLldb => true,
+ DebugInfoCdb | DebugInfoGdbLldb | DebugInfoGdb | DebugInfoLldb => true,
_ => false,
};
let output_file = if will_execute {
rustc.arg(dir_opt);
}
- RunFail | RunPassValgrind | Pretty | DebugInfoBoth | DebugInfoGdb | DebugInfoLldb
- | Codegen | Rustdoc | RunMake | CodegenUnits | JsDocTest | Assembly => {
+ RunFail | RunPassValgrind | Pretty | DebugInfoCdb | DebugInfoGdbLldb | DebugInfoGdb
+ | DebugInfoLldb | Codegen | Rustdoc | RunMake | CodegenUnits | JsDocTest | Assembly => {
// do not use JSON output
}
}